fix(sp16): floor Wiener-α at 0.4 for non-stationary control loops

Wiener-optimal α minimizes MSE for stationary stochastic signals.
The min_hold_temp / hold_cost_scale loops track a target driven by
the adapting policy itself — non-stationary by construction.

Empirical evidence: train-multi-seed-b5gmp sha 641aa0dfd, Fold 1
collapse Sharpe 88.65 → 55.55 because α decayed to 0.248 by HD4
and could no longer track the climbing overrun signal.

Pearl: pearl_wiener_alpha_floor_for_nonstationary (memory).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-08 20:14:18 +02:00
parent 641aa0dfde
commit 25ff1d4195
6 changed files with 169 additions and 36 deletions

View File

@@ -40,8 +40,20 @@
* the third observation, sample_var and diff_var are well-defined and
* α emerges from the data.
*
* Defensive bounds [WELFORD_ALPHA_MIN=0.01, WELFORD_ALPHA_MAX=0.95]
* applied to the Wiener-derived α before the blend.
* Bounds [WELFORD_ALPHA_MIN=0.4, WELFORD_ALPHA_MAX=0.95] applied to
* the Wiener-derived α before the blend. The MIN floor is a
* structural-control constraint (NOT a tuned value): Wiener-optimal
* α is MSE-optimal only for *stationary* signals, and this loop
* tracks a hold-rate overrun driven by a co-adapting policy. Once
* Welford accumulates 3+ samples and the policy converges toward
* target, diff_var → 0 and α_wiener → 0, leaving the controller
* unable to catch up when the policy subsequently drifts back above
* target (empirical anchor: train-multi-seed-b5gmp sha 641aa0dfd
* Fold 1 collapse — α decayed to 0.248 by HD4, blended scale lagged
* climbing target by HD5, Hold% 30.6% → 52.6% → Sharpe 55.55 vs
* 88.65 baseline). The 0.4 floor preserves ~2.5-step catch-up
* bandwidth. The MAX 0.95 prevents pathological α=1.0 (no
* smoothing). Per `pearl_wiener_alpha_floor_for_nonstationary`.
*
* Algorithm (single-block single-thread — cold path, per-epoch boundary):
* Phases 1-7 mirror the MHT producer; only the slot indices and
@@ -99,7 +111,14 @@
#include <cuda_runtime.h>
#define EPS_F 1e-6f
#define WELFORD_ALPHA_MIN_F 0.01f
// WELFORD_ALPHA_MIN_F = 0.4f — non-stationary control-loop floor.
// Wiener-α is MSE-optimal for stationary signals; this controller
// tracks a hold-rate overrun driven by a co-adapting policy, so α
// must never decay below the bandwidth needed to catch up when the
// policy drifts. Cold-start (sample_count < WELFORD_MIN_SAMPLE_COUNT_F)
// uses WELFORD_COLD_START_ALPHA_F=1.0 REPLACE per Pearl A, bypassing
// this floor. See `pearl_wiener_alpha_floor_for_nonstationary`.
#define WELFORD_ALPHA_MIN_F 0.4f
#define WELFORD_ALPHA_MAX_F 0.95f
#define WELFORD_MIN_SAMPLE_COUNT_F 3.0f
#define WELFORD_COLD_START_ALPHA_F 1.0f
@@ -180,6 +199,15 @@ void hold_cost_scale_update(
? diff_M2 / (diff_count - 1.0f)
: diff_M2;
alpha = diff_var / (diff_var + sample_var + EPS_F);
/* Floor at WELFORD_ALPHA_MIN_F=0.4: Wiener-α is MSE-optimal
* for stationary signals only; this control loop tracks an
* overrun driven by the co-adapting policy. Without the floor
* α decays toward 0 once observed_hold_rate converges toward
* target, leaving the controller unable to react when the
* policy drifts (canonical regression: train-multi-seed-b5gmp
* sha 641aa0dfd Fold 1, α=0.248 at HD4 → Hold% 52.6% at HD5).
* Cap at WELFORD_ALPHA_MAX_F=0.95 prevents α=1.0 (no
* smoothing). Per `pearl_wiener_alpha_floor_for_nonstationary`. */
alpha = fmaxf(WELFORD_ALPHA_MIN_F, fminf(alpha, WELFORD_ALPHA_MAX_F));
}

View File

@@ -63,11 +63,20 @@
* - Steady state: signal stabilises → diff_var drops → α → 0.05 or
* lower → smoothing emerges naturally without a hardcoded constant.
*
* Defensive bounds [WELFORD_ALPHA_MIN=0.01, WELFORD_ALPHA_MAX=0.95]
* applied to the Wiener-derived α before the blend. The Wiener
* formula naturally yields α ∈ [0, 1] but numerical edge cases (both
* variances identically zero within ε) could push α to a denormal
* value or floor at 0 (no learning).
* Bounds [WELFORD_ALPHA_MIN=0.4, WELFORD_ALPHA_MAX=0.95] applied to
* the Wiener-derived α before the blend. The MIN floor is a
* structural-control constraint (NOT a tuned value): Wiener-optimal
* α is MSE-optimal only for *stationary* signals, and this loop
* tracks a hold-rate overrun driven by a co-adapting policy. Once
* Welford accumulates 3+ samples and the policy converges toward
* target, diff_var → 0 and α_wiener → 0, leaving the controller
* unable to catch up when the policy subsequently drifts back above
* target (empirical anchor: train-multi-seed-b5gmp sha 641aa0dfd
* Fold 1 collapse — α decayed to 0.248 by HD4, blended scale lagged
* climbing target by HD5, Hold% 30.6% → 52.6% → Sharpe 55.55 vs
* 88.65 baseline). The 0.4 floor preserves ~2.5-step catch-up
* bandwidth. The MAX 0.95 prevents pathological α=1.0 (no
* smoothing). Per `pearl_wiener_alpha_floor_for_nonstationary`.
*
* Algorithm (single-block single-thread — cold path, per-epoch boundary):
*
@@ -154,7 +163,14 @@
#include <cuda_runtime.h>
#define EPS_F 1e-6f
#define WELFORD_ALPHA_MIN_F 0.01f
// WELFORD_ALPHA_MIN_F = 0.4f — non-stationary control-loop floor.
// Wiener-α is MSE-optimal for stationary signals; this controller
// tracks a hold-rate overrun driven by a co-adapting policy, so α
// must never decay below the bandwidth needed to catch up when the
// policy drifts. Cold-start (sample_count < WELFORD_MIN_SAMPLE_COUNT_F)
// uses WELFORD_COLD_START_ALPHA_F=1.0 REPLACE per Pearl A, bypassing
// this floor. See `pearl_wiener_alpha_floor_for_nonstationary`.
#define WELFORD_ALPHA_MIN_F 0.4f
#define WELFORD_ALPHA_MAX_F 0.95f
#define WELFORD_MIN_SAMPLE_COUNT_F 3.0f
#define WELFORD_COLD_START_ALPHA_F 1.0f
@@ -237,9 +253,15 @@ void min_hold_temperature_update(
? diff_M2 / (diff_count - 1.0f)
: diff_M2;
alpha = diff_var / (diff_var + sample_var + EPS_F);
/* Defensive bounds — Wiener formula naturally yields α ∈ [0, 1]
* but numerical edge cases (both variances identically zero
* within ε) could push α to a denormal or floor at 0. */
/* Floor at WELFORD_ALPHA_MIN_F=0.4: Wiener-α is MSE-optimal
* for stationary signals only; this control loop tracks an
* overrun driven by the co-adapting policy. Without the floor
* α decays toward 0 once observed_hold_rate converges toward
* target, leaving the controller unable to react when the
* policy drifts (canonical regression: train-multi-seed-b5gmp
* sha 641aa0dfd Fold 1, α=0.248 at HD4 → Hold% 52.6% at HD5).
* Cap at WELFORD_ALPHA_MAX_F=0.95 prevents α=1.0 (no
* smoothing). Per `pearl_wiener_alpha_floor_for_nonstationary`. */
alpha = fmaxf(WELFORD_ALPHA_MIN_F, fminf(alpha, WELFORD_ALPHA_MAX_F));
}

View File

@@ -585,18 +585,38 @@ pub const MHT_SAMPLE_COUNT_INDEX: usize = 473; // Welford counter (number of
// the third observation onward — the Wiener formula stabilises by sample 3.
pub const SENTINEL_WELFORD_ZERO: f32 = 0.0;
// Defensive α bounds. The Wiener formula naturally yields α ∈ [0, 1] but
// numerical edge cases (target_M2 = diff_M2 = 0 within ε) could push α
// to a denormal value or floor at 0 (no learning). Clamp to keep the
// blend responsive to genuine signal even under degenerate input
// distributions. Bounds chosen so:
// - WELFORD_ALPHA_MIN=0.01: ensures every observation carries some
// weight, avoiding a permanently-frozen EMA when signals are
// deterministically constant.
// α bounds applied to the Wiener-derived α before the blend.
//
// - WELFORD_ALPHA_MIN=0.4: non-stationary control-loop floor (2026-05-08).
// Wiener-optimal α = diff_var / (diff_var + sample_var + ε) is
// MSE-optimal for *stationary* signals. The SP16 T3 producers
// (min_hold_temperature, hold_cost_scale) track a hold-rate overrun
// driven by the co-adapting policy — the controller's output
// directly shapes the per-bar reward / min-hold penalty that the
// policy is being trained against. Once Welford accumulates 3+
// samples and the policy converges toward target, diff_var → 0
// and α_wiener → 0, leaving the controller unable to catch up
// when the policy subsequently drifts back above target.
//
// This is a structural-control constraint (Wiener stationarity
// assumption violated), NOT a tuned constant: it encodes "always
// keep at least ~2.5-step responsiveness in case the policy
// drifts under us." Floor only applies once sample_count ≥
// WELFORD_MIN_SAMPLE_COUNT (cold-start path uses α=1.0 REPLACE
// per Pearl A, bypassing the floor).
//
// Empirical anchor: train-multi-seed-b5gmp sha 641aa0dfd Fold 1
// collapse — with the prior 0.01 floor, α decayed to 0.248 by
// HD4, blended scale 8.18 → 8.51 lagged climbing target_scale
// 9.53 → 10.38, Hold% 30.6% → 52.6% across HD3-HD5, dir_entropy
// hit 0.70 kill threshold, Sharpe 55.55 vs 88.65 baseline.
//
// See `pearl_wiener_alpha_floor_for_nonstationary.md`.
//
// - WELFORD_ALPHA_MAX=0.95: avoids pathological α=1.0 (which is
// equivalent to "no smoothing at all" — only sensible during the
// cold-start path, not steady state).
pub const WELFORD_ALPHA_MIN: f32 = 0.01;
pub const WELFORD_ALPHA_MIN: f32 = 0.4;
pub const WELFORD_ALPHA_MAX: f32 = 0.95;
// Sample-count threshold for switching from cold-start REPLACE to Wiener
@@ -934,7 +954,9 @@ mod tests {
assert_eq!(MHT_SAMPLE_COUNT_INDEX, 473);
// Sentinel + bounds — Pearl-A bootstrap on every fold boundary.
assert_eq!(SENTINEL_WELFORD_ZERO, 0.0);
assert_eq!(WELFORD_ALPHA_MIN, 0.01);
// 0.4 floor — non-stationary control-loop bandwidth preservation
// for SP16 T1/T2 producers. Pearl: pearl_wiener_alpha_floor_for_nonstationary.
assert_eq!(WELFORD_ALPHA_MIN, 0.4);
assert_eq!(WELFORD_ALPHA_MAX, 0.95);
assert_eq!(WELFORD_MIN_SAMPLE_COUNT, 3.0);
}

View File

@@ -237,7 +237,22 @@
#define ISV_MHT_DIFF_M2_IDX 471 // Welford ΣΔ² (diff)
#define ISV_MHT_PREV_TARGET_IDX 472 // previous-epoch target_temp
#define ISV_MHT_SAMPLE_COUNT_IDX 473 // Welford counter (T1 producer)
#define WELFORD_ALPHA_MIN_F 0.01f
// Wiener-α floor for non-stationary control loops (2026-05-08). Wiener-
// optimal α = diff_var / (diff_var + sample_var + ε) is MSE-optimal for
// stationary signals. The SP16 T3 producers (min_hold_temperature,
// hold_cost_scale) track a hold-rate overrun driven by a co-adapting
// policy — non-stationary by construction. Once Welford accumulates
// 3+ samples and the policy converges toward target, diff_var collapses
// and α_wiener → 0, leaving the controller unable to react when the
// policy subsequently drifts. The 0.4 floor preserves ~2.5-step
// catch-up bandwidth. This is a structural-control constraint (Wiener
// stationarity assumption violated), NOT a tuned constant; floor only
// applies once sample_count ≥ WELFORD_MIN_SAMPLE_COUNT_F (cold-start
// branch keeps α = 1.0 REPLACE per Pearl A). See
// `pearl_wiener_alpha_floor_for_nonstationary.md`. Empirical anchor:
// train-multi-seed-b5gmp sha 641aa0dfd Fold 1 collapse (HD4 α=0.248,
// blended_scale lag → Hold% 52.6% → Sharpe 55.55 vs 88.65 baseline).
#define WELFORD_ALPHA_MIN_F 0.4f
#define WELFORD_ALPHA_MAX_F 0.95f
#define WELFORD_MIN_SAMPLE_COUNT_F 3.0f
#define WELFORD_EPS_F 1.0e-6f

View File

@@ -2695,15 +2695,21 @@ mod sp16_phase3_wiener_alpha_gpu {
);
}
/// SP16 T3 — Test 2: α LOW in steady state (Welford diff_var decays).
/// SP16 T3 — Test 2: α floors at WELFORD_ALPHA_MIN in steady state.
///
/// Feed a converging signal: `0.30 + 0.001 / (epoch + 1)` for 60
/// epochs. The decaying perturbation makes consecutive diffs ever-
/// smaller (so diff_var → 0) while sample_var converges toward the
/// steady-state value — α = diff_var / (diff_var + sample_var + ε)
/// → 0 (clamped at WELFORD_ALPHA_MIN). Verifies the "smoothing
/// emerges naturally" property over the deleted hardcoded α=0.05
/// cadence.
/// → 0 in the unclamped formula, then clamps UP to WELFORD_ALPHA_MIN.
///
/// Per `pearl_wiener_alpha_floor_for_nonstationary` (2026-05-08), the
/// MIN floor is 0.4 — a non-stationary control-loop bandwidth
/// guarantee, not a "natural smoothing emerges" outcome. Without
/// this floor, train-multi-seed-b5gmp Fold 1 collapsed (Sharpe
/// 88.65 → 55.55) because α decayed too far during convergence
/// and the controller could not catch up to the climbing overrun
/// signal as the policy drifted post-convergence.
#[test]
#[ignore = "requires GPU"]
fn sp16_phase3_alpha_low_in_steady_state() {
@@ -2723,25 +2729,28 @@ mod sp16_phase3_wiener_alpha_gpu {
// After ~50 epochs the Welford state should have stabilised:
// sample_var stays bounded (target_scale fluctuates within
// ~0.001 of 13.0) while diff_var → 0 (consecutive diffs are
// sub-permille). α should approach the WELFORD_ALPHA_MIN floor.
// sub-permille). The unclamped formula would yield α → 0; the
// 0.4 floor (non-stationary control-loop constraint) clamps the
// recovered α back up to WELFORD_ALPHA_MIN.
let tail: Vec<f32> = traj[50..60].iter()
.map(|&(_, a)| a)
.filter(|a| !a.is_nan())
.collect();
let mean_tail_alpha = tail.iter().sum::<f32>() / tail.len() as f32;
// Tail α should be substantially below the cold-start ceiling.
// With a converging signal the diff_var collapses → α floors near
// WELFORD_ALPHA_MIN. We assert a generous threshold (0.40) to
// tolerate fp rounding in the recovered-α calculation; the
// smoking gun is that this is well below the cold-start α=1.0.
// Tail α should sit AT the WELFORD_ALPHA_MIN floor (not above —
// the unclamped formula has decayed → 0; not below — the floor
// forbids it). Tolerance accommodates fp rounding in the
// recovered-α reconstruction inside training_loop.rs.
let tol = 1e-3_f32;
assert!(
mean_tail_alpha < 0.40 && mean_tail_alpha > 0.0,
"Steady-state α should decay below 0.40, got mean over [50..60): {mean_tail_alpha:.5}"
(mean_tail_alpha - WELFORD_ALPHA_MIN).abs() < tol,
"Steady-state α must clamp to WELFORD_ALPHA_MIN={WELFORD_ALPHA_MIN}; \
got mean over [50..60): {mean_tail_alpha:.5} (tol {tol:.0e})"
);
// Defensive bound check — α stays positive (no permanent freeze).
assert!(
mean_tail_alpha >= WELFORD_ALPHA_MIN,
mean_tail_alpha >= WELFORD_ALPHA_MIN - tol,
"Steady-state α must respect WELFORD_ALPHA_MIN={}; got {mean_tail_alpha:.5}",
WELFORD_ALPHA_MIN
);