Advanced Estimation · PhD Track

Adaptive & Robust
Kalman Filtering

The Kalman filter is only optimal if you hand it the right Q and R — this lesson is about what happens when you don't, how the filter can tell, and how it can fix itself.

Prerequisites: Kalman Filter + Debugging Consistency. Predict/update fluency and the NIS/NEES tests assumed.
12
Chapters
10
Live Simulations
7
Quals Q&A

Chapter 0: The Right Filter, the Wrong Numbers

Your Kalman filter is mathematically perfect. The derivation is airtight, the code is bug-free, the matrices are the right shapes. And it is about to sail serenely past the target while reporting its best confidence ever — because someone typed Q two orders of magnitude too small.

Here is how it happens. The tracker worked beautifully in the lab. Before the demo, an engineer "cleaned up" the tuning: the process noise looked generous, so they cut Q by a factor of 100 to smooth the output. The demo got smoother — noticeably, satisfyingly smoother. Everyone approved. Then the target turned, and the filter didn't.

The mechanism is one number (chapter 1 derives it). For a random-walk state with measurement variance r = 1, cutting q from 0.01 to 0.0001 shrinks the steady-state gain from K = 0.095 to K = 0.00995. Each measurement now moves the estimate by one percent of the innovation. Since the filter's response time is roughly 1/K steps, it went from a 10-step memory to a 100-step memory. It is not broken. It is asleep.

The trap: the Kalman filter's optimality theorem is CONDITIONAL. "Optimal" means optimal given F, H, Q, R. The derivation in kalman-filter.html never checks those inputs — Q and R are confessions about the world, and the filter believes every word. Feed it a false confession and it computes the exactly optimal response to a world that does not exist.

What makes this failure dangerous is its asymmetry: it is not a noisy-estimate failure you would notice. Q too small produces the smoothest, most confident output the filter will ever give you — right up until the model is wrong. A maneuver, a gust, a slope in a sensor's drift — anything the model didn't predict — and the sleeping gain cannot wake up. The claimed σ says centimeters; the actual error is meters and compounding.

The Sleeping Filter

Truth (teal) is a slow random walk; red dots are measurements. Two filters run on the same data: honest q and mistuned q ÷ slider. Before the maneuver the mistuned filter looks BETTER — smoother, tighter tube. Trigger the maneuver and watch which one wakes up. The corner readout turns red when actual error exceeds 3× the claimed σ.

Q mismatch ÷100

This lesson exists as the bridge from sf-21-debugging-consistency.html. That lesson taught you the instruments — NIS, NEES, whiteness — for detecting a lying filter offline, on the bench, with ground truth available. But Q and R are not constants of nature: sensors degrade, vibration changes with speed, maneuvers come and go. A filter tuned honest on Tuesday is a liar by Friday. The fix must run inside the filter, online, with no ground truth.

Here is the arsenal this lesson builds. First, learn to read the innovation sequence — the one signal the filter can audit about itself (chapter 2). Second, close the loop: estimate R and Q from innovations (chapters 3–4). Third, forget the past on purpose, so the filter can never fall this deeply asleep (chapter 5). Fourth, when the mismatch is structural rather than a scale, run competing filters and let Bayes referee (chapter 6). And fifth — because an adaptive filter is a numerical stress test by construction — make the covariance arithmetic itself trustworthy (chapters 7–8).

Honest scope: this lesson OWNS online adaptation and Kalman numerics. The theory of the consistency tests lives in sf-21 (we use NIS bands here as instruments, not derive them), and full multi-model switching logic lives in imm-estimation.html — chapter 6 walks you to its doorstep.

Misconception alert: students think a mistuned Kalman filter degrades gracefully — a bit more error, a bit less optimal. Actually mistuning attacks the COVARIANCE first: the filter's claimed uncertainty decouples from its actual error, so every downstream consumer (gates, planners, fusion partners) inherits a confident lie, and the failure surfaces suddenly at the first model violation, not gradually.
PhD lens: examiners open this topic with a trap question: "The Kalman filter is the optimal MMSE estimator — so how can it diverge?" The expected answer distinguishes conditional optimality from robustness: optimality holds under the assumed (F, H, Q, R); with a wrong Q the gain sequence is optimal for the wrong problem, and divergence is the actual error growing unbounded relative to the computed covariance. Bonus points for naming the two classic divergence mechanisms: modeling error with overconfident Q, and numerical loss of covariance positive-definiteness (chapters 7–8).
Check: A tracker's Q is set 100x too small. What does its output look like BEFORE the target maneuvers?
Why? (answer first)

Small Q means the filter trusts its model, so the gain is tiny and the output is a heavily-averaged, beautiful, smooth curve with a tiny claimed σ. On benign data this LOOKS like a better filter — which is exactly why the mistuning ships. The lie only becomes error when the model is violated, and by then the gain is too small to recover.

Chapter 1: What Q and R Actually Encode

Before we can adapt Q and R, we need to feel what they DO. Not their definitions — kalman-filter.html gave you those — but their fingerprints: what exactly goes wrong, in which direction, when each one lies.

Reframe both matrices as confessions rather than parameters. Q says: "between two ticks, the world does things my model F did not predict — this much of them." It is the error bar on your physics. R says: "when the sensor speaks, this is the variance of its lie." It is the error bar on your evidence. The filter's entire behavior — every gain it ever computes — is the argument between these two confessions.

Let's make the argument quantitative with the simplest possible system: a random-walk state (F = 1) with Var(w) = q and Var(v) = r. The predicted variance obeys Ppred = P + q with the posterior P = Ppred r / (Ppred + r). At steady state these two lines close into a quadratic, and the quadratic has a closed-form solution:

Ppred = ( q + √(q² + 4qr) ) / 2      K = Ppred / (Ppred + r)

Every claim in this chapter is this one formula, evaluated. Read its regimes: for q much smaller than r, Ppred ≈ √(qr), so K ≈ √(q/r). The gain depends on the RATIO of the two confessions — and only as a square root. Cut q by 100 and K falls by 10, not 100. Hold that thought: it has a sharp consequence for adaptation later, because steady-state behavior cannot identify q and r separately — only their ratio. Chapter 4 pays this bill.

Worked Numbers — the Gain Law, Evaluated

Take r = 1 and the two q values from chapter 0's disaster.

q = 0.01: Ppred = (0.01 + √(0.0001 + 0.04))/2 = (0.01 + 0.200250)/2 = 0.105125. Gain K = 0.105125/1.105125 = 0.095125. Response time 1/K = 10.5 steps.

q = 0.0001 (the chapter-0 mistuning): Ppred = (0.0001 + √(1e-8 + 4e-4))/2 = 0.010050, K = 0.010050/1.010050 = 0.009950. Response time 1/K = 100.5 steps.

The ratio of gains is 0.095125/0.009950 = 9.56 — cutting q by 100 cut K by roughly √100 = 10, confirming K ≈ √(q/r) in the small-q regime (√0.01 = 0.1 and √0.0001 = 0.01, both within 5% of the exact gains). Sanity-check the closed form by brute force: start P = 10 and iterate the two-line recursion 10,000 times with q = 0.0001; the iterated Ppred lands on 0.010050 — the same number, so the quadratic solution is the true fixed point.

The load-bearing interpretation: 1/K is a memory length. The steady-state filter is an exponential moving average whose window the ratio q/r controls. K = 0.095 means "average roughly the last 10 measurements"; K = 0.00995 means "average roughly the last 100." Everything else in this chapter is this sentence in four directions.

The Four-Quadrant Symptom Table

Each mistuning direction has a distinct, diagnosable symptom — this table is the diagnostic heart of the chapter:

MistuningBehaviorSymptom you see
Q too smallGain collapses, memory lengthensSluggish, smooth, overconfident — lags maneuvers, diverges gracefully (chapter 0's disease)
Q too bigGain saturates highTwitchy estimate that shadows the raw measurements — honest but wasteful; you paid for a filter and got a wire
R too smallFilter treats noise as signalChases jitter, plus OVERconfidence — claimed σ far below actual scatter
R too bigMeasurements barely registerFree-runs on the model — smooth, lagging, and (if the model drifts) biased

The diagnostic: Q errors and R errors both change smoothness, but their INNOVATION signatures differ — which is exactly what chapter 2 learns to read.

The (q, r) Mistuning Map

Left: a live tracker running your assumed (q, r) against fixed true values (q = 0.01, r = 1). Right: drag the dot on the log-log map to set the filter's assumptions; the cross marks truth. Drag ALONG an iso-gain diagonal (constant q/r): the estimate trace is unchanged while the claimed σ tube inflates and deflates — the identifiability lesson made physical.

One more confession, about the confessions. Engineers commit a deliberate crime with Q: they use it as a tuning knob rather than a physical quantity. Real systems inflate Q to cover unmodeled dynamics — linearization error in the EKF, unmodeled accelerations in constant-velocity trackers — a lie told in a safe direction. The steady-state formula is the evidence for why the direction is safe: overestimating q costs you variance like a square root (gentle), underestimating costs you divergence (catastrophic). When forced to guess, guess fat.

Close with the identifiability foreshadow. Since K depends only on q/r in steady state, two engineers — one with the true (Q, R), one with both doubled — ship IDENTICAL estimate sequences. But NOT identical covariances: the computed P and the promised innovation variance S = Ppred + r both scale up with the common factor. The estimates cannot tell the difference; the innovation STATISTICS can. That asymmetry is the crack that chapters 2–3 wedge open.

Misconception alert: students think doubling Q and doubling R "cancels out completely" since only the ratio matters. Only the GAIN — and hence the estimate sequence — is invariant. The computed P and the promised S both scale up, so the two filters make identical state claims but different uncertainty claims, and exactly one of them (at most) passes a NIS test.
PhD lens: a standard quals warm-up: "Derive the steady-state variance of the scalar random-walk filter and give the asymptotic gain law." The examiner then pivots to judgment: "Your CV tracker must handle occasional 5 m/s² maneuvers it does not model — how do you choose Q, and what did that choice cost during cruise?" The expected shape: Q covers unmodeled acceleration (q ~ a²maxT³-type sizing), the square-root law means the cruise-variance cost is gentle, and underestimating is catastrophically asymmetric to overestimating.
Check: In steady state, the scalar gain K is approximately √(q/r) for small q/r. What does this imply about tuning?
Why? (answer first)

K depends on q and r only through their ratio (and only as a square root). Scaling both by the same constant c leaves K — and therefore every estimate — unchanged, while scaling P and S by c. That is the identifiability constraint every adaptive method must respect: innovations expose the ratio easily, the absolute scale only through S.

Chapter 2: The Innovation — the Filter's Only Mirror

The filter cannot see its own error — that would require the truth. But there is one quantity it predicts BEFORE nature answers: the next measurement. The gap between prediction and answer is the innovation, and it is the only mirror the filter will ever get.

Define the object: νk = zk − H x̂pred is the part of the measurement the filter could NOT predict — the genuinely new information, hence the name. Everything predictable was already in x̂pred; what remains should be pure surprise. You met innovations in sf-21-debugging-consistency.html as a way to TEST a filter on the bench. Here is the pivot this lesson needs: the innovation becomes the sensor that DRIVES adaptation, in flight.

A correctly-modeled filter makes a precise, three-part promise about its innovation stream:

(1) E[ν] = 0     (2) E[ννT] = S = H Ppred HT + R     (3) E[νkνjT] = 0 for k ≠ j

Zero mean: no systematic surprise. Covariance exactly S: the filter knows HOW surprised it should be. And whiteness: innovations at different times are uncorrelated, because an optimal filter extracts every predictable pattern, leaving residue with no structure. The derivations live in sf-21; what we need here is what each violation MEANS.

Whiteness is the deep one, and the intuition is worth a PhD: if innovations are correlated in time, there is a pattern in the surprises — and a pattern in the surprises is information the filter failed to extract. A too-small gain leaves tracking error that persists across steps, so consecutive innovations share the same sign: positive autocorrelation. This is Mehra's classic 1970 observation, and it is directional evidence: fat-but-white innovations indict the noise levels; colored innovations indict the gain — the q/r ratio, or the model itself.

Worked Numbers — Auditing the Sleeping Filter

Quantify chapter 0's disaster. Truth: random walk with qtrue = 0.25, measured with rtrue = 1. The filter assumes q = 0.0025 — 100x too small.

Step 1 — the promise. The filter's internal steady state under its assumed q: Ppred = (0.0025 + √(0.0025² + 4×0.0025))/2 = 0.0513. So it PROMISES S = Ppred + r = 1.0513 and runs gain K = 0.0488.

Step 2 — the reality. For a fixed suboptimal gain the actual error dynamics are epost = (1−K)epred + Kv with epred picking up a fresh w each step, which closes into a steady state Pactual = (K²rtrue + qtrue)/(1 − (1−K)²) = 2.6523.

Step 3 — the broken promise. The ACTUAL innovation variance is C = Pactual + rtrue = 3.6523. The filter promised 1.0513 and delivers 3.6523 — a broken promise by the factor C/S = 3.47. That ratio is exactly what a running NIS average would read.

Step 4 — Monte Carlo confirms. A 200,000-step seeded run measures innovation variance 3.634, and lag-1 autocorrelation 0.674 — the surprises are massively patterned, because the sleepy gain leaves persistent tracking error that leaks into consecutive innovations.

Step 5 — the control. Same data, filter assuming the TRUE q = 0.25: promised S = 1.6404, measured 1.648, lag-1 autocorrelation −0.002. Right scale, white residue. The mirror works.

Innovation Oscilloscope

Top: the live innovation stream with the PROMISED ±2√S band (teal). Bottom: sample autocorrelations ρ1..ρ10 over the trailing 300 innovations, with the ±2/√N whiteness band (dashed). Use the presets to learn each mistuning's signature: band spill = scale violation, tall bars = color violation.

Q factor ×1.0
R factor ×1.0

The promise hands you a free gift on the way out: the innovation gate. Since ν should be N(0, S), a measurement with ν²/S > 9 (a 3-σ event) is a once-in-370 occurrence; reject it as an outlier before it touches the state. This is the cheapest robustness in all of estimation — and it is only as good as S is honest. An overconfident filter (small S) starts gating TRUE measurements, which is how the confident-lie failure cascades. Depth on outlier theory — influence functions, Huber losses, when hard gates fail — belongs to robust-estimation.html; the gate itself belongs here, because S does.

The instrument rating: from a single scalar stream the filter can estimate its own innovation mean (bias detector), its variance ratio against S (scale detector — the NIS of sf-21 running online), and its autocorrelation (structure detector). Three statistics, no ground truth required, all cheap enough for every tick. Adaptation — next chapter — is just closing the loop from these statistics back to Q and R.
Misconception alert: students think a healthy filter should produce SMALL innovations. The innovations of a perfectly-tuned filter are as large as S says they should be — health is CALIBRATION (variance matching S) and WHITENESS (no temporal pattern), not smallness. Tiny innovations with strong autocorrelation are the signature of a filter chasing the measurements, not of a good one.
PhD lens: the canonical oral question: "Prove that the innovations of the optimal filter are white, and explain what a suboptimal gain does to them." The expected sketch: by the orthogonality principle the optimal x̂pred is the projection of x onto all past measurements, so νk is orthogonal to every earlier measurement and hence to every earlier innovation; a suboptimal gain leaves a component of past error unremoved, which propagates through F and correlates consecutive innovations — Mehra's 1970 starting point, and the reason innovation correlations carry enough information to identify the mistuning.
Check: A filter's innovations have the promised variance but a strong positive lag-1 autocorrelation. What does this indicate?
Why? (answer first)

Whiteness is the sharper test: correlated surprises mean a pattern was left on the table — persistent tracking error leaking across steps, the fingerprint of a wrong gain or wrong dynamics. Scale can accidentally match while structure betrays the filter; that is why adaptation schemes read the autocorrelation, not just the variance ratio.

Chapter 3: Innovation-Based Adaptation — Closing the Loop

Chapter 2 built the diagnosis: measured surprise versus promised surprise. The adaptive move is almost embarrassingly direct — if the promise does not match the measurement, change the promise until it does.

Set up the estimator. Over a sliding window of the last W innovations, form the sample covariance Ĉ = (1/W) Σ ννT. This is the measured side. The promised side is S = H Ppred HT + R. The innovation identity says that IF the model were right, these would agree in expectation. So read the identity backwards, as an equation FOR R:

R̂ = Ĉ − H Ppred HT

Everything on the right is observable. Nothing needs truth. This is innovation-based adaptive estimation (IAE), the correlation-family member of Mehra's classic taxonomy of adaptive filters — Bayesian, maximum-likelihood, correlation-matching, covariance-matching (chapter 4 takes the last).

Now the subtlety that separates this from a homework exercise: the Ppred in the formula is the FILTER'S internal covariance, computed under its current wrong R. So the first solve gives an IMPROVED R, not the true one. The equation is implicit: changing R changes the gain, which changes Ppred, which changes what R̂ solves to. The honest algorithm is a fixed-point iteration — and the fixed point is exactly the truth, because when assumed R equals true R the filter is consistent and C = H Ppred HT + R holds with equality.

Worked Numbers — Walking the Fixed Point

Scalar random walk with q = 0.09 (known and correct) and true measurement variance r = 4.0. The filter starts believing r = 1.0. At each iteration: compute the filter's internal steady-state Ppred and gain under its assumed r, compute the ACTUAL innovation variance C that gain produces (closed form, exactly as in chapter 2), then update via R̂ = C − Ppred.

Iteration 1: assumed r = 1.0 gives internal Ppred = 0.3484, gain K = 0.2584. Actual innovation variance: C = Pactual + rtrue where Pactual = (K²rtrue + q)/(1−(1−K)²), giving C = 4.7934. Update: R̂ = 4.7934 − 0.3484 = 4.4450. An overshoot — the overconfident filter underestimates its own Ppred, so too much of C gets attributed to R.

Iteration 2: assumed r = 4.4450 gives Ppred = 0.6791 and C = 4.6475, so R̂ = 3.9684. Now slightly under — the loop is contracting.

Iterations 3–5: R̂ = 4.0024, then 3.9998, then 4.0000. Geometric convergence onto the truth.

Fixed-point check: at assumed r = 4.0 exactly, Ppred = 0.6467, the filter is consistent so C = Ppred + 4.0 = 4.6467, and R̂ = C − Ppred = 4.0 — the update maps the truth to itself.

Two lessons in one table: the estimator works, and it works BECAUSE you keep applying it. A one-shot correction from a mistuned filter's logs is systematically biased (4.445 for a true 4.0). The deployed algorithm does not iterate to convergence at a frozen instant — it applies one cheap update per tick against the live window, and the closed loop performs the iteration THROUGH TIME.

Window size W is the knob. W = 30–100 is typical. Smaller tracks sensor changes faster but injects estimation noise into the gain: the variance of a sample variance of Gaussian data is 2σ⁴/W, so R̂'s relative jitter is roughly √(2/W) — about 20% at W = 50, and that jitter feeds straight into K. Larger W is calmer but slower to notice a degrading sensor. You are spending one statistical budget two ways.

Adaptation, Caught in the Act

Three strips, one time axis. (1) Innovations with the promised ±2√S band — at start the filter believes r = 1 (true 4): band far too tight, dots spilling. (2) R̂ (warm) versus the true r (teal dashed) — press Degrade sensor to double the true r and watch R̂ re-converge after a W-length lag. (3) Running mean NIS: starts near 3.5, collapses toward 1 as R̂ locks on. Step iteration performs one frozen-time fixed-point solve per press — the worked table, animated. Freeze shows the frozen filter's NIS staying broken, for contrast.

Window W50

What about Q? Same door, harder passage. The innovation identity constrains H Ppred HT + R as a SUM — with R trusted, surplus innovation variance can be attributed to Q: raise q until S matches Ĉ, using Ppred's dependence on q from chapter 1's Riccati closed form. But H projects the state: innovations only see Q through the measured subspace, so process noise on unobserved states is weakly identified, slowly, through the dynamics. Practical systems therefore adapt a scalar inflation on a fixed Q shape rather than a full matrix — estimate the level, trust the structure.

And the boundary every adaptive filter must fear: adapt R or Q from the same innovation stream and you are fine; adapt both freely and you have one equation (the S level) with two unknowns — chapter 1's ratio identifiability, now with teeth. The standard discipline: fix whichever side you can characterize offline (R from sensor bench data, usually), adapt the other online, and let whiteness — not just scale — arbitrate when both are suspect. Colored innovations indict the Q side; white-but-fat indicts the R side. Chapter 4 shows what happens when this discipline is skipped.

A real deployment bug — the gate ratchet: the adaptive window must EXCLUDE gated outliers (they are not evidence about R). But if R̂ ever collapses too low, S shrinks, the 3-σ gate tightens, more TRUE measurements get gated, the window sees only the survivors — and R̂ ratchets DOWN. An adaptive filter and an innovation gate form a feedback loop that must be designed together: floor R̂, cap the gated fraction, and alarm (do not silently adapt) when the gate rate exceeds a few percent.

From Scratch: the Complete IAE Loop

The whole chapter in 40 lines. A mistuned filter (assumed R four times too small) versus the same filter with sliding-window adaptation, on the same seeded data. The frozen filter's mean NIS reads ~3.65 — chapter 2's broken-promise ratio; the adaptive one heals to ~1.03 with R̂ ≈ 3.85 against a true 4.0. No ground truth used anywhere.

python
import numpy as np

# Scalar random-walk tracker: q known and correct, R badly wrong.
rng = np.random.default_rng(11)
q_true, r_true = 0.09, 4.0
N = 4000
xs = np.cumsum(rng.normal(0, np.sqrt(q_true), N))   # truth
zs = xs + rng.normal(0, np.sqrt(r_true), N)          # measurements

def run(adapt, W=50, r_assumed=1.0):
    """One filter pass. adapt=True closes the IAE loop:
    R_hat = mean(nu^2 over window) - P_pred  (innovation identity,
    read backwards), floored to stay positive."""
    xh, Pf, Rh = 0.0, 1.0, r_assumed
    window, nis = [], []
    for k in range(N):
        Pp = Pf + q_true                 # predict (F = 1)
        S = Pp + Rh                      # the promise
        nu = zs[k] - xh                  # the surprise
        nis.append(nu*nu/S)              # running self-audit (sf-21)
        window.append(nu*nu)
        if len(window) > W:
            window.pop(0)
        if adapt and len(window) == W:
            Chat = np.mean(window)       # measured surprise level
            Rh = max(1e-3, Chat - Pp)    # solve the promise for R
        K = Pp/(Pp + Rh)                 # gain under current belief
        xh = xh + K*nu
        Pf = (1-K)*Pp                    # (short form is fine here:
    return float(np.mean(nis[500:])), Rh #  scalar, exact-arithmetic K)

nis_frozen, _ = run(adapt=False)
nis_adapt, R_final = run(adapt=True)
print('frozen  (R assumed 1, true 4): mean NIS =', round(nis_frozen, 2))
print('adaptive (window W=50):        mean NIS =', round(nis_adapt, 2))
print('final R_hat =', round(R_final, 2), ' (true 4.0)')
# frozen mean NIS ~3.65 (the broken promise, cf. chapter 2's ratio);
# adaptive ~1.03 with R_hat ~3.85: consistency restored from innovations
# alone -- no ground truth was used anywhere above.
Misconception alert: students think R̂ = Ĉ − H Ppred HT is a one-shot formula: measure, solve, done. Ppred is computed under the CURRENT wrong assumption, so the first solve is biased (4.445 for a true 4.0) — the formula is a fixed-point map, correct only at its fixed point. That is why adaptive filters apply it continuously rather than once, and why a single offline correction from a mistuned filter's logs systematically overshoots.
PhD lens: examiners probe the implicit equation: "Write the innovation-covariance identity, derive the R estimator, and explain why iterating is necessary. Then: your window is W = 50 — what is the standard deviation of R̂, and what does that jitter do downstream?" Expected: the fixed-point argument with the overshoot mechanism (internal Ppred understated when overconfident), then the ~√(2/W) ≈ 20% relative jitter at W = 50, which enters the gain and slightly colors the innovations — adaptation noise is itself a mistuning, which is why W trades tracking speed against consistency.
Check: In the IAE update R̂ = Ĉ − H Ppred HT, which Ppred must be used, and what does that imply?
Why? (answer first)

Only the internal Ppred is available without truth. Since it is computed under the current wrong R, one application improves but does not correct the assumption (4.445 from a true 4.0 in the worked numbers). Repeated application contracts to the unique fixed point — assumed R equal to true R — because exactly there the filter is consistent and the identity closes.

Chapter 4: Covariance Matching & Sage-Husa — Recursive Noise Estimation

A sliding window stores W innovations and recomputes a covariance every tick. An engineer with a microcontroller asks the obvious question: can I get the same adaptation in one line, no buffer? Sage and Husa answered in 1969 — with a formula and, hidden inside it, a trap.

Derive the recursive form from the window. A sample mean over a growing window obeys mk = mk−1 + (1/k)(new − mk−1). Replace 1/k with a forgetting schedule dk = (1−b)/(1−bk+1) built from a forgetting factor b (typically 0.95–0.999): early on, dk is near 1/k (use all data); asymptotically it settles at 1−b (an exponential window of effective length 1/(1−b)). Apply this averaging to chapter 3's IAE quantity and you get the Sage-Husa measurement-noise estimator:

k = (1 − dk) R̂k−1 + dk ( νkνkT − H Ppred HT )

Worked Numbers — One Update by Hand, Then a Seeded Run

The hand step. Forgetting factor b = 0.95, so the asymptotic weight is d = 0.05. Previous R̂ = 2.5, innovation ν = 3.1, H Ppred HT = 0.6. Instantaneous evidence: ν² − 0.6 = 9.61 − 0.6 = 9.01 — a one-sample estimate of R, huge because this particular innovation was large. Blend with the memory: R̂ = 0.95 × 2.5 + 0.05 × 9.01 = 2.375 + 0.4505 = 2.8255. The estimate moves 5% of the way toward the instantaneous evidence — that 5% IS the forgetting factor's asymptotic weight d = 1−b. One multiply-accumulate per tick; the buffer is gone.

The seeded run. The chapter-3 system (q = 0.09 known, true r = 4.0, filter starting at R̂ = 1.0), b = 0.995, 5000 steps, seed 7, R̂ floored at 10−6. The early schedule dk ≈ 1/k learns fast from nothing: R̂ climbs to 3.54 by step 500 and 3.99 by step 2000 — converged within noise. But it does not STAY there: over steps 3000–5000 the estimate wanders with mean 3.78, because an exponential window of effective length 1/(1−b) = 200 has irreducible relative jitter of roughly √(2/200) ≈ 10%. The wander is not a bug to fix but a budget to spend: shrink it only by growing the memory, which slows real-change tracking by the same factor.

Be honest about the estimator's texture: each instantaneous term ν² − H Ppred HT is a ONE-SAMPLE variance estimate — brutally noisy, and frequently negative (whenever a small innovation arrives, ν² < H Ppred HT). The exponential average smooths it, but two engineering guards are mandatory: floor R̂ at a small positive value (a negative or zero R breaks the gain arithmetic and the gate), and treat persistent flooring as an alarm, not a state.

Sage-Husa, Live — and the Identifiability Ridge

Main strip: R̂ (warm) against the true r (teal dashed) — press Degrade to step r from 4 to 8 (rain on the radar). The gray dots are the raw evidence ν² − Ppred: violently noisy, many negative — see what the exponential average tames. Low b: leaps in ~10 steps but rattles. High b: glides, arrives ~1000 steps late. Then flip Also adapt Q: the inset plots the (Q̂, R̂) trajectory sliding ALONG the identifiability ridge away from truth while the innovations stay plausible.

Forgetting b0.995

The forgetting factor b controls the same tradeoff as chapter 3's W, because it is the same object: b → 1 gives long memory — low estimator variance, slow response to a genuinely degrading sensor; small b gives fast tracking that jitters. The wander scales like √(2(1−b)) relative — at b = 0.995, about 10% — and that jitter feeds straight into the gain. You are always choosing where to spend the same statistical budget: confidence in the noise estimate versus freshness of it.

Now the trap the title promised. Sage and Husa also published the dual Q estimator — Q̂ from the state-correction statistics (KννTKT plus the covariance-decrease terms). Run BOTH estimators simultaneously and the scheme can diverge, and quals examiners love asking why: chapter 1 proved the steady-state innovation statistics constrain roughly the SUM H Ppred(q) HT + R — one observable level, two unknowns. The joint estimator has a ridge of near-fixed-points: raise Q̂, lower R̂ compatibly, and the innovations look statistically identical. Estimation noise performs a random walk along the ridge until the gain is nonsense. The whiteness statistic breaks the tie in principle — Q-mistuning colors innovations, pure R-scale mistuning does not — but the classic recursions never use it.

Deployment doctrine (where this chapter earns its keep): (1) characterize R offline when the sensor allows it and adapt Q online — the EKF/GNSS habit: R from datasheet plus bench, Q absorbs linearization and dynamics surprises. (2) Or adapt R online with Q frozen to a physically-sized shape — the indoor-tracking habit: multipath makes R wild, dynamics are calm. (3) If both truly drift, adapt a scalar level on ONE side plus a slow whiteness-driven trim on the other, and alarm on disagreement. Never two free matrices funded by one scalar promise.

Know the map beyond the classics. Offline, autocovariance least squares (ALS, Odelson-Rawlings 2006) solves for Q and R jointly from logged innovation autocorrelations at MULTIPLE lags — the extra lags restore the identifiability that the single-lag level lacks. Online, variational Bayes adaptive filters put an inverse-Wishart posterior on R and update it jointly with the state (Sarkka-Nummenmaa 2009) — principled uncertainty on the noise itself. Sage-Husa survives in practice because it costs nothing; know what it approximates, and where its blind spot is.

Misconception alert: students think running the published Sage-Husa Q estimator and R estimator together gives you both for free. The steady-state innovation level constrains only (roughly) the sum H Ppred(q) HT + R — one observable, two unknowns — so the joint scheme has a ridge of indistinguishable solutions and estimation noise random-walks along it. The classic divergence of joint Sage-Husa is an identifiability failure, not a numerical one, and no amount of tuning d fixes it.
PhD lens: a two-part probe: (1) "Derive the recursive covariance-matching update from the sliding window and identify the asymptotic weight" — expecting d → 1−b and the effective memory 1/(1−b); (2) "Your logged innovations pass the variance test but Q̂ has tripled and R̂ halved since deployment — what happened, and which single statistic would have caught it?" — expecting the ridge-walk diagnosis and the lag-autocorrelation (whiteness) statistic, since Q-side mistuning colors innovations while pure R-side does not.
Check: Why is simultaneously adapting full Q and R from the innovation variance level fundamentally dangerous?
Why? (answer first)

One scalar promise (the S level) cannot pin down two free parameters. The likelihood surface has a ridge of near-equivalent (Q, R) pairs; noise moves the estimates along it while every scale diagnostic stays green. Identifiability returns only with more observables — innovation autocorrelations at multiple lags (ALS) or whiteness-based tie-breaking — or by freezing one side.

Chapter 5: Fading Memory — Forgetting as a Feature

Every divergence story in this lesson ends the same way: the filter becomes too sure of a past that no longer describes the present. The bluntest cure is also the most elegant: make the filter constitutionally incapable of remembering too much.

The mechanism is one line. In the predict step, replace Ppred = F P FT + Q with

Ppred = α² F P FT + Q      α slightly above 1

with a fading factor α (Fagin 1964, Sorenson-Sacks 1971). Inflating covariance is the filter's language for humility: each tick, the filter is forced to concede a fixed fraction of its accumulated certainty.

"Inflate P" sounds like a hack until you see what it means in data terms. Unroll the recursion: a measurement j steps old enters the current estimate with weight proportional to α−2j. The fading filter is EXACTLY exponentially-weighted least squares. The effective data window is Neff = α²/(α² − 1): α = 1.01 remembers about 51 samples, α = 1.05 about 11. One knob, direct control over memory length.

Worked Numbers — the Gain-Floor Theorem

Take the WORST case for responsiveness: q = 0. The model claims the state never changes, so the standard filter's gain decays to zero and it falls asleep permanently. With fading (measurement variance r):

Steady state: Ppred = α²(1 − K) Ppred with K = Ppred/(Ppred + r). Substituting K and solving: Ppred + r = α² r, so Ppred = (α² − 1) r — the predicted variance refuses to fall below a floor set purely by α.

The gain floor follows: K = Ppred/(Ppred + r) = (α² − 1)/α². For α = 1.05: α² = 1.1025, K = 0.1025/1.1025 = 0.092971. For α = 1.01: K = 0.019704.

Effective memory: Neff = α²/(α² − 1): α = 1.05 gives 10.76 samples; α = 1.01 gives 50.75. The two views agree: K ≈ 1/Neff — a gain floor IS a memory ceiling.

Verification by iteration: starting P = 5 with α = 1.05, q = 0, the faded Riccati recursion settles at K = 0.092971 and Ppred = 0.1025 = (α² − 1) r exactly — the index-card theorem survives contact with the recursion.

The insurance premium: the un-faded filter on a truly constant state would drive variance to 0; the faded one pays a permanent steady-state Ppred of 0.1025. That is the fixed cost of never being fully asleep.

The theorem in one sentence: α = 1.05 guarantees K ≥ 0.093 forever, even against a model that swears nothing ever changes. No mistuned Q can put this filter fully to sleep.
The Step-Response Race

Left: truth is constant, then steps (press Trigger step). Three q = 0 filters race: the standard KF (gain draining to zero — the sleeping filter made pure), the fading filter at the slider's α, and a ghost at a comparison α. The gain bars under the pane show the standard filter's bar draining while the fading filters rest on their floors. Right: the theorem as a picture — K(α) and Neff(α) with your slider position marked.

Fading α1.050

Place the tool against chapters 3–4. Adaptive estimation MEASURES the mismatch and corrects the model — accurate, but with a lag, an estimator variance, and failure modes (ridges, gated windows). Fading memory measures nothing. It is open-loop humility: always on, zero parameters estimated, working against ANY slow mismatch — drifting biases, aging sensors, unmodeled dynamics — including ones your adaptive estimator was not designed to see. The price is permanent: steady-state variance is inflated even when the model happens to be right. An insurance premium, paid every tick.

You will meet this idea wearing other costumes. Fading with factor α² is the same medicine as adding pseudo-noise Qextra proportional to F P FT (state-proportional inflation), and the same idea as the forgetting factor in recursive least squares (λ = α−2 — EE269's adaptive filters are this lesson's cousins). In the EKF it earns its keep twice over: linearization error grows with P, and fading keeps the filter responsive enough to re-linearize before the error compounds — many production EKFs run α = 1.01–1.05 as a matter of policy (see ekf.html).

The honest tuning doctrine: choose α from the timescale of trust — if you believe your model for roughly N steps, set α² = N/(N−1). Watch the failure directions: α too large discards information you paid for (the estimate jitters at the gain floor), and fading is NOT a substitute for gross mistuning — it bounds the sleep depth but cannot fix a wrong H or a biased sensor. It composes cleanly with chapter 3: adapt R with IAE, fade against everything else — belt and suspenders, and the showcase runs exactly that stack.

Misconception alert: students think fading memory is a crude approximation to proper adaptive estimation — what you use when you cannot afford chapter 3. They are different instruments for different threats: adaptation corrects mismatches it can MEASURE (with lag and estimator noise); fading bounds the damage from every mismatch, including unmeasurable and unanticipated ones, at a fixed always-on variance cost. Production filters run both — and the fading factor is the reason the adaptive layer gets innovations lively enough to estimate from after a long quiet period.
PhD lens: examiners like the equivalence triangle: "Show that fading-memory filtering, exponentially-weighted least squares, and RLS with forgetting factor λ are the same algorithm, and give the dictionary between α, λ, and effective window." Expected: unroll the faded Riccati to exhibit weights α−2j, identify λ = α−2, Neff = 1/(1−λ) = α²/(α²−1) — then the judgment question: "when is fading the WRONG tool?" (fast structural changes: a bounded gain floor still responds at O(Neff) timescale; bias and wrong-H errors: inflation cannot fix a wrong subspace, only re-open the gain).
Check: With q = 0 (model claims the state never changes) and fading factor α, the steady-state gain is:
Why? (answer first)

The inflation fights the contraction to a standoff: Ppred settles at (α² − 1) r, giving K = (α² − 1)/α² > 0 forever. This is the theorem that makes fading an insurance policy — no model claim, however confident, can drive the gain below the floor, so the filter can always respond to a reality its model denied.

Chapter 6: A Bank of Filters — MMAE and the Road to IMM

Chapters 3–5 assumed the model family was right and only the noise levels lied. But sometimes the disagreement is structural: is the target cruising or maneuvering? Is the sensor healthy or degraded? You cannot slide continuously between hypotheses. So stop sliding — run them all.

Reframe adaptation as hypothesis testing. Postulate a finite set of models M1..MN — here, the same F and H with different Q hypotheses (q, 10q, 100q), though the same machinery covers different R, F, or H. Each model gets its own complete Kalman filter running on the same measurements. This is the multiple-model adaptive estimator (MMAE, Magill 1965) — the Bayesian answer to "which confession is true?"

The referee takes two lines to derive. The filter matched to the true model produces innovations that are N(0, Si) — that is chapter 2's promise. So the likelihood of the observed innovation under model i is:

Li = exp( −νi² / 2Si ) / √(2πSi)      pi ← pi Li , then renormalize

Note carefully what is being scored: not smallness of the innovation, but AGREEMENT WITH THE PROMISE. A model that predicted big surprises and got them outscores a model that promised small surprises and got big ones. The exponent penalizes surprise relative to the promise; the 1/√(2πS) normalization taxes over-wide promises. The referee scores calibration — the same virtue NIS tests.

Worked Numbers — the Referee's Arithmetic

A two-filter bank with equal priors p = (0.5, 0.5). The quiet filter promises innovation variance S1 = 1.05; the maneuver filter promises S2 = 4.0. A maneuver begins, and innovations ν = 2.5, −2.2, 2.8 arrive.

First innovation ν = 2.5: L1 = exp(−6.25/2.1)/√(2π × 1.05) = 0.01985; L2 = exp(−6.25/8)/√(2π × 4) = 0.09132. The quiet model called a 2.4-σ event; the maneuver model called a comfortable 1.25-σ event. Bayes: unnormalized (0.5 × 0.01985, 0.5 × 0.09132), normalized: p = (0.1786, 0.8214). One measurement moved the bank from agnostic to 82% maneuver.

Second innovation ν = −2.2: the likelihood ratio again favors the wide promise; p = (0.0719, 0.9281).

Third innovation ν = 2.8: p = (0.0095, 0.9905). Three consistent surprises produce 99% confidence — multiplicative Bayes compounds evidence exponentially fast. No threshold was tuned, no detector designed; the promise-versus-evidence ratio did all the work.

Read the design lesson in both directions: the same compounding that detects the maneuver in 3 ticks would, after 1000 quiet ticks, leave p(maneuver) around 10−40 if unfloored. Detection speed and saturation depth are the same mechanism — which is exactly what IMM's Markov mixing repairs.

The bank has two outputs, and you should know when to use which. The MMSE estimate is the probability-weighted blend Σ pii — smooth, hedges during ambiguity. The MAP choice takes the highest-probability filter's estimate — commits, better when models are genuinely exclusive. And the blended covariance must include the spread-of-means term, Σ pi(Pi + (x̂i − x̂)(x̂i − x̂)T), because disagreement between filters IS uncertainty — dropping that term reproduces this lesson's original sin: overconfidence.

Filter-Bank Cockpit

Top: truth alternates cruise segments and maneuver bursts (tinted bands = maneuver truth); a bank with q hypotheses {q, 10q, 100q} tracks it — faint individual estimates, bold blend. Middle: the model-probability bars animate as likelihoods arrive. During cruise the small-q bar saturates; when a maneuver starts, count the ticks before the big-q bar overtakes. Bottom: the log-odds pit deepening during cruise and the climb-out delay after each switch. The floor visibly shortens the climb-out; the cruise-length slider makes pit-depth-versus-lag experimentally discoverable.

Cruise length240

Now the structural flaw that makes this chapter a bridge rather than a destination. STATIC MMAE assumes one model is true for all time. The probability update is multiplicative, so log-odds accumulate; after a long cruise, p(maneuver) decays exponentially toward zero — and when the target finally does turn, the bank must climb out of a log-probability pit that deepened for the entire cruise. The referee saturates. Band-aids exist — floor the probabilities, exponentially discount old likelihoods (chapter 5's forgetting trick applied to log-odds) — but they treat the symptom.

Name the disease precisely: "which model?" is not a static unknown. It is a STATE that switches — cruise, then turn, then cruise — with its own dynamics. The principled fix gives model probability its own transition model: a Markov chain over modes, mixing the bank's estimates before each cycle so no hypothesis ever starves. That is the Interacting Multiple Model (IMM) filter, and it is the entire subject of the sibling lesson imm-estimation.html: this chapter hands you the bank and the referee; IMM adds the mode dynamics, the mixing step, and the full worked cycle.

Where MMAE belongs in your toolbox: it is the right tool when hypotheses are FEW and DISCRETE — sensor healthy/failed, target cruise/maneuver, bias present/absent. Fault detection by filter bank is standard in aerospace: each actuator-failure mode gets a filter, and the referee IS the fault detector. It is the wrong tool for continuous mistuning — a 100-model grid over q is an expensive discretization of what chapter 3 estimates directly. And its referee statistics assume the innovations it scores are honestly computed — which chapters 7–8 now go underwrite.
Misconception alert: students think the bank rewards the filter with the SMALLEST innovations. It rewards the filter whose innovations best match its own promised S — the likelihood penalizes both surprise (ν²/S too big) and overcaution (the √(2πS) normalization taxes wide promises), so a paranoid filter promising huge S loses to an honest one even when both "contain" the data.
PhD lens: the bridge question examiners use to enter multiple-model theory: "Write the MMAE probability recursion, state its two convergence properties (consistency when the true model is in the bank; convergence to the closest model in KL divergence when it is not), and explain the failure that motivates IMM." Strong answers write pi ∝ pi N(νi; 0, Si), note the KL-projection result for the misspecified case, then the saturation argument — and know that the blended covariance needs the spread-of-means term or the bank inherits the overconfidence disease it was hired to cure.
Check: After a very long cruise, static MMAE responds sluggishly to the first real maneuver. Why?
Why? (answer first)

Static MMAE treats the model as fixed forever, so log-odds accumulate linearly with every quiet tick — the wrong-model probability decays exponentially without bound. The first maneuver innovations must reverse an arbitrarily deep deficit. Flooring probabilities caps the pit; modeling the mode as a Markov chain that can SWITCH (IMM) removes it in principle.

Chapter 7: Numerics I — Roundoff, the Joseph Form, and Sequential Updates

An adaptive filter changes its gain constantly, from noisy estimates, in finite-precision arithmetic. Time to confess something about the covariance update you have been using since kalman-filter.html: it silently assumes the gain is EXACTLY right — and yours never is.

Derive the general truth first. If you update with ANY gain K, the posterior error is epost = (I − KH)epred − Kv, with epred and v independent. Its true covariance is therefore:

Ppost = (I − KH) Ppred (I − KH)T + K R KT     (the Joseph form)

No optimality invoked anywhere: this formula is just bookkeeping for whatever estimator you actually built. Substitute the OPTIMAL K and the cross terms collapse algebraically to the short form P = (I − KH)Ppred. The short form is a special case that happens to be cheaper — and everyone shipped it.

Now expose the fragility with a derivative. Perturb K to K + δ. The Joseph form, being the true variance, changes only at second order — dP/dK vanishes at the optimum, because the optimal gain minimizes posterior variance; that is what optimal means. The short form is LINEAR in K: its error is first-order in δ, and worse, in the wrong direction — increase K past optimal and (I − KH)Ppred DECREASES, claiming improvement while the actual variance grows. The short form does not report your estimator's variance; it reports what the variance would have been had your K been perfect.

Worked Numbers — the Impossible Improvement and the Negative Variance

Scalar update, exact arithmetic: Ppred = 1, R = 1, so S = 2 and the optimal gain is K = 0.5 with true posterior variance 0.5.

At K = 0.5 both forms agree: short (1−0.5)×1 = 0.5; Joseph (1−0.5)²×1 + 0.5²×1 = 0.25 + 0.25 = 0.5. At the exact optimum the forms are algebraically identical.

At K = 0.6 (a 20% gain error — one bad adaptive step): the short form claims (1−0.6)×1 = 0.40 — an improvement over the optimal 0.5, which is impossible: no gain can beat the optimum. The short form is not measuring the estimator; it is extrapolating a formula outside its validity. Joseph: (0.4)²×1 + (0.6)²×1 = 0.16 + 0.36 = 0.52 — which IS the true variance of the K = 0.6 estimator, since Var[(1−K)epred + Kv] = (1−K)²Ppred + K²R. A 2-million-sample Monte Carlo agrees: 0.5196. Note 0.52 = 0.5 + 2×(0.1)² — second-order in the gain error, the optimality of K = 0.5 made visible.

At K = 1.2 (an adaptive overshoot): the short form gives (1−1.2)×1 = −0.20 — a negative variance, which downstream becomes a negative S, an imaginary gate threshold, and a sign-flipped gain. The filter is not degraded; it is insane. Joseph: (−0.2)² + (1.2)² = 0.04 + 1.44 = 1.48 — a terrible estimator, honestly reported.

The moral in one line: the short form answers "what WOULD my variance be if K were perfect"; Joseph answers "what IS my variance given the K I used." An adaptive filter, whose K is never perfect by construction, must ask the second question.

Floating point makes "wrong gain" not exotic but guaranteed: even the optimal K is stored rounded, so the short form is ALWAYS evaluated off-optimum. Its first-order error accumulates over thousands of updates, and because (I − KH)P is a product of non-symmetric factors, P drifts asymmetric (P01 ≠ P10) and eventually indefinite. In float32 — still the currency of embedded autopilots and GPU-resident filter banks — a strong measurement (small R) can push the naive update's smallest eigenvalue negative within a handful of steps. Joseph's quadratic structure plus an explicit symmetrization P ← (P + PT)/2 degrades gracefully under the same conditions. Cost accounting: Joseph is roughly 2x the flops of the short form — the cheapest robustness you will buy this lesson.

Float32 Torture Chamber

Single precision emulated live with Math.fround. A 2-state filter absorbs alternating near-collinear strong measurements (H rows [1,1] and [1,1+ε], R = ε²) — the classic ill-conditioned pair. Naive short form versus Joseph + symmetrization, identical inputs. The plot is each P's smallest eigenvalue per update on a signed-log axis — at aggressive ε the red trace dives below zero within a few updates. Flip to float64: both healthy. The failure is arithmetic, not algebra.

ε (collinearity)2.5e-4

While we are at the update step, take the second numerics upgrade: sequential processing. When R is diagonal (independent sensor channels), a vector measurement need not be absorbed in one matrix-inverse gulp. Process each scalar component through its own scalar update — Si = Hi P HiT + ri, one division each, no matrix inversion at all. The posterior is exactly the same: conditioning on independent evidence commutes. Three wins: no inverse to go ill-conditioned; per-channel innovation gates (chapter 2's gate now isolates WHICH channel is lying — one bad GPS pseudorange gets rejected without discarding the epoch); and per-channel adaptive-R hooks for chapter 3's machinery.

From Scratch: One Update, Three Ways

A vector KF update written as short form, Joseph, and sequential scalar channels — agreeing at the optimum, then deliberately fed an overshooting adaptive gain. Watch the assertions: the short form's covariance goes indefinite (a negative eigenvalue); Joseph reports the honest, larger covariance of the gain actually used.

python
import numpy as np

rng = np.random.default_rng(3)
n = 2
P = np.array([[1.0, 0.3], [0.3, 0.5]])   # predicted covariance
H = np.array([[1.0, 0.0], [0.0, 1.0]])   # two independent channels
R = np.diag([1.0, 2.25])                  # diagonal -> sequential OK

def joseph(P, K, H, R):
    IKH = np.eye(n) - K @ H
    Pn = IKH @ P @ IKH.T + K @ R @ K.T
    return 0.5*(Pn + Pn.T)                # explicit symmetrization

# --- optimal gain: all forms agree ---
S = H @ P @ H.T + R
K = P @ H.T @ np.linalg.inv(S)
P_short = (np.eye(n) - K @ H) @ P
P_jos = joseph(P, K, H, R)
assert np.allclose(P_short, P_jos), 'forms agree at the optimum'

# --- sequential scalar processing: same posterior, no inversion ---
Ps = P.copy()
for i in range(2):                        # one channel at a time
    h = H[i:i+1, :]
    Si = float(h @ Ps @ h.T) + R[i, i]    # scalar S: one division
    Ki = (Ps @ h.T) / Si
    IKHi = np.eye(n) - Ki @ h
    Ps = IKHi @ Ps @ IKHi.T + (Ki * R[i, i]) @ Ki.T   # Joseph per channel
    Ps = 0.5*(Ps + Ps.T)
assert np.allclose(Ps, P_jos, atol=1e-12), 'sequential == batch'

# --- perturbed gain (one bad adaptive transient): forms diverge ---
Kbad = K * 2.2                            # gain overshoot, 2.2x optimal
P_short_bad = (np.eye(n) - Kbad @ H) @ P
P_jos_bad = joseph(P, Kbad, H, R)
print('short-form eigenvalues :', np.round(np.linalg.eigvalsh(P_short_bad), 4))
print('joseph eigenvalues     :', np.round(np.linalg.eigvalsh(P_jos_bad), 4))
assert np.linalg.eigvalsh(P_short_bad).min() < 0   # short form: indefinite!
assert np.linalg.eigvalsh(P_jos_bad).min() > 0     # joseph: honest and PD
# prints short-form eigenvalues [-0.138, 0.2524]: the short form under an
# overshooting gain reports a covariance with a NEGATIVE eigenvalue -- a
# filter that believes an impossible thing. Joseph reports the honest
# (larger) covariance of the gain actually used, eigenvalues
# [0.3841, 1.393]: that honesty is what an adaptive filter, whose gain is
# always slightly wrong, is buying for its ~2x flops.

Set the boundary honestly, to hand off to chapter 8: Joseph plus symmetrization plus sequential updates fixes the UPDATE STEP for realistic conditioning, and it is the right default for every filter in this lesson. But when the condition number of P itself approaches the reciprocal of machine precision — strong measurements, near-perfect correlations from an adaptive transient, float32 budgets — no arrangement of P-arithmetic survives, because P can no longer even REPRESENT its own small eigenvalues. Then you stop computing with P, and start computing with its square root.

Misconception alert: students think P = (I−KH)Ppred and the Joseph form are interchangeable implementations of the same equation, with Joseph merely "more stable." They compute different things: Joseph is the true covariance of whatever gain you applied (valid unconditionally); the short form is an algebraic shortcut valid ONLY at the exactly-optimal gain. With any gain error the short form reports a variance that is wrong at first order, can claim better-than-optimal, and can go negative. "More stable" understates it — the short form is answering a counterfactual question.
PhD lens: a favorite derivation demand: "Prove the Joseph form is valid for arbitrary gain, show it reduces to (I−KH)P at the optimal gain, and establish the sensitivity orders of both forms to a gain perturbation." The expected finish is judgment: sequential scalar updates for diagonal R (exactness by conditional independence, per-channel gating as the operational win), the 2x flop cost of Joseph as the price of running adaptive gains safely, and knowing that beyond condition numbers around 1/√(machine ε) the update-form question becomes moot — the square-root methods of the next chapter take over.
Check: An adaptive filter's noisy R̂ produced a gain 20% above optimal for one step. What do the two covariance forms report?
Why? (answer first)

The short form is linear in K and decreasing through the optimum, so an overshoot yields a claimed variance better than the best achievable — the 0.40-versus-0.5 impossibility from the worked numbers. Joseph, being the actual variance of the applied gain, shows the second-order penalty: 0.52. For a filter whose gain is perpetually perturbed by adaptation noise, Joseph is not a luxury but the only honest bookkeeping.

Chapter 8: Numerics II — Square-Root and UD Filtering

Chapter 7 patched the update formula. But there is a deeper problem no formula fixes: at extreme conditioning, the matrix P cannot faithfully HOLD its own small eigenvalues in finite precision. The 1969 Apollo-era answer remains the state of the art: never store P at all.

Diagnose the representation problem, not the algorithm. P's eigenvalues span its condition number κ(P); float32 carries about 7.2 decimal digits; and the entries of P mix the large and small eigendirections, so the small eigenvalue lives in the trailing digits of every entry. Quantify with this chapter's running example: P = [[1, 0.999],[0.999, 1]] has eigenvalues 1.999 and 0.001 — κ = 1999, so log10(κ) = 3.3 of your 7.2 digits are spent before any algorithm runs. Perturb a single entry by 0.1% (0.999 → 1.000) and the small eigenvalue is annihilated: the matrix goes exactly singular. The weak direction was never safely stored.

The cure is a factorization with a theorem attached. Store S with P = S ST — any square root; Cholesky's lower-triangular factor is the canonical choice. Since P's eigenvalues are the squares of S's singular values:

κ(S) = √κ(P)

The log-condition halves. For the running example: 44.71 versus 1999. In digit terms: 1.65 digits spent instead of 3.3. The rule of thumb this buys is famous and worth stating exactly: square-root filtering achieves the same effective precision as covariance filtering with TWICE the word length — float32 square-root roughly matches float64 naive.

Worked Numbers — Conditioning, then a Float32 Stress Test

(a) Conditioning. Eigenvalues of P: 1.999 and 0.001, so κ(P) = 1999.0. Cholesky: S = [[1, 0],[0.999, 0.04471]] — note the weak direction gets its OWN entry, 0.04471 = √(1 − 0.999²). κ(S) = 44.71 = √1999: the doubling theorem, numerically. Now perturb P's 0.999 to 1.000 — a 0.1% change to one entry: the eigenvalues become (0, 2). The small eigenvalue is 100% destroyed; P is singular. The same-sized relative perturbation applied to S's off-diagonal entry moves the reconstructed small eigenvalue by 0.1% — three orders of magnitude less damage from the same-sized wound. Digit accounting: at κ(P) = 107, P in float32 has NO digits left for the weak direction while S retains ~3.7 — which predicts exactly what (b) shows.

(b) Precision — the Grewal-Andrews stress test. From P0 = I, absorb two near-collinear strong measurements (H = [1,1] then [1, 1+ε], R = ε², ε = 2.5×10−4). Naive float32 covariance update: the smallest eigenvalue of P comes out exactly 0.000e+00 — float32 could not represent the surviving sliver of uncertainty, so the filter now claims PERFECT knowledge along one direction, and the next measurement in that direction divides by a vanishing S. The exact float64 answer is 1.562e-08. A Potter square-root filter in the SAME float32: smallest eigenvalue 2.668e-08 — not exact (the problem sits at float32's edge) but positive and the right order of magnitude. Same arithmetic budget, qualitatively different integrity.

The structural guarantee (what certification engineers actually buy): whatever rounding does to S, the product S ST is positive semidefinite BY CONSTRUCTION. A square-root filter cannot produce chapter 7's negative variance, ever — the pathology is unrepresentable.
The Representation Problem, Drawn

Left: the covariance ellipse of P(ρ) = [[1, ρ],[ρ, 1]] degenerating into a needle as ρ → 1, with both eigenvalues as log-scale bars. Right: the precision budget — each bar is float32's 7.2 decimal digits; P spends log10κ(P) of them (red), S spends half. Press Perturb 0.1% against P: the ellipse snaps to a degenerate line (DESTROYED). The same perturbation against S barely moves it.

Correlation ρ0.999

Quals expect the names and the deltas of the classical family. Potter (1963, flew on Apollo): measurement update directly on S for scalar measurements, one square root per update. Carlson: triangular-factor variant. Bierman UD (1977): factor P = U D UT with U unit-upper-triangular and D diagonal; the measurement update is a rank-one modification needing NO square roots at all — pure multiply-add-divide, the reason it owned 1970s–80s flight computers and still owns radiation-hardened and fixed-point targets. Thornton: the companion time update, propagating U and D through F and Q via modified weighted Gram-Schmidt. UD is algebraically a square-root method (S = U √D) with the numerical benefits and integer-friendly arithmetic.

The modern framing connects to least squares. Today's preferred formulation is the array (QR) form: stack the propagated factor and noise factors into a tall block matrix, orthogonally triangularize, read off the updated factor. Numerically this is solving the underlying least-squares problem the way numerical analysts insist all least squares be solved — by orthogonal transforms on the square root of the information, never by forming the normal equations. P-arithmetic IS the normal equations, squared condition number and all. This is the same principle that makes factor-graph smoothers do QR on square-root information matrices — the thread runs from Apollo to modern SLAM unchanged.

Production Framing: Bierman's UD Update, As Flight Software Ships It

python
import numpy as np

def ud_factorize(P):
    """P = U D U' with U unit upper-triangular, D diagonal (one-time)."""
    n = P.shape[0]
    U, D, P = np.eye(n), np.zeros(n), P.copy()
    for j in range(n-1, -1, -1):
        D[j] = P[j, j]
        for i in range(j):
            U[i, j] = P[i, j] / D[j]
            for k in range(i+1):
                P[k, i] -= U[k, j] * D[j] * U[i, j]
    return U, D

def bierman_update(x, U, D, h, R, z):
    """Scalar-measurement UD update (Bierman 1977). No square roots:
    multiply/add/divide only -- fixed-point and certification friendly.
    D stays positive BY CONSTRUCTION: indefiniteness is unrepresentable."""
    f = U.T @ h                # f = U'h
    v = D * f                  # v_i = D_i f_i
    alpha = R                  # innovation variance accumulator
    b = np.zeros(len(x))
    for j in range(len(x)):
        alpha_prev = alpha
        alpha += f[j]*v[j]
        D[j] *= alpha_prev/alpha
        p = -f[j]/alpha_prev
        bj = v[j]
        for i in range(j):
            Uij = U[i, j]
            U[i, j] = Uij + b[i]*p
            b[i] += Uij*bj
        b[j] = bj
    K = b/alpha                # gain, free byproduct
    return x + K*(z - h @ x), U, D

# verify against the textbook update
rng = np.random.default_rng(3)
A = rng.normal(size=(3, 3)); P = A @ A.T + 3*np.eye(3)
x = rng.normal(size=3); h = rng.normal(size=3); R, z = 0.7, 1.9
U, D = ud_factorize(P)
assert np.allclose(U @ np.diag(D) @ U.T, P)
x2, U2, D2 = bierman_update(x.copy(), U.copy(), D.copy(), h, R, z)
S = h @ P @ h + R; K = P @ h / S
x_ref = x + K*(z - h @ x)
P_ref = P - np.outer(K, h @ P)
assert np.allclose(x2, x_ref)
assert np.allclose(U2 @ np.diag(D2) @ U2.T, P_ref)
assert D2.min() > 0
print('UD update == reference KF: PASS   min(D) =', round(D2.min(), 4))
# Production notes: pair with Thornton's MWGS time update to propagate
# U, D through F and Q; process vector measurements as sequential scalar
# channels (decorrelate R first if needed). Modern alternative: the
# array/QR square-root form -- orthogonal triangularization of stacked
# factors -- the same numerics that power square-root information
# smoothers in factor-graph SLAM.

Close with the engineering decision table and the adaptive tie-in. Modern float64 plus Joseph plus symmetrization is adequate for most well-conditioned filters — square-root costs roughly 1.5–2x and is the wrong complexity to buy reflexively. Buy it when: float32 or fixed-point hardware (embedded, GPU-resident filter banks); condition numbers pushed by strong-sensor/weak-sensor mixes or by adaptive transients (an adaptive filter slamming R̂ down 4x in one window is a conditioning event BY DESIGN); long-mission covariance integrity requirements; or certification demands provable PSD. And note the adaptive synergy runs both ways: chapter 3's machinery keeps S honest statistically; this chapter keeps it honest arithmetically — the showcase runs the safe update forms underneath the adaptive loop.

Misconception alert: students think square-root filtering is an obsolete Apollo-era optimization made irrelevant by float64. The doubling theorem cuts both ways today — float32 is BACK (GPU-resident filter banks, embedded NPUs, fixed-point radiation-hardened parts), adaptive filters manufacture conditioning events on purpose, and the structural cannot-go-indefinite guarantee is a certification property no amount of word length provides. The array/QR form is also how modern factor-graph smoothers work — the "obsolete" idea quietly runs inside every serious SLAM stack.
PhD lens: expect a names-and-tradeoffs interrogation: "Compare Potter, Bierman UD, and array QR square-root filters — what does each cost, which needs no square-root operations, and why did UD dominate flight software?" Then the connecting question that separates strong candidates: "Relate covariance filtering versus square-root filtering to normal equations versus QR in least squares" — the expected insight being that P-arithmetic IS forming the normal equations (squaring the condition number), and orthogonal-transform updates on factors are the filtering incarnation of numerically-sound least squares, the same principle underlying square-root information smoothers in modern SLAM.
Check: Why does propagating S (with P = S ST) effectively double arithmetic precision?
Why? (answer first)

The damage ill-conditioning does is measured by log10(κ) — digits spent spanning the eigenvalue range. Since the factor's singular values are the square roots of P's eigenvalues, its log-condition is exactly half: the running example spends 1.65 digits instead of 3.3, and the float32 stress test shows the practical consequence — naive P collapses to exact singularity while the factor form retains the weak direction.

Chapter 9: Showcase — the Self-Tuning Tracker

You have the instruments (chapter 2), the estimators (chapters 3–4), the insurance (chapter 5), the referee (chapter 6), and trustworthy arithmetic (chapters 7–8). Time to fly the whole stack — and to break it on purpose, from the cockpit.

The cockpit: a 2D arena where a target flies constant-velocity legs punctuated by coordinated turns — the classic maneuvering profile. A position sensor reports every tick with true measurement variance rtrue. The filter is a 4-state CV Kalman tracker — exactly kalman-filter.html's chapter-8 machine — but its ASSUMED q and r are set by two mischief sliders (0.01x to 100x truth). The adaptive stack sits behind one master toggle: innovation-window R estimation (chapter 3), a fading factor on the predict (chapter 5), Joseph-form sequential updates underneath (chapter 7).

One honest detail before you fly. The 1.0x baseline q is not the raw random-acceleration variance of the truth — it is chapter 1's "deliberate crime" performed correctly: q inflated about 8x so the coordinated turns live inside the process-noise budget. That is the tuning a deployed CV tracker actually ships with, and it is why 1.0x/1.0x reads consistent: YES on a maneuvering target — with only brief instrument blips as each turn passes through the windows. Everything the mischief sliders do is measured against that covering tuning.

The Self-Tuning Tracker

Arena: truth, measurements, estimate with its 95% covariance ellipse — the single most important element on screen; it turns red whenever it fails to cover the truth. Instruments: running mean NIS with the 2-dof 95% band, whiteness bars (lags 1–5), and R̂ against rtrue. Run the four experiments below.

Assumed Q ×1.0
Assumed R ×1.0
Window W60

Experiment 1 — recreate chapter 0, honestly. Drag Q to 0.01x, adaptation OFF. During straight legs the picture is seductive: glassy estimate, tiny ellipse. Watch the instruments instead: mean NIS creeps above the 2-dof 95% band [0.05, 7.38] (sf-21's chart, running live), and the lag-1 autocorrelation bar pushes outside its whiteness band. Then the first turn: the estimate cuts the corner wide, NIS spikes past 20, and — the detail worth staring at — the ellipse does not grow, because the filter's Q says turns do not happen. Confidence and error have fully decoupled.

Experiment 2 — the opposite quadrant. Q back to truth, R to 0.01x. The estimate shivers, gluing itself to every measurement. Now read the instrument correctly: with R assumed tiny, the promised S is small, so NIS = ν²/S runs HIGH — the filter is more surprised than its promise allows. Contrast with R assumed huge (100x): S is fat, NIS sinks BELOW the band, and the filter free-runs. The panel teaches the diagnosis direction: NIS high means promised S too small (overconfident somewhere); NIS low means S too fat (underconfident); the autocorrelation bars say whether the GAIN — the q/r balance — is also wrong.

Experiment 3 — the payoff. Leave the sliders wrong. Flip ADAPT on. The R̂ readout walks from its wrong start toward rtrue within roughly two window-lengths; the fading factor keeps the gain floor up so maneuvers stop being catastrophic; mean NIS bends back into the band; the whiteness bars sink into their thresholds. Then fight it: yank the sliders mid-flight, hit Storm (rtrue doubles), trigger extra turns. The stack re-converges each time, with the lag the window slider predicts.

Experiment 4 — find the limit. Set BOTH Q and R wrong by the same factor (both 10x). The estimates are fine — chapter 1's ratio invariance; the gain was never wrong. NIS is broken by the common scale, and adaptation fixes NIS — but look at the R̂ readout against truth: the stack lands on A consistent scale, not necessarily THE true split. The identifiability ridge from chapter 4, now experienced: consistency is restorable from innovations alone; the true decomposition is not, without more structure. The display says so explicitly — it reports "consistent" and "correct" separately — rather than pretending.

The lesson's arc, closed: the tracker that began this lesson asleep — smooth, confident, doomed — now carries instruments that detect its own lies, estimators that repair them, a memory bound that limits how deeply it can ever sleep, and arithmetic that cannot manufacture negative certainty. That is what "adaptive and robust" means as an engineering discipline rather than two adjectives.
Misconception alert: students think that once ADAPT is on, the sliders stop mattering — the filter now finds the truth. Adaptation restores CONSISTENCY (NIS in band, white innovations), which is weaker than truth: with both noises mis-scaled the stack converges to a self-consistent wrong decomposition that tracks perfectly, and only extra structure — offline calibration of one side, multi-lag ALS, whiteness-based attribution — can pin the split. The readout panel says "consistent" and "correct" separately for exactly this reason.
PhD lens: this is the systems-judgment oral in simulator form: "Your deployed tracker's NIS is drifting high over weeks. Walk me through the triage tree." Expected structure, straight from the experiments: read NIS direction (high = overconfident) and whiteness (colored = gain/model, white = scale) to localize; check gate-rate feedback (chapter 3's ratchet); decide adapt-R versus adapt-Q from which side is physically drift-prone; state the identifiability caveat before reporting "fixed"; and name the numerics check — symmetric, positive-definite covariance, honest ellipse — that rules out chapter 7's failure masquerading as mistuning.
Check: With adaptation ON and both assumed Q and R initially wrong by the same 10x factor, what converges to truth and what does not?
Why? (answer first)

Innovations constrain the gain (through the ratio) strongly and the overall S level directly, so consistency and tracking heal. But the same innovation statistics are satisfied by a family of (Q, R) pairs — chapter 4's ridge — so the estimated decomposition need not be the true one. The showcase separates "consistent" from "correct" on screen because conflating them is this field's most-shipped bug.

Chapter 10: Quals Arsenal

The machinery is built. Now sit the exam: seven questions of the kind that open an estimation quals, each expecting derivation plus limits plus judgment — and the numbers you verified in this lesson are your answer key.

Protocol: attempt each question on paper or the Teach whiteboard BEFORE expanding the worked answer. The graded skill is not recall but the chain derivation → limit → decision — every answer here ends with a judgment call, because every examiner's follow-up is "so what would you ship?"

A map of the seven, so a miss tells you which chapter to revisit: gain-and-identifiability theory (Q1, Q3), the adaptive estimators and their fixed points (Q2, Q4), robustness mechanisms (Q5 fading, Q6 MMAE), and numerics (Q7 Joseph/square-root).

Q1. Derive the steady-state predicted variance and gain for the scalar random-walk filter (F = 1, Var(w) = q, Var(v) = r). Give the asymptotic gain law for q << r and use it to explain why Q-underestimation is catastrophically asymmetric to Q-overestimation.
Worked answer

The recursion Ppred = P + q with P = Ppred r/(Ppred + r) closes into Ppred² − q Ppred − qr = 0, giving Ppred = (q + √(q² + 4qr))/2 and K = Ppred/(Ppred + r). For q << r: Ppred ≈ √(qr), so K ≈ √(q/r) — gain depends on the RATIO, and only as a square root. Numbers: q = 0.01, r = 1 gives K = 0.0951 (10.5-step memory); q = 0.0001 gives K = 0.00995 (100.5-step memory) — cutting q by 100 cuts K by ~10. Asymmetry: overestimating q by a factor c inflates steady-state error like c to the 1/4-to-1/2 power (gentle, and the filter stays responsive); underestimating q lengthens memory like an inverse square root, and when reality violates the model the tiny gain cannot recover before the error compounds — variance cost versus divergence risk. Judgment line: when forced to guess Q, guess fat, and let chapter-3 adaptation trim it down from data.

Q2. State the innovation-covariance identity, derive the IAE measurement-noise estimator, and prove that (a) a single application from a wrong assumption is biased and (b) the truth is the unique fixed point. Illustrate with numbers.
Worked answer

For a consistent filter, E[ννT] = S = H Ppred HT + R — the promise. Read backwards: R̂ = Ĉ − H Ppred HT with Ĉ the windowed sample covariance. (a) The available Ppred is the filter's INTERNAL one, computed under the current wrong R; if assumed R is too small the filter is overconfident, its internal Ppred understates the actual predicted error variance, so too much of Ĉ is attributed to R: with q = 0.09, true r = 4, assumed r = 1, the internal Ppred = 0.3484 while the actual is ~2.65 under that gain; the first solve gives R̂ = 4.4450 — overshoot. (b) At assumed R = true R the filter is consistent, so C = H Ppred HT + R holds exactly and the update maps R to itself; the iteration contracts (4.4450, 3.9684, 4.0024, 3.9998, 4.0000) onto the truth. Follow-up the examiner wants: the deployed algorithm performs this iteration THROUGH TIME with a sliding window, and the window size trades tracking lag against estimator jitter of order √(2/W) relative.

Q3. Why does jointly running the Sage-Husa Q and R estimators from the same innovation stream risk divergence? What restores identifiability, offline and online?
Worked answer

The steady-state innovation variance constrains (approximately) the single level S = H Ppred(q) HT + R — one observable, two unknowns. There is a ridge of (Q, R) pairs producing statistically indistinguishable innovation VARIANCE: raise Q̂ and lower R̂ compatibly and every scale diagnostic stays green while the individual estimates drift; estimation noise random-walks the pair along the ridge until the gain is nonsense — an identifiability failure, not numerics, so no forgetting-factor tuning fixes it. What breaks the tie: innovation AUTOCORRELATIONS at multiple lags depend on q and r differently (a wrong gain colors innovations; a pure R-scale error does not) — offline, autocovariance least squares (Odelson-Rawlings) solves for both from multi-lag correlations of logged data; online, the discipline is freeze-one-adapt-one (bench-calibrate R, adapt Q, or vice versa), optionally with a slow whiteness-driven trim on the frozen side. Judgment: never two free noise matrices funded by one scalar promise.

Q4. Derive the fading-memory gain floor: with fading factor α and q = 0, show the steady-state gain is (α² − 1)/α², relate α to the effective data window, and place fading against innovation-based adaptation.
Worked answer

With Ppred = α² P (q = 0) and the scalar update P = (1−K) Ppred, steady state requires Ppred = α²(1−K) Ppred; substituting K = Ppred/(Ppred + r) gives Ppred + r = α² r, so Ppred = (α² − 1) r and K = (α² − 1)/α² — strictly positive for any α > 1, whatever the model claims. Numbers: α = 1.05 gives K = 0.092971 and effective window Neff = α²/(α² − 1) = 10.76; α = 1.01 gives 0.019704 and 50.75; note K ≈ 1/Neff — a gain floor IS a memory ceiling. Unrolling shows data j steps old carries weight α−2j: fading is exponentially-weighted least squares, i.e., RLS with λ = α−2. Placement: adaptation measures and corrects specific mismatches with lag and estimator variance; fading is measurement-free open-loop insurance against ALL slow mismatches, purchased with a permanent variance floor of (α² − 1) r — production stacks run both.

Q5. Write the MMAE probability recursion, compute one update by hand (S1 = 1.05, S2 = 4.0, equal priors, ν = 2.5), state what the likelihood actually scores, and explain the saturation failure that motivates IMM.
Worked answer

pi(k) ∝ pi(k−1) N(νi; 0, Si), renormalized. Hand update: L1 = exp(−6.25/2.1)/√(2π × 1.05) = 0.01985; L2 = exp(−6.25/8)/√(2π × 4) = 0.09132; posterior (0.1786, 0.8214). The likelihood scores CALIBRATION, not smallness: the exponent penalizes surprise relative to the promise, and the 1/√(2πS) normalization taxes over-wide promises — an honestly-matched filter beats both a jumpy and a paranoid one. Convergence: with the true model in the bank, its probability converges to 1; if not, MMAE converges to the model closest in KL divergence. Saturation: the update is multiplicative, so log-odds accumulate linearly during a long regime; the wrong-mode probability decays exponentially (10−40-scale after long cruises), and the first real mode change must climb out of that pit — sluggish detection precisely when it matters. Floors and likelihood-discounting are band-aids; modeling the mode as a Markov chain with mixing before each cycle — the IMM — is the principled repair, and its full cycle is the sibling lesson's subject.

Q6. Prove the Joseph form is the correct posterior covariance for ANY gain, show the short form is its restriction to the optimal gain, establish the sensitivity of each to a gain perturbation, and give the negative-variance demonstration.
Worked answer

For arbitrary K: epost = (I−KH)epred − Kv with independent epred and v, so Ppost = (I−KH) Ppred (I−KH)T + K R KT — pure bookkeeping, no optimality. Substituting the optimal K = PpredHTS−1 and expanding collapses the quadratic to (I−KH)Ppred. Sensitivity: the Joseph value is the actual variance, minimized at Kopt, so its derivative in K vanishes there — second-order in δ (scalar case: 0.5 + 2δ² for Ppred = R = 1); the short form is linear in K — first-order error, and DECREASING through the optimum, so an overshoot claims impossible improvement. Numbers: K = 0.6 gives short 0.40 (below the optimal 0.5 — impossible) versus Joseph 0.52 (Monte Carlo 0.5196); K = 1.2 gives short −0.20 — a negative variance that poisons S, gates, and the next gain — versus Joseph 1.48, awful but true. Deployment corollary: an adaptive filter's gain is perpetually perturbed by estimation noise, so Joseph (plus explicit symmetrization, plus sequential scalar updates for diagonal R) is the mandatory update form, at ~2x flops.

Q7. Explain why square-root filtering "doubles precision": relate κ(S) to κ(P), quantify with P = [[1, 0.999],[0.999, 1]], describe what UD filtering adds over Potter, and connect covariance-versus-square-root filtering to normal-equations-versus-QR in least squares.
Worked answer

Since P = S ST, P's eigenvalues are the squares of S's singular values, so κ(S) = √κ(P) — log-condition halves, and digits spent on dynamic range halve: square-root filtering in word length L matches covariance filtering in ~2L. Numbers: eigenvalues of the example are 1.999 and 0.001, κ(P) = 1999, κ(S) = 44.71; a 0.1% perturbation of P's off-diagonal (0.999 → 1.000) makes P exactly singular — the weak direction lived in trailing digits — while the same relative perturbation on S's own entry (0.04471 stores the weak direction explicitly) moves the small eigenvalue 0.1%. Structural bonus: S ST is PSD under any rounding — negative variance is unrepresentable. In float32 on the Grewal-Andrews near-collinear pair (ε = 2.5e-4), the naive update collapses the small eigenvalue to exactly 0 while float32 Potter retains 2.7e-8 versus the exact 1.6e-8. UD (Bierman): P = U D UT updated by rank-one modification with NO square-root operations — flight-computer- and fixed-point-friendly, with Thornton's MWGS as the time update. The deep connection: P-arithmetic is forming normal equations (squares the condition number); factor updates by orthogonal transforms are QR on the square-root information — the same principle that makes modern factor-graph smoothers numerically sound.

Debates — Oral-Exam Finishers

Defensible positions exist on both sides of each. But they must be argued with the detection-lag / estimator-variance / identifiability vocabulary this lesson built — "it felt more robust" is a failing answer.

Debate 1. Defend or refute: "Online adaptation is a confession of lazy engineering — a properly characterized system (bench-calibrated R, physically-sized Q, sf-21-style offline verification) should ship with FROZEN noise matrices, because an adaptive layer adds an estimator whose own variance, lag, and gate-feedback failure modes are harder to certify than the mistuning it prevents."
Debate 2. Defend or refute: "The fading-memory factor is strictly superior to Sage-Husa for production use: zero estimated parameters, a provable gain floor, immunity to the identifiability ridge, and one tuning knob with physical meaning — whereas recursive noise estimation buys accuracy in a narrow regime at the cost of an unbounded failure surface."
Debate 3. Your tech lead proposes: "We run float64 everywhere — delete the UD filter module and use the textbook update; square-root methods are Apollo nostalgia." Defend or refute with specifics: which of this lesson's failure modes float64 actually eliminates (chapter 7's short-form-with-wrong-gain is precision-INDEPENDENT), which return with GPU float32 filter banks and adaptive conditioning transients, and what the certification-grade PSD guarantee is worth.
Misconception alert: students prepare by memorizing the four adaptation families (Bayesian, MLE, correlation, covariance-matching) as a list. Examiners probe the SHARED constraint underneath — what innovation statistics can and cannot identify — and grade on whether you can diagnose which mechanism (scale mismatch, colored gain error, structural mode switch, numerical defect) is active in a described system before prescribing a fix. The taxonomy is the index, not the understanding.
PhD lens: the arsenal IS the lens: these seven are transcribed from the standard repertoire — Mehra's identifiability results, Sage-Husa's recursions and their divergence, Fagin/Sorenson fading memory, Magill's MMAE, Bucy-Joseph sensitivity, Potter/Bierman/Thornton numerics — with grading emphasis on limits (what the innovation stream cannot tell you) and selection judgment (which mechanism for which mismatch).
Check: A described filter has healthy mean NIS but strongly colored innovations. An examiner asks which knob to turn first. The answer is:
Why? (answer first)

Scale statistics (NIS level) and structure statistics (autocorrelation) separate the failure modes: matching variance with patterned residue means the promised S happens to be right while predictable information is being left unextracted — a gain/model problem, chapter 2's directional diagnosis. Turning R first would break the scale to chase the structure.

Chapter 11: Connections — the Filter That Doubts Itself

Strip the algebra and this was a lesson about one signal: the gap between how surprised the filter said it would be and how surprised it was. The innovation promise S = H Ppred HT + R is the filter's testable claim about itself. IAE and Sage-Husa CLOSE THE LOOP on it; fading memory BOUNDS the damage when the loop is too slow; MMAE runs the loop over DISCRETE hypotheses; and Joseph/square-root arithmetic guarantees the claim is at least computed honestly. One signal, five disciplines.

Method Selection

MethodCorrectsMust knowReaction timeCostFailure mode
IAE window (ch 3)Wrong R (or Q level)The other side, trusted~2 window-lengthsOne buffer, one meanGate-window ratchet; jitter at small W
Sage-Husa (ch 4)Drifting R (or Q)The other side, trusted~1/(1−b) stepsOne line/tickRidge walk if both adapted; negative evidence needs floor
Fading memory (ch 5)Any slow mismatch (bounds it)NothingAlways onPermanent variance floorCannot fix wrong H or bias; O(Neff) response only
MMAE bank (ch 6)Discrete structural hypothesesA model set containing (or KL-near) truthFew ticks after switch (if unsaturated)N full filtersLog-odds saturation; needs IMM for switching modes
Joseph + sequential (ch 7)Update-step arithmetic under gain errorNothingImmediate~2x update flopsNone at sane conditioning
Square-root / UD (ch 8)Representation at extreme conditioningNothingImmediate~1.5–2x, more codeOverkill for well-conditioned float64

Habitats

GNSS/INS integration adapts R against multipath and jamming while fading covers IMU aging — sf-14-ins-gnss-coupling.html is the natural habitat. EKF-based VIO inflates Q for linearization error and runs Joseph religiously (ekf.html). Target trackers hand the maneuver problem to full IMM — the sibling lesson imm-estimation.html — with this lesson's MMAE as the vestibule. Flight software still ships UD filtering, and GPU-resident filter banks are re-discovering float32 square-root discipline (chapter 8).

What This Lesson Deliberately Did Not Cover

Go toFor
kalman-filter.htmlThe machine this whole lesson tunes: predict/update, Q/R, and the gain — chapter 0 is what happens after its chapter-8 tracker ships with the wrong numbers
sf-21-debugging-consistency.htmlThe NIS/NEES/whiteness test theory this lesson wields as live instruments — sf-21 detects the lying filter on the bench; this lesson closes the loop in flight
imm-estimation.htmlSibling PhD-track lesson: chapter 6's bank saturates because model choice is really a switching STATE — IMM adds the Markov mode dynamics, mixing step, and full worked cycle
ekf.htmlWhere adaptation earns double pay: Q inflation absorbs linearization error, fading keeps re-linearization honest, Joseph is non-negotiable under EKF conditioning
robust-estimation.htmlOutlier theory beyond chapter 2's 3-σ gate — influence functions, M-estimators, and the Huber loss that generalizes gating into soft downweighting (Huber-KF)
rts-smoothing.htmlSibling PhD-track lesson: offline noise identification (EM, ALS) uses SMOOTHED estimates and multi-lag statistics to recover the Q/R identifiability that online innovations alone cannot
estimator-eval.htmlThe empirical evaluation discipline (NEES/ATE benchmarking, error statistics) for judging whether an adaptive filter actually improved anything offline
sf-14-ins-gnss-coupling.htmlThe adaptive filter's natural habitat: GNSS R varies with multipath, jamming, constellation geometry — online R adaptation plus innovation gating is standard practice
sf-20-learned-differentiable-filters.htmlThe frontier extension: learn the noise models — networks mapping sensor context to R, trained end-to-end through a differentiable Kalman filter, replacing chapter 3's hand-built estimators
sf-09-information-filter.htmlThe dual coordinates: sequential scalar updates and the normal-equations-versus-QR numerics story reappear in information form, where square-root information filtering leads to modern smoothers

The research frontier argues in this lesson's vocabulary: learned noise models trained through differentiable filters (sf-20); adaptive filtering under adversarial spoofing, where the innovation stream itself is attacked; and interacting multiple-model banks over learned dynamics. Promises, identifiability, gain floors — the words do not change.

Misconception alert: students leave believing adaptation is the advanced default every filter should ship. Every adaptive layer adds an estimator with its own variance, lag, and failure modes on top of a filter that may not need it. The professional sequence: characterize offline (sf-21), tune honestly, add fading as cheap insurance, and only close the online adaptation loop for noise sources with a physical reason to drift. An adaptive filter you cannot explain is a second bug generator, not a robustness feature.
PhD lens: closing viva question: "Rank this lesson's mechanisms by what they must KNOW versus what they can GUARANTEE, and name a deployed system for each." The examiner is checking the taxonomy is load-bearing: IAE needs a trusted side and guarantees consistency at its fixed point; fading needs nothing and guarantees only a gain floor; MMAE needs a model set containing (or KL-near) the truth; square-root needs nothing and guarantees representable, PSD covariance — and the strong candidate picks per-system from preconditions, not fashion.
Check: The single signal that funds every mechanism in this lesson is:
Why? (answer first)

IAE and Sage-Husa estimate noise from the promised-versus-measured gap; MMAE scores hypotheses by promise-likelihood; fading bounds how wrong the promise can silently become; the numerics chapters keep the promise honestly computed. The innovation promise is the lesson's conserved thread — the only self-audit that needs no ground truth.

"A filter's covariance is a promise about its own ignorance. An adaptive filter is one engineered to notice when it breaks that promise — and to know precisely which repairs the innovation stream can fund, and which require paying with outside knowledge."
— this lesson's version of the site's motto

You now know how a filter doubts itself. Next door: what happens when the doubt is about which world it lives in.