Files
foxhunt/docs
jgrusewski b8a7ac6f70 fix(dqn): SP3 Mech 8 — slow_ema fold-boundary reset (real root cause)
User-identified root cause: grad_norm_slow_ema (α=0.001, half-life
~693 steps) persists across fold boundaries while every other
distribution-tracking signal (Adam m/v, target nets, atom positions)
gets reset. Mech 6's upper_bound formula (100 × slow_ema × isv) was
anchored to the WRONG scale during F1 ramp-up, driving the
non-monotonic multiplier-tuning dance across smokes:
  - smoke-test-fxvkk (mult=100×): F0=44, F1 NaN @ 3720
  - smoke-test-ftdjz (mult=100×, +Mech7): F0=38, F1 collapse @ 2040
  - smoke-test-d25vq (mult=5×): F0=21, F1 collapse @ 2820
The multiplier was searching the wrong dimension — the anchor itself
was stale.

Three coordinated changes (per feedback_no_partial_refactor):

1. RESTORE Mech 6 multiplier 5× → 100×. The original 100× was correct
   for steady-state; the F1 saturation was driven by anchor staleness,
   not multiplier looseness. Tightening it harmed F0 (over-clip during
   ramp-up when slow_ema lagged grad_norm_ema).

2. ADD reset_grad_norm_slow_ema method on GpuDqnTrainer. Zeroes the
   mapped-pinned scalar. First step of new fold builds up slow_ema
   fresh, with MIN_CLIP=1.0 floor active during the brief transient.

3. WIRE Mech 8 call in fused_training.rs::reset_for_fold, alongside
   Mech 4's existing Adam resets. Mech 6's anchor now aligns with
   the new fold's grad scale from step 1 — same philosophy as Mech 3
   (target net hard-sync) and Mech 4 (Adam EMA reset).

Net SP3 design: Mech 6 stays at 100× multiplier (broad, principled
headroom), Mech 8 keeps the anchor honest. The pair is more robust
than either change alone:
  - Without Mech 8: anchor is stale, multiplier tuning has no winning
    setting (5× hurts F0 ramp, 100× lets F1 saturate at slot 36).
  - With Mech 8: anchor is fresh per fold; 100× multiplier provides
    legitimate per-step headroom over CURRENT fold's grad norm.

Mech 7 stays reverted (per-element clip was misdiagnosis — over-clipped
legitimate gradient outliers without addressing the saturation root
cause).

F0 risk: low — F0 starts with slow_ema=0 anyway (cold start), so Mech 8
is a no-op on F0. Only changes F1+F2 fold-boundary behavior.

F1+F2 expectation: Mech 6's upper_bound now scales with the CURRENT
fold's grad norm, providing legitimate ~10× headroom per step without
allowing Adam EMA saturation. Slot 36-42 should stay quiet.
2026-04-30 12:10:46 +02:00
..