S9 — Marginalization
The sliding window has to stay bounded, so each step S9 drops the oldest clone (and any features anchored only to it). For a filter, this is exact principal-submatrix extraction of the kept states — and its correctness check is a clean mathematical identity.
What it does
Section titled “What it does”Slide the window: remove a clone’s 6 error-state rows/columns from the covariance. In covariance form this is principal-submatrix extraction of the kept states (a principal submatrix of a PD matrix stays PD). The clone is selected by policy (oldest, or a two-way keyframe / non-keyframe scheme).
Why it matters
Section titled “Why it matters”The defining property of marginalization: marginalizing a variable never alters the marginal of the others. So after dropping a clone, every kept state’s covariance — and the cross-covariances among kept states — must be exactly unchanged. If they shift, the extraction is wrong (or the covariance wasn’t consistent going in). For a filter doing submatrix extraction there is no FEJ / marginalization-prior subtlety (unlike a smoother); correctness rides entirely on the S3 augmentation cross-covariance having been right.
The contract
Section titled “The contract”| Signature | marginalize(x, P, clone_idx) → (x', P') (slide the window) |
| Pre | clone selected by policy (oldest, or two-way keyframe/non-keyframe) |
| Post / invariants | P' symmetric PSD · kept-state marginal unchanged by removing a clone (pure extraction) · marginalization does not constrain the 4 gauge directions |
| cortex | msckf/state_helper.hpp — marginalize_clone, principal submatrix via cov.marginalize(keep) |
Metrics
Section titled “Metrics”The S9 probe builds a three-clone correlated
covariance, drops the oldest clone via the shipped marginalize_clone, and
checks the kept-marginal identity directly:
| invariant | residual | verdict |
|---|---|---|
| kept-state marginal unchanged | 0 | PASS — pure extraction |
P' positive semidefinite | — | PASS |
| state dimension | 33 → 27 | shrinks by one 6-DoF clone |
| window length | 3 → 2 clones | stays bounded |
The kept-marginal residual is identically zero — cov.marginalize(keep) is an
exact principal-submatrix extraction, so dropping the oldest clone leaves every
retained state’s covariance untouched, and P' stays PSD.
Real-data inspector (s9_inspect)
Section titled “Real-data inspector (s9_inspect)”The measurements above come from the synthetic probe. Its real-data companion,
s9_inspect (the inverse of s3_inspect),
runs the real estimator over a real EuRoC sequence until the window holds several
clones, then takes a copy of that keyframe state and runs the real
StateHelper::marginalize_clone on it. No SDK change — the operator is standalone
and backend().state() is exposed.
s9_inspect --dataset /path/to/V1_01_easy/mav0 --out build/s9 --min-clones 5 --idx 0node docs-site/scripts/gen-marginalization-figures.mjs build/s9 # → marginalize_covariance.svgIt writes marginalization.json and renders the before/after covariance heatmap
with the dropped clone block outlined in the “before” matrix — its row band and
column band (the cross-covariance) are what’s discarded. The HUD reports the
invariant on real data: the kept-state marginal residual (the others’ covariance
must be untouched) and the information dropped (the clone’s own σ and its
cross-covariance with the kept states). On EuRoC the kept-marginal residual comes
out identically zero, confirming the exact extraction holds on a real,
fully-correlated filter covariance, not just the synthetic one.