cultivars.state_space.regime_switching#
Discrete Markov-switching inference: the Hamilton filter, the Kim smoother, and Kim’s collapse.
A first-order \(K\)-state Markov chain \(S_t\) with row-stochastic transition matrix \(P\), \(P_{ij} = \Pr(S_t = j \mid S_{t-1} = i)\), drives the parameters of an observation model. Given the per-regime conditional densities \(\eta_{t,j} = p(y_t \mid S_t = j, y_{1:t-1})\), the Hamilton filter carries the regime distribution forward,
and the Kim smoother runs it backward,
so that the sum over all \(K^n\) regime paths is carried out
implicitly by a \(K\)-vector. When the observation model is an
autoregression whose intercept and variance switch with the regime,
\(\eta_{t,j}\) depends on \(S_t\) alone and the likelihood is
exact; that is MarkovSwitchingSSM. When each regime carries a
whole linear-Gaussian state space, exact filtering would need one
Kalman filter per regime path, and Kim (1994) collapses instead: run
the \(K \times K\) per-pair Kalman updates, weight them by the
Hamilton probabilities, and moment-match each regime’s mixture back to
a single mean and covariance. That likelihood is an approximation and
RegimeSwitchingLinearSSM labels it one; it is exact in the two
limits that verify it – identical regimes, where it is the Kalman
filter, and a chain with no persistence, where it is the Hamilton
filter over a static mixture.
Two commitments shape the surface. First, the chain and the observation
model are separated: the recursions above know nothing about what
produced the densities, so MarkovSwitchingSSM exposes
density_matrix and filter_densities as well as filter, and
a switching model of any other form – a switching VAR, a switching
variance – reuses the same two recursions by supplying its own
(n, K) matrix of log densities. Second, the conventions are fixed
and stated: transition matrices are row-stochastic and validated as
such, regime vectors are length \(K\) with the prediction step
xi_filt @ transition, and densities are passed in logarithms and
combined by log-sum-exp so that a small-variance regime meeting an
outlier does not underflow to a zero-probability path.
Layout. MarkovSwitchingSSM holds a fully specified
intercept-switching AR model – transition, intercepts,
ar_params of shape (K, p), variances and an optional
initial_prob defaulting to the ergodic distribution – and its
filter returns HamiltonFilterResult with filtered and
predicted regime probabilities on the n - p rows the lags leave,
smooth returns KimSmootherResult, and loglikelihood
the exact likelihood. RegimeSwitchingLinearSSM holds per-regime
system matrices stacked on a leading regime axis with the chain’s
regime_transition; its filter returns KimFilterResult
carrying regime probabilities, the regime-marginal state and its
mixture covariance, and the per-regime collapsed states, and smooth
returns the same KimSmootherResult. All five names are public
aliases of _MarkovSwitchingStateSpace,
_RegimeSwitchingLinearStateSpace and the three result records in
_internals, where the generic hamilton_filter and
kim_smoother recursions the two models share also live, in
_filters and _smoothers. The estimator that fits the switching
autoregression by maximum likelihood, and whose result exposes the
fitted MarkovSwitchingSSM as state_space, is
MSAR; the multivariate
forms are in markov_switching.
References
Hamilton, J. D. (1989). A new approach to the economic analysis of nonstationary time series and the business cycle. Econometrica, 57(2), 357-384.
Hamilton, J. D. (1994). Time Series Analysis, chapter 22. Princeton University Press.
Kim, C.-J. (1994). Dynamic linear models with Markov-switching. Journal of Econometrics, 60(1-2), 1-22.
Kim, C.-J., & Nelson, C. R. (1999). State-Space Models with Regime Switching. MIT Press.
Example
A two-regime intercept-switching AR(1) with persistent regimes: the filter classifies 97% of periods correctly and the smoother 98%. A regime-switching linear model whose two regimes are identical reproduces the Kalman likelihood exactly, which is the first of the two limits that verify Kim’s collapse:
>>> import numpy as np
>>> from cultivars.state_space.linear_gaussian import LinearGaussianSSM
>>> rng = np.random.default_rng(0)
>>> chain = np.array([[0.95, 0.05], [0.1, 0.9]])
>>> regime, y = np.zeros(400, dtype=int), np.zeros(400)
>>> for t in range(1, 400):
... regime[t] = rng.choice(2, p=chain[regime[t - 1]])
... y[t] = [1.0, -1.0][regime[t]] + 0.5 * y[t - 1] + np.sqrt(0.5) * rng.standard_normal()
>>> model = MarkovSwitchingSSM(chain, [1.0, -1.0], [[0.5], [0.5]], [0.5, 0.5])
>>> filtered, smoothed = model.filter(y), model.smooth(y)
>>> filtered.filtered_prob.shape, round(filtered.loglikelihood, 2)
((399, 2), -501.16)
>>> hit = (filtered.filtered_prob[:, 1] > 0.5) == (regime[1:] == 1)
>>> hit_smoothed = (smoothed.smoothed_prob[:, 1] > 0.5) == (regime[1:] == 1)
>>> round(float(hit.mean()), 2), round(float(hit_smoothed.mean()), 2)
(0.97, 0.98)
>>> switching = RegimeSwitchingLinearSSM(
... design=np.ones((2, 1, 1)), obs_cov=np.ones((2, 1, 1)),
... transition=np.full((2, 1, 1), 0.8), state_cov=np.full((2, 1, 1), 0.5),
... regime_transition=chain, initial_state_cov=[[0.5 / (1 - 0.64)]],
... )
>>> linear = LinearGaussianSSM(
... design=[[1.0]], obs_cov=[[1.0]], transition=[[0.8]],
... selection=[[1.0]], state_cov=[[0.5]],
... )
>>> bool(abs(switching.loglikelihood(y) - linear.loglikelihood(y)) < 1e-8)
True