Commit Graph

4015 Commits

Author SHA1 Message Date
jgrusewski
18261c85a3 feat(tuning): temporal amplification + snapshot-on-winrate + stronger barrier
Tuning pass on the adaptive mechanisms. Changes:

1. F5 barrier weight raised 0.05 → 0.20 base, amplified 1×..2× by meta-Q
   collapse prediction (proactive, not reactive). Old 0.05 couldn't escape
   the Q-uniform attractor locally.

2. last_meta_q_pred field added on GpuDqnTrainer with set/get accessors,
   wired from DQNTrainer's meta_q.predict() each epoch boundary. Aux-op
   kernels now have per-step access to temporal collapse prediction.

3. DISTILL_HEALTH_THRESHOLD raised 0.4 → 0.55 (fire earlier). Additional
   temporal trigger: distill also fires when meta_q_pred > 0.5.

4. SNAPSHOT_HEALTH_THRESHOLD lowered 0.7 → 0.65.

5. Snapshot-on-winrate fallback: when last_epoch_win_rate >= 0.45, inflate
   effective health to 0.75 so a snapshot IS taken even if the health EMA
   is stuck in the 0.48 trough. Without this, the good moments (WinRate 56%,
   49%) are never captured → distillation has nothing to pull toward.

Result on local E1: distill=on every epoch (was permanently off), D6
fires only on bad-outcome epochs (was firing on convergence). Q-gap
still collapsed — tuning alone won't fix the underlying attractor;
root cause investigation next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:24:37 +02:00
jgrusewski
e36f63d1d4 cleanup: update scoreboard — iter 3 (FFLAG-006, FFLAG-009 resolved; note user WIP blocker) 2026-04-20 22:24:01 +02:00
jgrusewski
a05cae1e40 cleanup(fflag): remove use_soft_updates — always EMA — [FFLAG-006]
DQNConfig.use_soft_updates was true at all 4 construction sites; the
else branch (legacy hard update) was unreachable. Deleted field + 4
setters + collapsed update_target_networks() to the unconditional
cosine-annealed EMA path. Hard-copy mode removed per
feedback_no_feature_flags.md.
2026-04-20 22:23:22 +02:00
jgrusewski
9c9c7c6d8c cleanup(fflag): delete dead use_regime_conditioning flag — [FFLAG-009]
DQNConfig.use_regime_conditioning was declared, defaulted true at 3
construction sites, but never read anywhere in the workspace. Classic
write-only flag (like use_different_seeds in DEAD-001). Deleted field +
docstring + 3 assignment sites. Regime conditioning is already
unconditional per the trainer — the flag was vestigial.
2026-04-20 22:21:09 +02:00
jgrusewski
045c6f24e1 cleanup: update scoreboard — iter 2 (UNWRAP-002, FFLAG-015 resolved; UNWRAP-001 false-positive; GPUSYNC-006/008 deferred) 2026-04-20 22:17:11 +02:00
jgrusewski
dd57a9a938 fix(D6): temporal outcome gate prevents destroying converged models
Previous D6 logic fired plasticity on any ensemble_collapse_score > 0.8,
which destroyed models at the exact moment they converged to a consistent
policy (low per-branch Q-gap variance means either collapse OR convergence —
the signal alone cannot distinguish them). Result: WinRate=46% epoch killed
by plasticity → WinRate=0.9% next epoch.

Added temporal outcome gate: require last_epoch_win_rate < 0.45 OR
last_meta_q_pred > 0.5 alongside the ensemble-collapse signal. Local
smoke-test run shows D6 correctly fires on WinRate 0% / 0.2% epochs but
spares WinRate 49% / 46% epochs.

The underlying Q-collapse attractor still wins on short (10-epoch) runs,
but the adaptive system is no longer destroying the rare good epochs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:16:27 +02:00
jgrusewski
0c5186cdaa cleanup(fflag): delete dead enable_network_diagnostics flag — [FFLAG-015]
LoggingConfig.enable_network_diagnostics was declared and defaulted but
never read anywhere (only test round-trip assertions existed). Deleted
field + Default init + two test assertions that validated the round-trip.
Zero production callers per rg.
2026-04-20 22:16:23 +02:00
jgrusewski
e508475f3c cleanup(unwrap): NaN-safe q_gap partial_cmp in snapshot ring — [UNWRAP-002]
Three sites compared f32 q_gap values via partial_cmp().unwrap() — any
NaN q_gap would panic the snapshot swap-out / best-pointer paths on the
training hot path. Replaced with partial_cmp().unwrap_or(Ordering::Equal)
and tightened the outer Option unwrap to expect() with the invariant text
(len >= MAX_SNAPSHOTS guarantees a worst element exists).
2026-04-20 22:14:26 +02:00
jgrusewski
ca6a1a7ba6 fix(F6): backtest evaluator missing contrarian_active arg — caused L40S segfault
F6 added contrarian_active as the final parameter to experience_action_select
but gpu_backtest_evaluator.rs's launch site at line 980-1002 wasn't updated,
so CUDA read garbage for the new int arg and crashed the eval forward pass.

Added .arg(&0i32) as the final arg in the backtest launch — contrarian is
always off during deterministic backtest evaluation.

Verified locally via test_adaptive_learning_no_collapse: 10 epochs + 10
validation backtests now complete without segfault.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:13:13 +02:00
jgrusewski
2153929ed3 cleanup: update scoreboard — iter 1 (DEAD-001, FFLAG-001 resolved) 2026-04-20 22:11:10 +02:00
jgrusewski
3c65696794 cleanup(fflag): remove use_percentage_pnl — always on — [FFLAG-001]
The field was gated by validate() to always be true (absolute-dollar mode
caused Q-value explosion ±50,000). All call sites already set true. Deleted:
- RewardConfig.use_percentage_pnl field + Default init
- Absolute-dollar branch in calculate_pnl_reward (dead)
- Validation guard against false
- RewardConfigBuilder field + builder method + build() init
- Constructor call site

Percentage-based P&L is now unconditional per feedback_no_feature_flags.md.
2026-04-20 22:10:32 +02:00
jgrusewski
4bbe1e5610 cleanup(dead): remove unused use_different_seeds field — [DEAD-001]
EnsembleConfig.use_different_seeds was set but never read anywhere in
the workspace. rg confirmed zero readers. Field removed from struct,
Default impl, and new() constructor (3 sites).
2026-04-20 22:07:54 +02:00
jgrusewski
cc96dd7fd3 fix(F4/F5/D6): kernel buffer-stride bug + D6 warmup gate
Root cause of L40S segfault (train-tgdlq workflow, commit 0d639dfe9):

1. F4 (IB) and F5 (barrier) gradient kernels indexed cql_d_adv_logits and
   on_b_logits_buf with stride b0_size (4) instead of total_actions (13).
   The buffer is sized [B, total_actions, num_atoms]. Writes for batch i>0
   landed in other actions'/batches' gradient slots — corrupting CQL gradient
   for every batch beyond the first. After cuBLAS backward, weights diverged
   in undefined ways; validation forward then segfaulted reading NaN-laced
   parameters in GpuBacktestEvaluator init.

   Fix: add `total_actions` kernel parameter (int), use it as the full stride
   for adv_row and d_adv_a pointer arithmetic; keep b0_size as the loop
   bound (direction-branch-only update). All three launch sites updated:
   inlined F5 launch in apply_cql_gradient, inlined F4 launch alongside,
   and the standalone inject_barrier_into_cql_d_logits method.

2. D6 ensemble oracle fired at epoch 0 because per-branch Q-gaps are all 0
   at random init (range = 0 → score = 1.0 → plasticity trigger).
   Shrink-and-perturb ran immediately, then again next epoch, etc. Gate
   behind `learning_health.epoch > 5` (3 warmup + 2 buffer epochs for Q to
   move) so the oracle only fires on post-warmup real collapse, not
   untrained networks.

Both bugs are regressions from today's work — F4/F5 introduced yesterday,
D6 became live after the ens_disagreement real-signal fix in d9d35b6fa.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:03:45 +02:00
jgrusewski
e45e58a41a docs(cleanup): ralph-loop prompt + scoreboard for DQN code cleanup sweep
Adds scoreboard-driven cleanup infrastructure for bounded-batch Ralph
iteration across crates/ml*, services/ml_training_service, backtesting,
data_acquisition, and bin/fxt. Ten smell categories (GPUSYNC, FFLAG,
FALLBACK, ACCOUNT, DIMMIX, UNWRAP, DEAD, MAGIC, PDRIFT, TESTROT) with
per-iter ceiling of 3 commits / 200 LOC / 10 files. Completion gate:
empty scoreboard + cargo check --workspace + cargo test -p ml-dqn --lib.
2026-04-20 22:03:14 +02:00
jgrusewski
0d639dfe97 feat(F8/G5): apply c51_budget as real grad scale — was accounting-only before
Add GpuDqnTrainer::apply_c51_budget_scale() which runs scale_f32_ungraphed
on grad_buf immediately after submit_forward_ops_main (C51 backward) and
before CQL/IQN SAXPYs in submit_aux_ops. Final master gradient is now a
weighted sum: c51_budget×C51 + cql_budget×CQL + iqn_budget×IQN.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:45:07 +02:00
jgrusewski
85b7c10421 feat(F4/D5): Information Bottleneck variance gradient — spreads Q within direction branch
Adds ib_gradient_direction CUDA kernel that fires when population variance
of Q(a) across b0 actions falls below min_var=0.01, pushing each Q(a) away
from mean_q. Piggybacked on cql_d_adv_logits / cql_d_value_logits (same
atomicAdd path as F5 barrier). ib_weight = 0.05 × (1−health) → zero-op when
network is healthy (health≈1). Wired inside apply_cql_gradient immediately
after the F5 barrier launch, flows through the existing CQL SAXPY + backward.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:40:54 +02:00
jgrusewski
5bc712478e refactor(A2): production α=0.1, tests opt in to α=0.3 via with_alpha()
LearningHealth::new() now uses α=0.1 (the production-appropriate ~10-sample
EMA window for 80-100 epoch runs). Tests that need to reach threshold
assertions inside a 5-iteration loop (warmup=3 + 2 EMA steps) use
LearningHealth::with_alpha(0.3) instead of bending production behaviour
to satisfy test convenience.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:38:53 +02:00
jgrusewski
b05036ab93 feat(F5/D2): Q-gap barrier gradient — piggybacks on CQL SAXPY path
Add `barrier_gradient_direction` CUDA kernel to c51_loss_kernel.cu that
computes barrier = max(0, 0.05*health - q_gap) from the direction branch
logits and ISV[12], then injects gradient via atomicAdd into the CQL
d-logit accumulator buffers. When barrier > 0, it raises Q(argmax) and
lowers Q(second_max) to widen the direction Q-gap.

Wire-up: `apply_cql_gradient` now accepts `barrier_weight` and inlines
the kernel launch after the CQL kernel but before `backward_full`, so
both CQL and barrier share one cuBLAS backward pass with no extra SAXPY.
`submit_aux_ops` passes `barrier_weight = 0.05` every step (kernel is
internally a no-op when q_gap >= min_req). D2/N2 epoch-boundary block
updated to reflect that gradient is now live.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:35:06 +02:00
jgrusewski
0bd5c68f56 feat(F3): spectral_gap via rolling Gram + power iteration (sigma_1/sigma_2)
Replace single-sample max/min proxy with mathematically proper sigma_1/sigma_2
ratio. Maintains a host-side VecDeque of up to 64 Q-value samples; once ≥ 8
samples are available computes the n_cols×n_cols Gram matrix X^T X, then
extracts the two largest singular values via power iteration + rank-one
deflation. Falls back to the coarse max/min ratio until the buffer fills.
compute_q_spectral_gap changed to &mut self; callers updated accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:25:55 +02:00
jgrusewski
bda319cfd7 feat(F2): true vector cosine grad_consistency — replaces scalar delta proxy (A3 follow-up)
Replaces HealthEmaTrackers scalar delta-ratio proxy with real vector cosine
similarity across aggregated Adam m-state buffers (q_attn, sel, denoise,
mamba2, ofi_embed). Scalar fallback retained for fused_ctx=None / readback
failure cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:20:30 +02:00
jgrusewski
3fa9923a39 refactor(F7): remove dead iqn/cql/ens_grad_budget config fields — superseded by adaptive budgets (B4/G5)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:16:52 +02:00
jgrusewski
edaa078a17 fix(F1): silence unsafe_code warning on snapshot alloc — SAFETY comment + allow attribute
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:14:29 +02:00
jgrusewski
782633a47e refactor(F6/D7): contrarian Q-gap uses sign-flipped Q for consistent conviction 2026-04-20 21:10:44 +02:00
jgrusewski
d9d35b6fad fix: address all 3 precommit LOW findings
1. D6 ensemble oracle no longer dormant — replaces hardcoded ens_disagreement=0.1
   with range of per-branch Q-gap EMAs (max - min over 4 branches). This gives a
   real signal that collapses to 0 when branches agree on uniform Q and expands
   to non-trivial values when branches preserve diverse action differentiation.
   D6's smoothstep(0.01, 0.1) window now actually moves.

2. HEALTH_DIAG defaults aligned with constructor inits — cql_budget default
   0.10→0.00, c51_budget 0.45→0.55. Only visible when fused_ctx is None
   (pre-first-step); eliminates the cosmetic log jump on the first real epoch.

3. reward_v8 smoke-test module changed from pub mod → mod, resolving the
   pre-existing unreachable_pub warning. No external consumers of the module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:07:45 +02:00
jgrusewski
d5fea00108 test(E1): collapse-recovery smoke test for LearningHealth adaptive system
Trains a 10-epoch DQN run on fxcache data and asserts q_gap_ema > 0.05 and
learning_health.value > 0.3 — regression guard against the Q-uniform collapse
attractor the entire adaptive-learning-dynamics spec is designed to prevent.

Run via:
  FOXHUNT_TEST_DATA=test_data/futures-baseline \\
    cargo test -p ml --lib -- test_adaptive_learning_no_collapse --ignored --nocapture

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:00:32 +02:00
jgrusewski
8be3d05ab7 fix(D7/N7): zero out_q_gaps during contrarian mode — prevents full-conviction sizing on argmin trades
Addresses reviewer's IMPORTANT flag: the conviction Q-gap was not sign-flipped when
contrarian_active, so downstream env_step would size contrarian (argmin) positions
with maximum conviction, amplifying losses. During contrarian override, set
out_q_gaps=0 so those trades size minimally — the override is deliberate and
temporary; we don't want to compound it with aggressive position sizing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:59:23 +02:00
jgrusewski
f0d01478ec chore(D8): derive Debug on MetaQNetwork/SimpleLcg — fixes missing_debug_implementations lint
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:58:20 +02:00
jgrusewski
d74b085191 feat(D8/N8): meta-Q network — predicts collapse K epochs ahead as leading indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:56:54 +02:00
jgrusewski
598c1d57f7 feat(D7/N7 Part B): wire contrarian Q-negation into Boltzmann action selection
Add `contrarian_active` int parameter to `experience_action_select` kernel.
When non-zero, a `q_sign = -1.0f` multiplier is applied to Q values across
all 4 branches (direction/magnitude/order/urgency) before Boltzmann softmax,
converting argmax-favoring sampling to argmin-favoring without touching
temperature, epsilon, conviction filter, masking, or sampling logic.
When zero, q_sign = +1.0f — behavior is bit-identical to before.

Wire: GpuExperienceCollector gains `contrarian_active_cache: u8` field plus
`set_contrarian_active()` / `contrarian_active()` accessors. The flag is
appended as the last arg at the action_select launch site. training_loop.rs
propagates `self.contrarian_active` to the collector immediately after the
D7 Part A state machine updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:51:11 +02:00
jgrusewski
ac58b9d402 feat(D7/N7): contrarian override — argmin flag when WinRate<40% for 5+ epochs during collapse
Part A fully implemented: state machine tracks low_winrate_count across epochs,
activates contrarian_active for 2 epochs when WinRate<40% × 5 consecutive AND
health<0.3, deactivates on WinRate>=45% recovery. last_epoch_win_rate carries
financials.win_rate (f32 cast) from log_epoch_metrics_and_financials into next
process_epoch_boundary. HEALTH_DIAG already consumes last_contrarian_active as
contrarian=on/off. Part B (kernel argmin flip) deferred: direction selection uses
Boltzmann softmax, not simple argmax, making inversion non-trivial.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:45:56 +02:00
jgrusewski
af9373ced9 feat(D6/N6): ensemble as collapse oracle — pairwise Q-gap triggers plasticity
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:41:25 +02:00
jgrusewski
087c21050a feat(D5/N5): information bottleneck — scalar penalty visible in HEALTH_DIAG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:39:12 +02:00
jgrusewski
02f2f75f5e feat(D4/N4): counterfactual curriculum — cf_ratio scales with (1-health)
Replace hardcoded 0.5f flip probability in experience_env_step kernel with
dynamic cf_ratio = clamp(0.5 + 0.3*(1-health), 0.0, 1.0). Healthy (h=1)
keeps cf_ratio=0.5; collapsed (h=0) raises to 0.8 for more counterfactual
exposure to break collapse. Propagated via set_learning_health() each epoch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:36:43 +02:00
jgrusewski
7e345118f4 feat(D3/N3): plasticity injection — health-triggered shrink_perturb replaces periodic
Track consecutive epochs with health < 0.3 in `unhealthy_epoch_count`; after 3
consecutive unhealthy epochs, trigger shrink_and_perturb(alpha, sigma) and reset
the counter. Remove the periodic interval-based trigger (every N epochs). The
Phase 3 boundary trigger is intentionally kept as an independent mechanism.
last_plasticity_ready: Some(true) = accumulating, Some(false) = just triggered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:31:53 +02:00
jgrusewski
ebc1d9e9e2 feat(D2/N2): Q-gap barrier constraint — scalar loss visible in HEALTH_DIAG
Compute barrier_loss = 0.5 × max(0, 0.05×health − q_gap)² on the host
from cached q_gap_ema. Written to last_barrier_loss (already declared by
A4) and surfaced in HEALTH_DIAG `barrier=...`. Scalar-only / no gradient.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:29:01 +02:00
jgrusewski
4911563b2a feat(D1/N1): temporal self-distillation — snapshot at high health, pull toward best during collapse
- Add q_snapshot.rs: SnapshotRing ring buffer (MAX_SNAPSHOTS=5), health/q_gap admission gate
- Add GpuDqnTrainer::maybe_snapshot_params() — DtoD copy of params_buf into snapshot slot
- Add GpuDqnTrainer::apply_distillation_gradient() — two ungraphed saxpy_f32_aux calls:
  grad += alpha * (params - best_snapshot), alpha = 0.1 * (1 - health), skipped when health >= 0.99
- Wire FusedTrainingCtx::maybe_snapshot_qnet() / apply_distillation() / last_distill_active()
- Call from process_epoch_boundary: snapshot when health >= 0.7, distill when health < 0.4
- No new CUDA kernel — reuses existing dqn_saxpy_f32_kernel (saxpy_f32_aux handle)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:26:36 +02:00
jgrusewski
8669b74d1f feat(C4/P4): temporal-coupled gamma — regime × health scales discount factor
Apply gamma_base + 0.005×(regime_stability−0.5) − 0.05×(1−health), clamped
to [0.9, 0.995], so stable regimes get longer-horizon credit assignment while
collapse (health=0) forces short-horizon local credit to break the collapse.

- GpuDqnTrainer: add last_gamma_eff field + apply_adaptive_gamma() method
- FusedTrainingCtx: add apply_adaptive_gamma() wrapper + last_gamma_eff() accessor
- training_loop: replace both set_adaptive_gamma call sites with apply_adaptive_gamma
- process_epoch_boundary: propagate last_gamma_eff into DQNTrainer for HEALTH_DIAG

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:18:06 +02:00
jgrusewski
8d088cbc38 feat(B4/G5): adaptive gradient budget — IQN/CQL/C51 scale with health & regime
IQN and CQL SAXPY contributions into grad_buf are now scaled by adaptive
budget factors derived from learning_health (ISV[12]) and regime_stability
(ISV[11]):
  iqn_budget = 0.10 + 0.30 × health  (0.10..0.40)
  cql_budget = 0.10 × (1−regime) × health  (0 at collapse, volatile+healthy → 0.10)
  ens_budget = 0.05  (constant)
  c51_budget = 1 − iqn − cql − ens  (C51 absorbs headroom at collapse → 0.85)

At collapse (health=0): IQN backed off to 10%, CQL off, C51 takes 85% —
stable directional learning when distributional components are unreliable.

Changes:
- GpuDqnTrainer: add 4 last_*_budget_eff fields (initialized to health=1 defaults)
- GpuDqnTrainer: add read_isv_health_and_regime() public accessor
- apply_iqn_trunk_gradient(): add iqn_budget param; scale = iqn_lambda × readiness × iqn_budget
- apply_cql_saxpy(): add cql_budget param; SAXPY alpha = cql_budget (was 1.0)
- FusedTrainingCtx: add compute_adaptive_budgets() — reads ISV, computes all 4, caches to trainer
- FusedTrainingCtx: add last_{iqn,cql,c51,ens}_budget_eff() accessors
- submit_aux_ops(): call compute_adaptive_budgets() once per step, thread to IQN/CQL sites
- training_loop.rs: B4/G5 propagation block for HEALTH_DIAG logging

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:13:04 +02:00
jgrusewski
aaf6e70f7c feat(C1/P1): health-weighted PER priorities — boost diverse-action experiences during collapse
When learning_health < 0.8, the PER priority update switches from the
standard per_update_pa kernel to pow_alpha_diverse_f32, which multiplies
each priority by (1 + 2*(1-health)*|action - mean_action|). This rescues
the replay buffer's diversity signal during Q-collapse by surfacing
experiences whose action deviates from the batch mean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:04:38 +02:00
jgrusewski
e07c82976e feat(B3/G4): health-scaled Expected SARSA temperature — breaks collapse attractor
When health=1 (healthy): tau unchanged → sharp softmax → near-argmax target (deterministic).
When health=0 (collapsed): tau scales 6× → wide softmax → stochastic sampling breaks Q-collapse attractor.

- c51_loss_kernel.cu: add get_learning_health() helper, isv_signals as last param, tau_base → tau * factor
- gpu_dqn_trainer.rs: add last_sarsa_tau_factor field + init, launch_c51_loss → &mut self, host-side mirror, append isv_signals_dev_ptr arg
- fused_training.rs: add last_sarsa_tau_factor() accessor
- training_loop.rs: propagate SARSA tau factor from fused ctx for HEALTH_DIAG logging

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:02:43 +02:00
jgrusewski
edc59ed6bd feat(B2/G3): health-coupled tau — floor at 0.01×(1-health) for collapse recovery
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 19:57:11 +02:00
jgrusewski
46666afef9 feat(B1/G2): uncertainty-gated CQL — cql_alpha = base × (1-regime) × health
Replace static cql_alpha with cql_alpha_eff computed from ISV signals:
health (ISV[12]) × (1 − regime_stability (ISV[11])) × base.
Collapse→0 (CQL off); volatile+healthy→full; stable+healthy→0.
Adds last_cql_alpha_eff field to GpuDqnTrainer (f32, init 0.0),
accessor on FusedTrainingCtx, and propagation into DQNTrainer for
HEALTH_DIAG logging.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 19:52:01 +02:00
jgrusewski
1734ae7e34 feat(A4): extend HEALTH_DIAG with all effective values and novel placeholders
Adds 15 new last_* fields (all None) to DQNTrainer for B/C/D tasks to populate,
and replaces the A3 HEALTH_DIAG log line with the full format covering components,
effective hyperparams, and novel mechanism states.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 19:46:09 +02:00
jgrusewski
3e9a21d52b fix(A3): clarify docs and guard q_var against infinite sentinels 2026-04-20 19:43:24 +02:00
jgrusewski
6622d90906 feat(A3): compute LearningHealth per epoch, broadcast via ISV[12], HEALTH_DIAG logging
- Add HealthEmaTrackers struct (metrics.rs): EMA for q_gap/q_var/grad_norm with
  scalar grad_consistency proxy (successive grad_norm delta ratio)
- Add LearningHealth + HealthEmaTrackers + 5 last_* fields to DQNTrainer (mod.rs)
- Initialize new fields in constructor.rs
- Add write_isv_signal_at, read_atom_utilization, compute_q_spectral_gap to
  GpuDqnTrainer (gpu_dqn_trainer.rs) with coarse max/min spectral-gap proxy
- Forward same three methods on FusedTrainingCtx (fused_training.rs)
- Compute LearningHealth in process_epoch_boundary (training_loop.rs): reads
  per_branch_q_gap_ema, epoch min/max, atom_util, spectral_gap; emits HEALTH_DIAG log;
  broadcasts health_value to ISV[12] via write_isv_signal_at

Known proxy substitutions (documented in training_loop.rs comment):
  - grad_consistency: scalar proxy instead of full Adam vector cosine (per-component buffers)
  - spectral_gap: coarse max/min ratio on q_readback_pinned instead of SVD
  - ens_disagreement: 0.1 placeholder until D6/N6

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 19:36:58 +02:00
jgrusewski
31f4f6314c docs(A2): add rationale for smoothstep thresholds and clamp bounds 2026-04-20 19:29:10 +02:00
jgrusewski
586f5c1058 feat(A2): LearningHealth module with 7-component composition + EMA 2026-04-20 19:25:15 +02:00
jgrusewski
1ad09f63c9 chore(A1): refresh ISV_DIM annotation comments 12→13
Follow-up to 9f3cbc77d — field-doc comments referenced the old value.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 19:20:40 +02:00
jgrusewski
9f3cbc77d7 feat(A1): extend ISV_DIM 12→13, add LEARNING_HEALTH_INDEX constant 2026-04-20 19:18:22 +02:00
jgrusewski
ab7567875a plan: adaptive learning dynamics — 19 tasks across 5 phases
Implementation plan for unified LearningHealth system (all gems/pearls/novels):
- Phase A (A1-A4): ISV extension, LearningHealth module, training loop wiring,
  HEALTH_DIAG logging
- Phase B (B1-B4): G2 uncertainty-gated CQL, G3 health-coupled tau,
  G4 temp-continuous Expected SARSA, G5 adaptive gradient budget
- Phase C (C1-C4): P1 health-weighted PER priorities, P2/P3 subsumed by A3,
  P4 temporal-coupled gamma
- Phase D (D1-D8): N1 temporal self-distillation, N2 Q-gap barrier,
  N3 health-triggered plasticity, N4 CF curriculum, N5 information bottleneck,
  N6 ensemble oracle, N7 contrarian override, N8 meta-Q collapse predictor
- Phase E (E1-E2): collapse-recovery smoke test + L40S deployment verification

Spec: docs/superpowers/specs/2026-04-20-adaptive-learning-dynamics-design.md
Target: WinRate >55% by epoch 20, Q-gap stays above 0.1 (no collapse).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 19:12:41 +02:00