IdentifiedVAR#

class impulso.identified.IdentifiedVAR(*, idata, n_lags, data, var_names, volatility, error_dist=<factory>, scheme)[source]#

Bases: ImpulsoBaseModel

Immutable structural VAR with identified shocks.

The reduced-form VAR fits the dynamics — lag coefficients and an error covariance — with no economic interpretation attached: its residuals are correlated linear combinations of whatever drives the system. The structural VAR adds an identification scheme, a mapping from those reduced-form residuals to economically meaningful shocks, and every structural quantity below is computed through it.

Identification is confined to the endogenous block. Structural shocks, impulse responses, and the scheme itself are defined over the endogenous variables only. Exogenous regressors are exogenous by assumption, so there is nothing to identify; their effects are reported as dynamic multipliers on FittedVAR.dynamic_multiplier instead, where the driver is already exogenous and no scheme is involved.

Parameters:
idata#

InferenceData-schema container with the reduced-form posterior (B, intercept, L, …) — arviz.InferenceData on ArviZ 0, xarray.DataTree on ArviZ 1.

Type:

arviz.data.inference_data.InferenceData

n_lags#

Lag order.

Type:

int

data#

Original VARData.

Type:

impulso.data.VARData

var_names#

Endogenous variable names.

Type:

list[str]

volatility#

Volatility process carried through from the fitted VAR. Required for at= queries on impulse_response / fevd / historical_decomposition (P3), which re-call volatility.cholesky_at(at) for the requested time slice.

Type:

impulso.protocols.VolatilityProcess

error_dist#

Observation error distribution carried through from the fitted VAR (defaults to Gaussian()). Identification itself is unaffected — every scheme factorises the scale matrix — but structural_scenario refuses to run under a heavy-tailed law.

Type:

impulso.protocols.ErrorDistribution

scheme#

Identification scheme used to produce the structural shock matrix. Required for at= queries so the scheme can be re-applied to a different Cholesky factor on demand.

Type:

impulso.protocols.IdentificationScheme

counterfactual(shocks, start=None, end=None)[source]#

Historical counterfactual: edit realised structural shocks and re-propagate.

Backs out the realised structural shocks per posterior draw (eps_t = P_t⁻¹ u_t), overwrites the paths named by shocks (ShockPath values are in one-standard-deviation units; 0.0 switches a shock off; windows resolve against the lag-trimmed index), and re-runs the lag recursion from the actual initial conditions. Realised shocks are edited, never re-drawn, so the posterior spread of the counterfactual reflects parameter and identification uncertainty only. With shocks=[] the observed sample is reproduced exactly.

For a shock zeroed over the full sample, actual - counterfactual equals that shock’s historical- decomposition contribution exactly, per draw. For a windowed zero-edit it instead equals the propagation of the shock’s innovations dated inside the window only — zero before the window, persisting (decaying under stability) after it — which is not the windowed slice of the full-sample HD contribution (that slice also carries earlier impulses).

Note

The Lucas critique applies: fixed-path shock edits assume the estimated reduced-form dynamics are invariant to the intervention. Policy-rule replacement is a different object and out of scope.

Note

Valid under every error distribution, and exactly invariant to the scale-vs-covariance convention for zero edits: shocks are backed out as eps = P⁻¹u and re-propagated as P eps, so rescaling P -> cP leaves P eps untouched. Non-zero ShockPath values are the exception — they are stated in units of one column of P, which under Student-t errors is one scale unit rather than one unconditional standard deviation (multiply by sqrt(nu/(nu-2)) for the sd convention).

Parameters:
  • shocks (list[ShockPath]) – ShockPath edits to impose (may be empty).

  • start (Timestamp | None) – Optional start of the returned window. The simulation always runs from the sample start; start/end only slice the output (the historical_decomposition convention). Edit windows live on the ShockPath objects.

  • end (Timestamp | None) – Optional end of the returned window.

Returns:

CounterfactualResult carrying the counterfactual draws and the actual path over the same window.

Return type:

CounterfactualResult

fevd(horizon=20, at=None)[source]#

Compute forecast error variance decomposition.

Under partial identification (any shock column labelled unidentified_*), the shares of the unidentified columns are masked to NaN — see _fevd_guard().

Note

FEVD is exactly invariant to the scale-vs-covariance convention, so nothing here needs a Student-t correction: the shares are built from Theta = Phi @ P, and rescaling P -> cP multiplies numerator and denominator by c^2, which cancels. Do not “fix” this by inflating P by sqrt(nu/(nu-2)) — it would change nothing except the rounding. See ADR-0007.

Parameters:
  • horizon (int) – Number of periods.

  • at (int | Literal['last', 'all'] | None) – Time index for the structural shock matrix (see shock_matrix() for accepted forms).

Returns:

FEVDResult with FEVD posterior draws.

Return type:

FEVDResult

historical_decomposition(start=None, end=None, at=None)[source]#

Compute the propagated historical decomposition of the observed series.

Attributes each in-sample observation to a deterministic baseline (initial conditions, intercept, and any exogenous path) plus the propagated contribution of each structural shock,

c_{j,t} = P_t[:, j] eps_{j,t} + sum_i A_i c_{j,t-i},

so that y_t = baseline_t + sum_j c_{j,t} holds exactly for every posterior draw. Contributions carry forward through the lag dynamics: a shock keeps contributing beyond its impact period. The at= parameter controls which Cholesky factor identifies the shocks.

Under partial identification (shock columns labelled unidentified_*), the individual contributions of the unidentified shocks are rotation-arbitrary, but their sum is well-defined (it is the variation the identified shocks do not explain). Those columns are therefore collapsed into a single unidentified_remainder column. Propagation is linear in the impact, so the decomposition remains exactly additive and the identified shocks’ contributions stay invariant to both the orthogonal completion and any unit-effect column rescaling.

Note

Breaking change (scenario-analysis stack, 2026-07): earlier releases decomposed only the contemporaneous residual u_t = sum_j P[:, j] eps_{j,t} and offered a plain cumulative sum via cumulative=. The decomposition now propagates through the lag dynamics and always satisfies the additivity identity; the cumulative parameter is retired.

Parameters:
  • start (Timestamp | None) – Optional start date to restrict the returned window. Contributions are always propagated from the start of the estimation sample; the filter only slices the output.

  • end (Timestamp | None) – Optional end date to restrict the returned window.

  • at (int | Literal['last', 'all'] | None) – Time index for the structural shock matrix. None or “all” → per-t identification (correct for SV, identical to single-L under constant volatility). int or “last” → single-L hypothetical (warns under SV).

Returns:

HistoricalDecompositionResult carrying the contribution draws (“hd”) and the deterministic baseline (“baseline”).

Return type:

HistoricalDecompositionResult

impulse_response(horizon=20, at=None)[source]#

Compute structural impulse response functions.

The impulse response traces the dynamic response of each endogenous variable to a unit structural shock at horizons 0..h. It is built from the reduced-form lag matrices A_1..A_p, propagated into MA coefficients Phi_h, and the structural shock matrix P: the response at horizon h is Phi_h @ P.

Note

Shock size under heavy-tailed errors. A “unit shock” is one column of the structural matrix P, which factorises the scale matrix Ω. Under Gaussian errors that is one unconditional standard deviation. Under Student-t errors it is one scale unit, which is sqrt((nu-2)/nu) unconditional standard deviations — about 0.82 sd at nu = 6. Responses are therefore smaller by that constant factor than the one-standard-deviation convention; multiply by sqrt(nu/(nu-2)) to restore it. Relative shapes, ratios between responses, and FEVD shares are unaffected. See ADR-0007.

Parameters:
  • horizon (int) – Number of periods.

  • at (int | Literal['last', 'all'] | None) – Time index for the structural shock matrix (see shock_matrix() for accepted forms).

Returns:

IRFResult with IRF posterior draws.

Return type:

IRFResult

model_config = {'arbitrary_types_allowed': True, 'frozen': True}#

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

shock_matrix(at=None)[source]#

Query the structural shock matrix at a given time index.

This is the single pathway from the volatility process and identification scheme to a labelled structural shock matrix. IRF, FEVD, and historical decomposition all compute through it. Results are memoised per at value on this instance so that all quantities from one IdentifiedVAR share the same structural draws (deterministic per object, even under SignRestriction).

Parameters:

at (int | Literal['last', 'all'] | None) – Time index. None or "last" → most recent slice. An integer t → that specific time index. "all" → full time path (adds a time dim).

Returns:

DataArray with dims (chain, draw[, time], response, shock).

Raises:

ValueError – If at="all" under constant volatility.

Return type:

DataArray

property shock_names: list[str]#

Shock coordinate labels from the identification scheme.

structural_scenario(steps, conditions=None, shocks=None, adjusting=None, include_shock_uncertainty=True, seed=None, exog_future=None, path_uncertainty='none')[source]#

Structural scenario: conditions absorbed by named shocks, paths prescribed.

The ADPRR structural scenario (Antolín-Díaz, Petrella & Rubio-Ramírez 2021), combinable in both flavours: conditional-on-observablesVariablePath pins that must be absorbed by the adjusting shocks while non-adjusting shocks keep their unconditional draws — and conditional-on-shocks — forecast-side ShockPath prescriptions, substituted outright (positional from step 1; a prescription always wins over adjusting membership at its steps). With adjusting=None all shocks adjust, and with no prescriptions the result reproduces conditional_forecast (exactly per draw under natural-order Cholesky identification with a matched seed).

Feasibility is enforced twice: once at validation (conditions must not outnumber the effective adjusting entries, globally or in any leading horizon block) and per posterior draw (numerical rank of the adjusting-block constraint matrix — a Cholesky zero can make a condition load on no adjusting shock at its step). Infeasible draws error rather than being dropped, which would condition the posterior on feasibility.

The per-draw plausibility statistic includes the prescribed shocks’ own magnitude: q = c̃’(C_A C_A’)⁻¹c̃ + |v_S|² in one-standard-deviation units — prescribing a 3-sd shock registers as q += 9 even though prescriptions are substituted. The ADPRR-calibrated q_cal is finite only under path_uncertainty=”unconditional” with no prescriptions.

Note

Under time-varying volatility the scheme-identified forecast factors are built per simulated volatility path (conditional-on-path; see ADR-0005), and SignRestriction is not supported there — the scheme re-samples rotations per call, so no single structural coordinate system spans the forecast steps. Under constant volatility the memoised shock_matrix is broadcast, sharing rotation draws with counterfactual and the historical decomposition on this instance.

Note

Gaussian errors only. The ADPRR three-way partition solves a Gaussian conditioning problem for the adjusting block, and the plausibility statistic’s chi^2_r reference assumes Gaussian shocks. Under error_dist=”student_t” this method raises NotImplementedError rather than returning a half-valid answer.

Parameters:
  • steps (int) – Number of forecast steps.

  • conditions (list[VariablePath] | None) – VariablePath pins to be absorbed by the adjusting shocks.

  • shocks (list[ShockPath] | None) – Forecast-side ShockPath prescriptions (no start/end; positional from step 1; NaN = free).

  • adjusting (list[str] | None) – Names of the shocks permitted to absorb the conditions. None (default) lets every shock adjust. Must contain none or all of any unidentified_* columns.

  • include_shock_uncertainty (bool) – Density mode (default) vs mean mode (free block zeroed, conditional mean propagated).

  • seed (int | Generator | None) – RNG seed (int) or Generator.

  • exog_future (ndarray | None) – Future exogenous values, shape (steps, k). Required if the posterior carries B_exog.

  • path_uncertainty (Literal['none', 'unconditional']) – “none” (hard pins) or “unconditional” (pins restrict the mean; bands keep unconditional width).

Returns:

ScenarioResult with forecast draws, the scenario ingredients echoed, and the plausibility statistics.

Raises:
  • NotImplementedError – If the model was fitted with a heavy-tailed error distribution.

  • ValueError – On unknown shocks/variables, unidentified_* references, in-sample windows on prescriptions, duplicate pins or prescriptions, over-determination, per-draw rank failure, SignRestriction under time-varying volatility, an invalid path_uncertainty, or exogenous-data mismatches.

Return type:

ScenarioResult