Summary
Add detectability analysis to gds_analysis.linear as the weaker observability property needed for observer design.
Detectability asks whether every unobservable mode is at least stable. This is the mathematically correct precondition for steady-state observer synthesis and aligns with the existing kalman() API contract.
Motivation
The current control stack already provides:
observability_matrix() / is_observable() in gds_domains.symbolic
is_stable() in gds_analysis.linear
kalman() in gds_analysis.linear
But there is no first-class API for the intermediate property controls engineers actually use when full observability is too strong. Detectability completes the classical pair:
- controllability → stabilizability
- observability → detectability
Proposed API
In gds_analysis.linear
def unobservable_modes(
A: list[list[float]],
C: list[list[float]],
*,
tol: float = 1e-9,
) -> list[complex]:
"""Return eigenvalues corresponding to unobservable modes via PBH."""
def is_detectable(
A: list[list[float]],
C: list[list[float]],
*,
continuous: bool = True,
tol: float = 1e-9,
) -> bool:
"""Check detectability of (A, C).
Continuous-time: every mode with Re(lambda) >= 0 must be observable.
Discrete-time: every mode with |lambda| >= 1 must be observable.
"""
Implementation notes
Use the PBH observability test mode-by-mode:
- mode
λ is observable iff rank([[λI - A], [C]]) = n
- a system is detectable iff every unobservable mode lies strictly in the stable region
This belongs in gds_analysis.linear rather than gds_domains.symbolic because:
- it is a numerical matrix-level property
- it matches the existing home of
is_stable(), lqr(), and kalman()
- it should accept plain
list[list[float]] matrices, independent of any DSL object
Follow-on
- Update
kalman() docs and validation language to reference detectability explicitly.
- Export through
gds_analysis.__init__ lazy imports.
Testing
Add known-answer tests for:
- fully observable stable system → detectable
- unobservable but stable mode → detectable
- unobservable unstable mode → not detectable
- discrete-time analog with an unobservable pole inside/outside the unit circle
Labels
enhancement, control-theory
Summary
Add detectability analysis to
gds_analysis.linearas the weaker observability property needed for observer design.Detectability asks whether every unobservable mode is at least stable. This is the mathematically correct precondition for steady-state observer synthesis and aligns with the existing
kalman()API contract.Motivation
The current control stack already provides:
observability_matrix()/is_observable()ingds_domains.symbolicis_stable()ingds_analysis.linearkalman()ingds_analysis.linearBut there is no first-class API for the intermediate property controls engineers actually use when full observability is too strong. Detectability completes the classical pair:
Proposed API
In
gds_analysis.linearImplementation notes
Use the PBH observability test mode-by-mode:
λis observable iffrank([[λI - A], [C]]) = nThis belongs in
gds_analysis.linearrather thangds_domains.symbolicbecause:is_stable(),lqr(), andkalman()list[list[float]]matrices, independent of any DSL objectFollow-on
kalman()docs and validation language to reference detectability explicitly.gds_analysis.__init__lazy imports.Testing
Add known-answer tests for:
Labels
enhancement,control-theory