Estimation¶
Use the estimation namespace for recursive filters, smoothers, and fixed-window optimization.
The namespace includes:
kalman()andkalman_gain()for linear Gaussian filtering and steady-state estimator designkalman_predict(),kalman_update(),kalman_step()for one-step runtime loopsekf()andukf()for nonlinear filteringekf_predict(),ekf_update(),ekf_step()for one-step nonlinear loopsrts()anduks()for offline smoothinginnovation_diagnostics(),likelihood_diagnostics(),ukf_diagnostics(), andsmoother_diagnostics()for routine estimator-health summariesmhe_objective(),mhe(),mhe_warm_start(), andsoft_quadratic_penalty()for fixed-window optimization-based estimationpositive_exp(),positive_softplus(),spd_from_cholesky_raw(), anddiagonal_spd()for lightweight constrained parameterization
Minimal Example¶
import jax.numpy as jnp
import contrax as cx
sys = cx.dss(
jnp.array([[1.0, 0.1], [0.0, 1.0]]),
jnp.array([[0.0], [0.1]]),
jnp.eye(2),
jnp.zeros((2, 1)),
dt=0.1,
)
result = cx.kalman(sys, jnp.zeros((20, 2)), Q=0.001 * jnp.eye(2), R=0.01 * jnp.eye(2))
Conventions¶
ys: measurement sequence with shape(T, p)x0: prior mean onx_0P0: prior covariance onx_0KalmanResult.xs: filtered state sequence with shape(T, n)KalmanResult.Ps: filtered covariance sequence with shape(T, n, n)KalmanResult.innovations: measurement residual sequence with shape(T, p)forkalman()andekf()UKFResult.predicted_measurements: pre-update measurement means with shape(T, p)forukf()UKFResult.innovation_covariances: innovation covariance sequence with shape(T, p, p)forukf()
Batch kalman(), ekf(), and ukf() use an update-first convention: (x0,
P0) is the prior on x_0, and the first step updates with y_0.
Linear filtering accepts us=(T, m) for both feedthrough at k and the transition
from k to k+1. EKF uses the same convention. kalman and ekf accept a (T,)
has_measurement mask; missing observations may contain NaNs. Skipped updates
leave the prior unchanged and return zero innovations. The final predicted
transition is not included in KalmanResult.
Joseph covariance updates are the default for linear filtering and EKF:
Here H is C for the linear filter. Select compact form with
cx.solvers.Kalman(covariance_update="compact") or
cx.solvers.EKF(covariance_update="compact"). Pass consistent dtypes for the
model, noise, initial conditions, and observations; float32 workflows retain
float32.
For a controlled linear smoother, pass the forward controls to rts(..., us=us).
Estimator Equations¶
The batch pass starts from prior (x0, P0) on x_0, updates with y_0, and
then predicts the prior on x_1.
For linear discrete models, the state and observation maps are
with process noise covariance Q and measurement noise covariance R.
Linear Kalman Filter¶
kalman() implements the standard discrete Gaussian filter:
Extended Kalman Filter¶
For nonlinear models
ekf() linearizes the transition and observation maps with JAX Jacobians:
Pass solver=cx.solvers.EKF(num_iter=3) to ekf, ekf_update or ekf_step
to relinearize around each update iterate while keeping the prediction
covariance fixed. All three use the same configuration.
Unscented Kalman Filter¶
ukf() avoids local Jacobians and instead propagates sigma points. Given
dimension \(n\) and scaling parameter \(\lambda\),
At the update stage, sigma points from the current prior \((\hat{x}_k^-, P_k^-)\) are pushed through the observation map:
For prediction, sigma points from \((\hat{x}_k^+, P_k^+)\) are pushed through the transition map:
Smoothers¶
rts() is the linear Rauch-Tung-Striebel smoother on top of
KalmanResult:
uks() applies the same backward shape to the unscented filter, but replaces
\(A P_k^+\) with an unscented cross-covariance:
Moving-Horizon Estimation¶
mhe_objective() and mhe() expose the fixed-window optimization form:
The objective uses the same model and noise assumptions as the recursive filters, expressed as a cost over the full horizon.
Pass a discrete LTI or nonlinear system directly: mhe(model, xs_init, us, ys,
x_prior, P_prior, Q_noise, R_noise). The window contains N states and
measurements, with N−1 transition inputs; a one-state window is valid. For
input-dependent measurements, measurement_us supplies N controls; its default
holds the last transition input at the final observation (zero for N=1).
For sampled FOH models, transition inputs have shape (N-1, 2, m) and
measurement_us has shape (N, 2, m). Each observation uses its pair's left
endpoint. The default terminal pair holds the last right endpoint at both ends.
ts supplies explicit sample times for time-dependent models. Callable
transitions accept f(x, u) and require observation=h separately.
The arrival, process, and observation covariances must be finite, symmetric,
and positive definite. Invalid covariances report invalid_problem, even if
the optimizer converges.
Native Optimistix minimizers are accepted through solver=. The wrapper retains
the best finite accepted iterate when the iteration limit is reached and
returns .status, .successful, .num_steps, .gradient_norm, .initial_cost,
and .final_cost. Success requires solver convergence and valid result checks;
a finite cost reduction alone does not establish convergence. throw=True
enables checked convergence failures.
Implicit JVP/VJP derivatives include model, noise and explicit parameter data passed through optimizer arguments. They assume a converged stationary point with a nonsingular Hessian. Check convergence before interpreting derivatives of a limited or ill-conditioned solve.
Some Optimistix LBFGS versions that allocate history in JAX's default dtype
cannot initialize float32 trajectories while x64 mode is enabled. The wrapper
preserves the arrays and reports this configuration error. A validated option for small
float32 windows in that context is native optx.BFGS(rtol=1e-6, atol=1e-6);
its dense Hessian storage grows quadratically in the window's variable count.
Alternatively, select a float32 JAX context before solving. JAX global
precision settings are not changed by the estimator.
For rolling-window use:
mhe_warm_start(xs, ...)shifts a previous solution forward by one step to build the next initial guesssoft_quadratic_penalty(residuals, weight)is a small helper for soft state, terminal, or envelope penalties insideextra_cost
Scalar and matrix penalty weights represent cost or precision weighting:
w * sum(r**2) or sum(r.T @ W @ r). For covariance-weighted residuals,
apply an explicit factor solve before the penalty.
Explicit Linear Filter Algorithms¶
Ordinary kalman uses innovation Cholesky solves and Joseph covariance updates.
The innovation covariance must be positive definite. Choose another algorithm
explicitly when its assumptions fit the workflow:
import jax
jax.config.update("jax_enable_x64", True)
import jax.numpy as jnp
import contrax as cx
sys = cx.dss([[0.9]], [[1.0]], [[1.0]], [[0.0]], dt=0.1)
Q, R = 0.01 * jnp.eye(1), 0.1 * jnp.eye(1)
ys = jnp.zeros((20, 1))
design = cx.kalman_gain(sys, Q, R)
steady = cx.kalman(sys, ys, Q=Q, R=R,
solver=cx.solvers.SteadyStateKalman(design))
information = cx.kalman(sys, ys, Q=Q, R=jnp.diag(R),
solver=cx.solvers.InformationKalman())
assert steady.successful & information.successful
design.P is the predicted covariance before the first measurement update.
Steady-state mode uses it as the default P0 and computes the Joseph posterior
covariance once. It validates the gain, Riccati fixed point, initialization and
estimator stability. A mismatched design or missing observation reports
invalid_problem, with optional checked failure through throw=True.
SteadyStateKalman uses the supplied design on each call. To differentiate a
loss with respect to model or noise parameters, construct kalman_gain inside
that differentiable workflow and pass its result to the solver. A design held
fixed while its model or noise data changes is inconsistent.
Information mode takes a positive variance vector (p,) and factors matrices
in state dimension after whitening the prior. It forms no prior inverse and
no p-by-p innovation matrix. This can reduce work and storage for wide
observation maps and avoid loss of small diagonal noise in a dense innovation
matrix. It requires positive-definite priors at observed steps; covariance-form
filtering also supports semidefinite priors with nonsingular innovations.
Information mode retains the usual controls, whole-observation masks,
zero/identity initialization defaults, and float32/float64 precision. Supply
positive-definite covariances; numerical failures are returned in the result.
Both options are accepted by the
linear online update and combined-step helpers as well as batch filtering.
One-Step Helper Signatures¶
All batch filters take (model, ys, *, Q, R, us=None, x0=None, P0=None).
Initial means default to zero and covariances to identity; both describe the
prior at the first observation. Explicit steady-state mode instead defaults
the covariance to its design's predicted covariance. Omitted controls use the
model's input dimension. Set input_dim on controlled nonlinear models or
provide us. Sampled FOH models accept (T, 2, m) endpoint pairs.
The one-step helpers take (model, x, P) for predictions and
(model, x, P, y) for updates and combined steps. Noise and controls are
keyword-only, with the same Q, R and u names throughout.
# predict: advances state by one step
prediction = cx.ekf_predict(model, x, P, u=u, Q=Q_noise)
x_pred, P_pred = prediction.x, prediction.P
prediction = cx.kalman_predict(sys, x, P, Q=Q_noise, u=None) # u optional
x_pred, P_pred = prediction.x, prediction.P
# update: fuses a new measurement
update = cx.ekf_update(model, x_pred, P_pred, y, R=R_noise)
x, P, innov = update.x, update.P, update.innovation
update = cx.kalman_update(sys, x_pred, P_pred, y, R=R_noise, u=None)
x, P, innov = update.x, update.P, update.innovation
# combined predict-update step
update = cx.ekf_step(model, x, P, y, u=u, Q=Q_noise, R=R_noise)
x, P, innov = update.x, update.P, update.innovation
update = cx.kalman_step(sys, x, P, y, Q=Q_noise, R=R_noise, u=None)
x, P, innov = update.x, update.P, update.innovation
has_measurement=False can be passed to all step helpers to skip the update
(useful for streams with missing samples inside jax.lax.scan).
For an online predict-update step, u belongs to the transition from the
previous posterior. Pass u_measurement= separately when the input used by
the observation has changed. For time-dependent EKF models, t is the
transition start time and measurement_time is the observation time; the
default observation time adds the model's sample period when available.
Model Inputs¶
For nonlinear estimation, the main docs path is reusable system models such as
NonlinearSystem or
PHSSystem.
That keeps simulation, linearization, and estimation on one shared model object instead of redefining the same dynamics in several places.
The intended public workflow is:
sys_c = cx.nonlinear_system(f_continuous, output=h, dt=None)
sys_d = cx.sample_system(sys_c, dt=0.1)
result = cx.ukf(sys_d, ys, Q=Q_noise, R=R_noise, us=us, x0=x0, P0=P0)
smoothed = cx.uks(result)
ukf() returns UKFResult, which includes predicted
measurements, innovation covariances, per-step likelihood terms, and the
prediction quantities used by uks().
The filter result retains .has_measurement. Missing observations have zero
innovation, predicted measurement, innovation covariance and likelihood
placeholders. ukf_diagnostics excludes them from solves and averages. The
individual diagnostics functions also accept has_measurement=. With no
observations, aggregate means and extrema are NaN, likelihood totals are zero,
and nonfinite remains false when all observations are missing.
For continuous dynamics with discrete observations, sample_system() integrates
the continuous vector field over one sample
interval and returns a discrete-time NonlinearSystem that can be passed
directly to ekf() or ukf().
Transform Behavior¶
Batch filters, online helpers, and smoothers expose JAX scalar .status and
.successful diagnostics. Online prediction results have .x and .P;
updates also have .innovation. Invalid noise or initial covariances return
invalid_problem; nonfinite outputs or
loss of positive semidefiniteness return numerical_failure. Sampled-model
integration failures retain their backend status, including iteration limits.
Shape/configuration errors raise immediately.
throw=False returns these diagnostics. With throw=True, failure raises
during execution, including under jit when the caller selects only a state
field. Covariance validation uses dtype-scaled roundoff tolerances. Status
decisions are nondifferentiable; objectives on states,
covariances and likelihoods retain their transform contracts.
Use the one-step helpers when measurements arrive in a runtime loop or when the
filter needs to live inside a larger jax.lax.scan.
The important transform contracts are:
kalman(),ekf(), andukf()are batch scans over fixed-shape sequences- the one-step helpers expose missing-measurement handling without Python branching on traced values
mhe_objective()is a pure cost function over an explicit candidate trajectory, which makes it suitable for JAX-native optimization loops
Numerical Notes¶
kalman_gain() is the estimator dual of discrete LQR and returns the
measurement-update gain used by kalman_update().
rts() requires the same Q used by the forward kalman() pass so the
smoother uses the same process model. Pass zeros for deterministic dynamics.
mhe_objective() exposes the fixed-window cost directly. mhe() minimizes it
with a configured optimizer and returns convergence diagnostics.
Diagnostics¶
Estimation diagnostics summarize routine filter and smoother health:
innovation_diagnostics(innovations, innovation_covariances)computes per-step normalized innovation squared (NIS), innovation norms, and covariance conditioning summarieslikelihood_diagnostics(log_likelihood_terms)summarizes innovation-form Gaussian log-likelihood termsukf_diagnostics(result)builds the standard innovation and likelihood summaries directly from aUKFResultinnovation_rms(result)gives a quick residual-scale summary for eitherKalmanResultorUKFResultsmoother_diagnostics(smoothed, filtered)checks whether the smoother satisfied its covariance-reduction guarantee (P_smooth ≤ P_filtered) and reports the worst-case violation, state correction magnitudes, and PSD health of the smoothed covariances. AcceptsRTSResultpaired with eitherKalmanResultorUKFResult.
Use these summaries for filter tuning and regression checks. Statistical consistency still depends on the model, noise assumptions, and data.
Parameterization Helpers¶
Constrained-parameterization helpers support estimation and tuning workflows:
positive_exp(raw)andpositive_softplus(raw)map unconstrained values to positive onesspd_from_cholesky_raw(raw)maps an unconstrained square matrix to an SPD matrix through a lower-triangular Cholesky factordiagonal_spd(raw_diagonal)builds a diagonal SPD matrix from unconstrained diagonal parameters
These helpers accept real inputs and support jit, vmap and forward/reverse
differentiation. Integer raw parameters are promoted to floating point; floating
inputs retain their precision unless an explicitly higher-precision floor is
provided. Floors must be finite, nonnegative scalars. Cholesky maps require a
square matrix and diagonal_spd requires a vector. An overridden triangular
diagonal participates in dtype promotion, so fractional entries are retained.
Positive definiteness holds in exact arithmetic. With a zero floor, very negative parameters can underflow to zero; exponential overflow and ill-conditioned Cholesky factors can also defeat floating-point definiteness. The helpers apply the requested transform directly.
Related Pages¶
- Types for
KalmanResult,UKFResult,KalmanGainResult,RTSResult,InnovationDiagnostics,SmootherDiagnostics, andMHEResult - Kalman filtering for an end-to-end workflow
- Handle missing measurements for a task-oriented runtime recipe
- Estimation pipelines for how the batch, one-step, smoothing, and MHE pieces fit together
contrax.estimation
¶
Kalman filtering, smoothing, and moving-horizon estimation.
ekf(model_or_f: DynamicsLike, ys: Array, *, Q: Array, R: Array, us: Array | None = None, x0: Array | None = None, P0: Array | None = None, observation: ObservationFn | None = None, has_measurement: Array | None = None, solver: EKF | None = None, throw: bool = False) -> KalmanResult
¶
Run an extended Kalman filter for discrete nonlinear dynamics and observations.
Computes transition and observation Jacobians with automatic differentiation.
Joseph covariance updates and one measurement linearization are the defaults. Solver configuration selects compact covariance form or iterated updates. Gradients flow through the filter recursion and its JAX-defined model functions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_f
|
DynamicsLike
|
Nonlinear system model or dynamics function. |
required |
Q
|
Array
|
Process noise covariance. Shape: |
required |
R
|
Array
|
Measurement noise covariance. Shape: |
required |
ys
|
Array
|
Measurement sequence. Shape: |
required |
us
|
Array | None
|
Optional controls, shape |
None
|
x0
|
Array | None
|
Initial prior mean, shape |
None
|
P0
|
Array | None
|
Initial prior covariance, shape |
None
|
observation
|
ObservationFn | None
|
Observation function |
None
|
has_measurement
|
Array | None
|
Whole-observation availability flags. Shape: |
None
|
solver
|
EKF | None
|
Joseph covariance form and iterated measurement-update settings. |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Type | Description |
|---|---|
KalmanResult
|
KalmanResult: A bundle containing filtered means, covariances, and innovations. |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> def f(t, x, u):
... return jnp.array([x[0] + 0.1 * x[1], x[1] + u[0]])
>>> def h(x):
... return x[:1]
>>> result = cx.ekf(
... f, jnp.zeros((10, 1)), Q=1e-3 * jnp.eye(2), R=1e-2 * jnp.eye(1),
... us=jnp.zeros((10, 1)), observation=h,
... )
Source code in contrax/_ekf.py
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | |
ekf_predict(model_or_f: DynamicsLike, x: Array, P: Array, *, Q: Array, u: Array | None = None, t: Array | float = 0.0, throw: bool = False) -> FilterPredictionResult
¶
Predict one extended Kalman filter step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_f
|
DynamicsLike
|
A |
required |
x
|
Array
|
Filtered mean at the previous time step. Shape: |
required |
P
|
Array
|
Filtered covariance at the previous time step. Shape: |
required |
u
|
Array | None
|
Current input. Shape: |
None
|
Q
|
Array
|
Process noise covariance. Shape: |
required |
t
|
Array | float
|
Current sample time. |
0.0
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
FilterPredictionResult |
FilterPredictionResult
|
Mean x, covariance P, status and successful. |
Source code in contrax/_ekf.py
ekf_step(model_or_f: DynamicsLike, x: Array, P: Array, y: Array, *, Q: Array, R: Array, u: Array | None = None, t: Array | float = 0.0, has_measurement: Array | bool = True, solver: EKF | None = None, observation: ObservationFn | None = None, u_measurement: Array | None = None, measurement_time: Array | float | None = None, throw: bool = False) -> FilterUpdateResult
¶
Run one predict-update step of an extended Kalman filter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_f
|
DynamicsLike
|
Nonlinear system model or dynamics function. |
required |
x
|
Array
|
Filtered mean at the previous time step. Shape: |
required |
P
|
Array
|
Filtered covariance at the previous time step. Shape: |
required |
u
|
Array | None
|
Current input. Shape: |
None
|
t
|
Array | float
|
Transition start time. |
0.0
|
y
|
Array
|
Measurement vector. Shape: |
required |
Q
|
Array
|
Process noise covariance. Shape: |
required |
R
|
Array
|
Measurement noise covariance. Shape: |
required |
has_measurement
|
Array | bool
|
Whether to apply the measurement update. |
True
|
solver
|
EKF | None
|
Covariance update and number of iterated measurement passes. |
None
|
observation
|
ObservationFn | None
|
Observation function |
None
|
u_measurement
|
Array | None
|
Input at the measurement time, defaulting to u. |
None
|
measurement_time
|
Array | float | None
|
Observation time. Defaults to t plus the model's sample time when available, otherwise t. |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
FilterUpdateResult |
FilterUpdateResult
|
Mean x, covariance P, innovation, status and successful. |
Source code in contrax/_ekf.py
ekf_update(model_or_h: ObservationLike, x_pred: Array, P_pred: Array, y: Array, *, R: Array, u: Array | None = None, t: Array | float = 0.0, has_measurement: Array | bool = True, solver: EKF | None = None, throw: bool = False) -> FilterUpdateResult
¶
Update one extended Kalman filter step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_h
|
ObservationLike
|
Nonlinear system model or observation function. |
required |
x_pred
|
Array
|
Predicted mean. Shape: |
required |
P_pred
|
Array
|
Predicted covariance. Shape: |
required |
y
|
Array
|
Measurement vector. Shape: |
required |
R
|
Array
|
Measurement noise covariance. Shape: |
required |
u
|
Array | None
|
Current input used by the observation model. Shape: |
None
|
t
|
Array | float
|
Current sample time. |
0.0
|
has_measurement
|
Array | bool
|
Whether to apply the measurement update. |
True
|
solver
|
EKF | None
|
Joseph covariance form and number of iterated linearizations.
|
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
FilterUpdateResult |
FilterUpdateResult
|
Mean x, covariance P, innovation and status; unpacks |
FilterUpdateResult
|
as (x, P, innovation), with the filtered mean, filtered covariance, |
|
FilterUpdateResult
|
and final measurement residual. Missing-measurement steps return the |
|
FilterUpdateResult
|
prediction and a zero innovation. |
Source code in contrax/_ekf.py
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 | |
innovation_diagnostics(innovations: Array, innovation_covariances: Array, *, has_measurement: Array | None = None) -> InnovationDiagnostics
¶
Summarize innovation magnitude and covariance conditioning.
The returned NIS values are the per-step normalized innovation squared
terms v_k^T S_k^{-1} v_k. They are useful for routine filter health
inspection; interpret them alongside the model and noise assumptions.
Optional whole-observation flags exclude missing entries from summaries and
covariance solves. Missing per-step values are zero placeholders. With no
observations, summaries are NaN and nonfinite remains false.
Source code in contrax/_estimation_diagnostics.py
innovation_rms(result: KalmanResult | UKFResult) -> Array
¶
Return observed innovation RMS; NaN if there are no observed components.
Source code in contrax/_estimation_diagnostics.py
kalman(sys: DiscLTI, ys: Array, *, Q: Array, R: Array, x0: Array | None = None, P0: Array | None = None, us: Array | None = None, has_measurement: Array | None = None, solver: Kalman | SteadyStateKalman | InformationKalman | None = None, throw: bool = False) -> KalmanResult
¶
Run a linear discrete-time Kalman filter.
Filters a measurement sequence for a
DiscLTI system using the standard
predict-update recursion expressed as a pure lax.scan.
Innovation gains use Cholesky solves and posterior covariances use Joseph form by default. Innovation covariances must be positive definite. Gradients flow through the filter recursion, including covariance tuning. Inputs are promoted to a common floating dtype, including cached design data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI
|
Discrete-time linear system. |
required |
Q
|
Array
|
Process noise covariance. Shape: |
required |
R
|
Array
|
Measurement covariance |
required |
ys
|
Array
|
Measurement sequence. Shape: |
required |
x0
|
Array | None
|
Optional initial mean. Shape: |
None
|
P0
|
Array | None
|
Initial predicted covariance |
None
|
us
|
Array | None
|
Controls for feedthrough at k and transition k to k+1. Shape:
|
None
|
has_measurement
|
Array | None
|
Whole-observation availability flags. Shape: |
None
|
solver
|
Kalman | SteadyStateKalman | InformationKalman | None
|
Kalman (Joseph default, compact optional), InformationKalman for diagonal R and state-size solves, or SteadyStateKalman(design). Cached mode requires a fully observed stream and a gain/covariance consistent with A, C, Q and R; mismatch reports invalid_problem. |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Type | Description |
|---|---|
KalmanResult
|
KalmanResult: A bundle containing
filtered means |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys = cx.dss(
... jnp.array([[1.0, 0.1], [0.0, 1.0]]),
... jnp.array([[0.0], [0.1]]),
... jnp.eye(2),
... jnp.zeros((2, 1)),
... dt=0.1,
... )
>>> result = cx.kalman(
... sys, jnp.zeros((20, 2)), Q=1e-3 * jnp.eye(2), R=1e-2 * jnp.eye(2)
... )
Source code in contrax/_kalman.py
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 | |
kalman_gain(sys: DiscLTI, Q: Array, R: Array, *, solver: DARE | None = None, throw: bool = False) -> KalmanGainResult
¶
Design a steady-state Kalman filter gain for a discrete LTI system.
Solves the estimator-dual DARE for (A.T, C.T, Q, R) and returns the
measurement-update gain for kalman_update().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI
|
Discrete-time linear system. |
required |
Q
|
Array
|
Process noise covariance. Shape: |
required |
R
|
Array
|
Measurement noise covariance. Shape: |
required |
solver
|
DARE | None
|
Dual DARE solver configuration. |
None
|
throw
|
bool
|
Raise on failed Riccati design. |
False
|
Returns:
| Type | Description |
|---|---|
KalmanGainResult
|
KalmanGainResult: A bundle with
update gain |
Examples:
>>> import jax
>>> jax.config.update("jax_enable_x64", True)
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys = cx.dss(
... jnp.array([[1.0, 0.1], [0.0, 1.0]]),
... jnp.array([[0.0], [0.1]]),
... jnp.eye(2),
... jnp.zeros((2, 1)),
... dt=0.1,
... )
>>> design = cx.kalman_gain(sys, 0.001 * jnp.eye(2), 0.01 * jnp.eye(2))
Source code in contrax/_kalman_gain.py
kalman_predict(sys: DiscLTI, x: Array, P: Array, *, Q: Array, u: Array | None = None, throw: bool = False) -> FilterPredictionResult
¶
Predict one linear Kalman filter step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI
|
Discrete-time linear system. |
required |
x
|
Array
|
Filtered mean at the previous time step. Shape: |
required |
P
|
Array
|
Filtered covariance at the previous time step. Shape: |
required |
Q
|
Array
|
Process noise covariance. Shape: |
required |
u
|
Array | None
|
Optional input for the transition. Shape: |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
FilterPredictionResult |
FilterPredictionResult
|
Mean x, covariance P, status and successful. |
Source code in contrax/_kalman.py
kalman_step(sys: DiscLTI, x: Array, P: Array, y: Array, *, Q: Array, R: Array, u: Array | None = None, has_measurement: Array | bool = True, solver: Kalman | SteadyStateKalman | InformationKalman | None = None, u_measurement: Array | None = None, throw: bool = False) -> FilterUpdateResult
¶
Run one predict-update step of a linear Kalman filter.
The step can be called directly from a service loop or placed inside
jax.lax.scan for a batch workflow.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI
|
Discrete-time linear system. |
required |
x
|
Array
|
Filtered mean at the previous time step. Shape: |
required |
P
|
Array
|
Filtered covariance at the previous time step. Shape: |
required |
y
|
Array
|
Measurement vector. Shape: |
required |
Q
|
Array
|
Process noise covariance. Shape: |
required |
R
|
Array
|
Measurement covariance |
required |
u
|
Array | None
|
Optional input. Shape: |
None
|
has_measurement
|
Array | bool
|
Whether to apply the measurement update. |
True
|
solver
|
Kalman | SteadyStateKalman | InformationKalman | None
|
Covariance, information or explicit cached-design configuration, shared with batch filtering. |
None
|
u_measurement
|
Array | None
|
Input at the measurement time for direct feedthrough. Defaults to u; pass separately when consecutive controls differ. |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
FilterUpdateResult |
FilterUpdateResult
|
Mean x, covariance P, innovation, status and successful. |
Source code in contrax/_kalman.py
kalman_update(sys: DiscLTI, x_pred: Array, P_pred: Array, y: Array, *, R: Array, u: Array | None = None, has_measurement: Array | bool = True, solver: Kalman | SteadyStateKalman | InformationKalman | None = None, throw: bool = False) -> FilterUpdateResult
¶
Update one linear Kalman filter step.
When has_measurement=False, returns the predicted state and covariance
with a zero innovation. The flag may be a JAX scalar boolean, making
this helper usable inside jax.lax.scan for streams with missing samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI
|
Discrete-time linear system. |
required |
x_pred
|
Array
|
Predicted mean. Shape: |
required |
P_pred
|
Array
|
Predicted covariance. Shape: |
required |
y
|
Array
|
Measurement vector. Shape: |
required |
R
|
Array
|
Measurement noise covariance, shape |
required |
u
|
Array | None
|
Optional input for direct-feedthrough measurement models. Shape:
|
None
|
has_measurement
|
Array | bool
|
Whether to apply the measurement update. |
True
|
solver
|
Kalman | SteadyStateKalman | InformationKalman | None
|
Kalman (Joseph by default), InformationKalman for diagonal R, or SteadyStateKalman for an explicit cached design. Cached mode requires P_pred to match design.P and an available observation. |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
FilterUpdateResult |
FilterUpdateResult
|
Mean x, covariance P, innovation and status; unpacks |
FilterUpdateResult
|
as (x, P, innovation), with the filtered mean, filtered covariance, |
|
FilterUpdateResult
|
and measurement residual. Missing-measurement steps return a zero |
|
FilterUpdateResult
|
innovation with the same shape as |
Source code in contrax/_kalman.py
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | |
likelihood_diagnostics(log_likelihood_terms: Array, *, has_measurement: Array | None = None) -> LikelihoodDiagnostics
¶
Summarize observed log likelihoods, excluding optional missing entries.
Missing terms are zero. With no observations, total is zero and mean/min/max are NaN; nonfinite flags numerical failure only in observed entries.
Source code in contrax/_estimation_diagnostics.py
mhe(model_or_f: DiscLTI | SupportsNonlinearSystem | Callable[..., Array], xs_init: Array, us: Array, ys: Array, x_prior: Array, P_prior: Array, Q_noise: Array, R_noise: Array, *, observation: Callable[..., Array] | None = None, params: object | None = None, extra_cost: Callable[..., Array] | None = None, solver: AbstractMinimiser | None = None, max_steps: int = 256, ts: Array | None = None, measurement_us: Array | None = None, throw: bool = False) -> MHEResult
¶
Solve a fixed-window moving-horizon-estimation problem.
Minimises mhe_objective over the candidate state trajectory xs_init
using an optimistix solver. The default solver is
optx.LBFGS(rtol=1e-6, atol=1e-6), which is well-suited to the
deterministic, smooth, fixed-horizon objectives that arise in MHE.
The optimised terminal state final_state = xs[-1] is the natural input to a
downstream controller or observer. For a linear-Gaussian model, the
full trajectory xs matches the RTS smoother output within solver
tolerance.
The LBFGS implementation is a baseline fixed-window optimizer. The best
finite accepted iterate is retained on iteration limits; inspect
solver_converged, status, and gradient_norm when assessing a result.
Implicit JVP/VJP derivatives require a converged local stationary point
with a nonsingular objective Hessian.
Model, noise and optional parameter data are explicit optimizer arguments.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_f
|
DiscLTI | SupportsNonlinearSystem | Callable[..., Array]
|
Discrete system model or callable transition |
required |
xs_init
|
Array
|
Initial trajectory guess. Shape: |
required |
us
|
Array
|
Transition inputs. Shape: |
required |
ys
|
Array
|
Measurement sequence aligned with xs. Shape: |
required |
x_prior
|
Array
|
Prior mean for the start of the window. Shape: |
required |
P_prior
|
Array
|
Positive-definite arrival covariance. Shape: |
required |
Q_noise
|
Array
|
Positive-definite process covariance. Shape: |
required |
R_noise
|
Array
|
Positive-definite measurement covariance. Shape: |
required |
observation
|
Callable[..., Array] | None
|
Required for callable transitions; |
None
|
params
|
object | None
|
Optional parameters passed to |
None
|
extra_cost
|
Callable[..., Array] | None
|
Optional callable adding additional smooth costs.
Called as |
None
|
solver
|
AbstractMinimiser | None
|
Optimistix minimiser. Defaults to
|
None
|
max_steps
|
int
|
Maximum solver iterations. Defaults to 256. |
256
|
ts
|
Array | None
|
System-model sample times. Shape: |
None
|
measurement_us
|
Array | None
|
N observation inputs with the same trailing shape as us. Defaults to holding the final input endpoint at the final observation (zero for N=1). FOH observations use each pair's left input. |
None
|
throw
|
bool
|
Raise on failed convergence; otherwise return status diagnostics. |
False
|
Returns:
| Type | Description |
|---|---|
MHEResult
|
MHEResult: Estimated trajectory, terminal state, final cost, and convergence flag. |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys = cx.dss(
... jnp.array([[0.8]]), jnp.zeros((1, 1)),
... jnp.array([[1.0]]), jnp.zeros((1, 1)), dt=1.0,
... )
>>> ys = jnp.array([[0.1], [0.4], [0.6], [0.5], [0.4]])
>>> result = cx.mhe(
... sys,
... xs_init=jnp.zeros((5, 1)),
... us=jnp.zeros((4, 1)),
... ys=ys,
... x_prior=jnp.zeros(1),
... P_prior=jnp.eye(1),
... Q_noise=0.05 * jnp.eye(1),
... R_noise=0.2 * jnp.eye(1),
... )
Source code in contrax/_mhe.py
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | |
mhe_objective(model_or_f: DiscLTI | SupportsNonlinearSystem | Callable[..., Array], xs: Array, us: Array, ys: Array, x_prior: Array, P_prior: Array, Q_noise: Array, R_noise: Array, *, observation: Callable[..., Array] | None = None, params: object | None = None, extra_cost: Callable[..., Array] | None = None, ts: Array | None = None, measurement_us: Array | None = None) -> Array
¶
Evaluate a fixed-window moving-horizon-estimation objective.
The candidate state trajectory is explicit in xs, so callers can optimize
it with a JAX optimizer. Accepts discrete LTI or nonlinear system models,
or callable transitions with an explicit observation function. There are N
states/measurements and N-1 transitions/inputs; traced inputs must retain
fixed shapes.
The objective is a weighted nonlinear least-squares cost:
- arrival cost on
xs[0] - x_prior - process cost on
xs[k + 1] - f(xs[k], us[k], params) - measurement cost on
ys[k] - h(xs[k], params) - optional user
extra_cost(xs, us, ys, params)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_f
|
DiscLTI | SupportsNonlinearSystem | Callable[..., Array]
|
Discrete system model, or transition callable |
required |
xs
|
Array
|
Candidate state trajectory. Shape: |
required |
us
|
Array
|
Transition inputs. Shape: |
required |
ys
|
Array
|
Measurement sequence aligned with xs. Shape: |
required |
x_prior
|
Array
|
Prior state for the start of the window. Shape: |
required |
P_prior
|
Array
|
Positive-definite arrival covariance. Shape: |
required |
Q_noise
|
Array
|
Positive-definite process covariance. Shape: |
required |
R_noise
|
Array
|
Positive-definite measurement covariance. Shape: |
required |
observation
|
Callable[..., Array] | None
|
Required for callable transitions; |
None
|
params
|
object | None
|
Optional parameters passed to |
None
|
extra_cost
|
Callable[..., Array] | None
|
Optional callable adding additional smooth costs.
Called as |
None
|
ts
|
Array | None
|
Sample times for system-model callables. Shape: |
None
|
measurement_us
|
Array | None
|
N observation inputs with the same trailing shape as us. Defaults to us with the final input endpoint held at the terminal observation (zero for N=1). FOH observations use each pair's left input. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
Array |
Array
|
Scalar MHE cost. |
Source code in contrax/_mhe.py
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | |
mhe_warm_start(xs: Array, *, transition: Callable[[Array, Array], Array] | None = None, terminal_input: Array | None = None) -> Array
¶
Shift an MHE trajectory guess forward by one step.
By default, repeats the previous terminal state. Supply a transition and
terminal_input to propagate that state; update the arrival prior
separately when shifting the estimation window.
Source code in contrax/_mhe.py
rts(sys: DiscLTI, result: KalmanResult, *, Q: Array, us: Array | None = None, throw: bool = False) -> RTSResult
¶
Run a Rauch-Tung-Striebel smoother on filtered Kalman results.
Applies the standard backward smoothing pass to the output of kalman()
using the same process model and process noise covariance.
Smooths a completed kalman() pass using the same process model and noise
covariance. For deterministic dynamics, set Q to zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sys
|
DiscLTI
|
Discrete-time linear system used in the forward filter. |
required |
result
|
KalmanResult
|
Output of |
required |
Q
|
Array
|
Process noise covariance used by the forward filter. |
required |
us
|
Array | None
|
Controls from the forward filter. Shape: |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Type | Description |
|---|---|
RTSResult
|
RTSResult: A bundle containing smoothed state means and covariances. |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys = cx.dss(
... jnp.array([[1.0, 0.1], [0.0, 1.0]]),
... jnp.array([[0.0], [0.1]]),
... jnp.eye(2),
... jnp.zeros((2, 1)),
... dt=0.1,
... )
>>> filtered = cx.kalman(
... sys, jnp.zeros((20, 2)), Q=1e-3 * jnp.eye(2), R=1e-2 * jnp.eye(2)
... )
>>> smoothed = cx.rts(sys, filtered, Q=0.001 * jnp.eye(2))
Source code in contrax/_rts.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
smoother_diagnostics(smoothed: RTSResult, filtered: KalmanResult | UKFResult) -> SmootherDiagnostics
¶
Compute health diagnostics for an RTS or UKS smoother result.
Checks the covariance relation P_smooth ≤ P_filtered in the positive
semidefinite sense and reports its worst violation. Empty inputs return
empty per-step arrays and NaN extrema; nonfinite reflects invalid values
in the supplied samples.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
smoothed
|
RTSResult
|
Output of |
required |
filtered
|
KalmanResult | UKFResult
|
The forward filter result used to produce |
required |
Returns:
| Type | Description |
|---|---|
SmootherDiagnostics
|
SmootherDiagnostics: Diagnostic
bundle. |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> sys = cx.dss(
... jnp.array([[0.9]]), jnp.zeros((1, 1)),
... jnp.array([[1.0]]), jnp.zeros((1, 1)), dt=1.0,
... )
>>> filtered = cx.kalman(
... sys, jnp.zeros((10, 1)), Q=1e-3 * jnp.eye(1), R=1e-2 * jnp.eye(1)
... )
>>> smoothed = cx.rts(sys, filtered, Q=0.001 * jnp.eye(1))
>>> diag = cx.smoother_diagnostics(smoothed, filtered)
Source code in contrax/_estimation_diagnostics.py
soft_quadratic_penalty(residuals: Array, weight: Array) -> Array
¶
Return a quadratic soft penalty with matrix or scalar weighting.
A scalar weight multiplies the total squared residual norm; a matrix weight computes sum r.T @ weight @ r over the leading residual axes. The weight is a precision/cost weight. Choose a nonnegative scalar or a positive-semidefinite matrix to obtain a nonnegative penalty.
The penalty is differentiable with respect to residuals and weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
residuals
|
Array
|
Real residual array, with final dimension n for matrix weights. |
required |
weight
|
Array
|
Scalar weight or square cost matrix. Matrix shape: |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Array |
Array
|
Scalar weighted quadratic cost. |
Source code in contrax/_mhe.py
ukf(model_or_f: DynamicsLike, ys: Array, *, Q: Array, R: Array, us: Array | None = None, x0: Array | None = None, P0: Array | None = None, observation: ObservationFn | None = None, has_measurement: Array | None = None, solver: UKF | None = None, throw: bool = False) -> UKFResult
¶
Run an unscented Kalman filter for discrete nonlinear models.
Propagates means and covariances through nonlinear transition and observation models with a deterministic sigma-point transform.
Assumes additive process and measurement noise. Sigma-point and innovation covariances must be positive definite. One innovation Cholesky factor is used for both the gain and Gaussian likelihood.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model_or_f
|
DynamicsLike
|
Nonlinear system model or dynamics function. |
required |
Q
|
Array
|
Process noise covariance. Shape: |
required |
R
|
Array
|
Measurement noise covariance. Shape: |
required |
ys
|
Array
|
Measurement sequence. Shape: |
required |
us
|
Array | None
|
Optional controls, shape |
None
|
x0
|
Array | None
|
Initial prior mean, shape |
None
|
P0
|
Array | None
|
Initial prior covariance, shape |
None
|
observation
|
ObservationFn | None
|
Observation function |
None
|
has_measurement
|
Array | None
|
Whole-observation flags, shape |
None
|
solver
|
UKF | None
|
Sigma-point configuration; defaults to |
None
|
throw
|
bool
|
Raise on numerical failure; otherwise inspect status. |
False
|
Returns:
| Type | Description |
|---|---|
UKFResult
|
UKFResult: A bundle containing filtered means/covariances plus prediction, innovation, and likelihood intermediates useful for diagnostics and smoothing. |
Examples:
>>> import jax.numpy as jnp
>>> import contrax as cx
>>> def f(t, x, u):
... return jnp.array([x[0] + 0.1 * x[1], x[1] + u[0]])
>>> def h(x):
... return jnp.array([x[0] ** 2])
>>> result = cx.ukf(
... f, jnp.zeros((10, 1)), Q=1e-3 * jnp.eye(2), R=1e-2 * jnp.eye(1),
... us=jnp.zeros((10, 1)), x0=jnp.ones(2), observation=h,
... )
Source code in contrax/_ukf.py
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | |
ukf_diagnostics(result: UKFResult) -> tuple[InnovationDiagnostics, LikelihoodDiagnostics]
¶
Build the standard diagnostics pair for a UKF result.
Source code in contrax/_estimation_diagnostics.py
uks(result: UKFResult, *, throw: bool = False) -> RTSResult
¶
Run an unscented Rauch-Tung-Striebel-style smoother.
Uses the predicted means, covariances, and transition cross-covariances in
UKFResult. Empty passes return empty results.