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 MinnesotaPrior with cross_equation fixed 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 MinnesotaPrior to the point-estimate or Gibbs path instead, and the conjugate BVAR refuses it at fit by construction rather than approximating.

The prior is proper, which is what a marginal likelihood requires: every variance is finite, so exogenous is 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:

See also

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#

tightness

Overall confidence \(\lambda_1\); the standard deviation of an own first lag.

decay

Lag decay \(\lambda_3\); lag \(l\) is shrunk by \(l^{-\lambda_3}\).

exogenous

Looseness of the deterministic and exogenous block \(\lambda_4\).

persistence

a scalar, or one value per variable.

Methods#

coefficient_mean(context)

Each variable's own first lag at persistence, everything else zero.

coefficient_variance(context)

Litterman's variances with the cross-equation weight at one.

dummy_observations(context)

Artificial rows this prior contributes, for restrictions on sums.