SpectralDensity#

class cultivars.spectral.density.SpectralDensity(result, *, n_frequencies=256)[source]#

The frequency-domain view of any fitted closed reduced form.

Not an estimator: constructs with a fitted result – anything exposing the closed-system surface – and evaluates its exact spectral density matrix

\[f(\omega) = \frac{1}{2\pi}\,\Psi(\omega)\,\Sigma_u\,\Psi(\omega)^*, \qquad \Psi(\omega) = \Bigl[I - \sum_{l=1}^{p} A_l e^{-i\omega l}\Bigr]^{-1},\]

on an inclusive uniform grid of n_frequencies points on \([0, \pi]\). Nothing is re-estimated and no data are read: the coefficient stack \(A_1, \dots, A_p\) and the innovation covariance \(\Sigma_u\) are the whole input, so the same closed form serves an OLS VAR, a VARMA through its autoregressive representation, an error-correction model in levels, or a sparse hundred-variable system. The rest of the surface – spectra, coherence, gain, phase – is read off the returned SpectralDensityResult.

Variables:
  • _result – The fitted closed-system result the density is read from.

  • _n_frequencies – Grid points on \([0, \pi]\), endpoints included.

Parameters:
  • result (ClosedSystemResult) – A fitted closed reduced-form result: anything satisfying ClosedSystemResult.

  • n_frequencies (int) – Grid points on \([0, \pi]\), endpoints included; at least two.

Raises:

SpecificationError – If result does not expose the closed-system surface (an unfitted model, a univariate result, a result without a moving-average representation) or n_frequencies is below two.

Note

The density is the fitted model’s, not the data’s: every specification error the time-domain fit carries – a lag order chosen short, a near-unit root the OLS fit left at 0.98 – is inherited exactly. A near-unit root produces a legitimately enormous spectrum at frequency zero and is not an error; only an exact unit root, which makes the lag polynomial singular, raises, and it does so from compute() rather than from construction. The grid is a choice of resolution, not of estimation: a business cycle band of periods 6 to 32 quarters spans \(\omega \in [0.196, 1.047]\), and at the default 256 points that band holds roughly 70 grid points.

See also

  • SpectralDensityResult – the container this produces.

  • SpectralCausality – the directional decomposition of the same matrix.

  • SpectrumEstimate – the nonparametric estimate from the data, which this class deliberately does not stand in for.

  • Spillover – the other time-domain-free view built on the same closed-system surface.

References

Hamilton, J. D. (1994). Time Series Analysis, chapter 6. Princeton University Press.

Priestley, M. B. (1981). Spectral Analysis and Time Series. Academic Press.

Example

>>> from cultivars.multivariate.reduced_form.vector_autoregression import VAR
>>> rng = np.random.default_rng(0)
>>> y = np.zeros((400, 2))
>>> for t in range(1, 400):
...     y[t] = 0.6 * y[t - 1] + rng.standard_normal(2)
>>> spec = SpectralDensity(VAR(y, order=1).fit(), n_frequencies=128).compute()
>>> spec.density.shape
(128, 2, 2)
>>> bool(np.all(spec.coherence("y1", "y2") <= 1.0 + 1e-12))
True

For a univariate AR(1) the closed form is \(f(0) = \sigma^2 / (2\pi (1 - \phi)^2)\), and the view returns exactly that number at the first grid point:

>>> fit = VAR(y[:, :1], order=1).fit()
>>> phi, sigma2 = float(fit.coefficients[0, 0, 0]), float(fit.sigma_u[0, 0])
>>> spec = SpectralDensity(fit, n_frequencies=16).compute()
>>> bool(np.isclose(spec.spectrum("y1")[0], sigma2 / (2 * np.pi * (1 - phi) ** 2)))
True

Methods#

compute()

Evaluate the exact density on the grid.