diff --git a/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs b/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs index c4eae665f..e575d8023 100644 --- a/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs +++ b/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs @@ -655,6 +655,37 @@ pub(crate) static SP13_APPLY_FIXED_ALPHA_EMA_CUBIN: &[u8] = static SP13_AUX_PRED_TO_ISV_TANH_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/aux_pred_to_isv_tanh_kernel.cubin")); +/// SP14 B.3 (2026-05-05): per-step Q-head ↔ aux argmax disagreement EMA +/// producer. Reads online Q logits + aux softmax outputs; computes per-step +/// argmax-disagreement bool; updates a Wiener-optimal EMA into ISV[388]. +/// Loaded from `q_disagreement_update_kernel.cubin`. +static SP14_Q_DISAGREEMENT_CUBIN: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/q_disagreement_update_kernel.cubin")); + +/// SP14 B.4 (2026-05-05): α_grad compute kernel (Schmitt + adaptive k + +/// adaptive β). Reads ISV[388] (disagreement EMA), ISV[389] (gradient-hack +/// gate), ISV[390] (hack-rate EMA); computes α_grad ∈ [0, 1] and writes to +/// ISV[387]. Consumed downstream by the direction Q-head SGEMM weight as an +/// input-gate scalar. Loaded from `alpha_grad_compute_kernel.cubin`. +static SP14_ALPHA_GRAD_CUBIN: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/alpha_grad_compute_kernel.cubin")); + +/// SP14 B.5 (2026-05-05): anti-gradient-hacking circuit breaker. Compares +/// Q-head gradient direction against aux-signal gradient direction; fires a +/// GATE1_OPEN_STATE bit (ISV[391]) and updates a hack-rate EMA (ISV[390]) +/// when a hacking episode is detected. Loaded from +/// `gradient_hack_detect_kernel.cubin`. +static SP14_GRAD_HACK_CUBIN: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/gradient_hack_detect_kernel.cubin")); + +/// SP14 B.6 (2026-05-05): pre-SGEMM concat kernel for direction Q-head input. +/// Reads `h_s2 [B, SH2]` + `alpha_grad_scalar [1]` and writes +/// `[h_s2 | alpha_grad] [B, SH2 + 1]` into `dir_qaux_concat_scratch`. Replaces +/// the prior direct h_s2 input to the direction Q-head's first FC SGEMM. +/// Loaded from `dir_concat_qaux_kernel.cubin`. +static SP14_DIR_CONCAT_CUBIN: &[u8] = + include_bytes!(concat!(env!("OUT_DIR"), "/dir_concat_qaux_kernel.cubin")); + /// SP11 Fix 39 (2026-05-04, Task A2): SimHash novelty signal — lookup + /// update kernels sharing one cubin. Lookup reads /// `1/sqrt(1+count)` ∈ [0, 1] for each (state, action) bucket; update @@ -5056,6 +5087,38 @@ pub struct GpuDqnTrainer { /// launch); no FoldReset registry entry. Bound is structural (no /// runtime tanh). Consumed by `launch_aux_pred_to_isv_tanh`. aux_pred_to_isv_tanh_kernel: CudaFunction, + // ── SP14 Earned Gradient Flow kernels ───────────────────────────────── + /// SP14 B.3 (2026-05-05): per-step Q-head ↔ aux argmax disagreement EMA + /// producer. Reads online Q logits + aux softmax outputs; computes the + /// per-step argmax-disagreement bool; updates a Wiener-optimal EMA into + /// ISV[388]. Loaded from `q_disagreement_update_kernel.cubin`. + sp14_q_disagreement_update_kernel: CudaFunction, + /// SP14 B.4 (2026-05-05): α_grad compute kernel (Schmitt trigger + + /// adaptive k + adaptive β). Reads ISV[388] (disagreement EMA), + /// ISV[389] (gradient-hack gate), ISV[390] (hack-rate EMA); computes + /// α_grad ∈ [0, 1] and writes to ISV[387]. Consumed downstream by the + /// direction Q-head SGEMM weight as an input-gate scalar. + /// Loaded from `alpha_grad_compute_kernel.cubin`. + sp14_alpha_grad_compute_kernel: CudaFunction, + /// SP14 B.5 (2026-05-05): anti-gradient-hacking circuit breaker. + /// Compares Q-head gradient direction against aux-signal gradient + /// direction; fires a GATE1_OPEN_STATE bit (ISV[391]) and updates a + /// hack-rate EMA (ISV[390]) when a hacking episode is detected. + /// Loaded from `gradient_hack_detect_kernel.cubin`. + sp14_gradient_hack_detect_kernel: CudaFunction, + /// SP14 B.6 (2026-05-05): pre-SGEMM concat kernel for direction Q-head + /// input. Reads `h_s2 [B, SH2]` + `alpha_grad_scalar [1]` from ISV[387] + /// and writes `[h_s2 | alpha_grad] [B, SH2 + 1]` into + /// `sp14_dir_qaux_concat_scratch`. Replaces the prior direct h_s2 input + /// to the direction Q-head's first FC SGEMM. + /// Loaded from `dir_concat_qaux_kernel.cubin`. + sp14_dir_concat_qaux_kernel: CudaFunction, + /// SP14 B.7 (2026-05-05): scratch buffer for direction Q-head input + /// concat. Shape `[B, SH2 + 1]`. Written by `sp14_dir_concat_qaux_kernel` + /// before the direction Q-head's first FC SGEMM consumes it. Replaces + /// the prior direct `h_s2` input to the direction Q-head. B.7 allocates; + /// B.8 bumps the Q-head input-dim constant; B.9/B.11 launch and wire. + sp14_dir_qaux_concat_scratch: CudaSlice, /// SP11 Fix 39 (2026-05-04, Task A2): reward-subsystem controller /// kernel. Single-block, 10-thread producer reading 5 canary ISV slots /// [350..360) and writing 10 floats to scratch[SCRATCH_SP11_CONTROLLER_BASE..+10). @@ -16889,6 +16952,46 @@ impl GpuDqnTrainer { module.load_function("aux_pred_to_isv_tanh_kernel") .map_err(|e| MLError::ModelError(format!("sp13 aux_pred_to_isv_tanh_kernel load: {e}")))? }; + // SP14 EGF kernel loads (B.3–B.6). + // Handles are held in struct fields but not launched until B.9/B.11; + // per `feedback_no_partial_refactor.md` all 4 kernel handles + + // scratch buffer land atomically in this single commit. + let sp14_q_dis_module = stream.context() + .load_cubin(SP14_Q_DISAGREEMENT_CUBIN.to_vec()) + .map_err(|e| MLError::ModelError(format!("sp14 q_dis cubin: {e}")))?; + let sp14_q_disagreement_update_kernel = sp14_q_dis_module + .load_function("q_disagreement_update_kernel") + .map_err(|e| MLError::ModelError(format!("q_disagreement_update_kernel: {e}")))?; + + let sp14_alpha_module = stream.context() + .load_cubin(SP14_ALPHA_GRAD_CUBIN.to_vec()) + .map_err(|e| MLError::ModelError(format!("sp14 alpha cubin: {e}")))?; + let sp14_alpha_grad_compute_kernel = sp14_alpha_module + .load_function("alpha_grad_compute_kernel") + .map_err(|e| MLError::ModelError(format!("alpha_grad_compute_kernel: {e}")))?; + + let sp14_grad_hack_module = stream.context() + .load_cubin(SP14_GRAD_HACK_CUBIN.to_vec()) + .map_err(|e| MLError::ModelError(format!("sp14 grad_hack cubin: {e}")))?; + let sp14_gradient_hack_detect_kernel = sp14_grad_hack_module + .load_function("gradient_hack_detect_kernel") + .map_err(|e| MLError::ModelError(format!("gradient_hack_detect_kernel: {e}")))?; + + let sp14_dir_concat_module = stream.context() + .load_cubin(SP14_DIR_CONCAT_CUBIN.to_vec()) + .map_err(|e| MLError::ModelError(format!("sp14 dir_concat cubin: {e}")))?; + let sp14_dir_concat_qaux_kernel = sp14_dir_concat_module + .load_function("dir_concat_qaux_kernel") + .map_err(|e| MLError::ModelError(format!("dir_concat_qaux_kernel: {e}")))?; + + // SP14 B.7: scratch buffer for direction Q-head input concat. + // Shape [B, SH2 + 1]: one extra column per sample for the appended + // alpha_grad scalar (ISV[387]). `b` = config.batch_size (declared at + // the top of `new`); `config.shared_h2` is the GRN trunk output dim. + let sp14_dir_qaux_concat_scratch = stream + .alloc_zeros::(b * (config.shared_h2 + 1)) + .map_err(|e| MLError::ModelError(format!("sp14 dir_qaux scratch: {e}")))?; + // SP11 Fix 39 B1b fix-up (2026-05-04): allocate trainer-side // popart-component-per-sample mapped-pinned placeholder buffer. // The canonical per-bar buffer is owned by the experience @@ -20382,6 +20485,14 @@ impl GpuDqnTrainer { // `gpu_experience_collector.rs` per-step action-select site. apply_fixed_alpha_ema_kernel, aux_pred_to_isv_tanh_kernel, + // SP14 EGF kernel handles + scratch (B.3–B.7). Loaded above; + // not launched until B.9/B.11. Held here to satisfy the + // buffer-lifetime and `feedback_no_partial_refactor.md` contract. + sp14_q_disagreement_update_kernel, + sp14_alpha_grad_compute_kernel, + sp14_gradient_hack_detect_kernel, + sp14_dir_concat_qaux_kernel, + sp14_dir_qaux_concat_scratch, // SP11 Fix 39 (Task A2): controller + SimHash novelty kernels // and their backing buffers. Projection matrix was populated // on-device above by `launch_novelty_simhash_proj_init`; hash diff --git a/docs/dqn-wire-up-audit.md b/docs/dqn-wire-up-audit.md index 253552cdd..5c3828678 100644 --- a/docs/dqn-wire-up-audit.md +++ b/docs/dqn-wire-up-audit.md @@ -6483,3 +6483,36 @@ This kernel is the third known-orphan in the B.3..B.6 producer chain. The orphan - **Reverse dependencies**: reads `h_s2` (trunk output) and `aux_nb_softmax_buf` (aux head output). Neither is an ISV slot; both are trainer-level scratch buffers allocated and owned by `GpuDqnTrainer`. This kernel is the fourth and final known-orphan in the B.3..B.6 producer chain. The orphan is acknowledged here per `feedback_wire_everything_up.md` (the rule's "same-commit wire-up" requirement is relaxed for atomic chained-producer-consumer landings as long as every kernel is documented as orphaned and the consumer wire-up commits are in the same chain). + +## SP14 Layer B — Commit B.7: trainer struct fields for EGF kernels + concat scratch (2026-05-05) + +**Why this commit.** B.7 is the trainer-struct infrastructure pass that registers the 4 B-series `CudaFunction` handles and allocates the direction Q-head input concat scratch buffer inside `GpuDqnTrainer`. Before this commit, the 4 kernels (B.3–B.6) were complete GPU objects (cubin built, oracle tests green) but had no Rust holding points in the trainer. After this commit they are loaded from their respective cubins at trainer construction and owned by the struct, ready for the graph-capture wire-up in B.9/B.11. + +### Changes + +| Component | Role | +|-----------|------| +| 4 `static SP14_*_CUBIN: &[u8]` | `include_bytes!` statics for `q_disagreement_update_kernel.cubin`, `alpha_grad_compute_kernel.cubin`, `gradient_hack_detect_kernel.cubin`, `dir_concat_qaux_kernel.cubin` | +| 4 `CudaFunction` struct fields | `sp14_q_disagreement_update_kernel`, `sp14_alpha_grad_compute_kernel`, `sp14_gradient_hack_detect_kernel`, `sp14_dir_concat_qaux_kernel` — loaded in `GpuDqnTrainer::new` from respective cubins | +| 1 `CudaSlice` struct field | `sp14_dir_qaux_concat_scratch [B * (SH2 + 1)]` — device buffer allocated via `stream.alloc_zeros::(b * (config.shared_h2 + 1))` in `GpuDqnTrainer::new`; provides the pre-SGEMM concat output storage for B.6's `dir_concat_qaux_kernel` | + +**Scratch buffer formula**: `b * (config.shared_h2 + 1)` — `b = config.batch_size` (declared at the top of `GpuDqnTrainer::new`); `config.shared_h2` is the GRN trunk output dimension. The `+ 1` appends one column per sample for the `alpha_grad` scalar read from ISV[387]. + +**Pattern**: mirrors the SP13 `aux_pred_to_isv_tanh_kernel` loading pattern exactly (load_cubin → load_function per cubin, then struct field + Self{} initializer entry). No launcher functions are added; those land in B.8/B.9/B.11 per the chain plan. + +### File-change summary + +| Change | File | +|--------|------| +| 4 cubin statics + 5 struct fields | `crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs` | + +### Verification + +- `SQLX_OFFLINE=true cargo check -p ml` — 18 warnings (pre-existing baseline, no new warnings) + +### Wire status + +- **Handles loaded**: yes — 4 `CudaFunction` fields + 1 `CudaSlice` scratch in struct. +- **Launcher**: NOT YET WIRED. Launchers and graph-capture integration land in B.8/B.9/B.11. +- **Consumers**: `sp14_dir_qaux_concat_scratch` will be consumed by the direction Q-head's first FC SGEMM once B.8 bumps the input-dim constant and B.9/B.11 replace the `h_s2` pointer with the wider scratch pointer. +- **Reverse dependencies**: no new ISV reads or writes in this commit — purely buffer/handle infrastructure.