Defining SSMs using GenericSSMs.jl
For a quick reference to the information provided here, type ?GenericSSM in the Julia REPL after loading GenericSSMs.jl.
Overview
To use GenericSSMs.jl for the inference of an SSM, the user should define:
- A model struct representing the SSM.
- Methods that implement the SSM.
Steps 1 and 2 above are described below.
Model struct and the abstract type GenericSSM
The model struct is a normal Julia struct that represents a user-defined SSM. The purpose of the model struct is to act as:
- a type for code selection using multiple dispatch,
- a container for data and parameters related to the SSM.
All model structs need to be defined as a subtype of GenericSSM, which is an abstract type exported by GenericSSMs.jl. Otherwise, the user is free to define the model struct as they see fit for their problem.
As an example, to define an SSM named Model that has floating point observations, the following model struct could be used:
struct Model <: GenericSSM
y::Vector{Float64}
# .. arbitrary fields such as other data and parameters related to the SSM ..
endHere, the fieldname y is an arbitrary choice. Naturally, it is also possible to use type parameters in the definition of the model struct.
There are two optional methods that the user may wish to overload for their model struct:
GenericSSMs.particle_type — Functionparticle_type(model::GenericSSM)
Return the particle type associated with the model model. There exists a fallback for this function, which calls M1 and checks the type of the output.
GenericSSMs.observation_type — Functionobservation_type(model::GenericSSM)
Return the observation type associated with the model model. There exists a fallback for this function, which calls gk and checks the type of the output.
As discussed above, both of these functions have fallbacks that work once the user defines some of the methods discussed in the next section.
The section Examples contains examples of model struct definitions for more concrete SSMs.
Methods implementable for GenericSSMs
After the model struct has been defined, the user should implement (at minimum a subset of, see Method definitions required by use case) methods that define
- the components $(M_{1:n}, G_{1:n})$ of the Feynman-Kac model of interest
- the components $(m_{1:n}, g_{1:n})$ of the underlying SSM.
The interface below gives the method signatures for all implementable methods in GenericSSMs.jl, with
Model <: GenericSSMstanding for the type of a user-defined model struct,Pstanding for any (particle) type,Fstanding for a floating point type, i.eF <: AbstractFloat,Ystanding for any (observation) type,- the
rngargument reserved for a generic random number generator.
M1(model::Model, rng)::Pshould return a simulated draw from $M_1(\cdot)$.logG1(model::Model, x::P)::Fshould return $\log(G_1(x)) \in \mathbb{R}$.Mk(model::Model, x::P, k::Integer, rng)::Pshould return a simulated draw from $M_k(\cdot \mid x), k \geq 2$.logGk(model::Model, x::P, y::P, k::Integer)::Fshould return $\log(G_k(x, y)) \in \mathbb{R}, k \geq 2$.logM1(model::Model, x::P)::Fshould return $\log(M_1(x)) \in \mathbb{R}$.logMk(model::Model, y::P, x::P, k::Integer)::Fshould return $\log(M_k(y \mid x)) \in \mathbb{R}, k \geq 2$.m1(model::Model, rng)::Pshould return a simulated draw from $m_1(\cdot)$.mk(model::Model, x::P, k::Integer, rng)::Pshould return a simulated draw from $m_k(\cdot \mid x), k \geq 2$.gk(model::Model, x::P, k::Integer, rng)::Yshould return a simulated draw from $g_k(\cdot \mid x), k \geq 1$.
- The first argument in each method signature is used to distinguish the methods of separate SSMs.
Mk,logGk,logMkandmkshould be defined for $k \geq 2$. The cases $k = 1$ should be implemented by the separate functionsM1,logG1,logM1andm1. This is necessary since the mathematical function signatures for the cases $k \geq 2$ and $k = 1$ differ.- The order of the arguments
xandyin the signatures oflogGkandlogMkare reversed, again for consistency with the mathematical notation. - The methods are not exported by GenericSSMs.jl. When defining them, their names should be qualified by
GenericSSMs.
See the Examples section for examples of defining the above methods for concrete SSMs.
Method definitions required by use case
The user need not define all methods in the interface of GenericSSMs.jl, but may instead define the part of the interface that is needed for their use case. The following table displays the required functions for each use case of GenericSSMs.jl.
| Use case | M1 | logG1 | Mk | logGk | logM1 | logMk | m1 | mk | gk |
|---|---|---|---|---|---|---|---|---|---|
| Particle filtering | x | x | x | x | |||||
| CPF (with ancestor tracing) | x | x | x | x | |||||
| CPF (with backward sampling) | x | x | x | x | x | ||||
| Prediction at state level | x | x | x | x | x | x | |||
| Prediction at observation level | x | x | x | x | x | x | x | ||
| Simulation at state level | x | x | |||||||
| Simulation at observation level | x | x | x |
Note that logM1 is currently not required for any use case, but is included in GenericSSMs.jl since it is required by some particle filtering algorithms (such as the particle Gibbs algorithm).
A MethodError will be thrown if a particular method needed by a use case is not defined when the respective algorithm is invoked.
Unicode method aliases
GenericSSMs.jl also supports more aesthetically pleasing aliases that can be used instead of the above method names. The following table lists them:
| Function | Alias |
|---|---|
| M1 | M₁ |
| Mk | Mₖ |
| logG1 | logG₁ |
| logGk | logGₖ |
| logM1 | logM₁ |
| logMk | logMₖ |
| m1 | m₁ |
| mk | mₖ |
| gk | gₖ |
Type \_1[Tab] and \_k[Tab] (where [Tab] is a Tab press) in the Julia REPL to write ₁ and ₖ, respectively.