Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cuthbert/filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from jax.lax import associative_scan, scan

from cuthbert.inference import Filter
from cuthbert.utils import dummy_tree_like
from cuthbertlib.types import ArrayTree, ArrayTreeLike, KeyArray


Expand Down Expand Up @@ -53,6 +54,9 @@ def filter(
else:
prepare_keys = random.split(key, T)

sample_model_inputs = tree.map(lambda x: x[0], model_inputs)
init_state = init_state._replace(model_inputs=dummy_tree_like(sample_model_inputs))

if parallel:
other_prep_states = vmap(lambda inp, k: filter_obj.filter_prepare(inp, key=k))(
model_inputs, prepare_keys
Expand Down
33 changes: 13 additions & 20 deletions cuthbert/gaussian/kalman.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
from jax import numpy as jnp
from jax import tree

from cuthbert.gaussian.types import (
GetDynamicsParams,
GetInitParams,
GetObservationParams,
)
from cuthbert.gaussian.types import GetDynamicsParams, GetObservationParams
from cuthbert.inference import Filter, Smoother
from cuthbert.utils import dummy_tree_like
from cuthbertlib.kalman import filtering, smoothing
Expand Down Expand Up @@ -62,15 +58,17 @@ def chol_cov(self) -> Array:


def build_filter(
get_init_params: GetInitParams,
m0: Array,
chol_P0: Array,
get_dynamics_params: GetDynamicsParams,
get_observation_params: GetObservationParams,
) -> Filter:
"""Builds an exact Kalman filter object for linear Gaussian SSMs.

Args:
get_init_params: Function to get m0, chol_P0 to initialize filter state,
given model inputs sufficient to define p(x_0) = N(m0, chol_P0 @ chol_P0^T).
m0: The mean vector of the initial state.
chol_P0: The generalized Cholesky factor of the covariance matrix of
the initial state.
get_dynamics_params: Function to get dynamics parameters, F, c, chol_Q
given model inputs sufficient to define
p(x_t | x_{t-1}) = N(F @ x_{t-1} + c, chol_Q @ chol_Q^T).
Expand All @@ -82,10 +80,7 @@ def build_filter(
Filter object for exact Kalman filter. Suitable for associative scan.
"""
return Filter(
init_prepare=partial(
init_prepare,
get_init_params=get_init_params,
),
init_prepare=partial(init_prepare, m0=m0, chol_P0=chol_P0),
filter_prepare=partial(
filter_prepare,
get_dynamics_params=get_dynamics_params,
Expand Down Expand Up @@ -132,23 +127,21 @@ def build_smoother(


def init_prepare(
model_inputs: ArrayTreeLike,
get_init_params: GetInitParams,
m0: Array,
chol_P0: Array,
key: KeyArray | None = None,
) -> KalmanFilterState:
"""Prepare the initial state for the Kalman filter.

Args:
model_inputs: Model inputs.
get_init_params: Function to get m0, chol_P0 from model inputs.
m0: The mean vector of the initial state.
chol_P0: The generalized Cholesky factor of the covariance matrix of
the initial state.
key: JAX random key - not used.

Returns:
State for the Kalman filter.
Contains mean and chol_cov (generalised Cholesky factor of covariance).
"""
model_inputs = tree.map(lambda x: jnp.asarray(x), model_inputs)
m0, chol_P0 = get_init_params(model_inputs)
elem = filtering.FilterScanElement(
A=jnp.zeros_like(chol_P0),
b=m0,
Expand All @@ -157,7 +150,7 @@ def init_prepare(
Z=jnp.zeros_like(chol_P0),
ell=jnp.array(0.0),
)
return KalmanFilterState(elem=elem, model_inputs=model_inputs)
return KalmanFilterState(elem=elem, model_inputs=None)


def filter_prepare(
Expand Down
8 changes: 0 additions & 8 deletions cuthbert/gaussian/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@


### Kalman types
class GetInitParams(Protocol):
"""Protocol for defining the initial distribution of a linear Gaussian SSM."""

def __call__(self, model_inputs: ArrayTreeLike) -> tuple[Array, Array]:
"""Get initial parameters (m0, chol_P0) from model inputs."""
...


class GetDynamicsParams(Protocol):
"""Protocol for defining the dynamics model of a linear Gaussian SSM."""

Expand Down
8 changes: 1 addition & 7 deletions cuthbert/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@
class InitPrepare(Protocol):
"""Protocol for preparing the initial state for the inference."""

def __call__(
self,
model_inputs: ArrayTreeLike,
*,
key: KeyArray | None = None,
) -> ArrayTree:
def __call__(self, *, key: KeyArray | None = None) -> ArrayTree:
"""Prepare the initial state for the inference.

The state at the first time point, prior to any observations.

Args:
model_inputs: The model inputs at the first time point.
key: The key for the random number generator.
Optional, as only used for stochastic inference methods

Expand Down
Loading