LongRunRestriction#
- class impulso.identification.LongRunRestriction(*, ordering, shock_names=None, on_undefined='nan', max_condition=100000000.0)[source]#
Bases:
ImpulsoModelLong-run (cumulative) zero restrictions, Blanchard-Quah style.
Cholesky and sign restrictions constrain the impact matrix Theta(0) = P. This scheme constrains the cumulative matrix Theta(1): the total effect of each structural shock on the level of each variable, summed over all horizons. For a stable reduced-form VAR with moving-average coefficients Phi_h, the long-run multiplier is
C(1) = sum_h Phi_h = (I - sum_j A_j)^-1 = M^-1, M = I - sum_j A_j,
and the cumulative structural impact is Theta(1) = C(1) P. The restriction imposed here is that Theta(1) is lower-triangular in the coordinates given by ordering: shock j has no long-run effect on the variables ordered before it. In the Blanchard-Quah (1989) two- variable case — ordering=[“output_growth”, “unemployment”], shock_names=[“supply”, “demand”] — the single zero says the demand shock has no permanent effect on the level of output.
Construction is closed-form. Theta(1) Theta(1)’ = C(1) Sigma C(1)’, so the lower-triangular positive-diagonal Theta(1) is the Cholesky factor of that matrix and P = M Theta(1). Internally the equivalent QR route is used: with L L’ = Sigma, factor (M^-1 L)’ = Q R and set P = L Q (sign-fixed so diag(R) > 0). Because Q is orthogonal, P P’ = Sigma holds to machine precision, and the conditioning of C(1) Sigma C(1)’ is never squared. It also shows what the scheme is: a rotation of the Cholesky factor, chosen in closed form rather than searched for.
Sign convention: the diagonal of Theta(1) is positive, so shock j raises variable j’s long-run level. The diagonal of P itself may be negative — the normalisation is on the cumulative matrix, not on impact.
Restriction count: triangularity is n(n-1)/2 restrictions, exactly the number needed for point identification. For n = 2 that is the single restriction users usually want. For larger n it asserts every zero above the diagonal, which is a strong joint claim. Arbitrary (non-recursive) long-run zero patterns are out of scope, as are partial long-run identification and mixed short-run/long-run schemes (Gali 1999).
Two failure modes are reported separately:
M near-singular (C(1) numerically undefined). Controlled by on_undefined and max_condition.
Explosive draws (companion spectral radius above one). M may be perfectly well conditioned while sum_h Phi_h diverges, so the arithmetic succeeds but the interpretation does not. Those draws are always returned finite and always warned about.
- Parameters:
- ordering#
Variable names ordered most long-run-restricted first, mirroring Cholesky.ordering. The returned matrix keeps its rows in the data’s own variable order; ordering only defines the coordinates in which Theta(1) is triangular.
- shock_names#
Labels for the shock columns, in the same order as ordering. None (default) reuses ordering as the labels. Explicit naming is strongly preferred — the whole point of the scheme is that the columns mean something.
- on_undefined#
“nan” (default) blanks draws whose M is numerically singular and warns; “raise” errors instead. NaN draws propagate into IRF/FEVD and are rejected outright by the scenario methods, so “raise” catches the problem early.
- Type:
Literal[‘nan’, ‘raise’]
- max_condition#
Condition-number threshold above which M counts as numerically singular. Default 1e8.
- Type:
Note
C(1) is the long-run multiplier on the levels of the modelled variables. “No permanent effect on output” therefore requires output to enter the VAR as a growth rate; the library cannot check this for you.
- classmethod from_zero_restrictions(restrictions, var_names, shock_names, **kwargs)[source]#
Build the scheme from named long-run zeros instead of an ordering.
Each entry maps a variable to the shocks that must have no long-run effect on it. The pattern must be triangular under some ordering of the variables and some ordering of the shocks — that is what a recursive long-run scheme means — and this constructor recovers both from the restriction counts. Neither var_names nor shock_names need be given in that order; the names are labels, the pattern decides the positions.
- Parameters:
restrictions (dict[str, list[str]]) – Variable name -> list of shock names with zero long-run effect on it. Variables with no zeros may be omitted.
var_names (list[str]) – All endogenous variable names, in any order.
shock_names (list[str]) – All shock names, in any order. Their order in the returned scheme is inferred from the pattern.
**kwargs – Forwarded to the constructor (on_undefined, max_condition).
- Returns:
A LongRunRestriction whose ordering and shock_names reproduce the named pattern.
- Raises:
ValueError – If a name is unknown, or the pattern is not triangular under any pair of orderings.
- Return type:
- identify(L, var_names, posterior=None, data=None, n_lags=None)[source]#
Apply long-run-restriction identification.
- Parameters:
L (ndarray) – Lower-triangular Cholesky factor, shape (chains, draws, n_vars, n_vars).
var_names (list[str]) – Variable names in the data’s natural order.
posterior (xr.Dataset | None) – Full posterior; required, because the long-run multiplier is built from the lag coefficients B.
data (VARData | None) – Unused. Accepted for Protocol uniformity.
n_lags (int | None) – Lag order. Inferred from B’s trailing axis if omitted.
- Returns:
Structural shock matrix, shape (chains, draws, n_vars, n_vars). Rows follow var_names (the data’s order); columns follow shock_coords. Draws whose long-run multiplier is numerically undefined are NaN when on_undefined=”nan”.
- Raises:
ValueError – If posterior is missing or carries no B, if ordering does not match var_names, or if on_undefined=”raise” and some draw is undefined.
- Return type:
- property last_diagnostics: dict[str, float]#
Diagnostics from the most recent identify() call.
Scheme-prefixed scalars (see CONTEXT.md “Identification diagnostics”), overwritten per call and surfaced onto IdentifiedVAR.shock_matrix().attrs by the pipeline. Returns a copy.
- long_run_diagnostics(posterior, n_lags=None)[source]#
Per-draw conditioning and stability of the long-run multiplier.
- Parameters:
posterior (Dataset) – Posterior Dataset carrying B (the fit’s posterior group).
n_lags (int | None) – Lag order. Inferred from B’s trailing axis if omitted.
- Returns:
Dict with “condition” — the condition number of M = I - sum_j A_j, shape (chains, draws) — and “spectral_radius”, the companion spectral radius, same shape. A large condition number means C(1) is barely defined; a spectral radius above one means the long-run sum diverges.
- Return type:
- model_config = {'frozen': True}#
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- model_post_init(context, /)#
This function is meant to behave like a BaseModel method to initialize private attributes.
It takes context as an argument since that’s what pydantic-core passes when calling it.
- Parameters:
self (BaseModel) – The BaseModel instance.
context (Any) – The context.
- Return type:
None