fix(build): auto-detect GPU arch — sm_90 on H100, sm_89 on L40S, sm_86 local

Replace fatbin with single-arch cubin auto-detected via nvidia-smi.
Each node compiles for its own GPU — faster builds, guaranteed compat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-26 13:51:10 +02:00
parent 1668eb4283
commit b05dc1b40d
19 changed files with 159 additions and 134 deletions

View File

@@ -157,6 +157,7 @@ fn main() {
}
println!("cargo:rerun-if-env-changed=CUDA_HOME");
println!("cargo:rerun-if-env-changed=CUDA_COMPUTE_CAP");
let nvcc = match find_nvcc() {
Some(p) => p,
@@ -168,6 +169,10 @@ fn main() {
let out = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR not set by cargo"));
// Detect GPU arch: CUDA_COMPUTE_CAP env > nvidia-smi query > default 86
let arch = detect_arch(&nvcc);
eprintln!(" ml-alpha: compiling kernels for sm_{arch}");
for k in KERNELS {
let src = PathBuf::from(format!("cuda/{k}.cu"));
if !src.exists() {
@@ -175,24 +180,44 @@ fn main() {
continue;
}
println!("cargo:rerun-if-changed={}", src.display());
let fatbin = out.join(format!("{k}.fatbin"));
compile(&nvcc, &src, &fatbin);
let cubin = out.join(format!("{k}.cubin"));
compile(&nvcc, &src, &cubin, &arch);
}
}
fn compile(nvcc: &Path, src: &Path, fatbin: &Path) {
fn detect_arch(_nvcc: &Path) -> String {
// 1. Explicit env override
if let Ok(cap) = std::env::var("CUDA_COMPUTE_CAP") {
return cap;
}
// 2. Query the GPU on this machine
if let Ok(output) = Command::new("nvidia-smi")
.args(["--query-gpu=compute_cap", "--format=csv,noheader"])
.output()
{
if output.status.success() {
let s = String::from_utf8_lossy(&output.stdout);
let cap = s.trim().replace('.', "");
if !cap.is_empty() {
return cap;
}
}
}
// 3. Default to sm_86 (RTX 3050 Ti local dev)
"86".to_string()
}
fn compile(nvcc: &Path, src: &Path, cubin: &Path, arch: &str) {
let status = Command::new(nvcc)
.args([
"-fatbin",
"-gencode", "arch=compute_86,code=sm_86",
"-gencode", "arch=compute_89,code=sm_89",
"-gencode", "arch=compute_90,code=sm_90",
"-cubin",
&format!("-arch=sm_{arch}"),
"-O3",
"--use_fast_math",
"--ftz=true",
"--fmad=true",
"-o",
fatbin.to_str().unwrap(),
cubin.to_str().unwrap(),
src.to_str().unwrap(),
])
.status()
@@ -205,9 +230,9 @@ fn compile(nvcc: &Path, src: &Path, fatbin: &Path) {
);
}
eprintln!(
" ml-alpha: compiled {} -> {} (sm_86+sm_89+sm_90)",
" ml-alpha: compiled {} -> {} (sm_{arch})",
src.display(),
fatbin.display()
cubin.display()
);
}

View File

@@ -74,9 +74,9 @@ use crate::cfc::AUX_HIDDEN;
use crate::pinned_mem::MappedF32Buffer;
const AUX_HEADS_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/aux_heads.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/aux_heads.cubin"));
const AUX_LOSS_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/aux_loss.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/aux_loss.cubin"));
/// Number of aux-horizon outputs per (direction, head). Matches the
/// main BCE head's [`crate::heads::N_HORIZONS`] (= 3) by construction:

View File

@@ -61,7 +61,7 @@ use rand_chacha::ChaCha8Rng;
use crate::pinned_mem::MappedF32Buffer;
const KERNEL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/aux_trunk.fatbin"));
const KERNEL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/aux_trunk.cubin"));
/// Aux trunk hidden dimension. Half the main trunk's HIDDEN_DIM=128.
/// Kept constant (not configurable) so cubin compile-time `#define
@@ -126,7 +126,7 @@ pub struct AuxTrunk {
impl AuxTrunk {
/// Construct a fresh `AuxTrunk` with seeded Xavier + log-uniform τ
/// initialisation. Loads the `aux_trunk.fatbin` and resolves the
/// initialisation. Loads the `aux_trunk.cubin` and resolves the
/// `aux_trunk_fwd` / `aux_trunk_bwd` function handles once.
pub fn new(dev: &MlDevice, cfg: AuxTrunkConfig) -> Result<Self> {
anyhow::ensure!(

View File

@@ -69,7 +69,7 @@ pub const BUCKET_DIM_K: [u32; N_HORIZONS] = [43, 43, 42];
pub const BUCKET_CHANNEL_OFFSET: [u32; N_HORIZONS + 1] = [0, 43, 86, 128];
const BUCKET_TRANSITION_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/bucket_transition_kernels.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/bucket_transition_kernels.cubin"));
/// Controller A state — warmup-completion detector. Per spec §3.1, tracks:
/// * first-observation `noise_floor` (host-local f32, set at t=1 from the

View File

@@ -15,7 +15,7 @@ use ml_core::device::MlDevice;
use crate::pinned_mem::MappedF32Buffer;
const KERNEL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/snap_feature_assemble.fatbin"));
const KERNEL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/snap_feature_assemble.cubin"));
pub const ES_TICK_SIZE: f32 = 0.25;
pub const FEATURE_DIM: usize = 40;

View File

@@ -8,7 +8,7 @@ use ml_core::device::MlDevice;
use crate::pinned_mem::MappedF32Buffer;
const KERNEL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step.fatbin"));
const KERNEL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step.cubin"));
#[derive(Clone, Debug)]
pub struct CfcWeights {

View File

@@ -72,20 +72,20 @@ struct Checkpoint {
heads_w_skip: Vec<f32>, heads_b_skip: Vec<f32>,
}
const SNAP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/snap_feature_assemble.fatbin"));
const STEP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step.fatbin"));
const HEADS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/multi_horizon_heads.fatbin"));
const SNAP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/snap_feature_assemble.cubin"));
const STEP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step.cubin"));
const HEADS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/multi_horizon_heads.cubin"));
// X10: forward kernel cubins — loaded into the trunk so it owns
// every kernel handle the forward chain needs. Trainer will read
// these handles via `self.trunk.<fn>` in a subsequent commit (X10b).
const LAYER_NORM_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/layer_norm.fatbin"));
const VARIABLE_SELECTION_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/variable_selection.fatbin"));
const ATTENTION_POOL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/attention_pool.fatbin"));
const LAYER_NORM_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/layer_norm.cubin"));
const VARIABLE_SELECTION_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/variable_selection.cubin"));
const ATTENTION_POOL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/attention_pool.cubin"));
// Per-horizon CfC Phase 2 dispatch (spec §5.4 points 12). Modules
// stay alive on the trunk so the cached `CudaFunction` handles below
// remain valid through the trainer's lifetime.
const CFC_STEP_PER_BRANCH_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step_per_branch.fatbin"));
const HEADS_BLOCK_DIAGONAL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/heads_block_diagonal_fwd.fatbin"));
const CFC_STEP_PER_BRANCH_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step_per_branch.cubin"));
const HEADS_BLOCK_DIAGONAL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/heads_block_diagonal_fwd.cubin"));
#[derive(Clone, Debug)]
pub struct CfcConfig {

View File

@@ -16,8 +16,8 @@ use ml_core::device::MlDevice;
use crate::pinned_mem::MappedF32Buffer;
const HEADS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/multi_horizon_heads.fatbin"));
const PROJ_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/projection.fatbin"));
const HEADS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/multi_horizon_heads.cubin"));
const PROJ_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/projection.cubin"));
pub const N_HORIZONS: usize = 3;
pub const HORIZONS: [usize; N_HORIZONS] = [10, 100, 1000];

View File

@@ -490,7 +490,7 @@ impl Mamba2Block {
// ── Load the precompiled cubin and resolve kernel symbols. ────
// The cubin is produced by `build.rs` at compile time; no nvrtc.
static CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/mamba2_alpha_kernel.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/mamba2_alpha_kernel.cubin"));
let module = stream
.context()
.load_cubin(CUBIN.to_vec())
@@ -1962,11 +1962,11 @@ impl Mamba2AdamW {
let s_b_out = alloc(block.w_out.bias.len(), "w_out.bias")?;
// Load grad-norm kernel handles (compiled by build.rs into a
// dedicated grad_norm.fatbin) + the devscale AdamW variant
// (compiled into mamba2_alpha_kernel.fatbin alongside the
// dedicated grad_norm.cubin) + the devscale AdamW variant
// (compiled into mamba2_alpha_kernel.cubin alongside the
// host-scalar version).
static GRAD_NORM_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/grad_norm.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/grad_norm.cubin"));
let grad_norm_module = stream.context()
.load_cubin(GRAD_NORM_CUBIN.to_vec())
.map_err(|e| anyhow!("Mamba2AdamW: grad_norm cubin load: {e}"))?;

View File

@@ -55,15 +55,15 @@ use crate::rl::common::{N_ACTIONS, Q_N_ATOMS};
const DQN_HEAD_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/dqn_distributional_q.fatbin"
"/dqn_distributional_q.cubin"
));
const DQN_TARGET_SOFT_UPDATE_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/dqn_target_soft_update.fatbin"
"/dqn_target_soft_update.cubin"
));
const BELLMAN_PROJ_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/bellman_target_projection.fatbin"
"/bellman_target_projection.cubin"
));
/// Construction config for [`DqnHead`].

View File

@@ -57,13 +57,13 @@ use crate::heads::HIDDEN_DIM;
use crate::rl::common::{FRD_HIDDEN_DIM, FRD_N_ATOMS, FRD_N_HORIZONS};
use crate::trainer::integrated::write_slice_f32_d_pub;
const FRD_FWD_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_fwd.fatbin"));
const FRD_FWD_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_fwd.cubin"));
const FRD_SOFTMAX_CE_GRAD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_softmax_ce_grad.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_softmax_ce_grad.cubin"));
const FRD_LAYER2_BWD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_layer2_bwd.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_layer2_bwd.cubin"));
const FRD_LAYER1_BWD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_layer1_bwd.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_layer1_bwd.cubin"));
/// Per-batch output width: `FRD_N_HORIZONS × FRD_N_ATOMS`. Each batch
/// row contains 3 contiguous horizon blocks of 21 atom logits.

View File

@@ -80,15 +80,15 @@ const DEFAULT_N_TAU: usize = 32;
const IQN_FWD_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/rl_iqn_forward.fatbin"
"/rl_iqn_forward.cubin"
));
const IQN_LOSS_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/rl_iqn_loss.fatbin"
"/rl_iqn_loss.cubin"
));
const IQN_BWD_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/rl_iqn_backward.fatbin"
"/rl_iqn_backward.cubin"
));
/// Construction config for [`IqnHead`].

View File

@@ -53,16 +53,16 @@ use crate::pinned_mem::MappedF32Buffer;
const SAMPLE_NOISE_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/rl_sample_noise.fatbin"
"/rl_sample_noise.cubin"
));
const NOISY_FWD_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/rl_noisy_linear_forward.fatbin"
"/rl_noisy_linear_forward.cubin"
));
const NOISY_BWD_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/rl_noisy_linear_backward.fatbin"
"/rl_noisy_linear_backward.cubin"
));
/// Default σ_init when the ISV slot is sentinel-zero or not yet wired.

View File

@@ -57,13 +57,13 @@ impl OutcomeHead {
// Load cubins and resolve kernel symbols.
static CUBIN_FWD: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_fwd.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_fwd.cubin"));
static CUBIN_CE: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_ce.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_ce.cubin"));
static CUBIN_LABEL: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_label.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_label.cubin"));
static CUBIN_BWD: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_bwd.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_outcome_bwd.cubin"));
let module_fwd = stream
.context()

View File

@@ -48,10 +48,10 @@ use crate::rl::common::N_ACTIONS;
const PPO_SURR_CUBIN: &[u8] = include_bytes!(concat!(
env!("OUT_DIR"),
"/ppo_clipped_surrogate.fatbin"
"/ppo_clipped_surrogate.cubin"
));
const V_HEAD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/v_head_fwd_bwd.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/v_head_fwd_bwd.cubin"));
/// Construction config for [`PolicyHead`] and [`ValueHead`].
#[derive(Clone, Debug)]

View File

@@ -111,11 +111,11 @@ use crate::trainer::raw_launch::{
};
const GRAD_H_ACCUMULATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/grad_h_accumulate.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/grad_h_accumulate.cubin"));
const REDUCE_AXIS0_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/reduce_axis0.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/reduce_axis0.cubin"));
const RL_LR_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_lr_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_lr_controller.cubin"));
// Phase R1: cubin includes for the 7 RL adaptive controllers
// (γ / τ / ε / entropy_coef / n_rollout_steps / per_α / reward_scale).
@@ -125,52 +125,52 @@ const RL_LR_CONTROLLER_CUBIN: &[u8] =
// every consumer kernel sees the canonical default in its ISV slot
// before any in-loop access.
const RL_GAMMA_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_gamma_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_gamma_controller.cubin"));
const RL_TARGET_TAU_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_target_tau_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_target_tau_controller.cubin"));
const RL_PPO_CLIP_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_ppo_clip_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_ppo_clip_controller.cubin"));
const RL_ENTROPY_COEF_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_entropy_coef_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_entropy_coef_controller.cubin"));
const RL_ROLLOUT_STEPS_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_rollout_steps_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_rollout_steps_controller.cubin"));
const RL_PER_ALPHA_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_alpha_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_alpha_controller.cubin"));
const RL_REWARD_SCALE_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_reward_scale_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_reward_scale_controller.cubin"));
// Phase R3: GPU-resident EMA + advantage/return kernels. Generic over
// the ISV slot they update (one kernel serves multiple controller-input
// EMAs). Replace the host-side EMA + advantage loops the flawed Phase F
// shipped in step_with_lobsim (which violated `feedback_cpu_is_read_only`).
const EMA_UPDATE_ON_DONE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/ema_update_on_done.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/ema_update_on_done.cubin"));
const EMA_UPDATE_PER_STEP_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/ema_update_per_step.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/ema_update_per_step.cubin"));
const COMPUTE_ADVANTAGE_RETURN_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/compute_advantage_return.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/compute_advantage_return.cubin"));
// Phase R4: GPU-resident action sampling kernels. Replace the host
// Thompson + host argmax + host log-softmax loops the flawed Phase F
// shipped in step_with_lobsim (which violated `feedback_cpu_is_read_only`).
const RL_ACTION_KERNEL_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_action_kernel.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_action_kernel.cubin"));
const ARGMAX_EXPECTED_Q_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/argmax_expected_q.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/argmax_expected_q.cubin"));
const LOG_PI_AT_ACTION_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/log_pi_at_action.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/log_pi_at_action.cubin"));
// Phase R6: GPU-pure env interaction kernels. apply_reward_scale multiplies
// rewards by ISV[406]; actions_to_market_targets translates the 9-action
// grid to LobSim's market_targets[B*2] format on device.
const APPLY_REWARD_SCALE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/apply_reward_scale.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/apply_reward_scale.cubin"));
// Adaptive reward-clamp controller — audit fix 2026-05-24. Consumes the
// per-step positive-tail max written by apply_reward_scale into slot
// 478 and emits adaptive WIN/LOSS bounds into slots 452/453. See the
// kernel header for the design rationale (rmgm5 clamp-firing audit).
const RL_REWARD_CLAMP_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_reward_clamp_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_reward_clamp_controller.cubin"));
// C51 atom-support updater — audit 2026-05-24 followup. Refreshes
// `atom_supports_d` from the ISV V_MIN/V_MAX slots that the reward
// clamp controller writes (with ratchet semantics). Launched right
@@ -178,7 +178,7 @@ const RL_REWARD_CLAMP_CONTROLLER_CUBIN: &[u8] =
// just-updated V_MIN/V_MAX before any C51 forward / Bellman target
// projection in the next step. 21-thread one-block kernel.
const RL_ATOM_SUPPORT_UPDATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_atom_support_update.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_atom_support_update.cubin"));
// Q→π distillation gradient — audit 2026-05-24 vj5f6 followup. ADDS
// λ × (π_new - π_target) to pi_grad_logits after the PPO surrogate
// backward, where π_target = softmax(E_Q[s,*] / τ). Couples Q's
@@ -188,144 +188,144 @@ const RL_ATOM_SUPPORT_UPDATE_CUBIN: &[u8] =
// E_C51 and E_IQN into a single ensemble Q vector via ISV α blend.
// Feeds Q→π agreement diag and future ensemble-level selection.
const RL_ENSEMBLE_ACTION_VALUE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_ensemble_action_value.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_ensemble_action_value.cubin"));
const RL_Q_PI_DISTILL_GRAD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_pi_distill_grad.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_pi_distill_grad.cubin"));
// λ_distill adaptive controller (rljzl followup 2026-05-24).
// Drives λ via Schulman bounded step on KL_EMA toward target.
const RL_Q_DISTILL_LAMBDA_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_distill_lambda_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_distill_lambda_controller.cubin"));
// SP20 P1+P5 — audit-wiring catch on TrailTighten/Loosen + foundational
// per-unit trade state machine. Three kernels work together: state
// machine on position transitions (open/close/reverse), trail mutation
// from a7/a8 actions, trail breach check that overrides action to flat.
const RL_UNIT_STATE_UPDATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_unit_state_update.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_unit_state_update.cubin"));
const RL_TRAIL_MUTATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_trail_mutate.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_trail_mutate.cubin"));
const RL_POSITION_HEAT_CHECK_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_position_heat_check.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_position_heat_check.cubin"));
const RL_CONFIDENCE_GATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_confidence_gate.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_confidence_gate.cubin"));
const RL_FRD_GATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_gate.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_frd_gate.cubin"));
const RL_GATE_THRESHOLD_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_gate_threshold_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_gate_threshold_controller.cubin"));
// Fused kernel: 10 RL ISV controllers in one launch — saves 9 kernel
// launch overheads (~40-80μs/step). Replaces individual launches of
// gamma, tau, ppo_clip, entropy_coef, rollout_steps, per_alpha,
// reward_scale, ppo_ratio_clamp, gate_threshold, q_distill_lambda.
const RL_FUSED_CONTROLLERS_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_fused_controllers.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_fused_controllers.cubin"));
const RL_ASYMMETRIC_TRAIL_DECAY_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_asymmetric_trail_decay.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_asymmetric_trail_decay.cubin"));
const RL_SESSION_RISK_CHECK_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_session_risk_check.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_session_risk_check.cubin"));
const RL_MIN_HOLD_CHECK_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_min_hold_check.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_min_hold_check.cubin"));
const RL_TRADE_CONTEXT_UPDATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_trade_context_update.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_trade_context_update.cubin"));
const RL_MULTIRES_FEATURES_UPDATE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_multires_features_update.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_multires_features_update.cubin"));
const RL_TRAIL_STOP_CHECK_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_trail_stop_check.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_trail_stop_check.cubin"));
const ACTIONS_TO_MARKET_TARGETS_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/actions_to_market_targets.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/actions_to_market_targets.cubin"));
// Device-resident step counter bump — ISV[548] += 1.0 each step.
// Prerequisite for CUDA Graph capture: removes scalar current_step
// from all downstream kernel argument lists.
const RL_INCREMENT_STEP_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_increment_step.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_increment_step.cubin"));
const RL_SAMPLE_TAU_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_sample_tau.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_sample_tau.cubin"));
const RL_WRITE_U64_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_write_u64.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_write_u64.cubin"));
const RL_FUSED_REWARD_PIPELINE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_fused_reward_pipeline.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_fused_reward_pipeline.cubin"));
// P1.1 PopArt reward normalization (replaces apply_reward_scale).
const RL_POPART_NORMALIZE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_popart_normalize.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_popart_normalize.cubin"));
const RL_POPART_V_CORRECT_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_popart_v_correct.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_popart_v_correct.cubin"));
// P1.2 Spectral normalization + decoupling.
const RL_SPECTRAL_NORM_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_spectral_norm.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_spectral_norm.cubin"));
const RL_SPECTRAL_DECOUPLE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_spectral_decouple.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_spectral_decouple.cubin"));
// P2.1 Q-bias correction.
const RL_Q_BIAS_CORRECTION_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_bias_correction.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_bias_correction.cubin"));
// P2.2 Per-branch LR scaling.
const RL_PER_BRANCH_LR_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_branch_lr.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_branch_lr.cubin"));
// Element-wise in-place add: dst[i] += src[i]. Used by noisy
// exploration to fold NoisyLinear output into ensemble_q_d before
// action selection. Loaded from the same cubin that perception.rs
// uses for aux→encoder gradient accumulation.
const AUX_VEC_ADD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/aux_vec_add.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/aux_vec_add.cubin"));
// Phase R7a: element-wise abs-copy kernel for the |reward| signal
// feeding ema_update_on_done's MEAN_ABS_PNL_EMA slot.
const ABS_COPY_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/abs_copy.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/abs_copy.cubin"));
// Reduction kernels that derive scalar inputs for the controller-input
// EMAs (variance ratio + kurtosis). entropy_observed reuses
// ema_update_per_step's built-in mean reduce directly on entropy_d.
const RL_VAR_OVER_ABS_MEAN_STREAMING_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_var_over_abs_mean_streaming.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_var_over_abs_mean_streaming.cubin"));
const RL_KURTOSIS_STREAMING_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_kurtosis_streaming.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_kurtosis_streaming.cubin"));
// Derivation kernels for the EMA inputs that need a new signal
// computation (not just a reduction over an existing buffer):
// per-batch KL approximation, Q weight-divergence L2 norm, and the
// trade-duration step counter.
const RL_KL_APPROX_B_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_kl_approx_b.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_kl_approx_b.cubin"));
const RL_L2_DIFF_NORM_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_l2_diff_norm.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_l2_diff_norm.cubin"));
const RL_L2_NORM_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_l2_norm.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_l2_norm.cubin"));
// audit — PPO ratio clamp controller (emits ISV[440]) + per-step
// log-ratio max diagnostic kernel (writes ISV[441]).
const RL_PPO_RATIO_CLAMP_CONTROLLER_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_ppo_ratio_clamp_controller.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_ppo_ratio_clamp_controller.cubin"));
const PPO_LOG_RATIO_ABS_MAX_B_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/ppo_log_ratio_abs_max_b.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/ppo_log_ratio_abs_max_b.cubin"));
const RL_STREAMING_CLAMP_INIT_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_streaming_clamp_init.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_streaming_clamp_init.cubin"));
const RL_ISV_WRITE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_isv_write.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_isv_write.cubin"));
const RL_Q_PI_AGREE_B_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_pi_agree_b.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_q_pi_agree_b.cubin"));
const RL_PI_ACTION_KERNEL_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_pi_action_kernel.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_pi_action_kernel.cubin"));
// GPU-resident PER kernels (T2/T3 → T4 wiring).
const RL_PER_PUSH_RING_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_push_ring.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_push_ring.cubin"));
const RL_PER_PUSH_FLUSH_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_push_flush.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_push_flush.cubin"));
const RL_PER_SAMPLE_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_sample.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_sample.cubin"));
const RL_PER_UPDATE_PRIORITY_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_update_priority.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_update_priority.cubin"));
const RL_PER_TREE_REBUILD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_tree_rebuild.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_per_tree_rebuild.cubin"));
// Bidirectional HER kernels — track (mid-price ring + peak), backward
// inject (synthetic push on done if peak >> actual), forward (evaluate
// closed trades after lookahead window).
const RL_HINDSIGHT_TRACK_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_hindsight_track.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_hindsight_track.cubin"));
const RL_HINDSIGHT_INJECT_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_hindsight_inject.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_hindsight_inject.cubin"));
const RL_HINDSIGHT_FORWARD_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_hindsight_forward.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_hindsight_forward.cubin"));
/// Per-head LR controller Wiener-α target. The controller kernel floors
/// this at 0.4 per `pearl_wiener_alpha_floor_for_nonstationary` so the

View File

@@ -12,7 +12,7 @@ use ml_core::device::MlDevice;
use crate::pinned_mem::MappedF32Buffer;
const CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/bce_loss_multi_horizon.fatbin"));
const CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/bce_loss_multi_horizon.cubin"));
pub struct BceInput {
pub probs: Vec<f32>,

View File

@@ -16,7 +16,7 @@ use cudarc::driver::{CudaFunction, CudaModule, CudaSlice, CudaStream, DevicePtr,
use ml_core::device::MlDevice;
use crate::pinned_mem::MappedI32Buffer;
const CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/adamw_step.fatbin"));
const CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/adamw_step.cubin"));
pub struct AdamW {
stream: Arc<CudaStream>,

View File

@@ -63,20 +63,20 @@ use crate::mamba2_block::{
use crate::pinned_mem::{MappedF32Buffer, MappedI32Buffer, MappedI64Buffer};
use crate::trainer::optim::AdamW;
const SNAP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/snap_feature_assemble.fatbin"));
const STEP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step.fatbin"));
const HEADS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/multi_horizon_heads.fatbin"));
const BCE_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/bce_loss_multi_horizon.fatbin"));
const HORIZON_LAMBDA_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/horizon_lambda.fatbin"));
const LAYER_NORM_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/layer_norm.fatbin"));
const VARIABLE_SELECTION_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/variable_selection.fatbin"));
const ATTENTION_POOL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/attention_pool.fatbin"));
const REDUCE_AXIS0_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/reduce_axis0.fatbin"));
const SMOOTHNESS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/output_smoothness.fatbin"));
const SNAP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/snap_feature_assemble.cubin"));
const STEP_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/cfc_step.cubin"));
const HEADS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/multi_horizon_heads.cubin"));
const BCE_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/bce_loss_multi_horizon.cubin"));
const HORIZON_LAMBDA_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/horizon_lambda.cubin"));
const LAYER_NORM_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/layer_norm.cubin"));
const VARIABLE_SELECTION_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/variable_selection.cubin"));
const ATTENTION_POOL_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/attention_pool.cubin"));
const REDUCE_AXIS0_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/reduce_axis0.cubin"));
const SMOOTHNESS_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/output_smoothness.cubin"));
const ENCODER_CONTEXT_BROADCAST_CUBIN: &[u8] =
include_bytes!(concat!(env!("OUT_DIR"), "/rl_encoder_context_broadcast.fatbin"));
include_bytes!(concat!(env!("OUT_DIR"), "/rl_encoder_context_broadcast.cubin"));
const SMOOTHNESS_CONTROLLER_CUBIN: &[u8] = include_bytes!(
concat!(env!("OUT_DIR"), "/smoothness_lambda_controller.fatbin")
concat!(env!("OUT_DIR"), "/smoothness_lambda_controller.cubin")
);
/// Phase 1→2 transition kernels (Task 9 scope: tau_change_frobenius +
/// slack_factor_apply; the full transition orchestration in
@@ -84,12 +84,12 @@ const SMOOTHNESS_CONTROLLER_CUBIN: &[u8] = include_bytes!(
/// independently — this constant gives the trainer cached function
/// handles for the per-step Controller A signal kernels).
const BUCKET_TRANSITION_CUBIN: &[u8] = include_bytes!(
concat!(env!("OUT_DIR"), "/bucket_transition_kernels.fatbin")
concat!(env!("OUT_DIR"), "/bucket_transition_kernels.cubin")
);
/// SDD-3 Layer B5: element-wise `dst += src` for aux→encoder gradient
/// accumulation when the asymmetric stop-grad is LIFTED.
const AUX_VEC_ADD_CUBIN: &[u8] = include_bytes!(
concat!(env!("OUT_DIR"), "/aux_vec_add.fatbin")
concat!(env!("OUT_DIR"), "/aux_vec_add.cubin")
);
/// CB5 (SDD-3): clamp range for the BCE positive-class weight
@@ -1860,7 +1860,7 @@ impl PerceptionTrainer {
std::sync::Arc::new(buf)
};
let (gpu_log_tick_fn, gpu_log_module) = {
const GPU_LOG_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/gpu_log_ring.fatbin"));
const GPU_LOG_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/gpu_log_ring.cubin"));
let module = ctx.load_cubin(GPU_LOG_CUBIN.to_vec())
.context("load gpu_log_ring cubin")?;
let f = module.load_function("gpu_log_tick").context("load gpu_log_tick")?;