Commit Graph

3535 Commits

Author SHA1 Message Date
jgrusewski
3f4f0c7038 fix: all precommit LOWs — pinned sel_t, per-branch Q-gap detection
LOW 1: sel_t_buf CudaSlice + per-step memcpy_htod → sel_t_pinned
device-mapped. Zero copies, GPU reads directly from host memory.

LOW 2: is_q_gap_frozen uniform [q_gap; 4] → actual per-branch Q-gaps
from 48B DtoH readback in reduce_current_q_stats. Each branch now
independently detected as frozen/unfrozen. Liquid tau and trajectory
backtracking use real per-branch velocities.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 07:47:40 +02:00
jgrusewski
77fcdb2657 fix: VSN masking applies to magnitude branch via strided_scatter
mag_concat now uses vsn_masked (feature-selected) instead of raw h_s2
for its first SH2 columns. strided_scatter kernel writes tight [B,SH2]
vsn_masked into wide [B,SH2+3] mag_concat preserving Q_dir in last 3.
launch_mag_concat → launch_mag_concat_from with explicit source ptr.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 07:29:38 +02:00
jgrusewski
298455e24a fix: precommit audit — gradient offset s1_input_dim + q_attn_params zero-init
CRITICAL: backward_full used state_dim for goff_b_s1 offset but
compute_param_sizes uses s1_input_dim (smaller with bottleneck active).
All downstream gradient offsets were misaligned. Fixed: sd → s1d.

HIGH: q_attn_params used alloc_f32 (potentially non-zero) instead of
alloc_zeros. Cross-branch Q-attention residual connection needs near-zero
init for stability. Fixed: alloc_zeros.

MEDIUM (deferred): VSN masking bypassed for magnitude branch — mag_concat
reads raw h_s2 instead of vsn_masked. No impact while W_vsn2 is zero-init
(identity mask). Will fix when VSN backward is wired.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 07:16:56 +02:00
jgrusewski
1fdc712df6 feat: trajectory backtracking — checkpoint rewind + informed perturbation
Detects Q-gap plateau (5 consecutive frozen epochs), rewinds to
best-improvement checkpoint, applies perturbation strategy cycle:
1. Reset Adam momentum
2. Shrink-perturb branch heads
3. Temperature boost (2× for 5 epochs)
4. Learning rate boost (2× for 10 epochs)

3 rewinds × 4 routes = 12 max attempts. On exhaustion: save best model,
exit with PLATEAU_EXHAUSTED. Saves ~3 hours H100 vs frozen training.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 07:01:19 +02:00
jgrusewski
3a6cfe9a9b fix: correct CUDA kernel names for VSN/GLU in constructor
vsn_bottleneck_fwd → variable_select_bottleneck
glu_gate_combine → glu_combine
glu_gate_backward → glu_backward

19/19 smoke tests pass with full CPBI stack.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:36:34 +02:00
jgrusewski
f867064786 feat: Mamba-2 selectivity gate — forward/backward/Adam methods
selectivity_forward: sigmoid(dot(W_sel, h_s2) + b_sel) per sample.
selectivity_backward: BCE gradient on (sel, per_sample_loss/mean_loss).
Separate Adam at LR=1e-4. PER priority integration deferred.

Also fixes kernel names (selectivity_gate_fwd→selectivity_forward,
selectivity_gate_bwd→selectivity_backward) to match experience_kernels.cu.
Adds sel_norm_buf, sel_norm_partials, sel_clip_buf, sel_t_buf fields
needed by dqn_adam_update_kernel. Wired into run_full_step after main
Adam step with mean_loss=1.0 placeholder and non-fatal error handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:29:24 +02:00
jgrusewski
70f1446965 feat: per-branch liquid tau ODE replaces global spread_velocity
c51_grad_kernel reads liquid_mod[d] per branch instead of spread_velocity[0].
4 per-branch Q-gap EMAs with adaptive alpha drive continuous-time ODE.
Fast branch learning → large tau → slow adaptation → don't overshoot.
Stuck branch → small tau → fast adaptation → push harder.
spread_velocity infrastructure removed (-30 lines).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:22:52 +02:00
jgrusewski
90f4e0531b feat: cross-branch Q-attention launch method + training integration
launch_q_attention runs 2-head attention on 12 Q-values [B, 12].
Wired into reduce_current_q_stats after compute_expected_q.
Also fixes kernel function name: q_cross_branch_attn → cross_branch_q_attention.
Experience collection deferred — uses raw Q-values for now.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:19:07 +02:00
jgrusewski
524d81a45c feat: GLU backward replaces ReLU mask in branch backward pass
d_h_bd → glu_backward → d_value + d_gate_pre. Two weight gradient
GEMMs per branch (value + gate). Upstream gradient sums both paths.
VSN backward DEFERRED (W_vsn init to identity, zero gradient for now).
Gradient flows to gate weights at indices 34-41.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:14:48 +02:00
jgrusewski
728830f806 feat: VSN bottleneck + GLU gating in branch forward pass
Each branch FC now: variable_select_bottleneck → gate GEMM + value GEMM
→ GLU combine (sigmoid × value). Replaces ReLU activation with learned
soft gating. VSN produces per-sample feature masks via SH2→R=16→SH2
bottleneck. All 3 forward paths updated (online_raw, online_f32, target_raw).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 00:02:27 +02:00
jgrusewski
0e1ed5e2f7 feat: weight layout 26→42 + Q-attn/selectivity/liquid-tau/VSN/GLU buffers
NUM_WEIGHT_TENSORS expanded for 8 VSN bottleneck + 8 GLU gate tensors.
Q-attention (624 params), selectivity (257 params) in separate buffers.
Liquid tau: 4 pinned device-mapped floats for per-branch modulation.
VSN/GLU scratch buffers allocated for forward/backward.
6 CUDA kernel functions loaded from experience_kernels cubin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:49:34 +02:00
jgrusewski
d5414bec25 spec: Component 6 early termination — PLATEAU_EXHAUSTED after 12 attempts
3 rewinds × 4 routes = 12 max attempts. Failed = improvement rate below
threshold over 10 epochs. On exhaustion: save best model, exit cleanly,
log "structural change needed". Saves ~3 hours H100 vs frozen training.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:46:27 +02:00
jgrusewski
ecbd4c708e feat: 6 CUDA kernels for Cross-Pollinated Branch Intelligence
variable_select_bottleneck: TFT-style input-dependent feature masks
glu_combine/glu_backward: Gated Linear Unit activation (replaces ReLU)
cross_branch_q_attention: 2-head attention on 12 Q-values
selectivity_forward/backward: Mamba-2 learned replay priority gate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:41:51 +02:00
jgrusewski
734f19f3c3 cleanup: remove 11 dead code items across 6 files
Removed: _reward_norm_kernel (never launched), cea_weight (unused v7 compat),
mixup_alpha/seed/barrier (unused C51 mixup), clipped_saxpy_kernel + method,
clip_grad_buf_inplace, debug_forward_no_graph, DT LayerOffsets #[allow(dead_code)]
attr (struct is live, removed spurious suppress), curiosity launch_adam_step
dead_code attr (function is live). Cleaned callers in fused_training.rs,
training_loop.rs, and gradient_budget.rs smoke tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:38:56 +02:00
jgrusewski
f5985cff14 spec: add Component 6 — Trajectory Backtracking (single GPU)
Checkpoint ring buffer ranked by improvement rate, plateau detection
via liquid tau velocity, informed perturbation cycle (Adam reset,
shrink-perturb, temp boost, LR×2), depth-limited rewind. ~150 lines
in training_loop.rs, zero CUDA changes. Recovers from plateaus that
prevention mechanisms (spread gradient, liquid tau) fail to avoid.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:38:05 +02:00
jgrusewski
cc232f644d plan: Cross-Pollinated Branch Intelligence — 9 tasks, 64 steps
Task 1: Dead code cleanup (11 items across 6 files)
Task 2: 6 CUDA kernels (VSN, GLU, Q-attention, selectivity)
Task 3: Weight layout 26→42 + separate buffers + liquid_mod pinned
Task 4: VSN + GLU forward integration
Task 5: VSN + GLU backward integration
Task 6: Cross-branch Q attention (624 params, 2-head on 12 Q-values)
Task 7: Liquid tau per-branch dynamics (replaces spread_velocity)
Task 8: Mamba-2 selective replay (learned PER curriculum)
Task 9: Build verification + smoke tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:26:13 +02:00
jgrusewski
0cfb2c7786 fix: 6 concerns in Cross-Pollinated Branch Intelligence spec
1. Variable Selection: static → input-dependent bottleneck (true TFT VSN)
2. Q Attention: add direct gradient via Expected SARSA target integration
3. Liquid Tau: modulates IQL scales via separate liquid_mod[4] buffer
4. Selectivity: per_sample_loss proxy (per-sample grad norms infeasible at 32GB)
5. GLU gate: zero-init W_gate/b_gate → sigmoid(0)=0.5 (stable start)
6. Weight layout: updated for 8 VSN bottleneck matrices (indices 26-33)

NUM_WEIGHT_TENSORS: 26 → 42. Total new params: ~297K.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:14:41 +02:00
jgrusewski
8a9424b6eb spec: Cross-Pollinated Branch Intelligence — 5-component unified design
Combines TFT Variable Selection, GLU Gating, Cross-Branch Q Attention,
Liquid adaptive tau, and Mamba-2 Selective Replay into unified branch
architecture. Plus 11-item dead code cleanup.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:06:18 +02:00
jgrusewski
85b5f15520 feat: magnitude branch forward/backward uses [h_s2; Q_dir] conditioning
Forward: branch 1 FC GEMM reads [B, SH2+3] from mag_concat_buf.
All 3 forward paths (online_raw, online_f32, target_raw) updated.
Concat uses previous step's direction logits (one-step lag, negligible).

Backward: branch 1 dW uses wider X, dX writes to d_mag_concat_buf.
strided_accumulate extracts first SH2 columns into d_h_s2 with beta=1.
d_Q_dir (last 3 columns) discarded — detached, no backprop to direction.

Fixes backward goff_b_b1fc offset: uses ah*(sh2+3) matching compute_param_sizes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:48:22 +02:00
jgrusewski
cf009ad49e feat: widen w_b1fc [AH, SH2+3] + allocate mag_concat buffers + launch methods
param_sizes[12] grows by 3*adv_h for direction conditioning.
mag_concat_buf [B, SH2+3] and d_mag_concat_buf allocated.
mag_concat_qdir and strided_accumulate kernels loaded.
launch_mag_concat and accumulate_d_h_s2_from_concat methods added.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:37:17 +02:00
jgrusewski
abb0795759 feat: mag_concat_qdir + strided_accumulate kernels for Layer 3
mag_concat_qdir: computes direction E[Q] from softmax over C51 atoms,
normalizes by delta_z for matched gradient scale, concatenates with h_s2
to produce [B, SH2+3] input for magnitude branch FC.

strided_accumulate: extracts first SH2 columns from d_mag_concat [B, SH2+3]
into d_h_s2 [B, SH2] with beta accumulation for backward pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:32:20 +02:00
jgrusewski
f49ae6858e fix: Layer 3 plan — add Q_dir normalization by delta_z
Raw E[Q] ~ 0.1 while h_s2 ~ 1.0 post-ReLU. Without normalization,
Q_dir columns learn 10x slower. Dividing by delta_z scales to ~1-10.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:30:29 +02:00
jgrusewski
39ce4c15db fix: asymmetric spread gradient zero-sum — prevents Q-value inflation
Old: taken=+1, challenger=+0.5, rest=-0.33. Sum=+1.17 (biased up).
New: taken=+1, challenger=+0.5, rest=-1.5. Sum=0.0 (zero-mean).
Rest actions absorb -(1+0.5)/(A_d-2) to balance the challenger's push.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:29:56 +02:00
jgrusewski
3f9a75264c fix: reward_rank_normalize cross-block race — two-buffer read/write split
In-place ranking races across blocks: block X writes shaped rewards[i]
before block Y loads it in tile scan. __syncthreads is block-local.
Fix: read from rewards_out (raw), write to gpu_batch.rewards (clone).
The DtoD clone from collect_experiences_gpu provides the output buffer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:28:44 +02:00
jgrusewski
8d2ed799e6 plan: direction-conditioned magnitude head (Layer 3) — 7 tasks
Tasks: CUDA kernel, param widening, forward conditioning, backward split,
zero-init, experience collector, H100 integration test.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:19:41 +02:00
jgrusewski
234870d966 spec: direction-conditioned magnitude head (Layer 3)
Magnitude FC input changes from h_s2[B, SH2] to [h_s2; Q_dir][B, SH2+3].
Direction E[Q] values (3 scalars per sample) are computed from branch 0
logits and concatenated. w_b1fc grows by 3*adv_h parameters (768 for AH=256).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:14:49 +02:00
jgrusewski
b2eb1b7741 feat: asymmetric spread + per-branch scaling + distributional variance sizing + reward_std guard
Four improvements to the training pipeline:

1. Asymmetric spread gradient: challenger action (adjacent to taken) gets
   pushed UP at half strength instead of DOWN. Creates a two-horse race
   instead of single-action monopoly.

2. Per-branch spread scaling: spread_grad *= branch_scale. Direction branch
   (high impact) gets more spread than urgency (low impact).

3. Distributional variance position sizing (Layer 4): Var[Q] = E[Z²] - E[Z]²
   computed in compute_expected_q. Position scaled by 1/(1+sqrt(Var[Q_taken])).
   High uncertainty → smaller position. Kelly criterion from C51 atoms.

4. Reward std guard: skip rank normalization when observed_reward_std ≈ 0
   (epoch 0). Prevents zeroing all rewards before std is observed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 22:06:58 +02:00
jgrusewski
bde30c6c4d feat: Q-gap momentum modulates spread gradient — backs off during growth
spread_scale = inv_batch * delta_z * velocity_modulator
velocity_mod = clamp(1 - (Q_gap - Q_gap_ema) / delta_z, 0.1, 2.0)

Growing Q-gap → mod=0.1 (10% spread, let C51 learn).
Plateau → mod=1.0 (full spread, push Q-values apart).
Shrinking Q-gap → mod=2.0 (emergency spread, prevent collapse).
10% floor proven by train-w6qfd (val_Sharpe=21+ sustained).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:58:02 +02:00
jgrusewski
93e4185fa5 feat: rank-preserving signed reward standardization — SNR 0.001→0.5
r_shaped = sign(r) * rank(|r|) * std_ema. Counting-sort rank within
batch gives outlier-resistant, scale-free normalization. Sign preserved
for absolute direction. std_ema rescales to original magnitude range.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:52:52 +02:00
jgrusewski
e9e4bd54ac plan: Adaptive Training Dynamics Layers 1+2 implementation plan
Task 1: Reward rank normalization kernel (SNR amplification)
Task 2: Q-gap momentum for spread gradient (plateau modulation)
Task 3: H100 integration test (50 epochs, compare to train-w6qfd)

Layers 3+4 (dir→mag conditioning, variance sizing) deferred to
separate plan after L1+L2 validation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:42:57 +02:00
jgrusewski
a5945db538 spec: Adaptive Training Dynamics v2 — 4-layer design for breaking Sharpe plateau
Layer 1: Rank-preserving signed reward standardization (SNR 0.001→0.5)
Layer 2: Temporal Q-gap momentum (spread modulation at plateaus)
Layer 3: Direction-conditioned magnitude (dir Q-values → mag head input)
Layer 4: Distributional variance position sizing (Kelly from C51 atoms)

Based on train-w6qfd results: val_Sharpe=21-25 sustained, Q-gap=0.138
growing. Builds on 32 fixes from this session. Implementation order:
1→2→4→3 (signal → momentum → variance → architecture).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:40:03 +02:00
jgrusewski
7eccfc53c9 feat: Q-gap floor gradient — perpetual action differentiation pressure
Adds a spread gradient to the C51 advantage logits that pushes the
taken action's distribution toward higher atoms and non-taken toward
lower. Scale = inv_batch * delta_z (adaptive to per-sample atom
resolution, zero hardcoded constants).

This gradient is ORTHOGONAL to the Bellman equation — it depends on
atom position, not target match. Active on ALL samples, providing
perpetual pressure to differentiate Q-values even when the C51
cross-entropy gradient vanishes at convergence. Prevents the Q-gap
plateau where all actions have identical Q-values.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 21:07:23 +02:00
jgrusewski
6271322b06 fix: stochastic SARSA uses Adam step counter — fixed seed was deterministic
Philox seed used batch_size+sample_id (constant across steps) so the
same action was always sampled for the same sample → at the fixed point,
identical target every step → zero gradient. Now uses t_buf (Adam step
counter, device-mapped, increments every step). Each training step
samples a DIFFERENT action per sample → target is truly stochastic →
gradient variance prevents convergence.

Also moves philox_uniform to common_device_functions.cuh for reuse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 19:05:08 +02:00
jgrusewski
7a85a40788 fix: stochastic Expected SARSA — sample one action per target, not mixture
Deterministic softmax mixture produced a fixed target that the model
matched exactly → zero gradient → Q-gap frozen at 0.167, val_Sharpe
frozen at -5.62 from epoch 22. Stochastic sampling picks ONE action
per sample from the softmax weights. Expected target over many steps
is identical (unbiased) but per-step target varies → model can never
match a moving target → perpetual gradient → no convergence trap.

Also moves philox_uniform to common_device_functions.cuh so all
kernels can use it.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:59:11 +02:00
jgrusewski
b1b5682e2b fix: Expected SARSA tau floor scales with Q magnitude — was fixed 0.01
Floor = max(|mean_Q| * 0.01, 1e-6). At Q-mean=0.005: floor=5e-5.
At Q-mean=1.0: floor=0.01. Fully adaptive, zero hardcoded constants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:10:10 +02:00
jgrusewski
69b911a611 fix: Expected SARSA tau=Q_gap (scale-invariant) — tau=Q_gap/3 caused convergence trap
tau=Q_gap/3 gave 20:1 weight ratio (95%/4%/1%), making the target
essentially argmax. Once the online distribution matched this sharp
target, cross-entropy gradient vanished → Q-gap froze at 0.158,
val_Sharpe locked at 2.40 from epoch 22.

tau=Q_gap gives a CONSTANT e:1 ≈ 2.72:1 ratio (63%/23%/14% for 3
actions) regardless of Q-gap magnitude. The target always maintains
meaningful mixture → perpetual gradient pressure → Q-values keep
evolving. Scale-invariant: the softmax ratio depends only on relative
Q-differences, not their absolute magnitude.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 18:00:09 +02:00
jgrusewski
0b59b30549 fix: eval mode uses Boltzmann sampling — argmax froze val_Sharpe at 0.00
All 4 branches (dir, mag, order, urgency) now use Boltzmann sampling
in eval mode instead of argmax. argmax(softmax(Q/tau)) = argmax(Q) —
temperature is meaningless with argmax. Every state picked the same
winning action → identical trades → Sharpe=0.00 from epoch 8 onward.
Boltzmann sampling with Philox seed gives deterministic but
action-diverse evaluation. When Q-values differentiate, Boltzmann
naturally sharpens toward the best action.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:58:26 +02:00
jgrusewski
c55d9d0276 fix: Expected SARSA temperature prevents uniform-averaging plateau
Softmax weights used implicit tau=1, producing near-uniform weights
when Q-gap=0.06: exp(0.06)≈1.06 → weights [0.35, 0.33, 0.32].
The mixture averaged all distributions → zero gradient to differentiate
actions → Q-gap frozen → val_Sharpe locked at 0.00 after epoch 19.

Now uses tau = max(Q_gap/3, 0.001). With Q-gap=0.06 and tau=0.02:
exp(0.06/0.02) = exp(3) ≈ 20:1 weight ratio between best and worst.
The 0.001 floor means even tiny Q-differences produce sharp weights,
preventing the uniform-averaging fixed point.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:10:20 +02:00
jgrusewski
d06a18ef9f fix: IQN readiness update moved outside graph capture — cuStreamSynchronize invalidated capture
read_total_loss() calls cuStreamSynchronize which is illegal during
CUDA Graph stream capture. Was inside submit_aux_ops (captured in
graph_mega/graph_aux). Moved to Step 4 (conditional ops outside graph).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:26:53 +02:00
jgrusewski
7c00793c9f fix: exposure action select uses Boltzmann everywhere — argmax caused flips
branching_action_select exposure head used hard argmax while order/
urgency used Boltzmann. experience_action_select eval mode used pure
argmax for direction. Both replaced with Boltzmann softmax:
  tau = max(Q_max - Q_min, 0.01)
When Q-values differentiate: converges to argmax. When flat: spreads
evenly. Eval mode takes argmax of softmax probs (deterministic).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:19:44 +02:00
jgrusewski
1634dec042 fix: eval v_range width from Q-gap, baseline from Q-std — eliminates -40 dips
Two fixes:
1. Width: half = max(10*q_gap, 3*q_std, 0.1) instead of fixed 3*q_std+1.0.
   With q_gap=0.06 and 51 atoms, gives ~5 atoms of action resolution
   (was 1.5 atoms with the 1.0 floor). No more catastrophic -40 dips
   from atom underresolution.

2. Baseline: uses q_std_ema (proportional to natural Q oscillation)
   instead of fixed 0.01. Normal Q-oscillation (±q_std) no longer
   triggers aggressive alpha — only abnormal shifts do.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:13:36 +02:00
jgrusewski
60528390e1 fix: adaptive IQN lambda + complete bf16 elimination (312 calls total)
Adaptive IQN lambda: reads IQN total_loss synchronously, tracks EMA
with adaptive alpha, computes readiness = (loss_initial - loss_ema) /
loss_initial. SAXPY scale = iqn_lambda_base * readiness. Suppresses
noisy IQN gradient early (readiness≈0), ramps to full weight as IQN
converges (readiness→1).

bf16 cleanup: 185 wrapper calls replaced across 12 .cu files
(attention, backtest_ppo, backtest_supervised, curiosity, dqn_utility,
dt, ensemble, her, iqn_cvar, monitoring, mse_loss, statistics).
Definitions removed from common_device_functions.cuh (legacy compat
block retained for PPO kernels only). Total: 312 bf16 calls eliminated
across both sessions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:46:17 +02:00
jgrusewski
7000151493 fix: IQL readiness uses adaptive EMA — was binary pop-on
Readiness jumped from 0 to ~0.8 in one step when CV first improved,
causing per-sample support to snap from [-1,1] to V(s)-centered
discontinuously. Same adaptive alpha pattern as eval_v_range:
α = |error| / (|error| + 0.05), clamped [0.01, 0.3]. Smooth ramp-in.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:26:05 +02:00
jgrusewski
e3bd44f170 fix: eval EMA seeds from neutral v_range — snap-to-first caused epoch 3 spike
EMA init used == 0.0 sentinel and snapped to first non-zero Q-stats,
causing a discontinuous v_range transition → val_Sharpe=30 spike at
epoch 3 followed by -1 correction. Now seeds from neutral center
(mean=0, std=1 → v_range=[-4,4]) and the adaptive alpha tracks
to actual Q-stats smoothly over 2-3 steps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:24:59 +02:00
jgrusewski
9c2f5d75b5 fix: eval v_range EMA uses adaptive alpha — fixed α=0.05 lagged on Q-shifts
When Q-mean shifted from +0.01 to -0.01, fixed α=0.05 took ~20 updates
to catch up → misaligned backtest atoms → val_Sharpe dipped to -20.
Adaptive alpha: α = |error| / (|error| + 0.01), clamped [0.01, 0.5].
Tracks fast on Q-shifts (α→0.5), smooths when stable (α→0.01).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:01:49 +02:00
jgrusewski
8c3b7f5c5e fix: validation backtest uses EMA-smoothed eval v_range — was fixed [-240, 240]
Fixed v_range from hyperparams (reward_scale/gamma=±240) was 4800x too
wide for Q-values near 0. All Q-values mapped to the same 2-3 center
atoms, making backtest action selection essentially random → val_Sharpe
jumped between -11 and +0.14 arbitrarily. Now uses the same
EMA-smoothed eval_v_range that the experience collector uses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:39:44 +02:00
jgrusewski
5d877234b3 fix: Q-stats readback is synchronous — stale double-buffer caused 2-state oscillation
reduce_current_q_stats returned PREVIOUS call's results via async
double-buffer. When the eval_v_range EMA converged, stale readback
created a limit cycle: Q-mean alternated between exactly -0.0190 and
-0.2102 for 20+ epochs. Now uses synchronous cuStreamSynchronize +
cuMemcpyDtoH (5µs cost every 50 steps). Removes dead
flush_q_stats_readback and q_stats_ready field.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:16:26 +02:00
jgrusewski
5c725f7c9d fix: C51 target uses softmax-weighted mixture instead of hard argmax
Hard argmax picks actions based on noise when Q-values are flat
(all near zero). This causes policy collapse — the model locks into
a single action pattern after 1 epoch (Trades=56647 locked, action
distribution frozen). Expected SARSA-style softmax mixture:
  p_target(z) = Σ_a softmax(E[Q(s',a)])[a] * p_target_a(z)
When Q-values differentiate, converges to argmax. When flat, averages
across actions → stable gradient signal → breaks policy collapse.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:07:59 +02:00
jgrusewski
dea2ce1db7 fix: EMA-smooth eval v_range updates — per-step snapping caused val_Sharpe instability
update_eval_v_range snapped to current Q-stats every 50 steps. With
per-sample IQL support (the circular dependency fix), Q-stats fluctuate
between steps, causing the backtest atom placement to jump → val_Sharpe
oscillated between -42 and 0. EMA (α=0.05) smooths the transition,
giving the backtest consistent atoms across evaluations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:38:15 +02:00
jgrusewski
36c803f520 fix: Q-stats use per-sample IQL support — breaks circular eval_v_range feedback
compute_expected_q used eval_v_range to compute Q-values, then Q-stats
updated eval_v_range from those Q-values — circular dependency that
amplified any drift. Q-mean collapsed 0 → -0.69 in 10 epochs despite
mean_reward ≈ 0. Now uses per_sample_support from IQL (independently
trained V(s)), breaking the feedback loop.

Also removes 127 bf16 identity wrapper calls across 5 CUDA kernels
(c51_loss, epsilon_greedy, dqn_utility, experience, attention_backward).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 11:01:21 +02:00