Skip to content

Commit ef380e2

Browse files
committed
test(hpc): rolling-floor Belichtungsmesser probe — coarse loss as a feature under SD drift
Tests "the coarse loss is a feature" claim: a self-calibrating floor (μ+3σ) rides the distribution so the 256 buckets stay well-exposed as σ drifts, rather than reconstructing. Three floors over a 4-phase drifting stream, on the real Cascade. Measured: - EWMA rolling floor TRACKS perfectly (ρ=1.000 vs true μ+3σ; reject stable ~0.1% across all phases, spread 0.05pp; bucket exposure 29–77%). - FIXED floor mis-exposes: 97.6% reject after an up-shift (recall collapse), 0% after a down-shift; reject-rate spread 42pp. FINDING (real bug surfaced): the shipped Cascade (global Welford + ShiftAlert) behaved IDENTICALLY to FIXED — ShiftAlert NEVER fired, because cumulative-Welford per-sample Δμ is always ≪ 2σ. The drift check is inert on a per-sample observe() feed; it would only fire on batch means. The EWMA (per-step) floor is what tracks. ⇒ coarse loss is usable for routing via a rolling floor; reconstruction fidelity is irrelevant — only the floor's calibration is. https://claude.ai/code/session_01D2WSmezQBNC3bUdHuGfGmo
1 parent 40bb42e commit ef380e2

2 files changed

Lines changed: 168 additions & 0 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ required-features = ["std"]
9797
name = "morton_perturbation_probe"
9898
required-features = ["std"]
9999

100+
[[example]]
101+
name = "rolling_floor_probe"
102+
required-features = ["std"]
103+
100104
[dependencies]
101105
num-integer = { workspace = true }
102106
num-traits = { workspace = true }

examples/rolling_floor_probe.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//! Rolling-floor Belichtungsmesser probe — coarse loss AS A FEATURE.
2+
//!
3+
//! The bgz-hhtl-d coarse distances are lossy for reconstruction, but a
4+
//! self-calibrating cascade doesn't reconstruct — it exposes. Like a camera light
5+
//! meter, the floor (reject threshold = μ+3σ) rides the distribution so the 256
6+
//! buckets stay well-exposed as the standard deviation drifts. This probe puts
7+
//! that claim on the instrument: under distribution drift, does a ROLLING floor
8+
//! stay calibrated where a FIXED floor mis-exposes?
9+
//!
10+
//! Three floors over a drifting stream (4 phases, each a different μ,σ):
11+
//! • FIXED — `Cascade::calibrate` once on phase 1, never updated.
12+
//! • SHIPPED — real `Cascade`: `observe` (Welford) + `recalibrate` on ShiftAlert.
13+
//! • EWMA — the rolling 256-bucket floor: exponential μ/σ, threshold every step.
14+
//!
15+
//! Metrics: per-phase reject-rate (target ≈ the 0.13% one-sided 3σ tail), floor
16+
//! tracking error vs the true per-phase μ+3σ (Spearman), and 256-bucket exposure
17+
//! coverage (well-exposed vs clipped).
18+
//!
19+
//! cargo run --release --example rolling_floor_probe --features std
20+
21+
use ndarray::hpc::cascade::Cascade;
22+
use ndarray::hpc::reliability::spearman;
23+
24+
fn splitmix(s: &mut u64) -> f64 {
25+
*s = s.wrapping_add(0x9E37_79B9_7F4A_7C15);
26+
let mut z = *s;
27+
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
28+
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
29+
z ^= z >> 31;
30+
(z >> 11) as f64 / (1u64 << 53) as f64
31+
}
32+
fn randn(s: &mut u64) -> f64 {
33+
let u1 = splitmix(s).max(1e-12);
34+
let u2 = splitmix(s);
35+
(-2.0 * u1.ln()).sqrt() * (std::f64::consts::TAU * u2).cos()
36+
}
37+
38+
/// 256-bucket exposure coverage of `kept` distances over [0, threshold].
39+
fn bucket_coverage(kept: &[u32], threshold: u64) -> f64 {
40+
if kept.is_empty() || threshold == 0 {
41+
return 0.0;
42+
}
43+
let mut seen = [false; 256];
44+
for &d in kept {
45+
let b = ((d as f64 / threshold as f64) * 256.0).clamp(0.0, 255.0) as usize;
46+
seen[b] = true;
47+
}
48+
seen.iter().filter(|&&x| x).count() as f64 / 256.0
49+
}
50+
51+
fn main() {
52+
println!("== Rolling-floor Belichtungsmesser: coarse loss as a feature under SD drift ==\n");
53+
54+
// Drifting stream: 4 phases, each Normal(μ,σ) — a distribution that shifts.
55+
let phases = [(120.0, 25.0), (330.0, 70.0), (90.0, 12.0), (210.0, 40.0)];
56+
let per_phase = 4000usize;
57+
let mut s = 0xF100Du64;
58+
59+
let gen = |mu: f64, sg: f64, n: usize, s: &mut u64| -> Vec<u32> {
60+
(0..n)
61+
.map(|_| (mu + sg * randn(s)).max(0.0) as u32)
62+
.collect()
63+
};
64+
65+
// Phase-1 calibration shared by all three.
66+
let p1 = gen(phases[0].0, phases[0].1, per_phase, &mut s);
67+
let fixed = Cascade::calibrate(&p1, 64); // never updated
68+
let mut shipped = Cascade::calibrate(&p1, 64); // observe + recalibrate
69+
let (mut mu_e, mut var_e) = (fixed.mu(), fixed.sigma() * fixed.sigma()); // EWMA seed
70+
let alpha = 0.02;
71+
72+
println!(" phase true μ+3σ FIXED thr reject% SHIPPED thr reject% EWMA thr reject% exposure(EWMA)");
73+
println!(" ----- --------- --------- ------- ----------- ------- -------- ------- --------------");
74+
75+
let mut true_floor = Vec::new();
76+
let mut ewma_floor = Vec::new();
77+
let mut fixed_reject = Vec::new();
78+
let mut ewma_reject = Vec::new();
79+
80+
for (pi, &(mu, sg)) in phases.iter().enumerate() {
81+
let dists = if pi == 0 {
82+
p1.clone()
83+
} else {
84+
gen(mu, sg, per_phase, &mut s)
85+
};
86+
let true_thr = mu + 3.0 * sg;
87+
88+
let (mut rej_f, mut rej_s, mut rej_e) = (0usize, 0usize, 0usize);
89+
let mut kept_ewma = Vec::with_capacity(dists.len());
90+
for &d in &dists {
91+
// FIXED.
92+
if d as u64 > fixed.threshold {
93+
rej_f += 1;
94+
}
95+
// SHIPPED rolling: Welford observe + recalibrate on drift alert.
96+
if let Some(alert) = shipped.observe(d) {
97+
shipped.recalibrate(&alert);
98+
}
99+
if d as u64 > shipped.threshold {
100+
rej_s += 1;
101+
}
102+
// EWMA rolling floor (updates every step).
103+
let dv = d as f64 - mu_e;
104+
mu_e += alpha * dv;
105+
var_e += alpha * (dv * dv - var_e);
106+
let thr_e = (mu_e + 3.0 * var_e.max(0.0).sqrt()) as u64;
107+
if d as u64 > thr_e {
108+
rej_e += 1;
109+
} else {
110+
kept_ewma.push(d);
111+
}
112+
}
113+
let thr_e = (mu_e + 3.0 * var_e.max(0.0).sqrt()) as u64;
114+
let n = dists.len() as f64;
115+
let cov = bucket_coverage(&kept_ewma, thr_e);
116+
println!(
117+
" {:>3} {:>9.0} {:>9} {:>5.2}% {:>11} {:>5.2}% {:>8} {:>5.2}% {:>6.1}%",
118+
pi + 1,
119+
true_thr,
120+
fixed.threshold,
121+
100.0 * rej_f as f64 / n,
122+
shipped.threshold,
123+
100.0 * rej_s as f64 / n,
124+
thr_e,
125+
100.0 * rej_e as f64 / n,
126+
100.0 * cov
127+
);
128+
true_floor.push(true_thr);
129+
ewma_floor.push(thr_e as f64);
130+
fixed_reject.push(100.0 * rej_f as f64 / n);
131+
ewma_reject.push(100.0 * rej_e as f64 / n);
132+
}
133+
134+
// Tracking: does the rolling floor follow the true per-phase μ+3σ?
135+
let rho_ewma = spearman(&true_floor, &ewma_floor);
136+
let fixed_vec: Vec<f64> = (0..phases.len()).map(|_| fixed.threshold as f64).collect();
137+
let rho_fixed = spearman(&true_floor, &fixed_vec);
138+
// Calibration stability: spread of reject-rate across phases (lower = steadier).
139+
let spread = |v: &[f64]| {
140+
let m = v.iter().sum::<f64>() / v.len() as f64;
141+
(v.iter().map(|x| (x - m) * (x - m)).sum::<f64>() / v.len() as f64).sqrt()
142+
};
143+
144+
println!("\nfloor tracking (Spearman vs true μ+3σ): EWMA ρ={rho_ewma:+.3} FIXED ρ={rho_fixed:+.3}");
145+
println!(
146+
"reject-rate spread across phases (lower = stays calibrated): EWMA {:.2}pp FIXED {:.2}pp",
147+
spread(&ewma_reject),
148+
spread(&fixed_reject)
149+
);
150+
151+
let tracks = rho_ewma > 0.9;
152+
let stable = spread(&ewma_reject) < 1.0 && spread(&fixed_reject) > 10.0;
153+
let mark = |b: bool| if b { "PASS" } else { "FAIL" };
154+
println!("\nVERDICT:");
155+
println!(" rolling floor TRACKS the drifting SD (ρ>0.9) ...... {}", mark(tracks));
156+
println!(" rolling stays calibrated where FIXED mis-exposes ... {}", mark(stable));
157+
println!("\n ⇒ coarse loss IS a usable feature: the EWMA rolling floor rides the distribution, keeping the");
158+
println!(" 256 buckets well-exposed and the reject tail at ~0.1% as σ drifts (ρ=1.0 vs true μ+3σ). The");
159+
println!(" FIXED floor over-prunes after an up-shift (recall collapse, ~98% reject) and under-prunes");
160+
println!(" after a down-shift (0% reject). FINDING: the shipped Cascade behaved IDENTICALLY to FIXED —");
161+
println!(" its ShiftAlert never fired, because cumulative-Welford per-sample Δμ is always ≪ 2σ, so the");
162+
println!(" drift check is inert on a per-sample feed (fire it on batch means, or add an EWMA floor).");
163+
println!(" Reconstruction fidelity is irrelevant here — only the floor's calibration is.");
164+
}

0 commit comments

Comments
 (0)