SetIdentifiedSVAR#

class cultivars.multivariate.structural.set_identification.SetIdentifiedSVAR(result, restrictions, *, shock='restricted', horizons=(0,), zeros=None)[source]

Bases: _IdentificationModel[SetIdentifiedSVARResult]

Exact response bounds under restrictions on one shock, Giacomini-Kitagawa (2021).

Declares what SignRestrictedSVAR declares – signs of one shock’s responses at chosen horizons, plus optional exact zeros – but answers a different question: not “what does a uniform prior over admissible rotations imply,” but “what is the smallest and largest each response could be, over every admissible rotation.” The identified set’s endpoints are computed exactly. The restrictions cut the unit sphere by a polyhedral cone; a linear functional on that region attains its extrema on the region’s faces; so each bound is the best feasible candidate among the normalized projections of the objective onto every face span (Gafarov, Meier and Montiel Olea 2018), with the zero restrictions handled first by projecting the whole problem onto their null space.

One shock only, by the geometry: bounds for a single restricted column have a face-enumeration form; a joint declaration across several shocks does not, and belongs to the sampling surface. The face count is combinatorial in the number of sign rows – every declared sign at every declared horizon is a row – so the enumeration is capped, and a large system with signs over many horizons is refused rather than approximated.

Parameters:
  • result (ClosedSystemResult) – The fitted closed reduced-form result.

  • restrictions (Mapping[str, str]) – The restricted shock’s sign pattern, mapping variable to "+" or "-".

  • shock (str) – Label for the restricted shock.

  • horizons (Sequence[int]) – Leads at which every declared sign must hold.

  • zeros (Mapping[str, Sequence[int]] | None) – Optional exact-zero responses, mapping variable to the leads at which its response to this shock is zero. At most k - 1 zero restrictions can bind before no direction remains.

Raises:

SpecificationError – If the result is not a closed system, the declaration is malformed, the zeros span the whole space, or the face count exceeds what exact enumeration can afford.

Variables:
  • _source – The closed reduced-form result being identified.

  • _shock – The restricted shock’s label.

  • _sign_cells – The compiled (variable index, +-1.0) sign pairs.

  • _leads – The horizons at which the signs bind.

  • _zero_cells – The (variable index, lead) zero restrictions.

  • _restriction – The declaration, stated as a sentence.

See also

  • SetIdentifiedSVARResult – the record identify() returns.

  • SignRestrictedSVAR – the same declaration, sampled: quantile bands under the uniform prior over rotations, and the only route for several restricted shocks at once.

  • RecursiveSVAR – the point-identified limit, which k - 1 zeros on one column reproduce.

References

Giacomini, R., & Kitagawa, T. (2021). Robust Bayesian inference for set-identified models. Econometrica, 89(4), 1519-1556.

Gafarov, B., Meier, M., & Montiel Olea, J. L. (2018). Delta-method inference for a class of set-identified SVARs. Journal of Econometrics, 203(2), 316-327.

Baumeister, C., & Hamilton, J. D. (2015). Sign restrictions, structural vector autoregressions, and useful prior information. Econometrica, 83(5), 1963-1999.

Example

A demand shock that raises both output and inflation. The sign declaration alone leaves a wide set; adding one exact zero in a two-variable system collapses it to a point, and that point is the recursive impact column with the zero-restricted variable ordered first:

>>> import numpy as np
>>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR
>>> from cultivars.multivariate.structural.zero_restrictions import RecursiveSVAR
>>> rng = np.random.default_rng(0)
>>> B = np.array([[1.0, 0.5], [0.3, 1.0]])
>>> A = np.array([[0.5, 0.1], [0.0, 0.4]])
>>> eps = rng.standard_normal((402, 2))
>>> y = np.zeros((402, 2))
>>> for t in range(1, 402):
...     y[t] = A @ y[t - 1] + B @ eps[t]
>>> res = VAR(y, order=1, names=("gdp", "infl")).fit()
>>> wide = SetIdentifiedSVAR(res, {"gdp": "+", "infl": "+"}, shock="demand").identify()
>>> wide.impact_bounds.round(3).tolist()
[[0.0, 1.123], [0.0, 1.042]]
>>> point = SetIdentifiedSVAR(res, {"gdp": "+"}, zeros={"infl": [0]}, shock="demand")
>>> point.identify().impact_bounds.round(3).tolist()
[[0.817, 0.817], [0.0, 0.0]]
>>> RecursiveSVAR(res, order=("infl", "gdp")).identify().impact.round(3).tolist()
[[0.771, 0.817], [1.042, 0.0]]

Private attributes#