StudentT#

class impulso.observation.StudentT(*, name='student_t', is_heavy_tailed=True, nu='infer', prior_alpha=2.0, prior_beta=0.1)[source]#

Bases: ImpulsoModel

Student-t observation errors — y_t ~ MvT_nu(μ_t, Ω).

Heavy-tailed innovations for samples with outliers (crises, pandemic quarters, data revisions). Estimation stays fully Bayesian: the t likelihood downweights extreme observations automatically rather than requiring them to be dummied out by hand, because the t score with respect to the location is bounded and redescending — ∂ log p/∂μ = w · Ω⁻¹(y - μ) with w = (nu + n)/(nu + q) and q the squared Mahalanobis distance, so w → 0 as an observation moves far into the tail.

Ω = L Lᵀ is the scale matrix, not the covariance: E[y_t] = μ_t (nu > 1) and Cov[y_t] = nu/(nu-2) · Ω (nu > 2). The consequences of that convention — which downstream quantities change and which are exactly invariant — are set out in docs/adr/0007-student-t-errors-use-the-scale-matrix-convention.md.

Parameters:
name#

Discriminator key for the registry (always “student_t”).

Type:

Literal[‘student_t’]

is_heavy_tailed#

Always True.

Type:

bool

nu#

Degrees of freedom. A float strictly greater than 2 fixes nu; the default “infer” estimates it from the data. Small values mean fat tails (nu ≈ 4 is aggressively robust); large values approach the Gaussian.

Type:

float | Literal[‘infer’]

prior_alpha#

Shape of the Gamma prior on the excess degrees of freedom nu - 2. Must be strictly greater than 1 so the prior density vanishes at the origin — see PRIOR_ALPHA_LOWER. Only used when nu=”infer”.

Type:

float

prior_beta#

Rate of that Gamma prior. Only used when nu=”infer”.

Type:

float

Expand for references to impulso.observation.StudentT

Heavy-Tailed Observation Errors / Fitting

NU_LOWER: ClassVar[float] = 2.0#

below it the t has infinite variance, which would make innovation_covariance, density-forecast bands, and variance decompositions meaningless. Users who genuinely want infinite-variance innovations should build the PyMC model directly.

Type:

Hard lower bound on nu. Not configurable

PRIOR_ALPHA_LOWER: ClassVar[float] = 1.0#

Hard lower bound on prior_alpha. Gamma(alpha, ·) has zero density at the origin only for alpha > 1: at alpha = 1 the density there is the rate itself and for alpha < 1 it is unbounded. Since the prior on nu is that Gamma shifted by NU_LOWER, any alpha <= 1 piles mass against the nu = 2 infinite-variance boundary the shift exists to avoid.

build_likelihood(name, mu, chol, observed, dims=None)[source]#

Register the multivariate Student-t likelihood in the active PyMC model.

See _nu_tensor for how nu is registered under fixed and inferred parameterisations.

chol is passed straight through, so the manual Cholesky parameterisation the volatility seam builds carries over verbatim (pm.MvStudentT shares quaddist_matrix with pm.MvNormal).

Parameters:
  • name (str) – Name for the observed random variable (the pipeline uses “obs”).

  • mu (Any) – Conditional mean tensor, shape (T, n_vars).

  • chol (Any) – Lower-triangular Cholesky factor of the scale matrix Ω.

  • observed (ndarray) – Observed endogenous matrix, shape (T, n_vars).

  • dims (tuple[str, ...] | None) – PyMC dims for the observed variable.

Returns:

The registered PyMC random variable.

Return type:

Any

draw_standardised_innovations(shape, rng, posterior)[source]#

Draw standardised multivariate-t innovations ξ.

Reproduces PyMC’s own MvStudentT construction: ξ = z / sqrt(g / nu) with z ~ N(0, I) and g ~ χ²_nu drawn once per observation vector, not once per component. The shared mixing variable is what makes the components dependent (though uncorrelated) and the joint law genuinely multivariate-t; drawing one χ² per component would give a product of independent t marginals instead.

rng.standard_normal is consumed first, before the χ² draw, so the Gaussian adapter’s stream stays a strict prefix of this one and seeded Gaussian forecasts are unaffected by the seam.

nu is read per posterior draw, so a model with inferred degrees of freedom yields innovations whose tail weight varies across draws.

Parameters:
  • shape (tuple[int, ...]) – Draw shape, (chains, draws, n_vars).

  • rng (Generator) – Generator to consume.

  • posterior (xr.Dataset) – Posterior Dataset carrying nu with shape (chains, draws).

Returns:

Standardised t innovations of shape shape, with E[ξ] = 0 and Cov[ξ] = nu/(nu-2) · I. Each marginal is exactly a standard t_nu.

Raises:

ValueError – If the posterior carries no nu, or if its shape does not match the leading (chains, draws) of shape.

Return type:

ndarray

logp(mu, chol, value)[source]#

Symbolic log-density of value under the multivariate Student-t.

Registers the same nu_excess/nu auxiliary variables that build_likelihood registers (see _nu_tensor), so a model built via logp carries an identical posterior to one built via build_likelihood. chol is passed straight through as the scale-matrix factor, per ADR-0007.

Parameters:
  • mu (Any) – Conditional mean tensor, shape (T, n_vars).

  • chol (Any) – Lower-triangular Cholesky factor of the scale matrix Ω.

  • value (Any) – Endogenous matrix, observed or a symbolic latent, shape (T, n_vars).

Returns:

Scalar symbolic log-density, summed over time.

Return type:

Any

model_config = {'frozen': True}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

variance_inflation(posterior)[source]#

Factor converting the scale matrix Ω into the innovation covariance.

nu/(nu-2), evaluated per posterior draw. Finite by construction: nu is bounded below by 2 both by the validator (fixed mode) and by the shifted Gamma prior (inferred mode).

Parameters:

posterior (xr.Dataset) – Posterior Dataset carrying nu with shape (chains, draws).

Returns:

Array of shape (chains, draws).

Raises:

ValueError – If the posterior carries no nu.

Return type:

ndarray