cultivars.multivariate.structural.set_identification#
Set identification: the bounds of the set, not a prior’s summary of it.
A sign-restricted model is set-identified, and there are two honest ways to
report the set. sign_restrictions
samples it – quantile bands over accepted rotations, which inherit the
uniform prior over rotations whether or not anyone meant to impose one, the
Baumeister-Hamilton critique. This module reports the set itself: for each
response of each variable at each horizon, the exact smallest and largest
value any admissible rotation delivers. No prior, no draws, no inner
approximation.
The exactness comes from the geometry. With \(\Sigma_u = L L'\) and the moving-average matrices \(\Psi_h\), restrictions on one shock confine its rotation column \(q\) to
with each row of \(Z\) and \(S\) a row \(e_i' \Psi_h L\) of a
response the user has pinned to zero or signed at lead \(h\). Every
response to the shock, \(e_i' \Psi_h L q\), is linear in \(q\), and
a linear functional on the sphere cut by a polyhedral cone attains its
extrema on the region’s faces, so each bound is the largest or smallest
feasible value among the normalized projections of the objective onto every
face’s span (Gafarov, Meier, and Montiel Olea 2018). Zero restrictions ride
along by projecting the whole problem onto their null space first, and
k - 1 of them on one column collapse the set to the recursive point.
What does not fit this geometry is a joint declaration across several
shocks, whose identified set has no face-enumeration form; that case
belongs to the sampling surface, and this model says so rather than
approximating.
Two commitments shape the surface. First, the bounds are the deliverable and nothing is averaged over them: no posterior mean, no median path, no band – a result carries the endpoints and the geometry that produced them, and the summary says in its notes that they depend on no prior and must be read pointwise, since the box the bounds outline across horizons is not the set of admissible paths. Second, exactness is not traded for reach. 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 declaration past the cap is refused with the count, not approximated by sampling behind the user’s back; an empty set, a declaration no rotation satisfies, is likewise a refusal rather than a silent degenerate bound.
Layout. SetIdentifiedSVAR is an _IdentificationModel from
_internals; the sign pattern is compiled by _validate_sign_patterns
in _core, the whitening factor comes from _lower_cholesky, the zero
restrictions are reduced away by _null_basis, the faces are enumerated
once by _face_projectors, and every bound is a call to
_sphere_extrema, all in _core. SetIdentifiedSVARResult
holds that geometry so that bounds at any horizon are a query, and renders
through the shared _SET_BOUNDS_NOTE and _UNIT_SHOCK_NOTE. The same
declaration sampled, and the only route for several restricted shocks, is
sign_restrictions.
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
The same declaration on both surfaces. Every accepted rotation of the sampled set lies inside the exact bounds, and the 16-84 band sits strictly inside them: the band is a statement about the uniform prior, the bounds about the data:
>>> import numpy as np
>>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR
>>> from cultivars.multivariate.structural.sign_restrictions import SignRestrictedSVAR
>>> 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()
>>> signs = {"gdp": "+", "infl": "+"}
>>> exact = SetIdentifiedSVAR(res, signs, shock="demand").identify().irf(4)
>>> sampled = SignRestrictedSVAR(res, {"demand": signs}, draws=500, seed=0).identify()
>>> draws = sampled.irf_draws(4)[:, :, :, 0]
>>> bool(np.all((exact[..., 0] <= draws) & (draws <= exact[..., 1])))
True
>>> band = sampled.irf(4)[:, :, :, 0]
>>> bool(np.all(exact[..., 0] < band[0]) and np.all(band[2] < exact[..., 1]))
True
>>> exact[0].round(3).tolist(), band[[0, 2], 0].round(3).tolist()
([[0.0, 1.123], [0.0, 1.042]], [[0.383, 0.413], [1.102, 1.024]])
Classes#
Exact response bounds under restrictions on one shock, Giacomini-Kitagawa (2021). |
|
The exact bounds of a set-identified shock's response surface. |