diff --git a/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs b/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs index 4c7681d28..fa6308ef6 100644 --- a/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs +++ b/crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs @@ -634,7 +634,7 @@ const ISV_NETWORK_DIM: usize = 23; /// (shifted 112→116 in Plan 4 Task 6 Commit A). /// Written by the constructor; checked at checkpoint load. Fail-fast only — no migration /// path exists. See spec §4.A.2 and `LAYOUT_FINGERPRINT_CURRENT` for structural-hash rationale. -const ISV_TOTAL_DIM: usize = 173; // SP4: 131 + 42 magnitude/EMA-rate/regularization slots (40 Layer A/B + 2 Layer C #260) +const ISV_TOTAL_DIM: usize = 286; // SP5: 173 + 113 (110 SP5 slots @ 174..278/280..286, with 2-slot gap before cross-fold-persistent Kelly block) /// Legacy alias preserved for call sites that haven't been audited for the /// network-vs-total split. New code should pick `ISV_NETWORK_DIM` (for weight /// tensor sizing) or `ISV_TOTAL_DIM` (for the broadcast bus buffer). @@ -1262,7 +1262,14 @@ const fn layout_fingerprint_seed() -> &'static [u8] { WD_RATE_GROUP_4=164;WD_RATE_GROUP_5=165;WD_RATE_GROUP_6=166;WD_RATE_GROUP_7=167;\ GRAD_CLIP_BOUND=168;H_S2_BOUND=169;L1_LAMBDA_TRUNK=170;\ BW_D_H_S2_BOUND=171;Q_DIR_GRAD_BOUND=172;\ - ISV_TOTAL_DIM=173;\ + SP5_BASE=174;ATOM_V_CENTER_BASE=174;ATOM_V_HALF_BASE=178;ATOM_HEADROOM_BASE=182;\ + ATOM_CLIP_RATE_BASE=186;BUDGET_C51_BASE=190;BUDGET_IQN_BASE=194;BUDGET_CQL_BASE=198;\ + BUDGET_ENS_BASE=202;FLATNESS_BASE=206;NOISY_SIGMA_BASE=210;SIGMA_FRACTION_BASE=214;\ + BRANCH_ENTROPY_BASE=218;Q_VAR_PER_BRANCH_BASE=222;ADAM_BETA1_BASE=226;ADAM_BETA2_BASE=234;\ + ADAM_EPS_BASE=242;IQN_TAU_BASE=250;TRAIL_DIST_PER_DIR_BASE=270;ATOM_NUM_ATOMS_BASE=274;\ + KELLY_F_SMOOTH=280;CONVICTION_SMOOTH=281;TRADE_VAR_SMOOTH=282;\ + KELLY_SAMPLE_COUNT=283;WIN_RATE_SMOOTH=284;LOSS_RATE_SMOOTH=285;\ + ISV_TOTAL_DIM=286;\ PARAM_W_A_H_S1=0;PARAM_B_A_H_S1=1;PARAM_W_B_H_S1=2;PARAM_B_B_H_S1=3;\ PARAM_W_RESIDUAL_H_S1=4;PARAM_GAMMA_H_S1=5;PARAM_BETA_H_S1=6;\ PARAM_W_A_H_S2=7;PARAM_B_A_H_S2=8;PARAM_W_B_H_S2=9;PARAM_B_B_H_S2=10;\ diff --git a/crates/ml/src/cuda_pipeline/mod.rs b/crates/ml/src/cuda_pipeline/mod.rs index 99d4ec153..afdfffeb4 100644 --- a/crates/ml/src/cuda_pipeline/mod.rs +++ b/crates/ml/src/cuda_pipeline/mod.rs @@ -59,6 +59,7 @@ pub mod learning_health; pub mod q_snapshot; pub mod meta_q_network; pub mod sp4_isv_slots; +pub mod sp5_isv_slots; pub use sp4_isv_slots::{ TARGET_Q_BOUND_INDEX, ATOM_POS_BOUND_BASE, WEIGHT_BOUND_BASE, ADAM_M_BOUND_BASE, ADAM_V_BOUND_BASE, WD_RATE_BASE, diff --git a/crates/ml/src/cuda_pipeline/sp5_isv_slots.rs b/crates/ml/src/cuda_pipeline/sp5_isv_slots.rs new file mode 100644 index 000000000..fd916a895 --- /dev/null +++ b/crates/ml/src/cuda_pipeline/sp5_isv_slots.rs @@ -0,0 +1,154 @@ +// crates/ml/src/cuda_pipeline/sp5_isv_slots.rs +// +// SP5 per-branch + per-group adaptation ISV slot allocation. +// +// All adaptive values live on ISV via Pearls A+D smoothing per +// `feedback_isv_for_adaptive_bounds`. Slot ranges: +// +// 174..226 Per-branch (4 branches × 13 quantities for Pearls 1/2/3 + shared signal) +// 226..250 Per-group Adam β1/β2/ε (8 groups × 3 hparams) +// 250..270 Per-branch IQN τ schedule (4 branches × 5 quantiles) +// 270..274 Per-direction trail distance (4 directions) +// 274..278 Per-branch num_atoms (4 branches) +// 280..286 Cross-fold-persistent Kelly slots (NOT in fold-reset registry) +// +// Total: 110 new SP5 ISV slots (52 + 24 + 20 + 4 + 4 + 6). + +pub const SP5_SLOT_BASE: usize = 174; + +// ── Pearl 1: per-branch atom span (16 slots) ────────────────────────── +pub const ATOM_V_CENTER_BASE: usize = 174; // [4] +pub const ATOM_V_HALF_BASE: usize = 178; // [4] +pub const ATOM_HEADROOM_BASE: usize = 182; // [4] +pub const ATOM_CLIP_RATE_BASE: usize = 186; // [4] + +// ── Pearl 2: per-branch loss budget (20 slots) ──────────────────────── +pub const BUDGET_C51_BASE: usize = 190; // [4] +pub const BUDGET_IQN_BASE: usize = 194; // [4] +pub const BUDGET_CQL_BASE: usize = 198; // [4] +pub const BUDGET_ENS_BASE: usize = 202; // [4] +pub const FLATNESS_BASE: usize = 206; // [4] + +// ── Pearl 3: per-branch NoisyNet σ (12 slots) ───────────────────────── +pub const NOISY_SIGMA_BASE: usize = 210; // [4] +pub const SIGMA_FRACTION_BASE: usize = 214; // [4] +pub const BRANCH_ENTROPY_BASE: usize = 218; // [4] + +// ── shared signal: per-branch Q variance (4 slots) ──────────────────── +pub const Q_VAR_PER_BRANCH_BASE: usize = 222; // [4] + +// ── Pearl 4: per-group Adam hyperparams (24 slots) ──────────────────── +pub const ADAM_BETA1_BASE: usize = 226; // [8 param groups] +pub const ADAM_BETA2_BASE: usize = 234; // [8] +pub const ADAM_EPS_BASE: usize = 242; // [8] + +// ── Pearl 5: per-branch IQN τ schedule (20 slots) ───────────────────── +pub const IQN_TAU_BASE: usize = 250; // [4 branches × 5 quantiles] + +// ── Pearl 8: per-direction trail distance (4 slots) ─────────────────── +pub const TRAIL_DIST_PER_DIR_BASE: usize = 270; // [4 dirs: Short, Hold, Long, Flat] + +// ── Pearl 1-ext: per-branch num_atoms (4 slots) ─────────────────────── +pub const ATOM_NUM_ATOMS_BASE: usize = 274; // [4] + +// ── Pearl 6: cross-fold-persistent Kelly slots (6 slots) ────────────── +// NOT in fold-reset registry. Persist trade history across fold boundaries. +// Allocated in separate range 280..286 with intentional 2-slot gap from +// the rest of SP5 to make the carve-out structurally visible. +pub const KELLY_F_SMOOTH_INDEX: usize = 280; +pub const CONVICTION_SMOOTH_INDEX: usize = 281; +pub const TRADE_VAR_SMOOTH_INDEX: usize = 282; +pub const KELLY_SAMPLE_COUNT_INDEX: usize = 283; +pub const WIN_RATE_SMOOTH_INDEX: usize = 284; +pub const LOSS_RATE_SMOOTH_INDEX: usize = 285; + +pub const SP5_SLOT_END: usize = 286; +pub const SP5_PRODUCER_COUNT: usize = 110; +// 52 (per-branch) + 24 (Adam) + 20 (IQN τ) + 4 (trail) + 4 (num_atoms) + 6 (Kelly) = 110 + +// ── Convenience accessors ──────────────────────────────────────────── +#[inline] pub const fn atom_v_center(b: usize) -> usize { ATOM_V_CENTER_BASE + b } +#[inline] pub const fn atom_v_half(b: usize) -> usize { ATOM_V_HALF_BASE + b } +#[inline] pub const fn atom_headroom(b: usize) -> usize { ATOM_HEADROOM_BASE + b } +#[inline] pub const fn atom_clip_rate(b: usize) -> usize { ATOM_CLIP_RATE_BASE + b } +#[inline] pub const fn budget_c51(b: usize) -> usize { BUDGET_C51_BASE + b } +#[inline] pub const fn budget_iqn(b: usize) -> usize { BUDGET_IQN_BASE + b } +#[inline] pub const fn budget_cql(b: usize) -> usize { BUDGET_CQL_BASE + b } +#[inline] pub const fn budget_ens(b: usize) -> usize { BUDGET_ENS_BASE + b } +#[inline] pub const fn flatness(b: usize) -> usize { FLATNESS_BASE + b } +#[inline] pub const fn noisy_sigma(b: usize) -> usize { NOISY_SIGMA_BASE + b } +#[inline] pub const fn sigma_fraction(b: usize) -> usize { SIGMA_FRACTION_BASE + b } +#[inline] pub const fn branch_entropy(b: usize) -> usize { BRANCH_ENTROPY_BASE + b } +#[inline] pub const fn q_var_per_branch(b: usize) -> usize { Q_VAR_PER_BRANCH_BASE + b } +#[inline] pub const fn adam_beta1(g: usize) -> usize { ADAM_BETA1_BASE + g } +#[inline] pub const fn adam_beta2(g: usize) -> usize { ADAM_BETA2_BASE + g } +#[inline] pub const fn adam_eps(g: usize) -> usize { ADAM_EPS_BASE + g } +#[inline] pub const fn iqn_tau(b: usize, q: usize) -> usize { IQN_TAU_BASE + b * 5 + q } +#[inline] pub const fn trail_dist_per_dir(d: usize) -> usize { TRAIL_DIST_PER_DIR_BASE + d } +#[inline] pub const fn atom_num_atoms(b: usize) -> usize { ATOM_NUM_ATOMS_BASE + b } + +/// Layout fingerprint contribution. Appended to the existing +/// LAYOUT_FINGERPRINT_SEED in gpu_dqn_trainer.rs so existing checkpoints +/// fail-fast on layout change. +pub const SP5_LAYOUT_FINGERPRINT_FRAGMENT: &str = + "SP5_BASE=174;ATOM_V_CENTER_BASE=174;ATOM_V_HALF_BASE=178;ATOM_HEADROOM_BASE=182;\ + ATOM_CLIP_RATE_BASE=186;BUDGET_C51_BASE=190;BUDGET_IQN_BASE=194;BUDGET_CQL_BASE=198;\ + BUDGET_ENS_BASE=202;FLATNESS_BASE=206;NOISY_SIGMA_BASE=210;SIGMA_FRACTION_BASE=214;\ + BRANCH_ENTROPY_BASE=218;Q_VAR_PER_BRANCH_BASE=222;ADAM_BETA1_BASE=226;ADAM_BETA2_BASE=234;\ + ADAM_EPS_BASE=242;IQN_TAU_BASE=250;TRAIL_DIST_PER_DIR_BASE=270;ATOM_NUM_ATOMS_BASE=274;\ + KELLY_F_SMOOTH=280;CONVICTION_SMOOTH=281;TRADE_VAR_SMOOTH=282;\ + KELLY_SAMPLE_COUNT=283;WIN_RATE_SMOOTH=284;LOSS_RATE_SMOOTH=285;\ + ISV_TOTAL_DIM=286"; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn slot_layout_no_overlaps_and_total_correct() { + // Per-branch block 174..226 (52 slots, contiguous) + for b in 0..4 { + assert!(atom_v_center(b) >= 174 && atom_v_center(b) < 178); + assert!(atom_v_half(b) >= 178 && atom_v_half(b) < 182); + assert!(atom_headroom(b) >= 182 && atom_headroom(b) < 186); + assert!(atom_clip_rate(b) >= 186 && atom_clip_rate(b) < 190); + assert!(budget_c51(b) >= 190 && budget_c51(b) < 194); + assert!(noisy_sigma(b) >= 210 && noisy_sigma(b) < 214); + assert!(q_var_per_branch(b) >= 222 && q_var_per_branch(b) < 226); + } + // Per-group Adam block 226..250 (24 slots) + for g in 0..8 { + assert!(adam_beta1(g) >= 226 && adam_beta1(g) < 234); + assert!(adam_beta2(g) >= 234 && adam_beta2(g) < 242); + assert!(adam_eps(g) >= 242 && adam_eps(g) < 250); + } + // IQN τ block 250..270 (20 slots) + for b in 0..4 { + for q in 0..5 { + let s = iqn_tau(b, q); + assert!(s >= 250 && s < 270); + } + } + // Trail + num_atoms 270..278 (8 slots) + for d in 0..4 { + assert!(trail_dist_per_dir(d) >= 270 && trail_dist_per_dir(d) < 274); + assert!(atom_num_atoms(d) >= 274 && atom_num_atoms(d) < 278); + } + assert_eq!(SP5_SLOT_END, 286); + // SP5_PRODUCER_COUNT counts only allocated slots: 110 + // 52 per-branch + 24 Adam + 20 IQN τ + 4 trail + 4 num_atoms + 6 Kelly = 110 + assert_eq!(SP5_PRODUCER_COUNT, 110); + } + + #[test] + fn cross_fold_persistent_slots_distinct_from_per_fold_block() { + // Kelly slots 280..286 are SEPARATE from the per-branch/per-group + // block (174..278) by an intentional 2-slot gap (278, 279). + assert!(KELLY_F_SMOOTH_INDEX >= 280); + assert!(LOSS_RATE_SMOOTH_INDEX < 286); + // Verify no overlap with ATOM_NUM_ATOMS or trail + for b in 0..4 { + assert!(atom_num_atoms(b) < KELLY_F_SMOOTH_INDEX); + } + } +} diff --git a/docs/dqn-wire-up-audit.md b/docs/dqn-wire-up-audit.md index ac20def36..ddd5d5279 100644 --- a/docs/dqn-wire-up-audit.md +++ b/docs/dqn-wire-up-audit.md @@ -3147,3 +3147,14 @@ Site #9 (`calibrate_homeostatic_targets` per-step host EMA loop) GPU-ported per Sweep audit grid sites #2 and #9 resolved. Open deferrals: 0. Refs: SP4 Layer C close-out follow-up — sites #2 + #9 of the sweep audit grid resolved. + +## SP5 + +### SP5 Task A0 — ISV slot constants foundation + +- New file: `crates/ml/src/cuda_pipeline/sp5_isv_slots.rs` — 110 SP5 ISV slot index constants + 18 accessor fns + layout fingerprint fragment +- LAYOUT_FINGERPRINT_SEED bumped to chain SP5 fragment (existing checkpoints fail-fast) +- ISV_TOTAL_DIM 173 → 286 +- 2 unit tests pass (slot layout invariants + cross-fold-persistent Kelly carve-out) + +No behavior change. No producer/consumer wired yet. Subsequent Layer A commits (A1–A8) populate slots via per-pearl producer kernels.