'Impulse response function
Parameters
steps : int, optional
The number of steps for which impulse responses are calculated.
Default is 10. Note that the initial impulse is not counted as a
step, so if `steps=1`, the output will have 2 entries.
impulse : int or array_like
If an integer, the state innovation to pulse; must be between 0
and `k_posdef-1` where `k_posdef` is the same as in the state
space model. Alternatively, a custom impulse vector may be
provided; must be a column vector with shape `(k_posdef, 1)`.
orthogonalized : boolean, optional
Whether or not to perform impulse using orthogonalized innovations.
Note that this will also affect custum `impulse` vectors. Default
is False.
cumulative : boolean, optional
Whether or not to return cumulative impulse responses. Default is
False.
**kwargs
If the model is time-varying and `steps` is greater than the number
of observations, any of the state space representation matrices
that are time-varying must have updated values provided for the
out-of-sample steps.
For example, if `design` is a time-varying component, `nobs` is 10,
and `steps` is 15, a (`k_endog` x `k_states` x 5) matrix must be
provided with the new design matrix values.
Returns
impulse_responses : array
Responses for each endogenous variable due to the impulse
given by the `impulse` argument. A (steps + 1 x k_endog) array.
Notes
Intercepts in the measurement and state equation are ignored when
calculating impulse responses.'

def impulse_responses(self, steps=10, impulse=0, orthogonalized=False, cumulative=False, **kwargs):

    steps += 1
    if (type(impulse) == int):
        if ((impulse >= self.k_posdef) or (impulse < 0)):
            raise ValueError('Invalid    value    for    `impulse`.    Must    be    the    index    of    one    of    the    state    innovations.')
        idx = impulse
        impulse = np.zeros(self.k_posdef)
        impulse[idx] = 1
    else:
        impulse = np.array(impulse)
        if (impulse.ndim > 1):
            impulse = np.squeeze(impulse)
        if (not (impulse.shape == (self.k_posdef,))):
            raise ValueError(('Invalid    impulse    vector.    Must    be    shaped    (%d,)' % self.k_posdef))
    if orthogonalized:
        state_chol = np.linalg.cholesky(self.state_cov[:, :, 0])
        impulse = np.dot(state_chol, impulse)
    if self.time_invariant:
        design = self.design[:, :, 0]
        transition = self.transition[:, :, 0]
        selection = self.selection[:, :, 0]
        irf = np.zeros((steps, self.k_endog), dtype=self.dtype)
        states = np.zeros((steps, self.k_states), dtype=self.dtype)
        states[0] = np.dot(selection, impulse)
        irf[0] = np.dot(design, states[0])
        for t in range(1, steps):
            states[t] = np.dot(transition, states[(t - 1)])
            irf[t] = np.dot(design, states[t])
    else:
        representation = {}
        for (name, shape) in self.shapes.items():
            if (name in ['obs', 'obs_intercept', 'state_intercept']):
                continue
            representation[name] = getattr(self, name)
        warning = 'Model    has    time-invariant    %s    matrix,    so    the    %s    argument    to    `irf`    has    been    ignored.'
        exception = 'Impulse    response    functions    for    models    with    time-varying    %s    matrix    requires    an    updated    time-varying    matrix    for    any    periods    beyond    those    in    the    original    model.'
        for (name, shape) in self.shapes.items():
            if (name in ['obs', 'obs_intercept', 'state_intercept']):
                continue
            if (representation[name].shape[(-1)] == 1):
                if (name in kwargs):
                    warn((warning % (name, name)), ValueWarning)
            elif (name not in kwargs):
                raise ValueError((exception % name))
            else:
                mat = np.asarray(kwargs[name])
                validate_matrix_shape(name, mat.shape, shape[0], shape[1], steps)
                if ((mat.ndim < 3) or (not (mat.shape[2] == steps))):
                    raise ValueError((exception % name))
                representation[name] = np.c_[(representation[name], mat)]
        model_kwargs = {'filter_method': self.filter_method, 'inversion_method': self.inversion_method, 'stability_method': self.stability_method, 'conserve_memory': self.conserve_memory, 'tolerance': self.tolerance, 'loglikelihood_burn': self.loglikelihood_burn}
        model_kwargs.update(representation)
        model = KalmanFilter(np.zeros(self.endog.T.shape), self.k_states, self.k_posdef, **model_kwargs)
        model.initialize_approximate_diffuse()
        model._initialize_filter()
        model._initialize_state()
        steps += 1
        measurement_shocks = np.zeros((steps, self.k_endog))
        state_shocks = np.zeros((steps, self.k_posdef))
        state_shocks[0] = impulse
        (irf, _) = model.simulate(steps, measurement_shocks=measurement_shocks, state_shocks=state_shocks)
        irf = irf[1:]
    if cumulative:
        irf = np.cumsum(irf, axis=0)
    return irf
