diff --git a/crates/ml-alpha/src/trainer/perception.rs b/crates/ml-alpha/src/trainer/perception.rs index 49c4e13fb..db791ca75 100644 --- a/crates/ml-alpha/src/trainer/perception.rs +++ b/crates/ml-alpha/src/trainer/perception.rs @@ -1975,6 +1975,38 @@ impl PerceptionTrainer { unsafe { launch.launch(bce_cfg).context("bce launch")?; } } + // ── 5c. CRT.train output-smoothness regularizer ── + // λ[h]-weighted Σ (p[k]-p[k-1])² across adjacent positions + // in each sequence. Accumulates the analytic gradient INTO + // `self.grad_probs_per_k_d` (which BCE just overwrote); + // the heads_bwd K-loop below consumes the sum. + // + // Per `pearl_no_host_branches_in_captured_graph`: kernel + // launch only — all scalars (k_i, b_i) and buffer pointers + // are fixed across captures (seq_len and batch_size are + // config-immutable). λ is uploaded once at construction; + // when `cfg.smoothness_base_lambda == 0.0` the kernel + // produces zero loss + zero grad, behaviorally identical + // to the pre-intervention path. + let k_seq_i = k_seq as i32; + let b_sz_i = b_sz as i32; + let smooth_cfg = LaunchConfig { + grid_dim: (1, 1, 1), + block_dim: (256, 1, 1), + shared_mem_bytes: 0, + }; + { + let mut launch = self.stream.launch_builder(&self.smoothness_fn); + launch + .arg(&self.probs_per_k_d) + .arg(&self.smoothness_lambda_d) + .arg(&k_seq_i).arg(&b_sz_i) + .arg(&mut self.smoothness_loss_d) + .arg(&mut self.smoothness_loss_per_horizon_d) + .arg(&mut self.grad_probs_per_k_d); + unsafe { launch.launch(smooth_cfg).context("output_smoothness launch")?; } + } + // ── 5a. (v2 reserved) — per-horizon Q_h backward removed at V1. // v2 backward path will live here in commit V10. @@ -2523,6 +2555,27 @@ impl PerceptionTrainer { self.loss_host_d.dev_ptr, src_ptr, 4, self.stream.cu_stream(), ).context("loss dtod → mapped-pinned")?; } + // CRT.train: shadow smoothness telemetry buffers to mapped-pinned + // host slots for zero-sync operator reads. Same stream, same + // capture region — adjacent to the loss_d shadow above. + unsafe { + let (smooth_loss_src_ptr, _g) = self.smoothness_loss_d.device_ptr(&self.stream); + cudarc::driver::result::memcpy_dtod_async( + self.smoothness_loss_host_d.dev_ptr, + smooth_loss_src_ptr, + std::mem::size_of::(), + self.stream.cu_stream(), + ).context("smoothness_loss_host_d shadow")?; + } + unsafe { + let (smooth_loss_ph_src_ptr, _g) = self.smoothness_loss_per_horizon_d.device_ptr(&self.stream); + cudarc::driver::result::memcpy_dtod_async( + self.smoothness_loss_per_horizon_host_d.dev_ptr, + smooth_loss_ph_src_ptr, + N_HORIZONS * std::mem::size_of::(), + self.stream.cu_stream(), + ).context("smoothness_loss_per_horizon_host_d shadow")?; + } Ok(()) }