Files
foxhunt/crates
jgrusewski fd415d9b17 fix(rl): apply derive-from-input bootstrap to γ + coef controllers
Systematic completion of the per_α fix (commit 0857d40ac) across the
two other additive-target controllers that had the same dead-zone
anti-pattern: hardcoded bootstrap value that coincides with a target
the formula naturally produces.

## Audit

Across the 7 RL controllers in `crates/ml-alpha/cuda/rl_*_controller.cu`,
target formulas split into three classes:

1. **Additive target** (target = f(input)): `per_α`, `γ`, `coef`.
   Hardcoded bootstrap can collide with target value at specific
   input — the dead-zone fix applies cleanly via derive-from-input.
2. **Multiplicative target** (target = prev × ratio): `τ`, `ε`,
   `n_roll`. Bootstrap IS the initial `prev`; the "no movement when
   ratio=1" case corresponds to "input at steady-state value" which
   is correct behavior, not a dead-zone bug. Not changed.
3. **Special** (target = 1/input): `scale`. Sentinel input → 1/0
   would blow up. Hardcoded bootstrap 1.0 + production input range
   (mean_abs_pnl ≫ 1.0 for ES futures) means no practical dead-zone.
   Not changed.

## γ kernel fix

Hardcoded `GAMMA_BOOTSTRAP = 0.99` coincided with `target(d ≈ 69)`
via `γ = 0.5^(1/d)`. For canonical hold times near 69 events (which
is exactly the d at which γ=0.99 is correct), the Wiener blend
`prev=0.99, target=0.99` produced no movement.

Now: bootstrap = `clamp(0.5^(1/max(d, 1)), GAMMA_MIN, GAMMA_MAX)`.
At sentinel input (d=0 → clamped d=1) → target=0.5 → clamped to
GAMMA_MIN = 0.90 (the floor). Cold-start γ is the floor (more
myopic for first few steps); as `trade_duration_ema` stabilises in
the typical 10-100 range, the controller drifts γ up toward
`0.5^(1/d_observed)`.

## coef kernel fix

Hardcoded `COEF_BOOTSTRAP = 0.01` coincided with `target(h_obs ≈
1.099)` via `coef = (h_target - h_obs) / h_max × COEF_MAX`. For
mid-range observed entropy (≈ half of h_target ≈ 1.538), bootstrap
= target → frozen.

Now: bootstrap = `(h_target - max(h_obs, 0)) / h_max × COEF_MAX`,
clamped. At sentinel input (h_obs=0) → deficit = h_target = 1.538
→ target = (1.538 / 2.197) × 0.05 ≈ 0.035 (3.5× the previous
canonical 0.01). Lifts the entropy bonus's relative weight in early
training — desirable cold-start behavior (push exploration when no
entropy data yet) and self-corrects as `entropy_observed_ema`
stabilises.

## Test updates

* `isv_bootstrap.rs`: `GAMMA_BOOTSTRAP` 0.99 → 0.90, `COEF_BOOTSTRAP`
  0.01 → 0.035. Inline comments document the post-R9-audit derive-
  from-input rationale.
* `r5_controllers_and_soft_update.rs`: same constants updated;
  `trade_duration_ema` fixture input 1.0 → 20.0 because d=1 produces
  target=0.5 which clamps to GAMMA_MIN = 0.90 = new bootstrap = floor
  (canonical "all production-realistic trade durations are 10-100
  events" range). The d=1 fixture was an unrealistic edge case
  (sub-event trade duration is non-physical).
* `r3_ema_advantage.rs::r3_compute_advantage_return_formula_holds`:
  pre-condition assertion loosened from `γ == 0.99` to `γ ∈ [0.90,
  0.999]` — the test computes its expected values from whatever γ
  ISV holds, so the hardcoded comparison was incidental.
* `trainer/integrated.rs` `with_controllers_bootstrapped` docstring:
  γ and coef slot docs updated to reflect derive-from-input.

## Verified gates (post-fix, local sm_86)

  G1  isv_bootstrap                 γ=0.90 τ=0.005 ε=0.2 coef=0.035
                                       n_roll=2048 per_α=0.4 scale=1.0
  G3  controllers_emit              γ 0.9 → 0.926 (target 0.966)
                                       coef 0.035 → 0.030 (target 0.025
                                       at h_obs=0.5)
  G4  target_soft_update            unchanged
  G6  r7d_per_wiring                unchanged
  R3  ema/advantage (3 tests)       pre-cond loosened, formula intact
  R4  action kernels (3 tests)      unchanged
  end integrated_trainer_smoke      all 5 head losses finite

## What's NOT in this commit

Multiplicative controllers (τ, ε, n_roll) and the special-form scale
controller still use hardcoded bootstraps. Their dead-zones (if any)
are either correct steady-state behavior (multiplicative) or
practically unreachable (scale's dead-zone at mean_abs_pnl=1.0 is
not hit by ES dollar-scale rewards). Documenting these as
"intentionally hardcoded" is preferable to forcing derive-from-input
where it doesn't naturally fit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 13:56:40 +02:00
..