Filters¶
The consolidated solver API: gaussian_filter (fixed grid),
gaussian_filter_adaptive (adaptive, save-at-grid), and rts_smoother for the
backward smoothing pass — all returning a single FilterResult. The
linearization is a pluggable Correction (EK0 / EK1 /
IEKF), and adaptive stepping uses a step-size controller (PController /
PIController). Diffusion is calibrated per step by default
(calibration="dynamic", a scalar quasi-MLE), so the process noise is rescaled
each step; pass calibration="none" for a fixed diffusion. See
How to choose.
Dispatch is automatic: a PrecondIWP / PrecondMaternPrior prior selects the
preconditioned square-root recursion, and passing obs_model= adds a masked
observation update.
The FilterResult¶
Both gaussian_filter and gaussian_filter_adaptive return a FilterResult with
named fields (recover a covariance with P = P_sqr.T @ P_sqr; see
notation):
| Field | Meaning |
|---|---|
t |
time grid |
m, P_sqr |
filtered posterior mean and square-root covariance |
log_likelihood |
scalar post-calibration ODE-information log-marginal-likelihood (not comparable across calibration modes — each defines a different generative model) |
log_likelihood_obs |
observation log-likelihood (None without obs_model) |
m_pred, P_pred_sqr |
one-step predictions before each update (None for the adaptive save-at solver) |
G_back, d_back, P_back_sqr |
backward transitions consumed by rts_smoother |
sigma_sqr |
per-step calibrated diffusion sigma_hat^2 (None for the adaptive solver) |
success |
adaptive only: whether sub-stepping reached every save time and the log-likelihood is finite (None for the fixed grid) |
m_bar, P_bar_sqr, T |
preconditioned-space internals (None for a plain prior) |
gaussian_filter_adaptive is filtering-only by default (smoother=False):
its backward-pass fields are then None and rts_smoother raises. Pass
smoother=True to additionally compute a fixed-point smoothing backward pass
(one composite conditional per save interval, O(#save points) memory); the
result then carries G_back / d_back / P_back_sqr and rts_smoother(prior,
result) works directly. smoother=True is not supported together with
obs_model.
The low-level scan loops and step functions that gaussian_filter wraps live in
the ode_filters.filters.ode_filter_loop / ode_filter_step / ode_filter_adaptive
submodules; they are implementation detail, not part of the public API.
Filtering and smoothing routines for ODE models.
The public solver API is the consolidated :func:gaussian_filter /
:func:gaussian_filter_adaptive / :func:rts_smoother (returning
:class:FilterResult), plus the pluggable :class:Correction strategies and the
adaptive step-size controllers. The historical sqr_loop* / *_step*
matrix is now an implementation detail of those entry points; import it from the
ode_filters.filters.ode_filter_loop / ode_filter_step /
ode_filter_adaptive submodules if you need the low-level variants directly.
Modules:
| Name | Description |
|---|---|
adaptive_controller |
Step-size controllers for the adaptive EKF loop. |
correction |
Pluggable linearization/correction strategies for the Gaussian ODE filter. |
gaussian_filter |
Consolidated public solver API: |
ode_filter_adaptive |
Adaptive-step EKF loop with online sigma calibration. |
ode_filter_loop |
|
ode_filter_step |
|
Classes:
| Name | Description |
|---|---|
Correction |
Base class for linearization/correction strategies. |
CorrectionResult |
Output of a single :meth: |
FilterResult |
Filtered solution of a Gaussian ODE filter. |
IteratedTaylorCorrection |
Iterated EKF (IEKF): per-step Gauss-Newton relinearization. |
PController |
Proportional (single-error) step-size controller. |
PIController |
Gustafsson-style proportional-integral controller. |
StepSizeController |
Duck-typed interface for step-size controllers. |
TaylorCorrection |
Taylor-series correction: EK1 ( |
Functions:
| Name | Description |
|---|---|
gaussian_filter_adaptive |
Adaptive-step Gaussian filter, returning the solution at |
rts_smoother |
Rauch-Tung-Striebel smoothing of a Gaussian-filter result. |
Classes¶
Correction
¶
Bases: Module
Base class for linearization/correction strategies.
A Correction is an :class:equinox.Module (static configuration), so it can be
captured by jax.jit / jax.lax.scan without retracing. Subclasses own the
full predicted -> posterior update and return a :class:CorrectionResult.
Methods:
| Name | Description |
|---|---|
correct |
Update a predicted Gaussian with the measurement model at time |
Functions¶
correct
abstractmethod
¶
correct(
measure: BaseODEInformation,
m_pred: Array,
P_pred_sqr: Array,
*,
t: ArrayLike = 0.0,
) -> CorrectionResult
Update a predicted Gaussian with the measurement model at time t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
measure
|
BaseODEInformation
|
ODE-information measurement model. |
required |
m_pred
|
Array
|
Predicted (prior) state mean. |
required |
P_pred_sqr
|
Array
|
Predicted state covariance, square-root form. |
required |
t
|
ArrayLike
|
Current time. |
0.0
|
CorrectionResult
¶
Bases: NamedTuple
Output of a single :meth:Correction.correct call.
Attributes:
| Name | Type | Description |
|---|---|---|
m |
Array
|
Updated posterior mean (shape |
P_sqr |
Array
|
Updated posterior covariance in square-root form
( |
mz |
Array
|
Innovation (predicted-observation) mean (shape |
Pz_sqr |
Array
|
Innovation covariance in square-root form -- consumed by the Gaussian log-marginal-likelihood. |
FilterResult
¶
Bases: NamedTuple
Filtered solution of a Gaussian ODE filter.
Attributes:
| Name | Type | Description |
|---|---|---|
t |
Array
|
Time grid, shape |
m |
Array
|
Filtered state means at |
P_sqr |
Array
|
Square-root covariances at |
log_likelihood |
Array
|
Marginal log-likelihood of the ODE-information residuals,
taken after diffusion calibration. Because each |
m_pred |
Array | None
|
Predicted (prior) means per step, |
P_pred_sqr |
Array | None
|
Predicted square-root covariances per step. |
G_back |
Array | None
|
Backward-pass gains per step (smoother input). |
d_back |
Array | None
|
Backward-pass offsets per step. |
P_back_sqr |
Array | None
|
Backward-pass square-root covariances per step. |
mz |
Array | None
|
Predicted-observation (ODE-defect) innovation means per step; the input
to post-hoc diffusion calibration. For the adaptive solver this is the
innovation of the sub-step that lands on each save time (its |
Pz_sqr |
Array | None
|
Predicted-observation innovation square-root covariances per step. |
mz_obs |
Array | None
|
External-observation innovation means per step,
|
Pz_obs_sqr |
Array | None
|
External-observation innovation square-root covariances per
step, |
sigma_sqr |
Array | None
|
Per-step calibrated diffusion |
log_likelihood_obs |
Array | None
|
Marginal log-likelihood of the external observations
( |
m_bar |
Array | None
|
Preconditioned-space means ( |
P_bar_sqr |
Array | None
|
Preconditioned-space square-root covariances ( |
T |
Array | None
|
Preconditioner matrix ( |
success |
Array | None
|
Scalar boolean -- whether an adaptive solve reached every save
time and produced a finite log-likelihood (see
:class: |
IteratedTaylorCorrection
¶
Bases: Correction
Iterated EKF (IEKF): per-step Gauss-Newton relinearization.
A single forward pass; within each update, the EK1 linearization is recomputed
at the updated mean and the update redone, for a fixed max_iters passes
(max_iters=1 reproduces EK1). A fixed iteration count -- rather than a
convergence-based while_loop -- keeps the correction reverse-mode
differentiable, so it can be used inside gradient-based parameter inference.
Attributes:
| Name | Type | Description |
|---|---|---|
max_iters |
int
|
Number of relinearizations per step (>= 1). |
PController
dataclass
¶
PController(
order: int,
safety: float = 0.9,
alpha: float | None = None,
min_factor: float = 0.2,
max_factor: float = 5.0,
)
Proportional (single-error) step-size controller.
The proposed step is
h_new = h * safety * err^(-alpha)
clipped to [min_factor, max_factor] * h. The previous error is
ignored, so this controller has no memory.
Default alpha = 1 / order matches the standard "I-controller" of
Hairer-Wanner-Norsett (1993) and the elementary controller in scipy's
solve_ivp. For an EKF1 with IWP(q) prior, order = q.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
order
|
int
|
Convergence order of the local error estimate. |
required |
safety
|
float
|
Safety factor on the proposed step. |
0.9
|
alpha
|
float | None
|
Gain on the current error; defaults to |
None
|
min_factor
|
float
|
Lower clip on |
0.2
|
max_factor
|
float
|
Upper clip on |
5.0
|
Methods:
| Name | Description |
|---|---|
propose |
Return the next proposed step size. |
Functions¶
propose
¶
Return the next proposed step size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
h
|
float
|
Current step size. |
required |
err
|
float
|
Normalised error of the current step (1.0 = at tolerance). |
required |
err_prev
|
float | None
|
Ignored; accepted for protocol compatibility with
:class: |
None
|
PIController
dataclass
¶
PIController(
order: int,
safety: float = 0.9,
alpha: float | None = None,
beta: float | None = None,
min_factor: float = 0.2,
max_factor: float = 5.0,
)
Gustafsson-style proportional-integral controller.
The proposed step is
h_new = h * safety * err^(-alpha) * (err_prev / err)^(beta)
clipped to [min_factor, max_factor] * h. When err_prev is unknown
(e.g. on the first step or right after a reject) the I-term is dropped
and the update reduces to the :class:PController form
h_new = h * safety * err^(-alpha).
Defaults follow Gustafsson (1991): alpha = 0.7 / order, beta = 0.4 /
order, which are also the values used in Bosch et al. (2021). For an
EKF1 with IWP(q) prior, order = q.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
order
|
int
|
Convergence order of the local error estimate. |
required |
safety
|
float
|
Safety factor on the proposed step. |
0.9
|
alpha
|
float | None
|
Proportional gain; defaults to |
None
|
beta
|
float | None
|
Integral gain; defaults to |
None
|
min_factor
|
float
|
Lower clip on |
0.2
|
max_factor
|
float
|
Upper clip on |
5.0
|
Methods:
| Name | Description |
|---|---|
propose |
Return the next proposed step size. |
Functions¶
propose
¶
Return the next proposed step size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
h
|
float
|
Current step size. |
required |
err
|
float
|
Normalised error of the current step (1.0 = at tolerance). |
required |
err_prev
|
float | None
|
Normalised error of the previously accepted step, or
|
required |
StepSizeController
¶
Bases: Protocol
Duck-typed interface for step-size controllers.
err_prev may be None to indicate the absence of memory (first
step, or right after a reject). Besides :meth:propose, controllers expose
the static coefficients (safety, _alpha, min_factor,
max_factor) that the adaptive loop reads to re-implement the proposal
under jax.numpy tracing.
Methods:
| Name | Description |
|---|---|
propose |
Return the proposed next step size. |
TaylorCorrection
¶
Bases: Correction
Taylor-series correction: EK1 (order=1) or EK0 (order=0).
Attributes:
| Name | Type | Description |
|---|---|---|
order |
int
|
|
Functions¶
gaussian_filter_adaptive
¶
gaussian_filter_adaptive(
mu_0: Array,
P_0_sqr: Array,
prior: BasePrior,
measure: BaseODEInformation,
save_at: Array,
*,
correction: Correction | None = None,
obs_model: ObsModel | None = None,
atol: float = 0.0001,
rtol: float = 0.01,
h_init: float | None = None,
calibration: CalibrationMode = "dynamic",
controller=None,
min_sigma_sqr: float = 0.0,
max_steps: int = 4096,
smoother: bool = False,
) -> FilterResult
Adaptive-step Gaussian filter, returning the solution at save_at.
jit / vmap / reverse-grad-able (checkpointed adaptive loop).
See :func:sqr_adaptive_solve for the full argument docs.
correction selects the linearization (EK0/EK1/IEKF), matching
:func:gaussian_filter; result.success reports whether the adaptive
sub-stepping reached every save time.
With an obs_model the observation likelihood is folded into the combined
result.log_likelihood (summed over accepted steps); unlike the fixed-grid
:func:gaussian_filter, result.log_likelihood_obs is always None on
the adaptive path.
With smoother=True the result carries a fixed-point-smoothing backward
pass (one composite conditional per save interval, O(#save points) memory),
so :func:rts_smoother applies directly. Default False keeps the
filtering-only path (no backward pass, lower cost); smoother=True is not
supported together with obs_model.
rts_smoother
¶
rts_smoother(
prior: BasePrior, result: FilterResult
) -> tuple[Array, Array]
Rauch-Tung-Striebel smoothing of a Gaussian-filter result.
Works on a :func:gaussian_filter result (backward pass over the fixed grid)
or a :func:gaussian_filter_adaptive result run with smoother=True (the
fixed-point backward pass over the save grid); both carry the required
G_back / d_back / P_back_sqr. A filtering-only adaptive result
(smoother=False, the default) does not, and raises.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prior
|
BasePrior
|
The prior used for the forward filter (selects the preconditioned smoother when preconditioned). |
required |
result
|
FilterResult
|
A :class: |
required |
Returns:
| Type | Description |
|---|---|
Array
|
Tuple |
Array
|
covariances, shapes |