tramdag.transforms¶
tramdag.transforms ¶
Univariate transforms for CausalFlowDAG nodes.
Each continuous node carries a monotone 1-D transform h (zuko-backed) that maps
the observed value to the latent scale; ordinal nodes carry a cutpoint ("ordered
logit") transform. Together with the additive shift terms they form one triangular
flow from the standard-logistic latent to the observed variables.
- continuous: \(u = h(x) + s(\mathrm{pa})\) with \(h\) Bernstein / RQ-spline / affine
on the value range scaled from the train
range_qquantiles to \([-B, B]\). - ordinal: \(P(Y \le k) = \sigma(\vartheta_k - s(\mathrm{pa}))\) with increasing cutpoints \(\vartheta\).
docs/model.md is the guide to both.
Notes
BOUND is the half-width \(B\) of the pre-scaled domain. It is fixed, because the
quantile pre-map makes one domain work for any data scale.
RANGE_Q is the quantile level of the range pre-map. The flow calibration scales
the train q/1 - q quantiles onto \([-B, B]\). marginal_init_theta maps that
domain onto the latent q/1 - q quantiles. Both must use the same level,
because they calibrate each other only then.
_CDF_EPS has two uses in the marginal starts. As a floor on an empirical CDF
value, it bounds the initial cutpoints at about \(\pm\operatorname{logit}(10^{-3})
\approx \pm 6.9\). An empty class or a saturated tail then starts implausible but
finite. As a floor on the latent-scale gap between two neighbouring cutpoints or
Bernstein control points, it keeps the sequence strictly increasing. The softplus
inverse needs this.
_U_EPS is the clamp margin of the uniform draw in StandardLogistic. The
value 1e-7 keeps u off 0 and 1 at float32 resolution. It caps \(|z|\) at about
16.1, which is past any latent that a fitted shift reaches.
StandardLogistic ¶
Standard logistic base distribution (the TRAM latent).
Source code in src/tramdag/transforms.py
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | |
log_prob
staticmethod
¶
log_prob(z)
Give the log density at z.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
z
|
Tensor
|
Evaluation points. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The log density, same shape as |
Source code in src/tramdag/transforms.py
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | |
sample
staticmethod
¶
sample(shape, device=None, generator=None)
Draw samples of the given shape.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shape
|
tuple[int, ...]
|
Shape of the sample tensor. |
required |
device
|
device | str | None
|
Target device, by default |
None
|
generator
|
Generator | None
|
Random source for a reproducible draw, by default |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
The samples. |
Source code in src/tramdag/transforms.py
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | |
icdf
staticmethod
¶
icdf(u)
Give the quantile at probability u.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
u
|
Tensor
|
Probabilities in (0, 1). Clamped off 0 and 1 by |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The quantiles, same shape as |
Source code in src/tramdag/transforms.py
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | |
BernsteinUT ¶
Bases: _ScaledUT
TRAM-style Bernstein polynomial transform (zuko BernsteinTransform).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_coeffs
|
int
|
Number of unconstrained coefficients, by default 20. The polynomial
has degree |
20
|
range_q
|
float
|
Domain quantile level, see |
RANGE_Q
|
Source code in src/tramdag/transforms.py
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 | |
marginal_init_theta ¶
marginal_init_theta(column)
Give the unconstrained Bernstein coefficients of the marginal start.
With column the control points follow the node's empirical
marginal: control point \(k\) is \(\operatorname{logit} \hat F(y_k)\) at the
value \(y_k\) sitting at \(k/\text{order}\) of the pre-scaled domain, so
the polynomial starts as the Bernstein approximation of
\(\operatorname{logit} \hat F(y)\). Without it the control points are
equally spaced, the plain linear map from the pre-scaled domain
\([-B, B]\) onto \([\operatorname{logit} q, \operatorname{logit}(1-q)]\) with
\(q\) = range_q.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
column
|
ndarray | None
|
The node's raw training column. |
required |
Raises:
| Type | Description |
|---|---|
ValueError
|
With |
Returns:
| Type | Description |
|---|---|
Tensor
|
The coefficients, shape |
Notes
The unconstrained coefficients come from inverting zuko's
BernsteinTransform._constrain_theta (a cumsum of softplus
differences with the first two and the last two control points tied,
hence the two averaged pairs below).
Source code in src/tramdag/transforms.py
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 | |
SplineUT ¶
Bases: _ScaledUT
Monotone rational-quadratic spline (zuko MonotonicRQSTransform).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bins
|
int
|
Number of spline bins, by default 8. |
8
|
range_q
|
float
|
Domain quantile level, see |
RANGE_Q
|
Source code in src/tramdag/transforms.py
615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 | |
AffineUT ¶
Bases: _ScaledUT
Monotone affine transform: the node-conditional is a logistic GLM.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range_q
|
float
|
Domain quantile level, see |
RANGE_Q
|
Source code in src/tramdag/transforms.py
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 | |
ordinal_bounds ¶
ordinal_bounds(theta_tilde, shift, y)
Give the shifted cutpoint interval of each observed level.
scores.py reads these bounds to form the latent-scale derivative.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
theta_tilde
|
Tensor
|
Unconstrained cutpoint parameters, shape |
required |
shift
|
Tensor
|
The node's shift, shape |
required |
y
|
Tensor
|
Observed levels |
required |
Returns:
| Type | Description |
|---|---|
tuple[Tensor, Tensor]
|
The lower and upper shifted cutpoint of each row's level, |
Source code in src/tramdag/transforms.py
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
ordinal_cutpoints ¶
ordinal_cutpoints(theta_tilde)
Constrain unconstrained parameters to increasing cutpoints.
The cutpoints are \(\tilde\vartheta_0\) followed by \(\tilde\vartheta_0 + \mathrm{cumsum}(\exp \tilde\vartheta_{1:})\), with \(\pm\infty\) at both ends.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
theta_tilde
|
Tensor
|
Unconstrained cutpoint parameters, shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Increasing cutpoints with |
Notes
For K == 2, theta_tilde[:, 1:] is empty and its cumsum has shape
(n, 0). This case needs no special treatment.
Source code in src/tramdag/transforms.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | |
ordinal_marginal_init_theta ¶
ordinal_marginal_init_theta(counts)
Give the unconstrained cutpoint parameters that match class counts.
The marginal \(P(Y \le k) = \sigma(\vartheta_k)\) of the result matches the empirical class frequencies.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
counts
|
array - like
|
Per-class count vector, length K. An empty class is handled by
|
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The unconstrained parameters |
Notes
Inverts ordinal_cutpoints: the targets are
\(c_k = \operatorname{logit} \hat F(k)\) from the empirical CDF, clamped off 0
and 1.
Source code in src/tramdag/transforms.py
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | |
ordinal_log_prob ¶
ordinal_log_prob(theta_tilde, shift, y)
Give \(\log P(Y = y \mid \vartheta, s)\).
The model is \(P(Y \le k) = \sigma(\vartheta_k - s)\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
theta_tilde
|
Tensor
|
Unconstrained cutpoint parameters, shape |
required |
shift
|
Tensor
|
Total shift, shape |
required |
y
|
Tensor
|
Observed levels in |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The log probabilities, shape |
Notes
Computed in log-space as logsigmoid(b) + log1mexp(logsigmoid(a) -
logsigmoid(b)), taking per element the better-conditioned side (the CDF
or the survival side). Do not replace this with the difference of two
sigmoids: that form has exactly zero gradient once the sigmoids saturate
in float32, and a node stuck there never recovers.
The survival side is the CDF side of the negated, swapped bounds. One evaluation with flipped inputs replaces the computation of both sides and the selection between them.
Source code in src/tramdag/transforms.py
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
ordinal_pmf ¶
ordinal_pmf(theta_tilde, shift)
Give the class probabilities of every row.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
theta_tilde
|
Tensor
|
Unconstrained cutpoint parameters, shape |
required |
shift
|
Tensor
|
Total shift, shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The class probabilities, shape |
Source code in src/tramdag/transforms.py
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 | |
ordinal_sample ¶
ordinal_sample(theta_tilde, shift, z)
Map latents to ordinal levels.
The rule is \(x = \#\{j : \vartheta_j - s < z\}\) over the finite cutpoints.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
theta_tilde
|
Tensor
|
Unconstrained cutpoint parameters, shape |
required |
shift
|
Tensor
|
Total shift, shape |
required |
z
|
Tensor
|
Latent values, shape |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The levels as floats, shape |
Source code in src/tramdag/transforms.py
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | |
ordinal_abduct ¶
ordinal_abduct(theta_tilde, shift, y, generator=None)
Abduct the latent of an ordinal node (the abduction step).
The latent is drawn from the standard logistic, truncated to the interval that is consistent with the observed level.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
theta_tilde
|
Tensor
|
Unconstrained cutpoint parameters, shape |
required |
shift
|
Tensor
|
Total shift, shape |
required |
y
|
Tensor
|
Observed levels in |
required |
generator
|
Generator | None
|
Random source for the truncated draw, by default |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
The latents, shape |
Source code in src/tramdag/transforms.py
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | |
make_univariate_transform ¶
make_univariate_transform(name, **kwargs)
Build a scaled univariate transform by name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
One of |
required |
**kwargs
|
Passed to the transform class. |
{}
|
Returns:
| Type | Description |
|---|---|
_ScaledUT
|
The transform. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Source code in src/tramdag/transforms.py
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | |