feat(sp14): B.8 — direction Q-head input dim SH2 → SH2+1 + fingerprint
Forward wire (weight-side only — dispatch consumer lands in B.9): direction Q-head's first FC weight tensor `w_b0fc` (param-table index 17) input dim grows by 1 to accept aux_softmax_diff. The (rows, cols) shape table at the trainer-init Xavier site is updated in lock-step with `compute_param_sizes()`. Output dim (adv_h) and the bias `b_b0fc` are unchanged — bias is per-output, not per-input. `layout_fingerprint_seed()` entry renamed `PARAM_W_B0FC` → `PARAM_W_B0FC_AUX1`, forcing the FNV-1a hash to bump per Invariant 8 (old checkpoints fail-fast at load via `check_layout_fingerprint` instead of silently aliasing onto the new architecture). Per `feedback_no_legacy_aliases`, no `_DEPRECATED` shim — straight in-place rename. New weight column zero-initialised in trainer construction (mirrors the OFI column zeroing for w_b2fc/w_b3fc): the model starts ignoring the new input and learns to use it through gradient descent. Xavier- init on this column would inject day-0 noise the trunk would have to denoise — let the EGF gate decide when the aux signal is trustworthy. Atomic-migration consumers (`feedback_no_partial_refactor`) updated: - `gpu_dqn_trainer.rs` — Xavier (rows, cols) table at index 17 grows to (adv_h, SH2+1); spectral-norm descriptor entry [4] for W_a1 grows in_dim from sh2 to sh2+1; spec_v_a1 power-iteration vector grows from sh2 to sh2+1 floats. - `dqn/smoke_tests/gradient_budget.rs` — both `alloc_dueling` fixtures' slot 8 grow `cfg.adv_h * cfg.shared_h2` → `cfg.adv_h * (cfg.shared_h2 + 1)`. - `docs/dqn-wire-up-audit.md` — new SP14 Layer B B.8 entry per Invariant 7. Note: forward GEMM dispatch still uses `K = shared_h2` until B.9 lands the concat-then-SGEMM consumer (per plan §2358). Until then the new column reads as ignored padding; this is safe because (a) it's zero- initialised, (b) GPU-only smoke tests are skipped on this CPU CI, (c) the fingerprint bump invalidates any pre-SP14 checkpoint that would attempt to load. Test: `layout_fingerprint_bumps_after_sp14_wire` (CPU-only, in `sp14_oracle_tests.rs`) hashes the pre-B.8 seed verbatim with the single difference `PARAM_W_B0FC` (vs post-B.8 `PARAM_W_B0FC_AUX1`) and asserts `LAYOUT_FINGERPRINT_CURRENT` differs — any silent revert of the rename trips this test. Mirrors the `fingerprint_bumped_from_ pre_b1_1a` pattern from `sp13_layer_b_oracle_tests.rs`. Verified: - `SQLX_OFFLINE=true cargo check -p ml` clean, 18 warnings (baseline) - `cargo test -p ml --test sp14_oracle_tests` 2 passed, 6 ignored (GPU) - `cargo test -p ml --test sp13_layer_b_oracle_tests fingerprint_bumped_from_pre_b1_1a` still passes (sister test) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2134,7 +2134,7 @@ const fn layout_fingerprint_seed() -> &'static [u8] {
|
||||
PARAM_W_A_H_S2=7;PARAM_B_A_H_S2=8;PARAM_W_B_H_S2=9;PARAM_B_B_H_S2=10;\
|
||||
PARAM_GAMMA_H_S2=11;PARAM_BETA_H_S2=12;\
|
||||
PARAM_W_V1=13;PARAM_B_V1=14;PARAM_W_V2=15;PARAM_B_V2=16;\
|
||||
PARAM_W_B0FC=17;PARAM_B_B0FC=18;PARAM_W_B0OUT=19;PARAM_B_B0OUT=20;\
|
||||
PARAM_W_B0FC_AUX1=17;PARAM_B_B0FC=18;PARAM_W_B0OUT=19;PARAM_B_B0OUT=20;\
|
||||
PARAM_W_B1FC=21;PARAM_B_B1FC=22;PARAM_W_B1OUT=23;PARAM_B_B1OUT=24;\
|
||||
PARAM_W_B2FC=25;PARAM_B_B2FC=26;PARAM_W_B2OUT=27;PARAM_B_B2OUT=28;\
|
||||
PARAM_W_B3FC=29;PARAM_B_B3FC=30;PARAM_W_B3OUT=31;PARAM_B_B3OUT=32;\
|
||||
@@ -2804,7 +2804,7 @@ pub(crate) fn compute_param_sizes(cfg: &GpuDqnTrainConfig) -> [usize; NUM_WEIGHT
|
||||
cfg.value_h, // [14] b_v1
|
||||
cfg.num_atoms * cfg.value_h, // [15] w_v2
|
||||
cfg.num_atoms, // [16] b_v2
|
||||
cfg.adv_h * cfg.shared_h2, // [17] w_b0fc
|
||||
cfg.adv_h * (cfg.shared_h2 + 1), // [17] w_b0fc (SP14 aux→Q wire: SH2+1 input — last col = aux_softmax_diff)
|
||||
cfg.adv_h, // [18] b_b0fc
|
||||
cfg.branch_0_size * cfg.num_atoms * cfg.adv_h, // [19] w_b0out
|
||||
cfg.branch_0_size * cfg.num_atoms, // [20] b_b0out
|
||||
@@ -17949,8 +17949,8 @@ impl GpuDqnTrainer {
|
||||
alloc_spec_pair!(spec_u_v1, spec_v_v1, vh, sh2, "spec_u_v1", "spec_v_v1");
|
||||
// W_v2 [num_atoms, value_h]
|
||||
alloc_spec_pair!(spec_u_v2, spec_v_v2, na, vh, "spec_u_v2", "spec_v_v2");
|
||||
// W_a1 [adv_h, shared_h2]
|
||||
alloc_spec_pair!(spec_u_a1, spec_v_a1, ah, sh2, "spec_u_a1", "spec_v_a1");
|
||||
// W_a1 [adv_h, shared_h2 + 1] — SP14 aux→Q wire (input dim grew by 1)
|
||||
alloc_spec_pair!(spec_u_a1, spec_v_a1, ah, sh2 + 1, "spec_u_a1", "spec_v_a1");
|
||||
// W_a2 [b0*num_atoms, adv_h]
|
||||
alloc_spec_pair!(spec_u_a2, spec_v_a2, b0 * na, ah, "spec_u_a2", "spec_v_a2");
|
||||
// W_bo1 [adv_h, shared_h2]
|
||||
@@ -18010,8 +18010,8 @@ impl GpuDqnTrainer {
|
||||
w_ptrs[13], spec_u_v1.dev_ptr, spec_v_v1.dev_ptr, vh_u64, sh2_u64, 0,
|
||||
// [3] W_v2 [num_atoms, value_h] — GOFF 15 (was 6)
|
||||
w_ptrs[15], spec_u_v2.dev_ptr, spec_v_v2.dev_ptr, na_u64, vh_u64, 0,
|
||||
// [4] W_a1 [adv_h, shared_h2] — GOFF 17 (was 8)
|
||||
w_ptrs[17], spec_u_a1.dev_ptr, spec_v_a1.dev_ptr, ah_u64, sh2_u64, 0,
|
||||
// [4] W_a1 [adv_h, shared_h2 + 1] — GOFF 17 (was 8); SP14 aux→Q wire (input dim SH2+1)
|
||||
w_ptrs[17], spec_u_a1.dev_ptr, spec_v_a1.dev_ptr, ah_u64, sh2_u64 + 1, 0,
|
||||
// [5] W_a2 [b0*num_atoms, adv_h] — GOFF 19 (was 10)
|
||||
w_ptrs[19], spec_u_a2.dev_ptr, spec_v_a2.dev_ptr, b0_u64 * na_u64, ah_u64, 0,
|
||||
// [6] W_bo1 [adv_h, shared_h2] — GOFF 21 (was 12)
|
||||
@@ -27758,7 +27758,7 @@ impl GpuDqnTrainer {
|
||||
(0, 0), // [14] b_v1
|
||||
(cfg.num_atoms, cfg.value_h), // [15] w_v2
|
||||
(0, 0), // [16] b_v2
|
||||
(cfg.adv_h, cfg.shared_h2), // [17] w_b0fc
|
||||
(cfg.adv_h, cfg.shared_h2 + 1), // [17] w_b0fc (SP14 aux→Q wire: SH2+1 input)
|
||||
(0, 0), // [18] b_b0fc
|
||||
(cfg.branch_0_size * cfg.num_atoms, cfg.adv_h), // [19] w_b0out
|
||||
(0, 0), // [20] b_b0out
|
||||
@@ -27959,6 +27959,29 @@ impl GpuDqnTrainer {
|
||||
}
|
||||
}
|
||||
|
||||
// ── SP14 forward wire: zero the last (aux_softmax_diff) column of w_b0fc [17] ──
|
||||
// xavier_init fills the full tensor (adv_h × (shared_h2 + 1)). The new
|
||||
// column at index SH2 carries `softmax_up - softmax_down` from the aux
|
||||
// head; starting it at zero means the model initially ignores the new
|
||||
// input and learns to use it through gradient descent. Xavier-init on
|
||||
// this column would inject day-0 noise that the trunk would have to
|
||||
// denoise — we let the EGF gate decide when the signal is trustworthy.
|
||||
// (Mirrors the OFI column zeroing for w_b2fc/w_b3fc above.)
|
||||
{
|
||||
let fc_idx = 17_usize;
|
||||
let base: usize = sizes[..fc_idx].iter().map(|&s| align4(s)).sum();
|
||||
let sh2 = cfg.shared_h2;
|
||||
let sh2p1 = sh2 + 1;
|
||||
let ah = cfg.adv_h;
|
||||
// w_b0fc layout: [AH, SH2+1] row-major. Zero column [SH2] for all AH rows.
|
||||
for row in 0..ah {
|
||||
let idx = base + row * sh2p1 + sh2;
|
||||
if idx < host_buf.len() {
|
||||
host_buf[idx] = 0.0_f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── KAN spline coefficient init: uniform 0.125 (flat 0.5 gate ≈ sigmoid(0)) ──
|
||||
// Indices 51, 53, 55, 57 are kan_coeff_d [AH, 8]. Fill with 1/8 so the
|
||||
// spline sums to 0.5 regardless of input (mimics sigmoid(0) = 0.5).
|
||||
|
||||
@@ -53,7 +53,8 @@ fn alloc_dueling(stream: &Arc<cudarc::driver::CudaStream>, cfg: &GpuDqnTrainConf
|
||||
alloc(cfg.shared_h2 * cfg.shared_h1), alloc(cfg.shared_h2),
|
||||
alloc(cfg.value_h * cfg.shared_h2), alloc(cfg.value_h),
|
||||
alloc(na * cfg.value_h), alloc(na),
|
||||
alloc(cfg.adv_h * cfg.shared_h2), alloc(cfg.adv_h),
|
||||
// w_b0fc input dim grew to SH2+1 in SP14 (aux→Q wire); bias unchanged.
|
||||
alloc(cfg.adv_h * (cfg.shared_h2 + 1)), alloc(cfg.adv_h),
|
||||
alloc(cfg.branch_0_size * na * cfg.adv_h), alloc(cfg.branch_0_size * na),
|
||||
],
|
||||
};
|
||||
@@ -157,7 +158,8 @@ fn test_spectral_norm_constrains_operator_norm() -> anyhow::Result<()> {
|
||||
alloc_large(cfg.shared_h2 * cfg.shared_h1), alloc_large(cfg.shared_h2),
|
||||
alloc_large(cfg.value_h * cfg.shared_h2), alloc_large(cfg.value_h),
|
||||
alloc_large(na * cfg.value_h), alloc_large(na),
|
||||
alloc_large(cfg.adv_h * cfg.shared_h2), alloc_large(cfg.adv_h),
|
||||
// w_b0fc input dim grew to SH2+1 in SP14 (aux→Q wire); bias unchanged.
|
||||
alloc_large(cfg.adv_h * (cfg.shared_h2 + 1)), alloc_large(cfg.adv_h),
|
||||
alloc_large(cfg.branch_0_size * na * cfg.adv_h), alloc_large(cfg.branch_0_size * na),
|
||||
],
|
||||
};
|
||||
|
||||
@@ -768,3 +768,200 @@ mod gpu {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// Test B.8: Layout-fingerprint regression for direction Q-head input bump.
|
||||
//
|
||||
// CPU-only test (no GPU required). Ensures the fingerprint hash changed when
|
||||
// `w_b0fc`'s input dim grew from `SH2` to `SH2+1`, per Invariant 8 of the
|
||||
// SP14 plan: any structural reshuffle MUST bump
|
||||
// `LAYOUT_FINGERPRINT_CURRENT` so old checkpoints fail-fast on load instead
|
||||
// of silently mapping into the new architecture. The bump is achieved by
|
||||
// renaming the seed entry `PARAM_W_B0FC` → `PARAM_W_B0FC_AUX1`. Per
|
||||
// `feedback_no_legacy_aliases`, no `_DEPRECATED` shim is added.
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
/// Inline FNV-1a 64-bit hash for the regression test. Mirrors the
|
||||
/// `const fn fnv1a_64` in `gpu_dqn_trainer.rs` so the test computes
|
||||
/// the same hash the production fingerprint uses. (Mirrors the
|
||||
/// equivalent helper in `sp13_layer_b_oracle_tests.rs`.)
|
||||
const fn fnv1a_64(bytes: &[u8]) -> u64 {
|
||||
const OFFSET: u64 = 0xcbf29ce484222325;
|
||||
const PRIME: u64 = 0x00000100000001b3;
|
||||
let mut h: u64 = OFFSET;
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
h ^= bytes[i] as u64;
|
||||
h = h.wrapping_mul(PRIME);
|
||||
i += 1;
|
||||
}
|
||||
h
|
||||
}
|
||||
|
||||
/// Pre-B.8 seed — the post-B.7 seed string with `PARAM_W_B0FC` (instead of
|
||||
/// the post-B.8 `PARAM_W_B0FC_AUX1`). Verbatim except for that one rename.
|
||||
/// Hashing this constant gives the pre-B.8 fingerprint that the bump must
|
||||
/// differ from. A silent revert of the rename would make this test fail.
|
||||
const PRE_B8_SEED: &[u8] = b"SLOT_0_Q_DRIFT=0;\
|
||||
SLOT_1_GRAD_NORM_EMA=1;\
|
||||
SLOT_2_TD_ERR_EMA=2;\
|
||||
SLOT_3_ENS_VAR_EMA=3;\
|
||||
SLOT_4_ENS_VAR_VEL=4;\
|
||||
SLOT_5_REWARD_EMA=5;\
|
||||
SLOT_6_ATOM_UTIL_EMA=6;\
|
||||
SLOT_7_LOSS_EMA=7;\
|
||||
SLOT_8_ADX_EMA=8;\
|
||||
SLOT_9_REGIME_DISAGREE=9;\
|
||||
SLOT_10_REGIME_VEL_EMA=10;\
|
||||
SLOT_11_REGIME_STABILITY=11;\
|
||||
LEARNING_HEALTH=12;\
|
||||
Q_MAG_MEAN_QUARTER=13;Q_MAG_MEAN_HALF=14;Q_MAG_MEAN_FULL=15;Q_ABS_REF=16;\
|
||||
Q_DIR_MEAN_SHORT=17;Q_DIR_MEAN_HOLD=18;Q_DIR_MEAN_LONG=19;Q_DIR_MEAN_FLAT=20;Q_DIR_ABS_REF=21;\
|
||||
SHARPE_EMA=22;\
|
||||
V_CENTER_DIR=23;V_HALF_DIR=24;V_CENTER_MAG=25;V_HALF_MAG=26;\
|
||||
V_CENTER_ORD=27;V_HALF_ORD=28;V_CENTER_URG=29;V_HALF_URG=30;\
|
||||
GRAD_NORM_TARGET_DIR=31;GRAD_NORM_TARGET_MAG=32;GRAD_NORM_TARGET_ORD=33;GRAD_NORM_TARGET_URG=34;\
|
||||
GRAD_SCALE_LIMIT=35;IQL_BRANCH_SCALE_FLOOR=36;\
|
||||
EPOCH_IDX=39;TOTAL_EPOCHS=40;\
|
||||
EPSILON_EFF=41;TAU_EFF=42;\
|
||||
GAMMA_DIR_EFF=43;GAMMA_MAG_EFF=44;GAMMA_ORD_EFF=45;GAMMA_URG_EFF=46;\
|
||||
KELLY_CAP_EFF=47;CQL_ALPHA=48;PLAN_THRESHOLD=49;\
|
||||
Q_P05_DIR=50;Q_P05_MAG=51;Q_P05_ORD=52;Q_P05_URG=53;\
|
||||
Q_P95_DIR=54;Q_P95_MAG=55;Q_P95_ORD=56;Q_P95_URG=57;\
|
||||
TLOB_REGIME_FOCUS_EMA=60;\
|
||||
REWARD_POPART_EMA=63;REWARD_CF_EMA=64;REWARD_TRAIL_EMA=65;\
|
||||
REWARD_MICRO_EMA=66;REWARD_OPP_COST_EMA=67;REWARD_BONUS_EMA=68;\
|
||||
TRADE_ATTEMPT_RATE_EMA=71;TRADE_TARGET_RATE=72;\
|
||||
READINESS_EMA=75;\
|
||||
STATE_KL_EMA=78;STATE_KL_AMP=79;\
|
||||
SEED_STEPS_TARGET=82;SEED_STEPS_DONE=83;SEED_FRAC_EMA=84;\
|
||||
VSN_MAG_EMA=87;VSN_DIR_EMA=88;MAMBA2_RETENTION_EMA=89;\
|
||||
TARGET_DRIFT_MAG_EMA=92;TARGET_DRIFT_DIR_EMA=93;\
|
||||
H_S2_RMS_EMA=96;\
|
||||
IQN_Q_P05_EMA=99;IQN_Q_P25_EMA=100;IQN_Q_P75_EMA=101;IQN_Q_P95_EMA=102;\
|
||||
VSN_MASK_GROUP_0_EMA=105;VSN_MASK_GROUP_1_EMA=106;VSN_MASK_GROUP_2_EMA=107;\
|
||||
VSN_MASK_GROUP_3_EMA=108;VSN_MASK_GROUP_4_EMA=109;VSN_MASK_GROUP_5_EMA=110;\
|
||||
AUX_NEXT_BAR_MSE_EMA=113;AUX_REGIME_CE_EMA=114;\
|
||||
ISV_LAYOUT_FINGERPRINT_LO=115;ISV_LAYOUT_FINGERPRINT_HI=116;\
|
||||
MOE_EXPERT_UTIL_EMA_BASE=118;MOE_EXPERT_UTIL_EMA_COUNT=8;\
|
||||
MOE_GATE_ENTROPY_EMA=126;\
|
||||
MOE_LAMBDA_EFF=128;\
|
||||
Q_DRIFT_RATE=129;\
|
||||
FOLD_WARMUP_FACTOR=130;\
|
||||
TARGET_Q_BOUND=131;\
|
||||
ATOM_POS_BOUND_BRANCH_0=132;ATOM_POS_BOUND_BRANCH_1=133;ATOM_POS_BOUND_BRANCH_2=134;ATOM_POS_BOUND_BRANCH_3=135;\
|
||||
WEIGHT_BOUND_GROUP_0=136;WEIGHT_BOUND_GROUP_1=137;WEIGHT_BOUND_GROUP_2=138;WEIGHT_BOUND_GROUP_3=139;\
|
||||
WEIGHT_BOUND_GROUP_4=140;WEIGHT_BOUND_GROUP_5=141;WEIGHT_BOUND_GROUP_6=142;WEIGHT_BOUND_GROUP_7=143;\
|
||||
ADAM_M_BOUND_GROUP_0=144;ADAM_M_BOUND_GROUP_1=145;ADAM_M_BOUND_GROUP_2=146;ADAM_M_BOUND_GROUP_3=147;\
|
||||
ADAM_M_BOUND_GROUP_4=148;ADAM_M_BOUND_GROUP_5=149;ADAM_M_BOUND_GROUP_6=150;ADAM_M_BOUND_GROUP_7=151;\
|
||||
ADAM_V_BOUND_GROUP_0=152;ADAM_V_BOUND_GROUP_1=153;ADAM_V_BOUND_GROUP_2=154;ADAM_V_BOUND_GROUP_3=155;\
|
||||
ADAM_V_BOUND_GROUP_4=156;ADAM_V_BOUND_GROUP_5=157;ADAM_V_BOUND_GROUP_6=158;ADAM_V_BOUND_GROUP_7=159;\
|
||||
WD_RATE_GROUP_0=160;WD_RATE_GROUP_1=161;WD_RATE_GROUP_2=162;WD_RATE_GROUP_3=163;\
|
||||
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;\
|
||||
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;\
|
||||
PNL_TOTAL=286;PNL_MEAN=287;PNL_VAR=288;PNL_MAX_DD=289;\
|
||||
HEALTH_SCORE=290;Q_GAP_NORM=291;Q_VAR_NORM=292;GRAD_NORM_NORM=293;\
|
||||
TRAINING_SHARPE_EMA=294;MAX_DD_EMA=295;LOW_DD_RATIO=296;\
|
||||
LB_DIFF_VAR_CQL_BASE=297;LB_SAMPLE_VAR_CQL_BASE=301;\
|
||||
LB_DIFF_VAR_C51_BASE=305;LB_SAMPLE_VAR_C51_BASE=309;\
|
||||
LB_CQL_ACTIVE_BASE=313;LB_C51_ACTIVE_BASE=317;\
|
||||
TRAIN_ACTIVE_FRAC_INDEX=321;\
|
||||
LB_MAX_BUDGET_CQL_BASE=322;LB_MAX_BUDGET_C51_BASE=326;\
|
||||
KELLY_WARMUP_FLOOR=330;Q_VAR_MAG_EMA=331;\
|
||||
INTENT_EVAL_DIVERGENCE=332;KELLY_SAMPLE_COUNT_TARGET=333;\
|
||||
KELLY_DIVERGENCE_TARGET=334;KELLY_TEMPORAL_TARGET=335;\
|
||||
EVAL_DIST_Q=336;EVAL_DIST_H=337;EVAL_DIST_F=338;\
|
||||
EVAL_THOMPSON_TEMP=339;\
|
||||
REWARD_POPART_WEIGHT=340;REWARD_CF_WEIGHT=341;REWARD_TRAIL_WEIGHT=342;\
|
||||
REWARD_MICRO_WEIGHT=343;REWARD_OPP_COST_WEIGHT=344;REWARD_BONUS_WEIGHT=345;\
|
||||
CURIOSITY_PRESSURE=346;SABOTEUR_INTENSITY_MULT=347;\
|
||||
REWARD_WEIGHT_FLOOR=348;CURIOSITY_BOUND=349;\
|
||||
VAL_SHARPE_DELTA_EMA=350;VAL_SHARPE_VAR_EMA=351;\
|
||||
REWARD_COMPONENT_MAG_RATIO_BASE=352;\
|
||||
SABOTEUR_ENGAGEMENT_RATE=358;PNL_REWARD_MAGNITUDE_EMA=359;\
|
||||
POPART_COMPONENT_MAG_EMA=360;\
|
||||
REWARD_COMPONENT_VAR_EMA_BASE=361;\
|
||||
TARGET_DIR_ACC=372;AUX_DIR_ACC_SHORT_EMA=373;AUX_DIR_ACC_LONG_EMA=374;\
|
||||
AUX_DIR_PREDICTION=375;DIR_SKILL_BONUS_ALPHA=376;DIR_SKILL_BONUS_BETA=377;\
|
||||
LUCK_WIN_DISCOUNT=378;SKILL_BONUS_CAP_RATIO=379;\
|
||||
HOLD_COST=380;HOLD_RATE_TARGET=381;HOLD_RATE_OBSERVED_EMA=382;\
|
||||
ISV_TOTAL_DIM=383;\
|
||||
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;\
|
||||
PARAM_GAMMA_H_S2=11;PARAM_BETA_H_S2=12;\
|
||||
PARAM_W_V1=13;PARAM_B_V1=14;PARAM_W_V2=15;PARAM_B_V2=16;\
|
||||
PARAM_W_B0FC=17;PARAM_B_B0FC=18;PARAM_W_B0OUT=19;PARAM_B_B0OUT=20;\
|
||||
PARAM_W_B1FC=21;PARAM_B_B1FC=22;PARAM_W_B1OUT=23;PARAM_B_B1OUT=24;\
|
||||
PARAM_W_B2FC=25;PARAM_B_B2FC=26;PARAM_W_B2OUT=27;PARAM_B_B2OUT=28;\
|
||||
PARAM_W_B3FC=29;PARAM_B_B3FC=30;PARAM_W_B3OUT=31;PARAM_B_B3OUT=32;\
|
||||
PARAM_W_BN=33;PARAM_B_BN=34;\
|
||||
PARAM_W_VSN1_0=35;PARAM_W_VSN2_0=36;\
|
||||
PARAM_W_VSN1_1=37;PARAM_W_VSN2_1=38;\
|
||||
PARAM_W_VSN1_2=39;PARAM_W_VSN2_2=40;\
|
||||
PARAM_W_VSN1_3=41;PARAM_W_VSN2_3=42;\
|
||||
PARAM_W_GATE_0=43;PARAM_B_GATE_0=44;\
|
||||
PARAM_W_GATE_1=45;PARAM_B_GATE_1=46;\
|
||||
PARAM_W_GATE_2=47;PARAM_B_GATE_2=48;\
|
||||
PARAM_W_GATE_3=49;PARAM_B_GATE_3=50;\
|
||||
PARAM_KAN_COEFF_0=51;PARAM_KAN_RESID_0=52;\
|
||||
PARAM_KAN_COEFF_1=53;PARAM_KAN_RESID_1=54;\
|
||||
PARAM_KAN_COEFF_2=55;PARAM_KAN_RESID_2=56;\
|
||||
PARAM_KAN_COEFF_3=57;PARAM_KAN_RESID_3=58;\
|
||||
PARAM_W_REGIME=59;PARAM_B_REGIME=60;\
|
||||
PARAM_SPACING_RAW_0=61;PARAM_SPACING_RAW_1=62;\
|
||||
PARAM_SPACING_RAW_2=63;PARAM_SPACING_RAW_3=64;\
|
||||
PARAM_W_V1_5BAR=65;PARAM_B_V1_5BAR=66;\
|
||||
PARAM_W_V2_5BAR=67;PARAM_B_V2_5BAR=68;\
|
||||
PARAM_W_V1_20BAR=69;PARAM_B_V1_20BAR=70;\
|
||||
PARAM_W_V2_20BAR=71;PARAM_B_V2_20BAR=72;\
|
||||
PARAM_W_RISK_FC=73;PARAM_B_RISK_FC=74;\
|
||||
PARAM_W_RISK_OUT=75;PARAM_B_RISK_OUT=76;\
|
||||
PARAM_W_ISV_FC1=77;PARAM_B_ISV_FC1=78;\
|
||||
PARAM_W_ISV_FC2=79;PARAM_B_ISV_FC2=80;\
|
||||
PARAM_W_ISV_GATE=81;PARAM_B_ISV_GATE=82;\
|
||||
PARAM_W_ISV_GAMMA=83;PARAM_B_ISV_GAMMA=84;\
|
||||
PARAM_W_CONF_FC=85;PARAM_B_CONF_FC=86;\
|
||||
PARAM_W_FEATURE_GATE=87;PARAM_B_FEATURE_GATE=88;\
|
||||
PARAM_W_TEMPORAL_ROUTE=89;PARAM_B_TEMPORAL_ROUTE=90;\
|
||||
PARAM_W_PLAN_FC=91;PARAM_B_PLAN_FC=92;\
|
||||
PARAM_W_PLAN_OUT=93;PARAM_B_PLAN_OUT=94;\
|
||||
PARAM_VSN_W1_G0=95;PARAM_VSN_B1_G0=96;PARAM_VSN_W2_G0=97;PARAM_VSN_B2_G0=98;\
|
||||
PARAM_VSN_W1_G1=99;PARAM_VSN_B1_G1=100;PARAM_VSN_W2_G1=101;PARAM_VSN_B2_G1=102;\
|
||||
PARAM_VSN_W1_G2=103;PARAM_VSN_B1_G2=104;PARAM_VSN_W2_G2=105;PARAM_VSN_B2_G2=106;\
|
||||
PARAM_VSN_W1_G3=107;PARAM_VSN_B1_G3=108;PARAM_VSN_W2_G3=109;PARAM_VSN_B2_G3=110;\
|
||||
PARAM_VSN_W1_G4=111;PARAM_VSN_B1_G4=112;PARAM_VSN_W2_G4=113;PARAM_VSN_B2_G4=114;\
|
||||
PARAM_VSN_W1_G5=115;PARAM_VSN_B1_G5=116;PARAM_VSN_W2_G5=117;PARAM_VSN_B2_G5=118;\
|
||||
PARAM_AUX_NB_W1=119;PARAM_AUX_NB_B1=120;PARAM_AUX_NB_W2_K2=121;PARAM_AUX_NB_B2_K2=122;\
|
||||
PARAM_AUX_RG_W1=123;PARAM_AUX_RG_B1=124;PARAM_AUX_RG_W2=125;PARAM_AUX_RG_B2=126;\
|
||||
PARAM_MOE_GATE_W1=127;PARAM_MOE_GATE_B1=128;PARAM_MOE_GATE_W2=129;PARAM_MOE_GATE_B2=130;\
|
||||
PARAM_MOE_EXPERT_0_W1=131;PARAM_MOE_EXPERT_0_B1=132;PARAM_MOE_EXPERT_0_W2=133;PARAM_MOE_EXPERT_0_B2=134;\
|
||||
PARAM_MOE_EXPERT_1_W1=135;PARAM_MOE_EXPERT_1_B1=136;PARAM_MOE_EXPERT_1_W2=137;PARAM_MOE_EXPERT_1_B2=138;\
|
||||
PARAM_MOE_EXPERT_2_W1=139;PARAM_MOE_EXPERT_2_B1=140;PARAM_MOE_EXPERT_2_W2=141;PARAM_MOE_EXPERT_2_B2=142;\
|
||||
PARAM_MOE_EXPERT_3_W1=143;PARAM_MOE_EXPERT_3_B1=144;PARAM_MOE_EXPERT_3_W2=145;PARAM_MOE_EXPERT_3_B2=146;\
|
||||
PARAM_MOE_EXPERT_4_W1=147;PARAM_MOE_EXPERT_4_B1=148;PARAM_MOE_EXPERT_4_W2=149;PARAM_MOE_EXPERT_4_B2=150;\
|
||||
PARAM_MOE_EXPERT_5_W1=151;PARAM_MOE_EXPERT_5_B1=152;PARAM_MOE_EXPERT_5_W2=153;PARAM_MOE_EXPERT_5_B2=154;\
|
||||
PARAM_MOE_EXPERT_6_W1=155;PARAM_MOE_EXPERT_6_B1=156;PARAM_MOE_EXPERT_6_W2=157;PARAM_MOE_EXPERT_6_B2=158;\
|
||||
PARAM_MOE_EXPERT_7_W1=159;PARAM_MOE_EXPERT_7_B1=160;PARAM_MOE_EXPERT_7_W2=161;PARAM_MOE_EXPERT_7_B2=162;\
|
||||
PARAM_TOTAL_TENSORS=163";
|
||||
|
||||
#[test]
|
||||
fn layout_fingerprint_bumps_after_sp14_wire() {
|
||||
use ml::cuda_pipeline::gpu_dqn_trainer::LAYOUT_FINGERPRINT_CURRENT;
|
||||
let pre_b8_fp = fnv1a_64(PRE_B8_SEED);
|
||||
assert_ne!(
|
||||
LAYOUT_FINGERPRINT_CURRENT, pre_b8_fp,
|
||||
"B.8 fingerprint must differ from pre-B.8 value (the W_B0FC \
|
||||
rename to _AUX1 must change the seed hash). \
|
||||
current = {:#018x}, pre-B.8 = {:#018x}",
|
||||
LAYOUT_FINGERPRINT_CURRENT, pre_b8_fp
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6516,3 +6516,42 @@ This kernel is the fourth and final known-orphan in the B.3..B.6 producer chain.
|
||||
- **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.
|
||||
|
||||
## SP14 Layer B — Commit B.8: direction Q-head input dim SH2 → SH2+1 + fingerprint bump (2026-05-05)
|
||||
|
||||
**Why this commit.** B.8 is the architectural-shape change that opens the slot for the aux→Q forward wire. The direction Q-head's first FC weight tensor `w_b0fc` (param-table index 17) input dim grows from `cfg.shared_h2` to `cfg.shared_h2 + 1` to accept `aux_softmax_diff` (the EGF gate's gradient channel). Every consumer of the layout contract migrates atomically per `feedback_no_partial_refactor`; the layout fingerprint bumps so old checkpoints fail-fast at load. Forward-dispatch wiring (the concat-then-SGEMM at the call site in `forward_branch_q_head`) lands in B.9 — until then the new column is inert (zero-initialised, read as ignored padding by the existing K=SH2 SGEMM, will be activated when the dispatch grows to K=SH2+1).
|
||||
|
||||
### Changes
|
||||
|
||||
| Component | Role |
|
||||
|-----------|------|
|
||||
| `compute_param_sizes()` index 17 | `cfg.adv_h * cfg.shared_h2` → `cfg.adv_h * (cfg.shared_h2 + 1)` — drives the flat `params_buf` allocation, every prefix-sum offset table, and Adam m/v sizing |
|
||||
| Xavier (rows, cols) table at index 17 | `(cfg.adv_h, cfg.shared_h2)` → `(cfg.adv_h, cfg.shared_h2 + 1)` — keeps weight initialisation aligned with the new storage shape |
|
||||
| New zero-init block for column SH2 | After Xavier-init, zero `host_buf[base + row * (SH2+1) + SH2]` for `row in 0..adv_h` so the new aux_softmax_diff column starts at 0 (model ignores the wire on day 0; gradient descent learns to use it) |
|
||||
| `layout_fingerprint_seed()` rename | `PARAM_W_B0FC=17` → `PARAM_W_B0FC_AUX1=17` — forces FNV-1a hash difference per Invariant 8; no `_DEPRECATED` shim per `feedback_no_legacy_aliases` |
|
||||
| Spectral-norm descriptor entry [4] | `in_dim` `sh2_u64` → `sh2_u64 + 1` for `W_a1`; the spec_v_a1 power-iteration vector also grows from `sh2` to `sh2 + 1` floats |
|
||||
| Smoke-test fixtures (`gradient_budget.rs`) | Both `DuelingWeightBacking` slot 8 allocations (`alloc(...)` and `alloc_large(...)`) grow `cfg.adv_h * cfg.shared_h2` → `cfg.adv_h * (cfg.shared_h2 + 1)` |
|
||||
| New CPU oracle test | `layout_fingerprint_bumps_after_sp14_wire` — hashes a verbatim pre-B.8 seed (with `PARAM_W_B0FC` instead of `_AUX1`) and asserts `LAYOUT_FINGERPRINT_CURRENT` differs |
|
||||
|
||||
**Why zero-init the new column** (vs Xavier across all SH2+1 columns): mirrors the OFI column zeroing for `w_b2fc` / `w_b3fc` (gpu_dqn_trainer.rs:27941). Xavier-init on the new column would inject day-0 noise that the trunk would need to denoise — the EGF gate is designed to decide *when* the aux signal is trustworthy, not to fight off random initialisation.
|
||||
|
||||
### File-change summary
|
||||
|
||||
| Change | File |
|
||||
|--------|------|
|
||||
| `compute_param_sizes` index 17, Xavier (rows, cols) table, zero-init block, fingerprint-seed rename, spec-norm descriptor + spec_v_a1 size | `crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs` |
|
||||
| Both `DuelingWeightBacking` slot 8 fixtures resized | `crates/ml/src/trainers/dqn/smoke_tests/gradient_budget.rs` |
|
||||
| New CPU `layout_fingerprint_bumps_after_sp14_wire` test (mirrors SP13's `fingerprint_bumped_from_pre_b1_1a` pattern with verbatim pre-B.8 seed constant) | `crates/ml/tests/sp14_oracle_tests.rs` |
|
||||
|
||||
### Verification
|
||||
|
||||
- `SQLX_OFFLINE=true cargo check -p ml` — clean, 18 warnings (pre-existing baseline)
|
||||
- `SQLX_OFFLINE=true cargo test -p ml --test sp14_oracle_tests` — 2 passed (CPU), 6 ignored (GPU — Layer B oracle tests skip without `--features cuda --ignored`)
|
||||
- `SQLX_OFFLINE=true cargo test -p ml --test sp13_layer_b_oracle_tests fingerprint_bumped_from_pre_b1_1a` — sister regression passes (the SP14 fingerprint bump does not invalidate SP13's pre-B1.1a invariant)
|
||||
|
||||
### Wire status
|
||||
|
||||
- **Weight shape**: yes — flat buffer, Xavier (rows, cols), zero-init, spectral-norm descriptor, and smoke fixtures all agree on `[adv_h, SH2 + 1]`. Bias `b_b0fc` (index 18) unchanged — biases are per-output, not per-input.
|
||||
- **Fingerprint**: yes — `LAYOUT_FINGERPRINT_CURRENT` bumps via the `_AUX1` rename; `check_layout_fingerprint` (gpu_dqn_trainer.rs:21259) will refuse any pre-SP14 checkpoint at load.
|
||||
- **Forward dispatch**: NOT WIRED. Branch-0's `forward_branch_q_head` still computes `(fc_input, fc_k) = (h_s2_ptr, self.shared_h2)` (batched_forward.rs:1991). Until B.9 lands the concat → SGEMM consumer, the new column reads as ignored padding (safe because zero-init + GPU-only smoke tests are skipped on CPU CI + fingerprint bump invalidates pre-SP14 checkpoints).
|
||||
- **Reverse dependencies**: `gpu_weights::extract_dueling_weights` reads sizes from the `GpuVarStore` itself — old python checkpoints would expose a SH2-wide `advantage_fc.weight`, which the bumped fingerprint blocks before the extraction reaches that path.
|
||||
|
||||
Reference in New Issue
Block a user