Commit Graph

1889 Commits

Author SHA1 Message Date
jgrusewski
5b197151db feat: graph_utility_kernels.cu — gather_padded + increment_step_counters for true single-graph
Adds 4 GPU-native kernels replacing host-side operations on the training
hot path: gather_f32_rows_padded (row gather + 128-byte zero-pad),
gather_f32_scalar, gather_i32_scalar, and increment_step_counters (atomic
counter bumps + cosine-annealed tau — zero CPU sync per step).
Wired into build.rs (nvcc cubin compile) and gpu_dqn_trainer.rs
(GRAPH_UTILITY_CUBIN include_bytes!).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:22:45 +02:00
jgrusewski
26edbb6d8f fix: isolate shrink_perturb and scale_adam_momentum from graphed CUmodule
shrink_and_perturb() is called at epoch boundaries after child graphs are
captured. shrink_perturb_kernel lives in the same CUmodule as scale_f32,
saxpy_f32, spectral_norm, adam_update, and other graphed CUfunctions.
On Hopper (sm_90), launching any CUfunction from a graphed CUmodule
ungraphed corrupts the child graph kernel state → 3100ms replay.

scale_adam_momentum() is called at cosine LR warm restarts (also after
graph capture). scale_f32_kernel is captured in forward_child — same
CUmodule violation.

Fix: add shrink_perturb_ungraphed and scale_f32_ungraphed loaded from
the existing ungraphed_module (separate CUmodule instance of the same
DQN_UTILITY_CUBIN). Both ungraphed callers now use isolated handles.
CUmodule count: 5 → 5 (ungraphed_module already existed, reused).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:16:29 +02:00
jgrusewski
e8d382fc2f fix: isolate ungraphed pad_states + stochastic_depth_rng into separate CUmodule
ROOT CAUSE: pad_states_kernel and stochastic_depth_rng_kernel were loaded from
the SAME CUmodule as graph-captured kernels (adam_update, grad_norm, saxpy, etc.)
but launched UNGRAPHED every step. On Hopper, launching ANY CUfunction from a
CUmodule that also has graph-captured CUfunctions corrupts the graph's kernel
state — causing 3100ms replay instead of ~30ms.

Fix: load from a separate ungraphed_module. Zero CUmodule contamination between
graphed and ungraphed execution contexts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 00:02:23 +02:00
jgrusewski
19b6b5af47 fix: complete CUfunction isolation — zero cross-child sharing + raw memset + pinned memory
Three changes to eliminate the 3100ms replay regression on Hopper:

1. CUfunction isolation: saxpy_f32_kernel was shared across 3 child graphs
   (forward, aux, adam_grad). Added saxpy_f32_adam_grad and saxpy_f32_aux from
   separate CUmodules. Also isolated grad_norm_standalone for post_aux_child
   (was shared with forward_child's d_logits clipping path).

2. Raw cuMemsetD8Async: replaced all cudarc memset_zeros in graph-captured
   functions (submit_forward_ops_main, apply_cql_gradient,
   run_causal_intervention_unconditional) with raw cuMemsetD8Async which is
   properly captured by CUDA Graph. 6 call sites fixed.

3. DtoD memcpy audit: all memcpy_dtod_async calls verified — large buffer
   copies (grad snapshot 2.6MB, multi-horizon blend) are correct for DtoD;
   no scalar copies found that should use pinned memory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:24:52 +02:00
jgrusewski
79fce72afb fix: CUfunction isolation for post_aux child + raw memset in IQN/IQL/Attention
ROOT CAUSE of 3100ms adam_child: adam_update_kernel and grad_norm_finalize_kernel
were shared between post_aux_child and adam_update/forward children. On Hopper,
CUfunction sharing across child graphs corrupts kernel state.

Fix: load utility cubin from separate CUmodule for post_aux child, giving it
isolated adam_update_post_aux, grad_norm_finalize_post_aux, and scale_f32_post_aux
handles. Zero cross-child CUfunction sharing for these kernels.

Also: convert memset_zeros to raw cuMemsetD8Async in IQN (3 calls), IQL (1),
Attention (1) — eliminates cudarc device_ptr_mut() overhead during graph capture.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:11:15 +02:00
jgrusewski
c7185d2483 perf: convert all CudaSlice kernel args to raw u64 — eliminate cudarc overhead in graph
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 23:01:51 +02:00
jgrusewski
4b6d9d2e82 perf: fused RELU_BIAS epilogue for target, collector, and ensemble forward paths
Adds try-fused-fallback-to-separate pattern to forward_target_raw,
forward_online_f32, and forward_value_head — matching the existing
pattern in forward_online_raw. Eliminates ~10-14 separate bias+ReLU
kernel launches by fusing them into the preceding cuBLAS GEMM epilogue.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 22:40:46 +02:00
jgrusewski
76cfc0d185 perf: IQN quantiles 64→32 — saves 4.3GB VRAM, halves IQN GEMMs
Academic literature shows diminishing returns past 32 quantiles.
64 quantiles allocated 8.6GB for tiled intermediates (B×Q×hidden).
32 quantiles: 4.3GB (half), ~13 GEMMs halved in aux_child.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 22:28:23 +02:00
jgrusewski
1481ba2699 feat: unified single-graph — 7+ children, zero ungraphed kernel launches
Add post_aux_child (selectivity + denoise + risk_sgd + multi-horizon) and
maintenance_child (causal intervention + gradient vaccine) to the CUDA graph
pipeline. All kernel launches now run inside child graphs; only kernel-free
ops (pinned readbacks, host counters, IQL modulate_td_errors on its own
CUfunction) remain outside the graph.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 22:08:54 +02:00
jgrusewski
2334d1d6ce feat: unconditional submit_post_aux_ops + submit_maintenance_ops + prev_grad_buf
Add child-graph-capturable submit methods that run post-aux and
maintenance ops without conditional guards (graphs_captured, step %
interval). This enables moving all kernel launches into CUDA child
graphs, eliminating ungraphed ops that cause CUfunction corruption on
Hopper during 3100ms adam replays.

- prev_grad_buf: snapshot of grad_buf after Adam for next-step vaccine
- submit_post_aux_ops: selectivity, denoise, risk SGD, multi-horizon
- submit_maintenance_ops: causal intervention + gradient vaccine
- run_causal_intervention_unconditional: no step/graph guards
- apply_gradient_vaccine_from_prev: uses prev_grad_buf instead of
  separate forward+backward pass

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 21:52:51 +02:00
jgrusewski
075ed8ba4f fix: disable selectivity+denoise outside graph — CUfunction shared with graphed children
step_denoise_adam uses adam_update_kernel + grad_norm_finalize_kernel UNGRAPHED
after child graph replays. Same CUfunctions are captured in adam_child and
forward_child. On Hopper, launching a captured CUfunction ungraphed corrupts
graph kernel state → 3100ms adam_child replay on next step.

Fix: skip selectivity+denoise ungraphed training. These are auxiliary optimizers
with non-fatal error handling. To re-enable: load separate CUfunction instances
for ungraphed paths or move into child graphs.

Expected: adam drops from 3100ms to ~30ms. Epoch from ~690s to ~60s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 21:00:05 +02:00
jgrusewski
e28add3768 fix: adam_child 3100ms — grad_norm_finalize_kernel shared between forward+adam children
ROOT CAUSE: grad_norm_finalize_kernel (CUfunction) was captured in BOTH
forward_child (d_logits clipping at lines 7572/7821) AND adam_child
(compute_grad_norm_for_adam via launch_grad_norm_finalize at line 6346).
Sharing a CUfunction between two captured graphs corrupts kernel state
on Hopper (sm_90), causing the second graph to replay at 3100ms instead
of <1ms. This is the SAME class of bug as the earlier grad_norm_standalone
fix — we fixed one shared CUfunction but missed the finalize kernel.

FIX: Load grad_norm_finalize from a SEPARATE CUmodule (new cubin load)
for the adam path. Each CUfunction instance is now captured in exactly
one child graph. Also split adam into adam_grad + adam_update children
for per-operation timing visibility.

Expected: adam drops from 3100ms to ~30ms. Total step ~350ms. Epoch ~62s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 20:09:19 +02:00
jgrusewski
7b0280a187 debug: split adam into adam_grad + adam_update to isolate 3.1s bottleneck
adam_child = 3113ms even after CUfunction fix. Split into:
- adam_grad: mamba2_backward + step_mamba2_adam + pruning + grad_norm
- adam_update: Adam kernel + unflatten + ISV

If adam_grad is fast and adam_update is slow, the bottleneck is Adam/unflatten.
If adam_grad is slow, grad_norm_kernel has an issue.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 19:13:32 +02:00
jgrusewski
32151ba8bc fix: backward branches reuse forward workspace — saves 128MB (OOM fix)
The backward branch parallelism allocated 4 × 32MB = 128MB for per-branch
cuBLAS workspaces, pushing H100 VRAM over 80GB → OOM. Forward and backward
are in the same child graph (sequential) — workspaces never conflict.

Now backward receives forward's branch_workspace_ptrs as a parameter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 16:57:34 +02:00
jgrusewski
80d871ae1d feat: IQN prepare_buffers extraction for parallel stream dispatch
Separates buffer prep (decode_actions, DtoD copies) from training pipeline
so prep runs on main stream and execute_training_pipeline runs on iqn_stream.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 16:37:54 +02:00
jgrusewski
108a0993c7 perf: parallel backward branches using fork-join on branch_streams
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 16:35:27 +02:00
jgrusewski
9fb330da7e fix: adam_child 3120ms — CUfunction conflict between forward_child and adam_child
grad_norm_standalone was captured in BOTH forward_child (d_logits clipping)
and adam_child (gradient norm). Same CUfunction in two captured graphs
corrupts kernel state on Hopper — the GPU replayed a corrupted node for
3120ms per step (88% of total step time).

Fix: adam_child uses grad_norm_kernel (the regular handle, not captured
elsewhere) instead of grad_norm_standalone. Each CUfunction is now captured
in exactly one child graph.

Expected: adam_child drops from 3120ms to ~30ms. Total step from ~3550ms
to ~460ms. Epoch from ~700s to ~80s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 16:34:12 +02:00
jgrusewski
16f5147356 feat: IQN/Attention accept workspace+stream override for multi-stream
Add optional override_stream and override_workspace parameters to
execute_training_pipeline (GpuIqnHead), and forward/backward/adam_step
(GpuAttention). All existing callers pass None, None — behaviour is
unchanged. The parallel path can now dispatch these ops to dedicated
CUDA streams with separate cuBLAS workspaces.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 16:20:31 +02:00
jgrusewski
dc9f25df63 feat: allocate aux streams, workspaces, events for Phase 2 multi-stream
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:15:38 +02:00
jgrusewski
8ebb65a942 fix: add adam_child_end event — previous timing used wrong event (adam_end from old phase system)
The child graph breakdown showed adam=-1.0ms because it was measuring
elapsed(backward_end, adam_end) where adam_end is from the OLD phase
timing system, not the adam child graph. Added proper adam_child_end
event recorded after the adam_child launch.

This will reveal the actual adam_child GPU time — currently suspected
to be 3117ms (73% of step time) based on total - measured children.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 16:00:06 +02:00
jgrusewski
4295cbde79 perf: add per-child graph CUDA event timing breakdown
Records cuEventRecord between each of the 5 child graph launches.
At epoch end, cuEventElapsedTime reports per-child GPU time:
spectral, forward, ddqn, aux, adam.

This tells us exactly which child dominates the 4s/step graph replay
cost, informing Phase 2 multi-stream parallelism priorities.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 14:59:57 +02:00
jgrusewski
c8d87626d3 perf: move Q-stats to epoch boundary only — zero sync in training loop
Per-step reduce_current_q_stats created GPU pipeline bubbles every 50 steps.
Each sync waited for the GPU to drain all pending graph replays before
the CPU could continue. Q-stats are now computed at epoch boundary only
(process_epoch_boundary already calls reduce_current_q_stats).

Training loop is now fully async: 178 graph launches queue in <1ms,
GPU executes back-to-back with zero pipeline stalls.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 14:36:36 +02:00
jgrusewski
ff2b599965 fix: Q-stats readback via pinned device-mapped memory — zero cuStreamSynchronize
q_stats_kernel now writes directly to q_readback_dev_ptr (pinned device-mapped).
CPU reads previous step's values from q_readback_pinned — one-step lag, zero sync.

This eliminates the LAST cuStreamSynchronize from the per-step training path.
The training loop is now fully async: graph launch → CPU returns immediately →
next step starts while GPU still executes → zero pipeline bubbles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 14:15:04 +02:00
jgrusewski
a171eaa710 fix: reduce_current_q_stats now just reads graph-computed q_out_buf
The function re-ran 15+ kernels (expected_q, q_mean_center, q_anchoring,
risk_budget, branch_confidence, epistemic_gate, temporal_consistency,
branch_independence, q_attention, graph_message_pass, q_denoise) outside
the graph every step — duplicating work the graph already did.

Now: q_out_buf is populated by graph replay (populate_q_out in aux_child).
reduce_current_q_stats just runs q_stats_kernel (1 kernel) + DtoH sync.
Runs every step since cost is now ~1ms, not ~4s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 14:11:10 +02:00
jgrusewski
ba1c79763e fix: reduce_current_q_stats every 50 steps, not every step — 15 kernels + sync per call
reduce_current_q_stats ran 15+ GPU kernels (expected_q, q_mean_center,
q_anchoring, risk_budget, branch_confidence, epistemic_gate, temporal_consistency,
branch_independence, q_attention, graph_message_pass, q_denoise, q_stats)
PLUS cuStreamSynchronize for DtoH readback — EVERY training step.

This added ~4s per step (matching graph replay cost), making epochs 700s
instead of ~35s. Running every 50 steps reduces Q-stats overhead to <1%
of epoch time while still tracking Q-range changes within 200 steps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 14:07:27 +02:00
jgrusewski
a156b48844 perf: move ensemble into aux_child graph — eliminate ungraphed per-step overhead
Ensemble diversity (2 extra head forward passes + KL gradient + trunk backward)
was running OUTSIDE the CUDA graph on every training step. Now runs inside
submit_aux_ops, captured in the aux_child graph for zero launch overhead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 13:50:51 +02:00
jgrusewski
144e70ca29 fix: remove redundant temporal/ISV re-execution in reduce_current_q_stats
reduce_current_q_stats() re-ran 10 kernels (mamba2_step, predictive_coding,
regime_dropout, ISV forward/gate/route, recursive_confidence, trade_plan,
plan_noise, risk_budget) every training step OUTSIDE the graph replay.

These ops already executed inside the graph — the logit buffers are current.
The redundant execution added ~4s of GPU compute per step (matching the
graph replay cost), doubling total epoch time.

Fix: compute expected_q directly from the existing logit buffers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 13:42:35 +02:00
jgrusewski
c3cdb39081 fix: acquire write lock once per epoch, not per training step
The async write lock (self.agent.write().await) was acquired inside the
per-step training loop, causing 696s/epoch overhead from async yield on
every iteration — 178 steps × ~4s per lock acquisition. Actual GPU compute
was only 3.6s (graph replay at 2ms/step).

Fix: acquire the lock once before the loop. The training loop is the only
writer during this phase — no contention, no need to release between steps.

Expected: epoch time drops from 700s to ~5s.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 13:15:25 +02:00
jgrusewski
8d88c63434 debug: add per-step timing logs to identify hang vs slow compute
Logs step_ms for first 5 steps and every 50th step thereafter.
If steps aren't completing, the graph is hanging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 12:56:25 +02:00
jgrusewski
93ce3e118b fix: attention LayerNorm kernel arg mismatch — extra arg in fwd, missing 2 args in bwd
Forward: removed spurious ln_save_xnorm arg (kernel has 9 params, code passed 10).
The extra arg shifted D and B, causing CUDA_ERROR_ILLEGAL_ADDRESS on H100.

Backward: added missing residual (projected_buf) and save_mean args (kernel has
11 params, code passed 9). Without these, gamma/rstd/d_x/d_gamma/d_beta were all
shifted to wrong positions.

Both bugs existed since the cuBLAS attention rewrite but were latent because
attn_sdp_fwd was loaded from the wrong cubin (attention_kernel.cubin instead of
attention_backward_kernel.cubin), silently disabling attention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 12:41:41 +02:00
jgrusewski
3ff5bd696d debug: add attention tracing + sync barriers to isolate H100 segfault
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 12:23:05 +02:00
jgrusewski
f8f1147de7 fix: attention kernel load from wrong cubin + build.rs bf16 label
attn_sdp_fwd and attn_layer_norm_fwd were loaded from attention_kernel.cubin
(which only contains the legacy multihead_feature_attention) instead of
attention_backward_kernel.cubin where they actually live. This caused
CUDA_ERROR_NOT_FOUND on H100, silently disabling attention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 12:07:13 +02:00
jgrusewski
69ace546aa fix: eliminate cuStreamSynchronize in IQN loss readback — was serializing every step
IQN read_total_loss() called cuStreamSynchronize + cuMemcpyDtoH every training
step, blocking the CPU until all GPU work completed. This serialized the entire
async pipeline, causing 3537ms/step instead of <10ms.

Fix: pinned device-mapped memory for IQN total_loss (same pattern as DQN trainer).
GPU writes via device pointer, CPU reads via host pointer, zero sync.

Also re-applies the cuGraphClone elimination (was lost when agents modified
fused_training.rs) and adds recursive_confidence_reduce for deterministic
gradient accumulation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 11:35:10 +02:00
jgrusewski
6960a02af4 fix: eliminate cuGraphClone + recursive_confidence atomicAdd — deterministic graph replay
- capture_child_graph: use std::mem::forget to take ownership of cudarc's
  CudaGraph handles directly. cuGraphClone corrupts cublasLtMatmul HMMA tile
  scheduling state on Hopper, causing graph replay to hang with temporal ops.
- create_eval_forward_exec: instantiate directly from original graph (no clone).
  Multiple execs from the same CUgraph is safe per NVIDIA docs.
- recursive_confidence_backward: replace atomicAdd into grad_buf with per-block
  partial output + deterministic reduce kernel. Zero atomicAdd on gradient path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 10:50:46 +02:00
jgrusewski
80769abb9c cleanup: purge all bf16 naming remnants — pure f32/TF32 pipeline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 10:30:04 +02:00
jgrusewski
c80a253557 fix: rewrite curiosity inference to cuBLAS GEMMs — eliminates per-sample matmul inside graph
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 10:27:21 +02:00
jgrusewski
a230ad5979 fix: kan_gate_backward — eliminate atomicAdd with per-element output + deterministic reduce
Split into two kernels: kan_gate_backward writes per-element basis
contributions to intermediate buffers (no cross-element reduction),
then kan_grad_reduce deterministically accumulates them with one
thread per output parameter — fully eliminating atomicAdd from the
KAN spline gradient path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 10:25:22 +02:00
jgrusewski
661e1304a0 cleanup: rename all _bf16 kernel functions and variables — pure f32 pipeline
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 10:19:12 +02:00
jgrusewski
3065ae1f3b fix: DT kernels — eliminate atomicAdd, deterministic gradient reduction
Three atomicAdd anti-patterns removed from Decision Transformer kernels:

1. dt_linear_backward_kernel (critical): Split into dt_linear_backward_kernel
   (input gradient only, per-sample deterministic) + dt_linear_grad_kernel
   (one thread per weight, loop over samples — zero atomics).

2. dt_causal_attention_kernel: Replace cross-head atomicAdd output projection
   with per-head output buffer [B, H, T, E] + separate dt_sum_heads_kernel
   that sums heads in fixed order.

3. dt_cross_entropy_kernel: Remove atomicAdd for total_loss, add
   dt_reduce_loss_kernel (single-thread sequential sum for full determinism).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 10:05:05 +02:00
jgrusewski
020e9ba460 fix: rewrite curiosity training to per-block reduce — eliminates non-deterministic atomicAdd
Replace warp-reduced atomicAdd gradient accumulation in the curiosity
forward model with a two-kernel deterministic pipeline:

1. curiosity_fwd_bwd_per_block: each block reduces its threads' gradient
   contributions via shared memory tree reduction, writes one partial
   gradient vector [CUR_TOTAL_PARAMS] per block.

2. curiosity_grad_reduce: one thread per parameter sums block partials
   in fixed order (block 0, 1, 2, ...). Fully deterministic.

Deleted kernels: curiosity_forward_backward (atomicAdd path),
curiosity_fused_zero_fwd_bwd_adam (grid-wide atomic barrier),
curiosity_adam_step_fused (dead code), warp_sum_cur helper.

Rust side: removed block_counter, adam_fused_func,
fused_zero_fwd_bwd_adam_func. Added partial_grads buffer
[max_blocks * CUR_TOTAL_PARAMS] (~2.8 MB for 64 blocks).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 09:59:11 +02:00
jgrusewski
29d7a7bd60 fix: replace cuMemcpyHtoDAsync with pinned device-mapped memory in IQN/IQL/Attention — graph capture safe
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 09:51:55 +02:00
jgrusewski
5b90ebe9e9 fix: curiosity wiring + cleanup dead attention kernels + stale comments
- gpu_experience_collector: add curiosity_weight param to new(); conditionally
  initialize GpuCuriosityTrainer when weight > 0.001 (was always None)
- training_loop: pass hyperparams.curiosity_weight to GpuExperienceCollector::new()
- gpu_experience_collector: replace misleading 'curiosity disabled' log with
  weight-conditional message; zero-init comment explains trainer populates weights
- attention_backward_kernel.cu: remove dead per-sample attention_backward_kernel
  and attn_weight_grad_reduce (not loaded by any Rust code since cuBLAS rewrite);
  remove helpers (f32_shfl, f32_block_max/sum_bwd) and defines only used by them;
  update file header to describe current cuBLAS element-wise kernel contents
- c51_loss_kernel.cu: fix stale 'atomicAdd accumulator' comment on q_divergence
  param — it is written by the reduction kernel, not via atomicAdd

Audit (task 4): curiosity_training_kernel.cu uses warp-reduced atomicAdd in
curiosity_forward_backward and curiosity_fused_zero_fwd_bwd_adam. These run
outside CUDA graphs (experience collection between epochs), so atomicAdd is
acceptable; existing DETERMINISM NOTE comments document this accurately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 08:47:58 +02:00
jgrusewski
5c759aa621 fix: make temporal ops graph-safe — replace raw FFI with captured kernels
mamba2_step: replace memcpy_dtod_async (not captured in CUDA Graph) with
a mamba2_copy_enriched kernel that IS captured — fixes stale data on replay.

predictive_coding_loss: replace cuMemsetD8Async + atomicAdd with per-sample
output + c51_loss_reduce — eliminates both the uncaptured memset and the
non-deterministic atomicAdd, matching the existing loss pipeline pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 08:35:19 +02:00
jgrusewski
18cc9ff5ce fix: skip parent graph composition — launch children individually
cuGraphAddChildGraphNode may invalidate child graph handles on Hopper,
causing individual child launches to hang. Skip compose entirely.
5 cuGraphLaunch calls per step (~10µs overhead).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 08:15:29 +02:00
jgrusewski
defc663f29 fix: launch child graphs individually — parent composition hangs on Hopper
cudaGraphAddChildGraphNode parent replay hangs on sm_90.
Individual child launches (5 per step, ~10µs overhead) work.
Each child is instantiated (cuGraphInstantiateWithFlags) and
launched (cuGraphLaunch) sequentially on the training stream.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 01:43:22 +02:00
jgrusewski
5461915a97 fix: update test assertions for state_dim=65 (42 market + 20 OFI + 3 portfolio)
Tests had hardcoded state_dim=45/53 from before OFI was always enabled.
PPO tests use raw 45-dim data (no OFI), DQN tests use 65-dim.
899 unit tests pass, 20 GPU smoke tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 01:27:07 +02:00
jgrusewski
bc4c9f33fc feat: Q-spread opportunity cost — penalizes flat when model predicts edge
Adds opportunity cost to experience_env_step reward: when the model is
flat, subtract |q_gap| * opp_cost_scale from the reward. q_gap is the
model's own conviction signal (max_long_Q - max_short_Q), already
computed by experience_action_select and stored in q_gaps_buf[N].

- experience_kernels.cu: new opp_cost_scale kernel param; apply penalty
  after churn penalty, before plan conviction scaling
- backtest_env_kernel.cu: matching opp_cost_scale param in both kernels;
  set to 0.0 during backtest (no Q-gap available post-hoc)
- DQNHyperparameters: opportunity_cost_scale field, default 0.001
- ExperienceCollectorConfig: opportunity_cost_scale field, wired through
  training_loop.rs; backtest configs default to 0.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 01:09:13 +02:00
jgrusewski
108bb63fce feat: cost-driven hold timing — replace min_hold_bars with learned cost signals
Removed: enforce_hold(), min_hold_bars from config/kernels/backtest.
Added: holding_cost_rate (inventory penalty), churn_threshold_bars +
churn_penalty_scale (graduated flip penalty) to reward in
experience_env_step and backtest kernels.

The model learns optimal hold timing from cost signals:
- Per-trade tx cost prevents churning (existing)
- Inventory penalty makes large positions expensive to hold
- Churn penalty graduates cost for rapid flips
- Temporal attention learns when holding cost > expected profit

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 01:04:11 +02:00
jgrusewski
a24fd7c9bd cleanup: remove dead code, aux_frequency, hardcoded dimensions
- iql_value_kernel.cu: remove old per-sample kernels (iql_forward_loss_kernel,
  iql_backward_per_sample, iql_weight_grad_reduce) replaced by cuBLAS path
- gpu_experience_collector.rs: delete _portfolio_dim dead variable
- metrics.rs: replace hardcoded feature_dim=62 with market_dim+OFI_DIM
- config.rs: bars_per_day default 390.0→0.0, add validation at training start
- training_loop.rs: fail fast if bars_per_day==0 (uninitialized)
- common_device_functions.cuh: remove unused PORTFOLIO_DIM define, update comments
  to "42 market + 14 portfolio", keep STATE_DIM (used in TILE_LAYER_WARP_CLEAN)
- experience_kernels.cu: remove unused PORTFOLIO_DIM define
- dqn.rs, config.rs: update stale "42 market + 8 portfolio" comments to 14

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 00:47:34 +02:00
jgrusewski
0f8395450f feat: child graph architecture — 5 composable sub-graphs in single parent launch
Replaces 6 separate CUDA graphs (graph_forward, graph_forward_ddqn,
graph_adam, graph_aux, graph_spectral, graph_mega) with composable
child sub-graphs assembled into a single parent graph.

Architecture:
  parent_graph (single cuGraphLaunch per step)
    ├── spectral_child (spectral norm)
    ├── forward_child (cuBLAS trunk + temporal + ISV + loss + backward)
    ├── ddqn_child (Double DQN Pass 3)
    ├── aux_child (HER + EMA + IQL + IQN + attention + CQL)
    └── adam_child (mamba2 bwd + pruning + grad_norm + adam + ISV update)

Each child captured independently via cudarc begin/end_capture,
composed via cudaGraphAddChildGraphNode with sequential dependencies.
Only parent is instantiated and launched.

Removed: SendSyncGraph, RawCudaGraph, capture_training_graphs,
replay_forward, replay_adam, aux_frequency, all old graph fields.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 00:40:10 +02:00