NormalInverseWishartPrior#
- class cultivars.bayes.priors.NormalInverseWishartPrior(*, tightness=0.2, decay=1.0, exogenous=100.0, persistence=1.0)[source]#
The conjugate Normal-inverse-Wishart prior: Litterman’s variances, one weight pinned.
Exactly
MinnesotaPriorwithcross_equationfixed at one, and that is not a simplification but a purchase. With \(\lambda_2 = 1\) the prior variance of every coefficient in equation \(i\) is the equation’s residual variance \(s_i^2\) times a factor that depends only on the column,\[\operatorname{Var}(A_{l,ij}) = s_i^2 \cdot \frac{\lambda_1^2}{l^{2\lambda_3}\, s_j^2}, \qquad \operatorname{Var}(c_i) = s_i^2 \cdot (\lambda_1 \lambda_4)^2,\]so the coefficient prior factors as \(\operatorname{vec}(B) \mid \Sigma \sim \mathcal{N}(\operatorname{vec}(B_0), \Sigma \otimes \Omega)\) with \(\Omega\) diagonal. That Kronecker form is the condition for Normal-inverse-Wishart conjugacy (Kadiyala & Karlsson 1997; Banbura, Giannone & Reichlin 2010): the joint posterior over coefficients and covariance is then exact, its marginal likelihood has a closed form, and hierarchical shrinkage in the manner of Giannone, Lenza, and Primiceri (2015) becomes optimization of that closed form rather than simulation. What is given up is Litterman’s claim that cross-variable coefficients deserve extra shrinkage; a caller who wants that claim back passes a
MinnesotaPriorto the point-estimate or Gibbs path instead, and the conjugateBVARrefuses it at fit by construction rather than approximating.The prior is proper, which is what a marginal likelihood requires: every variance is finite, so
exogenousis large but not infinite and “flat” is not on offer here. Dummy-observation content – the sum-of-coefficients and dummy-initial-observation rows – is not a field of this prior but a composition:NormalInverseWishartPrior() + SumOfCoefficientsPrior().- Variables:
tightness – Overall confidence, \(\lambda_1\).
decay – Lag decay, \(\lambda_3\).
exogenous – Looseness of the intercept and exogenous block, \(\lambda_4\). Large but finite: the marginal likelihood requires a proper prior, so “flat” is not on offer here.
persistence – Prior mean of each variable’s own first lag. One is the random-walk prior for levels; zero suits differenced data.
- Raises:
SpecificationError – When a hyperparameter is used, not at construction: if
tightnessorexogenousis not positive ordecayis negative, at the firstcoefficient_mean()orcoefficient_variance()call, which is the model’s fit.DimensionError – If
persistenceis a sequence whose length is not the number of variables, at the same point.
See also
MinnesotaPrior– the general form with the cross-equation weight free, for the point-estimate and Gibbs paths.SumOfCoefficientsPriorandDummyInitialObservationPrior– the dummy-observation components this prior composes with.BVAR– the conjugate model this prior is the default for.HierarchicalBVAR– treatstightnessanddecayas unknowns and maximizes the closed-form marginal likelihood over them.
References
Kadiyala, K. R., & Karlsson, S. (1997). Numerical methods for estimation and inference in Bayesian VAR-models. Journal of Applied Econometrics, 12(2), 99-132.
Banbura, M., Giannone, D., & Reichlin, L. (2010). Large Bayesian vector auto regressions. Journal of Applied Econometrics, 25(1), 71-92.
Giannone, D., Lenza, M., & Primiceri, G. E. (2015). Prior selection for vector autoregressions. Review of Economics and Statistics, 97(2), 436-451.
Example
The default prior of the conjugate BVAR, tightened; the fit is exact and reports a closed-form marginal likelihood:
>>> import numpy as np >>> from cultivars.multivariate.large_dim.bayesian import BVAR >>> rng = np.random.default_rng(0) >>> y = np.zeros((120, 2)) >>> for t in range(1, 120): ... y[t] = 0.7 * y[t - 1] + rng.standard_normal(2) >>> prior = NormalInverseWishartPrior(tightness=0.1) >>> res = BVAR(y, order=1, prior=prior).fit(n_draws=100, seed=0) >>> res.prior_label, round(res.log_marginal_likelihood, 1) ('niw(l1=0.1, l3=1, l4=100)', -357.8)
Its variances are Litterman’s with the cross-equation weight at one, so the cross-lag entries carry only the scale ratio:
>>> from cultivars.bayes.priors import PriorContext >>> ctx = PriorContext( ... k_endog=2, order=1, scales=np.array([1.0, 4.0]), ... presample_mean=np.zeros(2), ... ) >>> NormalInverseWishartPrior().coefficient_variance(ctx).round(4) array([[4.0e+02, 6.4e+03], [4.0e-02, 6.4e-01], [2.5e-03, 4.0e-02]]) >>> minnesota = MinnesotaPrior(cross_equation=1.0).coefficient_variance(ctx) >>> np.array_equal(NormalInverseWishartPrior().coefficient_variance(ctx), minnesota) True
A Minnesota prior with any other cross-equation weight is refused by the conjugate model at fit:
>>> model = BVAR(y, order=1, prior=MinnesotaPrior(cross_equation=0.5)) >>> model.fit(n_draws=10, seed=0) Traceback (most recent call last): ... cultivars.exceptions.SpecificationError: the prior variance does not factor ...
Attributes#
Overall confidence \(\lambda_1\); the standard deviation of an own first lag. |
|
Lag decay \(\lambda_3\); lag \(l\) is shrunk by \(l^{-\lambda_3}\). |
|
Looseness of the deterministic and exogenous block \(\lambda_4\). |
|
a scalar, or one value per variable. |
Methods#
|
Each variable's own first lag at |
|
Litterman's variances with the cross-equation weight at one. |
|
Artificial rows this prior contributes, for restrictions on sums. |