Add support for time-varying linear systems#250
Conversation
Previously, Kalman filters/smoothers only support time-invariant systems; to this end, both LinearGaussianStateEvolution and LinearGaussianObservation were time-invariant. This is a problem when trying to include proper discretization schemes for CD linear systems, where time-varying linear systems naturally arise with non-uniform sampling. Main changes are to (i) the LinearGaussianStateEvolution interface, (ii) the LinearGaussianObservation interface, (iii) the KF/RTS Smoothing cuthbert integrations. Changes (i) and (ii) are quite similar; they both allow for parameters to depend on `(t_now,t_next)`/`t` or be constant, in a potentially mixed way. They are resolved via a `params_at` function, so that data about the system may still be extracted for KF/RTS use. This makes the changes for filtering and smoothing integrations somewhat minimal. They just go from accessing the info to using `evo.params_at(t_now, t_next)`. For `dynamax` (which I recall being somewhat fragile regarding LTV systems), we supply a `is_time_invariant` flag, and throw errors accordingly. We make additional guards at the integration stage later on as well.
There was a problem hiding this comment.
Pull request overview
This PR extends Dynestyx’s linear-Gaussian model components to support time-varying (callable) parameters, enabling Kalman filtering/smoothing on irregular time grids (e.g., for better CT discretizations as in #226). It introduces a params_at(...) resolution layer so Kalman/RTS integrations can consume per-step resolved matrices while preserving the existing constant-parameter workflow, and it explicitly guards the cd_dynamax backend against unsupported callable parameters.
Changes:
- Add callable-parameter support to
LinearGaussianStateEvolution((t_now, t_next) -> ...) andLinearGaussianObservation(t -> ...), plusparams_atandis_time_invariant. - Update cuthbert discrete Kalman filter/smoother integrations to resolve parameters per time step via
params_at. - Add
cd_dynamaxbackend guards that raiseTypeErrorwhen callable parameters are supplied; expand tests and docs accordingly.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/test_time_varying_linear_gaussian.py | New end-to-end tests covering constant-equivalence, true time-variation, mixed callable/constant cases, plates, gradients, simulator behavior, and cd_dynamax rejection. |
| tests/test_models_core.py | Unit tests for callable-field equivalence, time dependence, and dimension inference/shape validation with callable fields. |
| dynestyx/models/state_evolution.py | Allow callable transition parameters; add LinearGaussianParams, params_at(t_now,t_next), and is_time_invariant. |
| dynestyx/models/observations.py | Allow callable observation parameters; add LinearGaussianObservationParams, params_at(t), and is_time_invariant. |
| dynestyx/models/init.py | Export the new *Params named tuples in the public models namespace. |
| dynestyx/inference/filter_configs.py | Document backend support constraints for time-varying linear-Gaussian models. |
| dynestyx/inference/smoother_configs.py | Document backend support constraints for time-varying linear-Gaussian models. |
| dynestyx/inference/integrations/cuthbert/discrete_filter.py | Resolve observation params via params_at; add per-step Kalman parameter builders for time-varying support. |
| dynestyx/inference/integrations/cuthbert/discrete_smoother.py | Reuse the new Kalman dynamics parameter builder for smoother integration. |
| dynestyx/inference/integrations/cd_dynamax/utils.py | Add centralized guard to reject callable linear-Gaussian fields for cd_dynamax paths. |
| dynestyx/inference/integrations/cd_dynamax/discrete_filter.py | Enforce constant-parameter requirement for cd_dynamax discrete KF conversion. |
| dynestyx/init.py | Re-export new LinearGaussianParams / LinearGaussianObservationParams at top-level API. |
| docs/api_reference/public/models/specialized/linear_gaussian_state_evolution.md | Document callable parameter usage and params_at for time-varying transitions. |
| docs/api_reference/public/models/specialized/linear_gaussian_observation.md | Document callable parameter usage and params_at for time-varying observations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| obs = dsx_model.observation_model | ||
| if not isinstance(obs, LinearGaussianObservation): | ||
| raise TypeError("dsx_to_cdlgssm_params requires LinearGaussianObservation.") | ||
| _require_constant_linear_gaussian_fields( |
There was a problem hiding this comment.
I'm fine with this---we could probably support Dynamax's time-varying KF. It expects A.shape = (times, state, state), so would just have to pre-build the extra dimension. Doesn't seem worth the hassle here.
| from dynestyx.models.core import DiscreteTimeStateEvolution | ||
|
|
||
|
|
||
| class LinearGaussianParams(NamedTuple): |
There was a problem hiding this comment.
this is basically the same as the Observation version of this class, other than names. Makes me wonder if our LinearGaussian classes should not end in Observation or StateEvolution...they can all just have something like state2out_matrix, control2out_matrix, bias, cov
mattlevine22
left a comment
There was a problem hiding this comment.
Looks like things work here and the PR accomplishes its mission, so approving!
One question I'll let you think about and/or leave for later.
- Should we unify LinearGaussianObservation and LinearGaussianStateEvolution into a single class? or at least just have them shadow a single underlying class? ...same question w.r.t. the new
Paramsversion of these classes. I realized these are all literally the same other than field names.
They're not with callables -- state evolution has signature |
Previously, Kalman filters/smoothers only support time-invariant systems; to this end, both LinearGaussianStateEvolution and LinearGaussianObservation were time-invariant. This is a problem when trying to include proper discretization schemes for CD linear systems, where time-varying linear systems naturally arise with non-uniform sampling (cf. #226). This PR adds time-varying support for linear Gaussian models.
Main changes are to (i) the LinearGaussianStateEvolution interface, (ii) the LinearGaussianObservation interface, (iii) the KF/RTS Smoothing cuthbert integrations.
Changes (i) and (ii) are quite similar; they both allow for parameters to depend on
(t_now,t_next)/tor be constant, in a potentially mixed way. They are resolved via aparams_atfunction, so that data about the system may still be extracted for KF/RTS use.This makes the changes for filtering and smoothing integrations somewhat minimal. They just go from accessing the info to using
evo.params_at(t_now, t_next).For
dynamax(which I recall being somewhat fragile regarding LTV systems), we supply ais_time_invariantflag, and throw errors accordingly. We make additional guards at the integration stage later on as well.