Constant#

class impulso.volatility.Constant(*, name='constant', is_time_varying=False, sigma_sd_beta=2.5, tril_offdiag_sigma=0.5, innovation_scale_priors=None)[source]#

Bases: ImpulsoModel

Homoscedastic volatility — single Σ shared across all time points.

Lifts today’s manual-Cholesky parameterisation from spec.py:_build_pymc_model into the volatility-process seam: HalfCauchy(beta=sigma_sd_beta) on the diagonal scales, Normal(mu=0, sigma=tril_offdiag_sigma) on the lower-triangular off-diagonals (scaled by the row’s diagonal). For n_vars == 1 the off-diagonal block is empty.

The factor is assembled from primitives rather than with PyMC’s purpose-built LKJCholeskyCov / LKJCorr because those are broken on the dependency set Impulso supports — an einsum unpacking bug — so the obvious built-in is not an option here. See docs/adr/0014-manual-cholesky-parameterisation.md.

The PyMC variable names produced inside build_pymc_latent (sigma_sd, tril_offdiag) match today’s posterior contents exactly so existing identification and downstream code keep working unchanged. The Sigma = L @ L.T deterministic is registered by the caller in spec.py, not by the adapter.

Under ADR-0014’s manual Cholesky, row i’s Cholesky diagonal entry is only exactly that row’s innovation standard deviation for the first variable (i == 0): row 0 has no lower-triangular entries, so its innovation sd is sd[0] exactly. Every other row also carries off-diagonal tril_offdiag entries, so row i’s actual innovation sd is sd[i] * sqrt(1 + sum_j tril_ij**2), not sd[i] alone. A per-variable prior placed on the diagonal (via innovation_scale_priors) therefore means exactly what it says only for the first variable; for later variables it is a prior on one factor of a larger quantity.

Parameters:
name#

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

Type:

Literal[‘constant’]

is_time_varying#

Always False — Σ is shared across t.

Type:

bool

sigma_sd_beta#

HalfCauchy scale on diagonal SDs. Ignored when innovation_scale_priors is set.

Type:

float

tril_offdiag_sigma#

Normal SD on off-diagonal correlation factors.

Type:

float

innovation_scale_priors#

Optional per-variable override of the diagonal prior, one InnovationScalePrior per endogenous variable in data.endog_names order. None (the default) reproduces today’s behaviour exactly: a single vectorised HalfCauchy(sigma_sd_beta) registered as sigma_sd, with the same log-probability as before this field existed. When set, build_pymc_latent instead registers one scalar RV per variable, named sigma_sd_0, sigma_sd_1, … in variable order (mixed families are allowed), which are stacked into the diagonal — a different posterior variable layout from the default, documented here rather than silently changed. The length must equal n_vars; a mismatch raises ValueError from build_pymc_latent (this model does not know n_vars at construction time, so the check cannot happen earlier).

Type:

tuple[impulso.volatility.InnovationScalePrior, …] | None

build_pymc_latent(n_vars, T, data=None)[source]#

Register the constant-volatility latent vars in the active PyMC model.

Lifts the manual-Cholesky parameterisation from the previous location in spec.py:_build_pymc_model. When innovation_scale_priors is unset, PyMC variable names (sigma_sd, tril_offdiag) match the prior contents byte-for-byte so existing posterior-consuming code keeps working unchanged. When it is set, the diagonal is instead registered as one scalar RV per variable (sigma_sd_0, sigma_sd_1, …) — see innovation_scale_priors.

Parameters:
  • n_vars (int) – Number of endogenous variables.

  • T (int) – Number of observations after lag trimming. Ignored for constant volatility — kept in the signature for parity with stochastic adapters.

  • data (ndarray | None) – Accepted for Protocol parity with stochastic adapters and ignored — Σ is data-independent in the constant case.

Returns:

Lower-triangular Cholesky factor L of shape (n_vars, n_vars).

Raises:

ValueError – innovation_scale_priors is set and its length does not equal n_vars.

Return type:

pt.TensorVariable

cholesky_at(posterior, t)[source]#

Return the lower-triangular Cholesky factor of Σ for every draw.

Reads posterior[“L”] directly — the factor is registered as a deterministic in build_pymc_latent so this method does not re-decompose Σ. For constant volatility, t is ignored.

Parameters:
  • posterior (xr.Dataset) – An xarray Dataset (typically idata.posterior) containing L of shape (chains, draws, n_vars, n_vars).

  • t (int | None) – Time index. Ignored.

Returns:

Cholesky factors of shape (chains, draws, n_vars, n_vars).

Return type:

ndarray

cholesky_path(posterior, T)[source]#

Broadcast the constant Cholesky factor across all in-sample t.

For constant volatility there is no per-t variation; this is a broadcast convenience for the IdentifiedVAR query layer.

Parameters:
  • posterior (xr.Dataset) – An xarray Dataset containing L of shape (chains, draws, n_vars, n_vars). Read via Constant.cholesky_at, which is the canonical accessor.

  • T (int) – In-sample length (after lag trimming).

Returns:

Cholesky factor path of shape (chains, draws, T, n_vars, n_vars).

Return type:

ndarray

forecast_cholesky_path(posterior, steps, rng)[source]#

Broadcast the constant Cholesky factor across forecast steps.

For constant volatility there is nothing to simulate — the forecast covariance equals the in-sample covariance. rng is accepted for signature parity with stochastic adapters and is ignored.

Parameters:
  • posterior (xr.Dataset) – An xarray Dataset containing L of shape (chains, draws, n_vars, n_vars). Read via Constant.cholesky_at, which is the canonical accessor.

  • steps (int) – Forecast horizon.

  • rng (Generator) – Unused.

Returns:

Cholesky factor path of shape (chains, draws, steps, n_vars, n_vars).

Return type:

ndarray

model_config = {'frozen': True}#

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