Repository navigation
v0.3.0 Stochastic Observability and Constructability, per-state regularization
Stochastic observability and constructability, per-state regularization
Highlights
-
Process noise: stochastic observability and constructability.
ObservabilityAnalysishas four new methods:stochastic-observability-classic/-jax: Fisher information about the state at the start of each window;stochastic-constructability-classic/-jax: about the state at the end of each window. Its inverse is the posterior Cramér-Rao bound, which is what a Kalman filter's error covariance tracks.
With process noise
Q, information about a state saturates instead of growing with every added measurement. The method follows B. Boyacioglu & F. van Breugel, Duality of Stochastic Observability and Constructability and their Relation to the Fisher Information, IEEE L-CSS (2025), doi:10.1109/LCSYS.2025.3547297.- The model is linearized along the trajectory, with finite differences (
-classic) or JAX autodiff (-jax). Q,Randlamcan then be changed without callingrun()again.
oa = pybounds.ObservabilityAnalysis(sim, t, x, u, method='stochastic-constructability-classic', w=20, R={'r': 0.1}, Q={'g': 1e-3, 'd': 1e-6}) ev = oa.run().min_error_variance() ev_noisier = oa.min_error_variance(Q=1e-2) # no re-run needed
-
Per-state regularization.
lamcan be a dict keyed by state name, e.g.lam={'g': 1e-6, 'd': 1e-10}, or a 1-D array. Each state then gets its own ceiling 1/λᵢ on its minimum error variance. This is useful when states have very different units. It works for every method and storage mode. -
alignmentoption. It sets where each window's result is placed in time:'center': the window's center, as before (the default);'bounded_state': the state the result actually bounds, i.e. the start of the window for observability and the end for constructability.
-
Bring your own linearization.
ObservabilityAnalysis.from_linearization(Phi, C, method=..., w=...)runs the stochastic methods on a linearized trajectory computed elsewhere, without a simulator.analysis.linearizationreturns it as a frozenpybounds.Linearization.- Changing only the window size reuses the linearization, so sweeping
wis cheap.
-
Helpers for choosing
Q.analysis.deterministic_states()lists the states with no process noise, such as constant parameters and clocks; these are the ones to give a much smallerQ.analysis.model_state_namesgives the namesQis keyed by. -
Validation.
validation/stochastic_duality_fig2.ipynbchecks the recursions against the reference MATLAB implementation and redraws Fig. 2 of the letter. -
New public names.
pybounds.stochastic: the recursions themselves, for linear time-varying systems.pybounds.Linearization.pybounds.SlidingO, the input type offrom_sliding.
Behavior changes
- Results of the existing methods are unchanged. For the same settings,
bounds-empirical/bounds-jax(previouslyempirical/jax) give bit-identical results to 0.2.0. - Method names are now
bounds-empiricalandbounds-jax.'empirical'and'jax'still work as aliases, and 0.2.0 settings files load. Settings files saved by 0.3.0 use the new names, so 0.2.0 cannot read them. ObservabilityMatrixImagenow uses thecmap,vmin_ratioandvmax_percentilegiven to its constructor. They were silently ignored before.- The constructor's
vmin_ratiodefault is now0.0, the value that was actually in effect, so default plots look the same. - A value passed to
plot()now stays in effect for laterplot()calls.
- The constructor's
compute_observability()picks the backend from the simulator.use_jaxdefaults toNone; explicitTrue/Falsestill work. Its finite-difference step remainseps=1e-4, whileObservabilityAnalysisdefaults to1e-5. Passeps=1e-5to match it exactly.lam=Nonenow means the default1e-8everywhere. It previously raised an error inObservabilityAnalysisqueries.FisherObservabilityandSlidingFisherObservabilityaccept a 1-Dlamarray.SlidingFisherObservabilityalso has a newshift_indexargument.
Fixes
- 1-D
u_sim:SlidingEmpiricalObservabilityMatrixno longer crashes on a 1-Du_simarray (a single input). ObservabilityMatrixImage:- it now raises a clearer error when
Ois not a DataFrame; - numbered sensor names no longer contain a stray
$.
- it now raises a clearer error when
- CI: it now runs all JAX-dependent tests; previously, 11 tests outside the JAX test file never ran.
Upgrading
pip install --upgrade pybounds # or: pip install --upgrade "pybounds[jax]"
No code changes are needed for existing analyses. Python 3.10+ is still required.
Full changelog: v0.2.0...v0.3.0 (PR #32)