Janus exposes a structural preflight layer that answers two common model-quality questions: (1) are the selected states structurally observable from the chosen outputs, and (2) are the selected parameters structurally identifiable from the chosen outputs. The implementation works from the symbolic Jacobian sparsity pattern of a janus::Function and lives in <janus/core/Diagnostics.hpp>. This is a symbolic-mode analysis – it answers whether the measurement layout can separate variables based on symbolic dependence alone, not numeric coefficient values.
Quick Start
auto y = janus::SymbolicScalar::vertcat({
x(0) + x(1),
x(1),
});
Wrapper around casadi::Function providing Eigen-native IO.
Definition Function.hpp:46
Umbrella header that includes the entire Janus public API.
SymbolicScalar sym(const std::string &name)
Create a named symbolic scalar variable.
Definition JanusTypes.hpp:90
StructuralSensitivityReport analyze_structural_observability(const Function &fn, int state_input_idx=0, const StructuralSensitivityOptions &opts={})
Analyze which states are structurally observable from selected outputs.
Definition Diagnostics.hpp:547
Core API
StructuralDiagnosticsReport analyze_structural_diagnostics(const Function &fn, const StructuralDiagnosticsOptions &opts)
Run structural observability and identifiability checks together.
Definition Diagnostics.hpp:575
StructuralSensitivityReport analyze_structural_identifiability(const Function &fn, int parameter_input_idx, const StructuralSensitivityOptions &opts={})
Analyze which parameters are structurally identifiable from selected outputs.
Definition Diagnostics.hpp:561
Combined observability and identifiability analysis options.
Definition Diagnostics.hpp:108
int state_input_idx
State input block index (-1 to skip observability).
Definition Diagnostics.hpp:110
int parameter_input_idx
Parameter input block index (-1 to skip identifiability).
Definition Diagnostics.hpp:111
StructuralSensitivityOptions exposes:
- output_indices: optional subset of function outputs to analyze; defaults to all outputs
StructuralDiagnosticsOptions adds:
- state_input_idx: input block interpreted as the state vector
- parameter_input_idx: input block interpreted as the parameter vector
At least one of state_input_idx or parameter_input_idx must be provided for the combined helper.
StructuralSensitivityReport returns:
- structural_rank: structural rank of the selected Jacobian block
- rank_deficiency: n_variables - structural_rank
- deficient_local_indices: all input entries in a deficient structural component
- zero_sensitivity_local_indices: entries with no structural dependence on chosen outputs
- deficiency_groups: connected variable/output groups where structural rank is too small
- issues: user-facing remediation hints
The report also carries flattened input and output labels so you can connect a deficiency back to the original function blocks.
Usage Patterns
When To Use This
Use structural diagnostics before:
- fitting parameters against a measurement model
- introducing estimator states into a filter or smoother
- sending a large reduced-order model into an optimizer and wondering why some degrees of freedom drift
Typical setup:
where x is a dense column-vector state block, p is a dense column-vector parameter block, and y contains the measured or otherwise selected outputs.
Observability Analysis
auto y = janus::SymbolicScalar::vertcat({
x(0) + x(1),
x(1),
});
Here:
- x[2] has zero structural sensitivity
- the structural rank is 2 / 3
- the diagnostic suggests adding a sensor depending on x[2] or constraining/fixing it
Identifiability Analysis
auto y = janus::SymbolicScalar::vertcat({
p(0) + p(1) + x,
p(1) + p(2),
});
Here:
- p[3] is unused and therefore immediately unidentifiable
- p[0], p[1], and p[2] share only two structurally independent output rows
- the report surfaces one coupled deficiency group and recommends adding measurements that separate that block
Combined Diagnostics
If one measurement model carries both estimation states and calibration parameters, run both checks together:
Example Walkthrough: structural_diagnostics_demo.cpp
The example examples/math/structural_diagnostics_demo.cpp demonstrates:
- An observability gap caused by an unmeasured state.
- An identifiability gap caused by a coupled parameter block plus an unused parameter.
- The combined state-plus-parameter report on a shared measurement model.
Build and run:
ninja -C build structural_diagnostics_demo
./build/examples/structural_diagnostics_demo
Diagnostics & Troubleshooting
Current Limits
The current implementation is deliberately structural and local:
- selected input blocks must be dense column vectors
- selected outputs must be dense
- conclusions are based on symbolic Jacobian sparsity, not numeric coefficient values
- a structurally full-rank report does not guarantee good conditioning or practical estimator quality
That makes this pass useful as an early symbolic filter before you spend time on solver tuning, experiment design, or estimator debugging.
See Also