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):
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_intervalDK’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_varianceSample variance of the first differences of the first series, NaNs skipped; the scale of the default start values.