statespace_components#

Model components and their assembly into a state space model (DK ch. 3).

A component owns a block of the state vector: its columns of Z, its diagonal blocks of T, R and Q, and optionally a contribution to H (the irregular). structural_model stacks components as DK do (DK eq. 3.10):

\[Z_t = (Z^{[1]}_t, Z^{[2]}_t, \dots), \quad T_t = \mathrm{diag}(T^{[1]}_t, T^{[2]}_t, \dots),\]

and likewise R and Q, and initializes each block as the component asks: diffuse for nonstationary components, stationary for ARMA blocks and damped cycles (DK §5.6). The result is an ordinary ssm_model_t.

Signal loadings (DK §3.3.2, §3.3.3, §3.7): with loading (p × p_sig), the components describe p_sig signals and the observations load on them, \(Z_t = \Lambda Z^{sig}_t\); loading_free marks entries of \(\Lambda\) that are parameters, appended after the components’ parameters. Components whose observation_level() is true (the irregular, intercepts) act on the observations directly.

The built-in components are in statespace_structural and statespace_arima.

Example#

From example/dk_8_2_seatbelt.f90: a basic structural model, then the same with regression effects.

type(component_holder_t) :: comps(4)
type(structural_model_t) :: model

allocate (irregular_t :: comps(1)%c)
allocate (level_t :: comps(2)%c)
comps(3)%c = seasonal_t(period=12, form=SEASONAL_TRIG)
model = structural_model(y, comps(1:3), info)
call fit(model, res, info=info)

comps(4)%c = regression_t(x=x)             ! petrol price and the seat belt law
model = structural_model(y, comps, info)

component_t#

type, abstract :: component_t
  integer :: p, n        ! series and observations (set by setup)
  integer :: m, r, k     ! states, disturbances, parameters
  logical :: tv_Z, tv_H, tv_T, tv_R, tv_Q    ! time-varying matrices
  logical :: at_observations = .false.
contains
  procedure(setup_iface), deferred :: setup
  procedure(fill_iface), deferred :: fill
  procedure :: transform_params, untransform_params, start_params
  procedure :: param_names, init_blocks, observation_level
end type

To write a component, implement

subroutine setup(self, y)                      ! set m, r, k and the tv_ flags
  class(component_t), intent(inout) :: self
  real(dp), intent(in) :: y(:, :)              ! the data (or the signals' stand-ins)

subroutine fill(self, params, Z, H, T, R, Q)   ! write the blocks from constrained params
  class(component_t), intent(in) :: self
  real(dp), intent(in) :: params(:)
  real(dp), intent(inout) :: Z(:, :, :), H(:, :, :), T(:, :, :), R(:, :, :), Q(:, :, :)

fill receives the component’s blocks with time dimension 1 or n as its flags say; H is the full observation variance, to which only observation-level components add. The defaults of the other procedures: no transform, start values 0.1, names param1, ..., the whole block diffuse (init_blocks returns rows [first, last, kind] relative to the block), acting at the signals.

component_holder_t#

type :: component_holder_t
  class(component_t), allocatable :: c
end type

An array element holding any component.

structural_model_t#

type, extends(ssm_model_t) :: structural_model_t
  type(component_holder_t), allocatable :: comps(:)
  integer, allocatable :: s0(:), e0(:), k0(:)    ! offsets of states, disturbances, parameters
  real(dp), allocatable :: loading(:, :)         ! (p, p_sig)
  logical, allocatable :: loading_free(:, :)
  integer :: k_comp                              ! parameters of the components
  real(dp), allocatable :: Zsig(:, :, :)         ! (p_sig, m, 1|n)
end type

Component i owns states s0(i)+1 .. s0(i)+comps(i)%c%m. Parameters are ordered component by component, then the free loadings.

structural_model#

function structural_model(y, comps, info, loading, loading_free) result(model)
  real(dp), intent(in) :: y(:, :)                        ! (p, n)
  type(component_holder_t), intent(in) :: comps(:)
  integer, intent(out) :: info
  real(dp), intent(in), optional :: loading(:, :)        ! (p, p_sig)
  logical, intent(in), optional :: loading_free(:, :)
  type(structural_model_t) :: model

Assemble the components; the model starts at its start parameters.

Helpers#

elemental real(dp) function constrain_interval(psi, lo, hi) result(chi)
elemental real(dp) function unconstrain_interval(chi, lo, hi) result(psi)
real(dp) function diff_variance(y) result(v)
constrain_interval

DK’s bounded transform (§7.3.2) shifted to an interval: \(\chi = m + h \psi / \sqrt{1 + \psi^2}\) maps the real line onto (lo, hi), with m and h the midpoint and half-width.

diff_variance

Sample variance of the first differences of the first series, NaNs skipped; the scale of the default start values.