SpectralDensity#
- class cultivars.spectral.density.SpectralDensity(result, *, n_frequencies=256)[source]
Bases:
objectThe 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_frequenciespoints 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 returnedSpectralDensityResult.- 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 satisfyingClosedSystemResult.n_frequencies (
int) – Grid points on \([0, \pi]\), endpoints included; at least two.
- Raises:
SpecificationError – If
resultdoes not expose the closed-system surface (an unfitted model, a univariate result, a result without a moving-average representation) orn_frequenciesis 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