PerturbationDSGE.loglikelihood#

PerturbationDSGE.loglikelihood(theta, *, filter='unscented', n_particles=1000, seed=None)[source]#

The log-likelihood (or its particle estimate) at one parameter point.

Parameters:
  • theta (ArrayLike) – Structural parameters.

  • filter (str) – "kalman" (first order only), "extended", "unscented", or "particle".

  • n_particles (int) – Particles under "particle".

  • seed (int | None) – Seed under "particle".

Return type:

float

Returns:

The criterion value; exact only under "kalman".

Example

On a linear model the first-order exact likelihood, the second-order unscented approximation and a well-resourced particle estimate agree; a starved particle filter does not:

>>> import numpy as np
>>> class Latent:
...     parameter_names, bounds = ("rho", "sigma"), ((0.0, 0.99), (0.01, 1.0))
...     n_states, n_controls = 1, 1
...     def equations(self, th, y_next, y, x_next, x):
...         return np.array([y[0] - x[0], x_next[0] - th[0] * x[0]])
...     def steady_state(self, th):
...         return np.zeros(1), np.zeros(1)
...     def shock_loading(self, th):
...         return np.array([[th[1]]])
...     def observation(self, th):
...         return np.array([[0.0, 1.0]]), np.zeros(1), np.array([[1e-4]])
...     def log_prior(self, th):
...         return 0.0
>>> rng = np.random.default_rng(0)
>>> x = np.zeros(200)
>>> for t in range(1, 200):
...     x[t] = 0.8 * x[t - 1] + 0.5 * rng.standard_normal()
>>> first = PerturbationDSGE(Latent(), x, order=1)
>>> exact = first.loglikelihood([0.8, 0.5], filter="kalman")
>>> second = PerturbationDSGE(Latent(), x, order=2)
>>> approx = second.loglikelihood([0.8, 0.5])
>>> rich = second.loglikelihood([0.8, 0.5], filter="particle", n_particles=5000, seed=0)
>>> poor = second.loglikelihood([0.8, 0.5], filter="particle", n_particles=200, seed=0)
>>> bool(abs(approx - exact) < 1.0), bool(abs(rich - exact) < 1.0)
(True, True)
>>> bool(poor < exact - 50)
True