# How to specify a time-varying matrix in a state space model via statsmodels in Python

As far as I see, the parameters of state space models will be automatically treated as time-invariant and I don't know how to specify a time-varying matrix in a state space model via statsmodels in Python. I have read the document, but still failed to find a way.

statsmodels document: https://www.statsmodels.org/stable/statespace.html

For example, for the simplest local level model

\begin{align*} X_{t+1} &= X_t + V_t \\ Y_t &= X_t + W_t \end{align*}

where $$V$$ and $$W$$ are white noises. When estimating this model, the initial state must be specified. However, if I assmue the initial state $$X_1$$ is an unknown constant (thus a parameter) that should also be estimated, then the model above can be formulated as

\begin{align*} X_{t+1} &= a_t + X_t + V_t \\ Y_t &= X_t + W_t \end{align*}

where for $$j \ge 2$$, $$a_j=0$$. And with this formulation, I can initially set $$X_1 = 0$$ and estimate $$a_1$$ as if it was the unknown constant $$X_1$$. Then $$a_t$$ is a time-varying parameter.

Note that in this simple model, the difficulty mentioned above can be circumvented by formulating as \begin{align*} X_{t+1} &= X_t + V_t \\ Y_t &= b_t + X_t + W_t \end{align*} set $$X_1 = 0$$, and estimate $$b_t$$ as an time-invariant parameter. But in general, I can not do so.

class LocalLevel(sm.tsa.statespace.MLEModel):

def __init__(self, endog):
super().__init__(endog, k_states=1)

self['design', 0, 0] = 1.0
self['transition', 0, 0] = 1.0
self['selection', 0, 0] = 1.0

self.initialize_known([0], [[0]])

# Note the **kwargs argument must be included
def update(self, params, transformed=True, **kwargs):
params = super().update(params, transformed, **kwargs)
self['obs_cov', 0, 0] = params[0]
self['state_cov', 0, 0] = params[1]
self['obs_intercept', 0, 0] = params[2]

@property
def start_params(self):
return [1.0, 1.0, 0]


I wonder how can I set 'state_intercept' to be time-varying. It should be k_state * n_obs dimension, but I when I tried to set self['state_intercept'], I found it was treated automatically as k_state * 1 dimension, which is time-invariant.

The solution is to define the matrix to be time-varying in the constructor:

self['state_intercept'] = np.zeros((self.k_states, self.nobs))


so the full code would be:

class LocalLevel(sm.tsa.statespace.MLEModel):

def __init__(self, endog):
super().__init__(endog, k_states=1)

self['design', 0, 0] = 1.0
self['transition', 0, 0] = 1.0
self['selection', 0, 0] = 1.0
self['state_intercept'] = np.zeros((self.k_states, self.nobs))

self.initialize_known([0], [[0]])

# Note the **kwargs argument must be included
def update(self, params, transformed=True, **kwargs):
params = super().update(params, transformed, **kwargs)
self['obs_cov', 0, 0] = params[0]
self['state_cov', 0, 0] = params[1]
self['obs_intercept', 0, 0] = params[2]

@property
def start_params(self):
return [1.0, 1.0, 0]