LGM API¶
pyrox_lgm holds latent Gaussian models (LGMs) in precision form:
GMRF latent components, penalised-complexity priors and the inla() driver,
built on gaussx's sparse precision operators and Laplace modes and
kernellib's graph and GMRF structure matrices. GPs in covariance form stay in
pyrox-gp; pyrox_lgm imports nothing from it.
This page is a stub: the package is a scaffold (P6 of the
inla() epic), and the
API lands with its phases.
| Phase | Adds |
|---|---|
| P7 | Components (RW1, RW2, AR1, ICAR, BYM2, SPDE, generic, combinators) and PC priors |
| P8 | LGM, inla() and INLAResult |
| P9 | f(...) formula sugar, diagnostics, simplified Laplace, MCMC-INLA hybrid |
Penalised-complexity priors¶
Hyperpriors calibrated by a tail statement (Simpson et al., 2017). Each is a
NumPyro distribution, usable under NUTS as well as in inla().
PCPrecision
¶
Bases: Distribution
PC prior on the precision \(\tau\) of a Gaussian effect.
The distance to the base model \(\sigma = 0\) is proportional to the standard deviation \(\sigma = \tau^{-1/2}\), so \(\sigma \sim \mathrm{Exp}(\lambda)\) with \(\lambda = -\log\alpha / U\), i.e. \(P(\sigma > U) = \alpha\). On \(\tau\):
This is R-INLA's pc.prec.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
U
|
ArrayLike
|
Upper reference value for \(\sigma\), |
1.0
|
alpha
|
ArrayLike
|
Tail probability \(P(\sigma > U)\), in |
0.01
|
Examples:
>>> import jax
>>> import pyrox_lgm as lgm
>>> prior = lgm.PCPrecision(U=1.0, alpha=0.01) # P(sigma > 1) = 0.01
>>> tau = prior.sample(jax.random.key(0), (20_000,))
>>> round(float((tau < 1.0).mean()), 2) # sigma > 1 <=> tau < 1
0.01
Source code in packages/pyrox-lgm/src/pyrox_lgm/_priors/_pc.py
rate: Array
property
¶
The rate \(\lambda = -\log\alpha / U\) of the exponential on \(\sigma\).
PCAR1Rho
¶
Bases: Distribution
PC prior on the lag-one correlation \(\rho\) of an AR(1) process.
Base model \(\rho = 0\) (Sørbye & Rue, 2017): the distance is \(d(\rho) = \sqrt{-\log(1-\rho^2)}\), and the prior is symmetric,
so that \(P(\lvert\rho\rvert > U) = \alpha\). This is R-INLA's pc.cor0
(which states its calibration on \(\lvert\rho\rvert\)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
U
|
ArrayLike
|
Reference correlation, in |
0.5
|
alpha
|
ArrayLike
|
Tail probability \(P(\lvert\rho\rvert > U)\), in |
0.5
|
Examples:
>>> import jax, jax.numpy as jnp
>>> import pyrox_lgm as lgm
>>> prior = lgm.PCAR1Rho(U=0.5, alpha=0.1) # P(|rho| > 0.5) = 0.1
>>> rho = prior.sample(jax.random.key(0), (20_000,))
>>> round(float((jnp.abs(rho) > 0.5).mean()), 2)
0.1
Source code in packages/pyrox-lgm/src/pyrox_lgm/_priors/_pc.py
rate: Array
property
¶
The rate \(\lambda = -\log\alpha / d(U)\).
PCBYM2Phi
¶
Bases: Distribution
PC prior on the BYM2 mixing parameter \(\phi \in (0, 1)\).
BYM2 (Riebler et al., 2016) mixes unstructured and scaled-structured effects, \(b = \sigma(\sqrt{1-\phi}\,v + \sqrt{\phi}\,u_\ast)\). The base model is \(\phi = 0\) (no spatial structure); with \(\gamma_i\) the eigenvalues of \(R_\ast^{+}\),
and \(d(\phi) = \sqrt{2\,\mathrm{KLD}(\phi)}\) gets an exponential prior
with \(\lambda = -\log(1-\alpha)/d(U)\), so \(P(\phi < U) = \alpha\). This is
R-INLA's pc prior on BYM2's phi. The null-space eigenvalues
(\(\gamma = 0\)) send \(d(\phi) \to \infty\) as \(\phi \to 1\), which makes the
prior proper on \((0, 1)\), so the spectrum must contain at least one (a
BYM2 structure always has a null space).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
U
|
ArrayLike
|
Reference value of \(\phi\), in |
0.5
|
alpha
|
ArrayLike
|
\(P(\phi < U)\), in |
2.0 / 3.0
|
structure_spectrum
|
StructureSpectrum
|
The spectrum of \(R_\ast^{+}\), from
|
required |
Examples:
>>> import jax
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> g = kl.grid_graph((6, 6))
>>> R = kl.structure_matrix(g, scaled=True)
>>> spec = lgm.structure_spectrum(R, kl.graph_null_space(g))
>>> prior = lgm.PCBYM2Phi(U=0.5, alpha=2 / 3, structure_spectrum=spec)
>>> phi = prior.sample(jax.random.key(0), (20_000,))
>>> round(float((phi < 0.5).mean()), 2) # P(phi < 0.5) = 2/3
0.67
Source code in packages/pyrox-lgm/src/pyrox_lgm/_priors/_pc.py
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 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 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 | |
PCMatern
¶
Bases: Distribution
Joint PC prior on the range \(\rho\) and marginal sd \(\sigma\) of a Matérn field.
Fuglstad et al. (2019): in dimension \(d\), the base model is infinite range and \(\sigma = 0\), and
with \(\lambda_\rho = -\log(\alpha_\rho)\,\rho_0^{d/2}\) and
\(\lambda_\sigma = -\log(\alpha_\sigma)/\sigma_0\), so
\(P(\rho < \rho_0) = \alpha_\rho\) and \(P(\sigma > \sigma_0) = \alpha_\sigma\).
The value is the pair [range, sigma] (event shape (2,)). This is
R-INLA's inla.spde2.pcmatern prior.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
range0
|
ArrayLike
|
Reference range \(\rho_0 > 0\). |
1.0
|
alpha_range
|
ArrayLike
|
\(P(\rho < \rho_0)\), in |
0.05
|
sigma0
|
ArrayLike
|
Reference standard deviation \(\sigma_0 > 0\). |
1.0
|
alpha_sigma
|
ArrayLike
|
\(P(\sigma > \sigma_0)\), in |
0.05
|
d
|
int
|
Spatial dimension of the field. |
2
|
Examples:
>>> import jax
>>> import pyrox_lgm as lgm
>>> prior = lgm.PCMatern(
... range0=10.0, alpha_range=0.05, sigma0=2.0, alpha_sigma=0.05, d=2
... )
>>> x = prior.sample(jax.random.key(0), (20_000,))
>>> x.shape
(20000, 2)
>>> round(float((x[:, 0] < 10.0).mean()), 2) # P(range < 10)
0.05
>>> round(float((x[:, 1] > 2.0).mean()), 2) # P(sigma > 2)
0.05
Source code in packages/pyrox-lgm/src/pyrox_lgm/_priors/_pc.py
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 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 | |
Structure spectra¶
PCBYM2Phi needs the spectrum of the scaled structure's generalised inverse,
computed once per graph: exactly for a grid (gaussx.KroneckerSum) or
n ≤ 5000, else by deflated stochastic Lanczos quadrature.
structure_spectrum(structure: lx.AbstractLinearOperator, null_space: ArrayLike | None = None, *, method: Literal['auto', 'dense', 'lanczos'] = 'auto', num_probes: int = 64, order: int = 50, deflate: int = 32, key: jax.Array | None = None) -> StructureSpectrum
¶
The spectrum of \(R^{+}\) that PCBYM2Phi needs, computed once.
"dense": exact eigenvalues. Agaussx.KroneckerSum(the structure of akernellib.GridGraph) is diagonalised factor by factor, so a grid costs only its 1-D eigenproblems; anything else is a denseeigh."lanczos": a deflated stochastic Lanczos quadrature (SLQ) estimate. One Lanczos run ofmax(order, 8 * deflate)steps converges up todeflateof the largest eigenvalues of \(R_\ast^{+}\) (the smallest of \(R_\ast\)), which are kept exactly: they dominate \(\operatorname{tr}R_\ast^{+}\), and leaving them to the probes gives a large variance. The rest of the measure comes fromnum_probesRademacher probes, projected off the null space and the deflated eigenvectors, withorderLanczos steps each (full reorthogonalisation); the null space contributes exact zeros. Cost: aboutnum_probes * order + 8 * deflatematvecs. Runs eagerly (it picks the converged Ritz pairs), so call it once, outsidejit."auto":"dense"for agaussx.KroneckerSumorn <= 5000, else"lanczos".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
structure
|
AbstractLinearOperator
|
The (scaled) structure matrix \(R_\ast\), symmetric PSD. |
required |
null_space
|
ArrayLike | None
|
Orthonormal basis of its null space, |
None
|
method
|
Literal['auto', 'dense', 'lanczos']
|
See above. |
'auto'
|
num_probes
|
int
|
SLQ probes ( |
64
|
order
|
int
|
Lanczos steps per probe ( |
50
|
deflate
|
int
|
Most eigenvalues to deflate exactly ( |
32
|
key
|
Array | None
|
PRNG key for the probes; |
None
|
Returns:
| Type | Description |
|---|---|
StructureSpectrum
|
The spectral measure of \(R_\ast^{+}\). |
Examples:
>>> import jax.numpy as jnp
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> g = kl.grid_graph((4,))
>>> R = kl.structure_matrix(g, scaled=True)
>>> spec = lgm.structure_spectrum(R, kl.graph_null_space(g))
>>> float(spec.size)
4.0
>>> int(jnp.sum(spec.eigenvalues == 0.0)) # one connected component
1
Source code in packages/pyrox-lgm/src/pyrox_lgm/_priors/_pc.py
StructureSpectrum
¶
Bases: NamedTuple
Spectral measure of \(R_\ast^{+}\), the generalised inverse of a structure.
eigenvalues are the eigenvalues \(\gamma_i\) of \(R_\ast^{+}\) (zero on
the null space of \(R_\ast\)) and weights their multiplicities, which
sum to \(n\). A dense spectrum has unit weights; a Lanczos (SLQ) spectrum
has quadrature nodes and weights. Anything linear in the spectrum
(\(\operatorname{tr}R_\ast^{+}\), \(\log|(1-\phi)I + \phi R_\ast^{+}|\)) is a
weighted sum over it.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_priors/_pc.py
size: Array
property
¶
The dimension \(n\), the total weight.
Components¶
Each component is a GMRF over its own nodes with hyperpriors and a projector
to observations. prior(theta) gives the gaussx distribution that inla()
uses; sample(index) is the NumPyro face, for NUTS.
AbstractComponent
¶
Bases: Module
A latent field with hyperpriors and a projector to observations.
Subclasses define n_nodes, theta_spec and prior. The default
projector selects nodes by index; the default sample is the NumPyro
face (see pyrox_lgm._numpyro.sample_component).
Attributes:
| Name | Type | Description |
|---|---|---|
name |
AbstractVar[str]
|
Site-name prefix, unique within a model: the field is the
NumPyro site |
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_base.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 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 120 121 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 | |
n_nodes: int
abstractmethod
property
¶
Size of the latent field (including any padding node).
n_index: int
property
¶
Number of addressable nodes: what an observation index refers to.
Equal to n_nodes except where the field carries padding nodes
(an odd-length RW2), which observations never touch.
theta_spec() -> dict[str, tuple[dist.Distribution, Transform]]
abstractmethod
¶
Hyperparameters: name to (prior, bijection from the reals).
The transform maps an unconstrained real to the parameter's support,
which is how inla() explores \(\theta\).
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_base.py
prior(theta: dict[str, Array], *, constraint: Constraint = 'hard', soft_constraint_scale: float = 0.001) -> gx.GaussianMRF | gx.IntrinsicGMRF
abstractmethod
¶
The GMRF of the field given its hyperparameters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
theta
|
dict[str, Array]
|
Hyperparameter values keyed as in |
required |
constraint
|
Constraint
|
For intrinsic fields, how the null space is handled
( |
'hard'
|
soft_constraint_scale
|
float
|
Standard deviation \(s\) of the soft constraint \(V^\top x \sim \mathcal N(0, s^2 I)\). |
0.001
|
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_base.py
assembly_precision(theta: dict[str, Array], gmrf: gx.GaussianMRF | gx.IntrinsicGMRF) -> lx.AbstractLinearOperator
¶
The precision an LGM assembles into its sparse block-diagonal.
gmrf is prior(theta); its precision by default (scale *
structure for an intrinsic field). A component whose prior uses an
operator that cannot be assembled entry-wise (a grid SPDE's spectral
function) returns an equivalent sparse one here.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_base.py
constraint_basis(gmrf: gx.GaussianMRF | gx.IntrinsicGMRF) -> Float[Array, 'n c'] | None
¶
Orthonormal basis of the hard constraints inla() imposes.
The intrinsic field's null space by default (sum-to-zero per
connected component, say). A component may constrain fewer
directions and leave the rest to the data, as R-INLA's rw2
constrains only the sum and keeps the linear trend; the density's
rank is unchanged.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_base.py
projector(index: Int[ArrayLike, ' n_obs']) -> gx.SparseOperator
¶
(n_obs, n_nodes) selector with a one where observation i sits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
Int[ArrayLike, ' n_obs']
|
Node of each observation, integers in |
required |
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_base.py
sample(index: Int[ArrayLike, ' n_obs'] | None = None, *, soft_constraint_scale: float = 0.01) -> Array
¶
NumPyro face: sample \(\theta\) and the field, return it at index.
Draws each hyperparameter from its prior (site f"{name}_{k}"),
then the field (site name) with a soft constraint for intrinsic
fields, so it works under NUTS. Returns the field at index, or all
n_index addressable nodes when index is None. See
pyrox_lgm._numpyro.sample_component for soft_constraint_scale.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_base.py
Temporal and unstructured¶
IID
¶
Bases: AbstractComponent
Independent effects \(x_i \sim \mathcal N(0, 1/\tau)\), \(i < n\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of nodes. |
required |
name
|
str
|
Site-name prefix. |
'iid'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
Examples:
>>> import jax.numpy as jnp
>>> import pyrox_lgm as lgm
>>> iid = lgm.IID(4, name="u")
>>> q = iid.prior({"tau": jnp.asarray(2.0)}).precision.as_matrix()
>>> [float(v) for v in jnp.diag(q)]
[2.0, 2.0, 2.0, 2.0]
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_temporal.py
RW1
¶
Bases: _IntrinsicWalk
First-order random walk, \(x_{t+1} - x_t \sim \mathcal N(0, 1/\tau)\).
Intrinsic of order 1: the constant is its null space, removed by a sum-to-zero constraint. A cyclic walk also links \(x_{n-1}\) and \(x_0\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of time points. |
required |
name
|
str
|
Site-name prefix. |
'rw1'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
scale_model
|
bool
|
Rescale the structure so the geometric mean of the
constrained marginal variances is 1 (R-INLA's |
True
|
cyclic
|
bool
|
Join the ends. |
False
|
Examples:
>>> import jax.numpy as jnp
>>> import pyrox_lgm as lgm
>>> rw = lgm.RW1(20)
>>> var = rw.prior({"tau": jnp.asarray(1.0)}).marginal_variances()
>>> round(float(jnp.exp(jnp.mean(jnp.log(var)))), 4) # scale_model
1.0
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_temporal.py
RW2
¶
Bases: _IntrinsicWalk
Second-order random walk, \(x_{t+1} - 2x_t + x_{t-1} \sim \mathcal N(0, 1/\tau)\).
Intrinsic of order 2: constants and linear trends form its null space.
Inside inla() only the sum-to-zero constraint is imposed, as R-INLA's
rw2 does, and the linear trend is left to the data (the density's
rank stays n - 2); the NumPyro face soft-constrains both
directions, so add a linear fixed effect there if the data have a trend.
For odd n,
gaussx's banded structure carries one decoupled padding node
(n_nodes = n + 1); it never enters a projector, and its own scale
is taken out of scale_model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of time points (at least 3). |
required |
name
|
str
|
Site-name prefix. |
'rw2'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
scale_model
|
bool
|
Rescale as for |
True
|
Examples:
>>> import jax.numpy as jnp
>>> import pyrox_lgm as lgm
>>> rw = lgm.RW2(7) # odd: one padding node
>>> rw.n_nodes, rw.n_index
(8, 7)
>>> var = rw.prior({"tau": jnp.asarray(1.0)}).marginal_variances()[:7]
>>> round(float(jnp.exp(jnp.mean(jnp.log(var)))), 4)
1.0
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_temporal.py
AR1
¶
Bases: AbstractComponent
Stationary AR(1), \(x_t = \rho x_{t-1} + \varepsilon_t\).
The marginal precision is \(\tau\): every marginal variance is \(1/\tau\)
(R-INLA's ar1 convention, so \(\tau\) and \(\rho\) are separately
interpretable).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of time points (at least 2). |
required |
name
|
str
|
Site-name prefix. |
'ar1'
|
tau_prior
|
Distribution | None
|
Prior on the marginal precision; default
|
None
|
rho_prior
|
Distribution | None
|
Prior on \(\rho\); default |
None
|
Examples:
>>> import jax.numpy as jnp
>>> import pyrox_lgm as lgm
>>> ar = lgm.AR1(30)
>>> gmrf = ar.prior({"tau": jnp.asarray(4.0), "rho": jnp.asarray(0.7)})
>>> [round(float(v), 4) for v in gmrf.marginal_variances()[:3]]
[0.25, 0.25, 0.25]
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_temporal.py
Generic¶
Generic
¶
Bases: AbstractComponent
A field with precision \(\tau R\) for a user-supplied structure \(R\).
With null_space=None, \(R\) must be positive definite and the field is
the proper GMRF \(\mathcal N(0, (\tau R)^{-1})\). Otherwise \(R\) is
positive semidefinite with the given null space and the field is
intrinsic, constrained to be orthogonal to it (R-INLA's generic0
with constr).
Pass \(R\) as a gaussx / lineax operator: a gaussx.SparseOperator keeps
the sparse-Cholesky path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
structure
|
AbstractLinearOperator
|
\(R\), symmetric, size |
required |
null_space
|
ArrayLike | None
|
Basis of \(\ker R\), |
None
|
name
|
str
|
Site-name prefix. |
'generic'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
Examples:
>>> import jax.numpy as jnp
>>> import lineax as lx
>>> import pyrox_lgm as lgm
>>> R = lx.MatrixLinearOperator(
... jnp.array([[2.0, -1.0], [-1.0, 2.0]]), lx.positive_semidefinite_tag
... )
>>> comp = lgm.Generic(R, name="g")
>>> comp.prior({"tau": jnp.asarray(3.0)}).precision.as_matrix().tolist()
[[6.0, -3.0], [-3.0, 6.0]]
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_generic.py
Areal¶
On a kernellib graph (from edges, polygons' contiguity, or a grid).
Besag
¶
Bases: AbstractComponent
Intrinsic CAR (ICAR / Besag): precision \(\tau R\), \(R = D - W\).
One sum-to-zero constraint per connected component. With
scale_model=True (default) \(R\) is BYM2-scaled per component, so
\(\tau\) is comparable across graphs (R-INLA's scale.model).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
AbstractGraph
|
A kernellib graph. |
required |
name
|
str
|
Site-name prefix. |
'besag'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
scale_model
|
bool
|
Scale the structure per connected component. |
True
|
Examples:
>>> import jax, jax.numpy as jnp
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> g = kl.graph_from_edges([0, 1, 3], [1, 2, 4], 5) # two components
>>> icar = lgm.Besag(g)
>>> x = icar.prior({"tau": jnp.asarray(1.0)}).sample(jax.random.key(0))
>>> [round(float(s), 6) + 0.0 for s in (x[:3].sum(), x[3:].sum())]
[0.0, 0.0]
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_areal.py
BYM2
¶
Bases: AbstractComponent
BYM2 (Riebler et al., 2016), a mixture of unstructured and ICAR effects.
\(b = \tau^{-1/2}(\sqrt{1-\phi}\,v + \sqrt\phi\,u_\ast)\), with \(v\)
unstructured and \(u_\ast\) a scaled ICAR, so \(\tau\) is the total
precision of \(b\) and \(\phi\) the fraction of its variance that is spatially
structured. The field is the pair \((b, u_\ast)\) (n_nodes = 2n),
distributed as gaussx.BYM2GMRF with its exact constrained density;
observations see \(b\) only (n_index = n). The PC prior on \(\phi\)
needs the spectrum of \(R_\ast^{+}\), computed once here
(structure_spectrum), as is \(\log|R_\ast|_+\) for the normaliser.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
AbstractGraph
|
A kernellib graph. |
required |
name
|
str
|
Site-name prefix. |
'bym2'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
phi_prior
|
Distribution | None
|
Prior on \(\phi\); default |
None
|
spectrum_method
|
Literal['auto', 'dense', 'lanczos']
|
How to compute that spectrum (see
|
'auto'
|
Examples:
>>> import jax.numpy as jnp
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> bym2 = lgm.BYM2(kl.grid_graph((4, 4)))
>>> bym2.n_nodes, bym2.n_index
(32, 16)
>>> gmrf = bym2.prior({"tau": jnp.asarray(2.0), "phi": jnp.asarray(0.5)})
>>> var = gmrf.marginal_variances()
>>> var_b, var_u = var[:16], var[16:] # var(b) = (1 - phi + phi var(u*)) / tau
>>> bool(jnp.allclose(var_b, (0.5 + 0.5 * var_u) / 2.0))
True
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_areal.py
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 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 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | |
CAR
¶
Bases: _ProperAreal
Proper CAR: precision \(\tau(D - \rho W)\), \(\rho \in [0, 1)\).
\(D\) is the diagonal of degrees and \(W\) the weighted adjacency; \(\rho \to
1\) approaches the ICAR. Every node needs a neighbour. The
log-determinant comes from the spectrum \(\mu_i\) of
\(D^{-1/2} W D^{-1/2}\) (one dense eigh at construction, for
n ≤ 10⁴): \(\log|Q| = n\log\tau + \sum_i \log d_i + \sum_i \log(1 - \rho\mu_i)\);
larger graphs use gaussx's sparse Cholesky at each \(\theta\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
AbstractGraph
|
A kernellib graph without isolated nodes. |
required |
name
|
str
|
Site-name prefix. |
'car'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
rho_prior
|
Distribution | None
|
Prior on \(\rho\); default |
None
|
Examples:
>>> import jax.numpy as jnp
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> car = lgm.CAR(kl.graph_from_edges([0, 1], [1, 2], 3))
>>> Q = car.prior({"tau": jnp.asarray(1.0), "rho": jnp.asarray(0.5)})
>>> Q.precision.as_matrix().tolist()
[[1.0, -0.5, 0.0], [-0.5, 2.0, -0.5], [0.0, -0.5, 1.0]]
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_areal.py
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 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 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | |
Leroux
¶
Bases: _ProperAreal
Leroux CAR: precision \(\tau(\rho R + (1 - \rho) I)\), \(\rho \in [0, 1]\).
Interpolates between independent effects (\(\rho = 0\)) and the ICAR
(\(\rho \to 1\)). On a face-connected kernellib.GridGraph the precision
stays a gaussx.KroneckerSum (exact eigen-structured solves and
log-determinant); elsewhere it is a gaussx.SparseOperator, with
\(\log|Q| = n\log\tau + \sum_i \log(\rho\lambda_i + 1 - \rho)\) from the
Laplacian spectrum \(\lambda_i\) cached at construction (n ≤ 10⁴).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
graph
|
AbstractGraph
|
A kernellib graph. |
required |
name
|
str
|
Site-name prefix. |
'leroux'
|
tau_prior
|
Distribution | None
|
Prior on \(\tau\); default |
None
|
rho_prior
|
Distribution | None
|
Prior on \(\rho\); default |
None
|
scale_model
|
bool
|
Use the BYM2-scaled \(R\) (per connected component). |
False
|
Examples:
>>> import jax.numpy as jnp
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> lr = lgm.Leroux(kl.grid_graph((3, 3)))
>>> gmrf = lr.prior({"tau": jnp.asarray(1.0), "rho": jnp.asarray(0.0)})
>>> bool(jnp.allclose(gmrf.precision.as_matrix(), jnp.eye(9))) # IID
True
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_areal.py
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | |
SPDE (Matérn)¶
SPDE
¶
Bases: AbstractComponent
Matérn field via the SPDE, on a triangle mesh or a regular grid.
Exactly one of mesh and grid is given:
mesh=(vertices, triangles): P1 finite elements on a triangulated surface (verticesof shape(V, 2)or(V, 3)on a manifold), with lumped massC̃and stiffnessGfromgaussx.fem_matrices; the precision isgaussx.spde_precision(sparse). Nodes are the vertices;project_pointsbuilds the barycentric projector for arbitrary locations. The field is 2-D (d = 2).grid=shape: the finite-difference SPDE on a regular lattice withspacing(gaussx.spde_precision_grid, diagonalised exactly by its Kronecker structure). Nodes are the grid cells in C order;dislen(shape).
The hyperparameter "range_sigma" is the pair [range, sigma]
under the joint PCMatern prior.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mesh
|
tuple[ArrayLike, ArrayLike] | None
|
|
None
|
grid
|
tuple[int, ...] | None
|
Lattice shape. |
None
|
spacing
|
float
|
Grid spacing ( |
1.0
|
alpha
|
int
|
SPDE order \(\alpha = \nu + d/2\), an integer (2 is the default Matérn-1 field in 2-D). |
2
|
name
|
str
|
Site-name prefix. |
'spde'
|
prior
|
Distribution | None
|
Joint prior on |
None
|
Examples:
>>> import jax.numpy as jnp
>>> import pyrox_lgm as lgm
>>> spde = lgm.SPDE(grid=(30, 30), alpha=2)
>>> g = spde.prior({"range_sigma": jnp.array([8.0, 2.0])})
>>> var = g.marginal_variances().reshape(30, 30)
>>> # ~ sigma^2 = 4 away from the border, up to the lattice discretisation
>>> bool(abs(float(var[15, 15]) - 4.0) < 0.4)
True
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_spde.py
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 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 120 121 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 155 156 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 189 190 | |
nu: float
property
¶
Smoothness \(\nu = \alpha - d/2\).
assembly_precision(theta, gmrf)
¶
The grid precision as a gaussx.SparseOperator (mesh: unchanged).
Q = tau^2 h^d (kappa^2 I + h^-2 L)^alpha, L the Kronecker sum
of the axes' path-graph Laplacians, exactly gaussx.spde_precision_grid
written entry-wise: alpha sparse products on a theta-free pattern.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_spde.py
project_points(points: Float[ArrayLike, 'n D']) -> gx.SparseOperator
¶
Barycentric (n, V) projector from mesh vertices to points.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_spde.py
Combinators¶
Kronecker
¶
Bases: AbstractComponent
Separable product \(Q = Q_\text{main} \otimes Q_\text{group}\) (R-INLA group).
The precisions of the two factors multiply, so only the product of their
\(\tau\)'s is identified; as R-INLA does, the group factor's tau is
fixed at 1 and the group contributes only its correlation structure (an
AR1 group keeps rho). Hyperparameter k of a factor named f
is f"{f}_{k}".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
main
|
AbstractComponent
|
The main component (e.g. a spatial field). |
required |
group
|
AbstractComponent
|
The grouping component (e.g. an |
required |
name
|
str
|
Site-name prefix. |
'kronecker'
|
Examples:
>>> import jax.numpy as jnp
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> st = lgm.Kronecker(
... lgm.Leroux(kl.grid_graph((3, 3)), name="space"),
... lgm.AR1(4, name="time"),
... )
>>> sorted(st.theta_spec())
['space_rho', 'space_tau', 'time_rho']
>>> st.n_nodes
36
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_combinators.py
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 155 156 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 189 190 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 | |
Replicate
¶
Bases: AbstractComponent
n_rep independent copies of a component, sharing its hyperparameters.
\(Q = I_{n_\text{rep}} \otimes Q_\text{component}\) (R-INLA's
replicate); an intrinsic component keeps its constraints in every
copy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
component
|
AbstractComponent
|
The replicated component. |
required |
n_rep
|
int
|
Number of copies. |
required |
name
|
str | None
|
Site-name prefix; defaults to the component's. |
None
|
Examples:
>>> import pyrox_lgm as lgm
>>> rep = lgm.Replicate(lgm.RW1(10, name="trend"), 3)
>>> rep.n_nodes, list(rep.theta_spec())
(30, ['tau'])
Source code in packages/pyrox-lgm/src/pyrox_lgm/_components/_combinators.py
Models and inference¶
An LGM assembles components, fixed effects and an observation model;
inla() integrates over the hyperparameters (Rue, Martino & Chopin, 2009).
LGM
¶
Bases: Module
A latent Gaussian model: components, fixed effects and an observation model.
\(\theta\) collects every component's and the likelihood's hyperparameters
under f"{name}.{k}". inla() works on the unconstrained vector
\(u\), \(\theta = T(u)\) with each prior's bijection, whose log-prior
includes the Jacobian; log_posterior_theta is \(\log\tilde\pi(u\mid y)\)
in those coordinates.
data holds "y", an optional "offset", the node index of each
observation for every component (data[component.name]; an
SPDE on a mesh also takes (n, 2) points) and the fixed-effect
covariates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
components
|
tuple[AbstractComponent, ...]
|
Latent components, with distinct names. |
required |
fixed
|
FixedEffects | None
|
Fixed effects, or |
None
|
likelihood
|
AbstractObservation | None
|
Observation model. |
None
|
Examples:
>>> import jax.numpy as jnp
>>> import numpy as np
>>> import pyrox_lgm as lgm
>>> model = lgm.LGM(
... components=(lgm.RW1(5, name="t"),),
... fixed=lgm.FixedEffects(("intercept",)),
... likelihood=lgm.Poisson(),
... )
>>> list(model.theta_spec())
['t.tau']
>>> data = {"y": np.array([1, 0, 2, 3, 1]), "t": np.arange(5)}
>>> model.projector(data).as_matrix().shape # 5 nodes + intercept
(5, 6)
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
148 149 150 151 152 153 154 155 156 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 189 190 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 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 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 305 306 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 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | |
n_theta: int
property
¶
Length \(m\) of the unconstrained vector \(u\).
theta_spec() -> dict[str, tuple[dist.Distribution, Transform]]
¶
Every hyperparameter: f"{name}.{k}" to (prior, bijection).
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
unflatten(u: Float[Array, ' m']) -> dict[str, Array]
¶
Constrained \(\theta\) by key, from the unconstrained vector.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
log_prior(u: Float[Array, ' m']) -> Array
¶
\(\log\pi(u) = \sum_k \log\pi_k(T_k(u_k)) + \log|T_k'(u_k)|\).
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
slices() -> dict[str, tuple[int, int]]
¶
name -> (start, stop) in the latent vector; fixed effects last.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
latent_prior(theta: Mapping[str, Array]) -> LatentGMRF | LatentIntrinsicGMRF
¶
The assembled prior of \((x_1, \dots, x_k, \beta)\) at theta.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
projector(data: Mapping[str, ArrayLike]) -> gx.SparseOperator
¶
[A_1 | ... | A_k | X] for data, built on the host.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
log_posterior_theta(u: Float[Array, ' m'], data: Mapping[str, ArrayLike], *, projector: gx.SparseOperator | None = None, max_newton: int = 50) -> Array
¶
The unnormalised log-posterior of the unconstrained hyperparameters.
\(\log\tilde\pi(u \mid y) = \log\tilde\pi(y\mid\theta)
+ \log\pi(u)\). Exact gradients flow through gaussx's
implicit-differentiated Laplace mode. Pass a prebuilt projector to
keep host work out of a traced loop.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
laplace(u: Float[Array, ' m'], data: Mapping[str, ArrayLike], *, projector: gx.SparseOperator | None = None, max_newton: int = 50) -> gx.LaplaceResult
¶
The Laplace approximation of \(x \mid y, \theta = T(u)\).
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
FixedEffects
¶
Bases: Module
Fixed effects \(\beta \sim \mathcal N(0, \lambda^{-1} I)\), columns of \(X\).
Column "intercept" is all ones unless data has its own; every
other name is read from data[name].
Attributes:
| Name | Type | Description |
|---|---|---|
names |
tuple[str, ...]
|
Fixed-effect names, in the order they enter the latent vector. |
prior_precision |
float
|
\(\lambda\) (R-INLA's default is 0.001). |
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
design(data: Mapping[str, ArrayLike], n_obs: int) -> Array
¶
The (n_obs, p) design matrix.
Covariates may be traced JAX values (a covariate that depends on a parameter sampled outside the LGM, as in an MCMC-INLA hybrid): the design is dense, so its pattern does not depend on them.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_model.py
inla(model: LGM, data: Mapping[str, ArrayLike], *, strategy: Literal['vb', 'gaussian', 'sla'] = 'vb', integration: Literal['auto', 'eb', 'grid', 'ccd'] = 'auto', key: jax.Array | None = None, max_newton: int = 50, theta_init: ArrayLike | None = None, max_theta_iter: int = 200, theta_tol: float = 1e-05, grid_step: float = 1.0, grid_threshold: float = 2.5, verbose: bool = False) -> INLAResult
¶
Integrated nested Laplace approximation of a latent Gaussian model.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
LGM
|
The |
required |
data
|
Mapping[str, ArrayLike]
|
|
required |
strategy
|
Literal['vb', 'gaussian', 'sla']
|
|
'vb'
|
integration
|
Literal['auto', 'eb', 'grid', 'ccd']
|
The design over \(\theta\). |
'auto'
|
key
|
Array | None
|
Unused by the deterministic design; reserved for stochastic strategies. Accepted so call sites stay stable. |
None
|
max_newton
|
int
|
Newton iterations per inner Laplace fit; a design point that does not converge is re-run with four times as many, then dropped with a warning. |
50
|
theta_init
|
ArrayLike | None
|
Starting unconstrained \(u\) (default zeros: every positive hyperparameter at 1, every interval one at its centre). |
None
|
max_theta_iter
|
int
|
L-BFGS iterations for the mode; not reaching
|
200
|
theta_tol
|
float
|
Stop when \(\max|\nabla_u| <\) |
1e-05
|
grid_step
|
float
|
Step in standardised \(z\) of |
1.0
|
grid_threshold
|
float
|
Log-density drop from the mode at which
|
2.5
|
verbose
|
bool
|
Print the mode search. |
False
|
Returns:
| Type | Description |
|---|---|
INLAResult
|
An |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the theta-mode search misses |
Examples:
>>> import jax.numpy as jnp
>>> import numpy as np
>>> import pyrox_lgm as lgm
>>> t = np.arange(30)
>>> y = np.sin(t / 5.0) + 0.1 * np.cos(7.0 * t) # smooth + wiggle
>>> model = lgm.LGM(
... components=(lgm.RW2(30, name="trend"),),
... fixed=lgm.FixedEffects(("intercept",)),
... likelihood=lgm.Gaussian(),
... )
>>> res = lgm.inla(model, {"y": y, "trend": t})
>>> sorted(res.hyperpar)
['lik.prec', 'trend.tau']
>>> res.random["trend"].mean.shape
(30,)
Source code in packages/pyrox-lgm/src/pyrox_lgm/_inla.py
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 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 514 515 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 | |
INLAResult
¶
Bases: Module
What inla() returns.
Attributes:
| Name | Type | Description |
|---|---|---|
fixed |
dict[str, Summary]
|
Fixed effect name to its |
random |
dict[str, Summary]
|
Component name to the |
hyperpar |
dict[str, Summary]
|
Hyperparameter key ( |
theta_mode |
Float[Array, ' m']
|
Unconstrained mode \(u^\ast\). |
theta_points |
Float[Array, 'K m']
|
Design points |
theta_weights |
Float[Array, ' K']
|
Their normalised weights |
latent_means |
Float[Array, 'K N']
|
Per-point latent means |
latent_variances |
Float[Array, 'K N']
|
Per-point marginal variances |
latent_skewness |
Float[Array, 'K N']
|
Per-point marginal skewness |
predictor_means |
Float[Array, 'K M']
|
Per-point means of the linear predictor
\(\eta = Ax + o\) at the observations |
predictor_variances |
Float[Array, 'K M']
|
Their variances |
linear_predictor |
Summary
|
|
log_marginal_likelihood |
Float[Array, '']
|
\(\log\tilde\pi(y)\), Gaussian approximation over \(\theta\) (R-INLA's "Gaussian" mlik). |
n_dropped |
int
|
Design points dropped because their inner Newton did not converge. |
newton_iters |
tuple[int, ...]
|
The Newton budget each kept point converged with
( |
Source code in packages/pyrox-lgm/src/pyrox_lgm/_result.py
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 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 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 305 306 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 | |
sample_latent(key: jax.Array, n: int) -> Float[Array, 'n N']
¶
Draws of the latent vector from the mixture over design points.
Each draw picks a design point by its weight, then samples that point's Gaussian approximation \(\mathcal N(\bar x_k, H_k^{-1})\) (kriged onto the hard constraints) through the sparse Cholesky factor of \(H_k\).
Source code in packages/pyrox-lgm/src/pyrox_lgm/_result.py
diagnostics(order: int = 80)
¶
DIC, WAIC, CPO and PIT, without refits (see pyrox_lgm.diagnostics).
predict(new_data: Mapping[str, ArrayLike], key: jax.Array, n_samples: int = 1000) -> Summary
¶
Monte Carlo summary of the linear predictor \(\eta = Ax + o\) at new_data.
new_data has the component indices and covariates of the new
locations (and "y", any placeholder of the right length, which
sizes the projector); "offset" is optional.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_result.py
to_xarray()
¶
The summaries as an xarray.Dataset (needs the xarray extra).
Source code in packages/pyrox-lgm/src/pyrox_lgm/_result.py
Summary
¶
Bases: NamedTuple
Posterior summary: mean, sd and the 2.5 / 50 / 97.5 % quantiles.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_result.py
Formula sugar¶
f(column, model, ...) builds a component named after the data column that
indexes it, as R-INLA's f() does.
f(column: str, model: str, *, n: int | None = None, graph: kl.AbstractGraph | None = None, hyper: Mapping[str, dist.Distribution] | None = None, **kwargs: Any) -> AbstractComponent
¶
Build a latent component named after the data column it indexes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
column
|
str
|
The data column with each observation's node index; becomes
the component's |
required |
model
|
str
|
|
required |
n
|
int | None
|
Number of nodes, for the temporal and unstructured models. |
None
|
graph
|
AbstractGraph | None
|
A kernellib graph, for the areal models. |
None
|
hyper
|
Mapping[str, Distribution] | None
|
Hyperparameter priors by name ( |
None
|
**kwargs
|
Any
|
Passed through to the constructor ( |
{}
|
Returns:
| Type | Description |
|---|---|
AbstractComponent
|
The component. |
Raises:
| Type | Description |
|---|---|
ValueError
|
For an unknown model, a missing size or graph, or an unknown hyperparameter. |
Examples:
>>> import kernellib as kl
>>> import pyrox_lgm as lgm
>>> g = kl.grid_graph((4, 4))
>>> tau = lgm.PCPrecision(1.0, 0.01)
>>> region = lgm.f("region", "bym2", graph=g, hyper={"tau": tau})
>>> region.name, type(region).__name__
('region', 'BYM2')
>>> lgm.f("week", "rw2", n=52, scale_model=True).n_index
52
Source code in packages/pyrox-lgm/src/pyrox_lgm/_formula.py
Diagnostics¶
CPO, PIT, WAIC and DIC from one fit, with no refits.
diagnostics(result: INLAResult, *, order: int = 80) -> Diagnostics
¶
DIC, WAIC, CPO and PIT of an INLAResult, with no refits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
INLAResult
|
What |
required |
order
|
int
|
Gauss–Hermite nodes per site and design point. |
80
|
Returns:
| Type | Description |
|---|---|
Diagnostics
|
A |
Examples:
>>> import numpy as np
>>> import pyrox_lgm as lgm
>>> t = np.arange(25)
>>> y = np.sin(t / 4.0) + 0.2 * np.cos(5.0 * t)
>>> model = lgm.LGM((lgm.RW2(25, name="t"),), lgm.FixedEffects(("intercept",)))
>>> d = lgm.inla(model, {"y": y, "t": t}).diagnostics()
>>> d.cpo.shape, bool(((d.pit > 0) & (d.pit < 1)).all())
((25,), True)
Source code in packages/pyrox-lgm/src/pyrox_lgm/_diagnostics.py
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 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 120 121 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 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | |
Diagnostics
¶
Bases: NamedTuple
Leave-one-out and information criteria of an INLAResult.
Attributes:
| Name | Type | Description |
|---|---|---|
cpo |
Float[Array, ' n']
|
Conditional predictive ordinates \(\pi(y_i\mid y_{-i})\). |
pit |
Float[Array, ' n']
|
Probability integral transforms \(P(Y_i \le y_i \mid y_{-i})\). |
log_score |
Float[Array, '']
|
\(-\frac1n\sum_i \log\mathrm{CPO}_i\) (lower is better). |
waic |
Float[Array, '']
|
\(-2\sum_i(\mathrm{lppd}_i - p_{\mathrm{WAIC},i})\). |
p_waic |
Float[Array, '']
|
Effective number of parameters of WAIC. |
dic |
Float[Array, '']
|
\(\bar D + p_D\). |
p_d |
Float[Array, '']
|
Effective number of parameters of DIC, \(\bar D - \hat D\). |
Source code in packages/pyrox-lgm/src/pyrox_lgm/_diagnostics.py
Observation models¶
Gaussian
¶
Bases: AbstractObservation
y ~ N(eta, 1/prec); prec under PCPrecision(1, 0.01).
Source code in packages/pyrox-lgm/src/pyrox_lgm/_likelihood.py
Poisson
¶
Bases: AbstractObservation
y ~ Poisson(exp(eta)); put the log exposure in data["offset"].
Source code in packages/pyrox-lgm/src/pyrox_lgm/_likelihood.py
Bernoulli
¶
Bases: AbstractObservation
y ~ Bernoulli(sigmoid(eta)), y in {0, 1}.
Source code in packages/pyrox-lgm/src/pyrox_lgm/_likelihood.py
Binomial
¶
Bases: AbstractObservation
y ~ Binomial(n, sigmoid(eta)) with n = data["n_trials"].
Source code in packages/pyrox-lgm/src/pyrox_lgm/_likelihood.py
NegativeBinomial
¶
Bases: AbstractObservation
y ~ NegBin(mean exp(eta), size); size under Gamma(1, 0.1).