feat(dqn-v2): Plan 4 Task 1B-ii — VSN params (24 tensors) + 6 ISV slots (additive)
Per-group VSN MLP parameter tensors and per-group importance-mask EMA ISV
slots land additively — no production callers in this commit. The
forward orchestrator + cuBLAS Linear_1/Linear_2 wire-in lands in 1B-iii;
backward orchestrator + autograd integration lands in 1B-iv.
Param tensors (24 new, indices [95..119)):
* Per-group quad (`vsn_w1_g{g}`, `vsn_b1_g{g}`, `vsn_w2_g{g}`,
`vsn_b2_g{g}`) for each `FEATURE_GROUP_RANGES` entry
(market / ofi / tlob / mtf / portfolio / plan_isv).
* `VSN_HIDDEN_DIM = 16`. Per-group element count =
16*group_dim_g + 33; sum = 16*121 + 6*33 = 2134 floats.
* Xavier init on weights, zero on biases — initial logits ≈ 0 yield
softmax mask ≈ 1/SL_NUM_FEATURE_GROUPS (uniform cold-start).
* `NUM_WEIGHT_TENSORS` 95 → 119; `compute_param_sizes()` and
`xavier_init_params_buf()` rewritten with a runtime per-group loop
reading `FEATURE_GROUP_RANGES` from `ml_core::state_layout`
(Task 1A's prerequisite).
* Adam state, target params, and grad buffers grow automatically
since their allocations use `compute_total_params(cfg) +
cutlass_tile_pad`.
ISV slots (6 new, indices [105..111)):
* `VSN_MASK_GROUP_0_EMA_INDEX = 105` (market) through
`VSN_MASK_GROUP_5_EMA_INDEX = 110` (plan_isv).
* Cold-start `1.0 / SL_NUM_FEATURE_GROUPS = 1/6` (uniform prior — no
group preference at init).
* FoldReset reapplies the same uniform value at fold boundary; 6 new
`RegistryEntry` rows + a single dispatch arm in
`training_loop.rs::reset_named_state` covering all six names.
* Producer kernel `vsn_mask_ema_update` (1B-i) overwrites these slots
once the launch site is wired in 1B-iii.
Fingerprint:
* Pair shifted 103/104 → 111/112; `ISV_TOTAL_DIM` 105 → 113.
* `layout_fingerprint_seed()` extended with the 6 new ISV slot names
+ 24 new `PARAM_VSN_*` entries terminating at
`PARAM_TOTAL_TENSORS=119`.
* New `LAYOUT_FINGERPRINT_CURRENT = 0x1b28321bb816f246`
(was `0x5789155b683ab59c`). Checkpoint break by intent — fail-fast
at constructor load on mismatch, no migration path per
`feedback_no_legacy_aliases.md`.
Cubin refs:
* `VSN_FEATURE_SELECTION_CUBIN` and `VSN_MASK_EMA_CUBIN` added as
`pub(crate) static` `#[allow(dead_code)]` byte arrays — the
`include_bytes!()` runs at compile time, but no `load_cubin(...)` /
`load_function(...)` call exists yet (deferred to 1B-iii).
Mirrors the 2c.3a pattern (param tensor reshuffle without runtime
callers — that landed cleanly because the runtime sites were panic-
gated; here, the runtime sites simply don't exist yet so no gating
needed).
cargo check clean at 11 warnings (workspace baseline preserved); cargo
build compiles 62/62 cubins clean (unchanged — no new `.cu` files).
Smoke deferred — behaviour byte-identical to the f3e3ac347 /
0x5789155b683ab59c post-bottleneck-fix baseline (700.43s, 3 folds × 5
epochs, per-fold best train Sharpe 6.53 / 80.11 / 66.72, geom-mean
39.27) since no consumer reads the new params or ISV slots; smoke
re-validation lands at 1B-iii.
Audit doc updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,6 +141,30 @@ static H_S2_RMS_EMA_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/h_s
|
||||
/// Diagnostic only — producer-only in this commit.
|
||||
static IQN_QUANTILE_EMA_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/iqn_quantile_ema_kernel.cubin"));
|
||||
|
||||
/// Plan 4 Task 1B-i / 1B-ii: VSN feature-selection kernels (forward + backward).
|
||||
/// Cubin loaded here as a `pub(crate) static` so the `include_bytes!()` happens
|
||||
/// at compile time; the runtime `load_cubin(...)` + `load_function(...)` calls
|
||||
/// are deferred to 1B-iii's forward orchestrator wire-in. Marked `dead_code`
|
||||
/// here because the static is declared in advance of its consumer to keep
|
||||
/// 1B-iii's diff scoped to the orchestrator + Linear_1/Linear_2 cuBLAS plumbing.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) static VSN_FEATURE_SELECTION_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/vsn_feature_selection_kernel.cubin"));
|
||||
|
||||
/// Plan 4 Task 1B-i / 1B-ii: VSN per-group mask EMA producer.
|
||||
/// Same `pub(crate) static` + deferred-load pattern as `VSN_FEATURE_SELECTION_CUBIN`.
|
||||
/// Launch site lands in 1B-iii alongside the forward orchestrator so the EMA
|
||||
/// updates ISV[VSN_MASK_GROUP_0_EMA_INDEX..VSN_MASK_GROUP_5_EMA_INDEX) only
|
||||
/// after the producer kernel is actually invoked.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) static VSN_MASK_EMA_CUBIN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/vsn_mask_ema_kernel.cubin"));
|
||||
|
||||
/// Plan 4 Task 1B-ii: per-group VSN MLP hidden dim. `Linear_1[g]` is
|
||||
/// `[VSN_HIDDEN_DIM, group_dim_g]`, `Linear_2[g]` is `[1, VSN_HIDDEN_DIM]`.
|
||||
/// 16 chosen as a tractable expansion that fits comfortably against the
|
||||
/// smallest group (`plan_isv` = 7 features); per-group params total
|
||||
/// `VSN_HIDDEN_DIM*group_dim_g + VSN_HIDDEN_DIM + VSN_HIDDEN_DIM + 1`.
|
||||
pub const VSN_HIDDEN_DIM: usize = 16;
|
||||
|
||||
/// Mamba2 temporal scan configuration.
|
||||
const MAMBA2_HISTORY_K: usize = 8; // Rolling history length
|
||||
const MAMBA2_STATE_DIM: usize = 16; // SSM state dimension
|
||||
@@ -318,11 +342,21 @@ const ISV_NETWORK_DIM: usize = 23;
|
||||
/// Note: the median (τ=0.50) lives in the existing C51 / IQL greedy-Q
|
||||
/// diagnostic — not duplicated here. Only the 4 off-median quantiles
|
||||
/// are new.
|
||||
/// [103] ISV_LAYOUT_FINGERPRINT_LO_INDEX — low 32 bits of layout fingerprint (shifted from 97).
|
||||
/// [104] ISV_LAYOUT_FINGERPRINT_HI_INDEX — high 32 bits of layout fingerprint (shifted from 98).
|
||||
/// [105] VSN_MASK_GROUP_0_EMA_INDEX — Plan 4 Task 1B-ii — `market` feature
|
||||
/// group importance mask EMA. Producer: `vsn_mask_ema_update` GPU kernel
|
||||
/// (single-block shmem reduction over per-sample mask buffer; α=0.05).
|
||||
/// Launch site wired in 1B-iii. Cold-start `1.0/SL_NUM_FEATURE_GROUPS`
|
||||
/// = 1/6 (uniform prior — no group preference at init). FoldReset → 1/6.
|
||||
/// [106] VSN_MASK_GROUP_1_EMA_INDEX — `ofi` group, same producer/contract.
|
||||
/// [107] VSN_MASK_GROUP_2_EMA_INDEX — `tlob` group, same producer/contract.
|
||||
/// [108] VSN_MASK_GROUP_3_EMA_INDEX — `mtf` group, same producer/contract.
|
||||
/// [109] VSN_MASK_GROUP_4_EMA_INDEX — `portfolio` group, same producer/contract.
|
||||
/// [110] VSN_MASK_GROUP_5_EMA_INDEX — `plan_isv` group, same producer/contract.
|
||||
/// [111] ISV_LAYOUT_FINGERPRINT_LO_INDEX — low 32 bits of layout fingerprint (shifted from 103).
|
||||
/// [112] ISV_LAYOUT_FINGERPRINT_HI_INDEX — high 32 bits of layout fingerprint (shifted from 104).
|
||||
/// 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 = 105;
|
||||
const ISV_TOTAL_DIM: usize = 113;
|
||||
/// 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).
|
||||
@@ -611,7 +645,35 @@ pub const IQN_Q_P25_EMA_INDEX: usize = 100;
|
||||
pub const IQN_Q_P75_EMA_INDEX: usize = 101;
|
||||
pub const IQN_Q_P95_EMA_INDEX: usize = 102;
|
||||
|
||||
/// ISV slot [103] — low 32 bits of the u64 layout fingerprint (stored as raw f32 bits).
|
||||
/// ISV slots [105..111) — Plan 4 Task 1B-ii: per-group VSN mask EMAs.
|
||||
///
|
||||
/// One slot per `FEATURE_GROUP_RANGES` entry (market / ofi / tlob / mtf /
|
||||
/// portfolio / plan_isv). Producer: `vsn_mask_ema_update` GPU kernel
|
||||
/// (single-block, 256 threads, shmem-reduction, no atomicAdd) — landed in
|
||||
/// 1B-i, launch site wired in 1B-iii alongside the forward orchestrator that
|
||||
/// produces the per-sample softmax mask. EMA α matches the rest of the
|
||||
/// Plan 4 producer family (cold-path-cadence, one launch per training step
|
||||
/// via `training_loop.rs`).
|
||||
///
|
||||
/// Cold-start: `1.0 / SL_NUM_FEATURE_GROUPS = 1/6` (uniform prior — no
|
||||
/// group preference at init). The producer kernel overwrites these slots on
|
||||
/// every fire, so the cold-start value matters only for consumers that read
|
||||
/// the EMA before the first kernel launch (currently none — this commit is
|
||||
/// producer/consumer-orchestrator-free; 1B-iii adds the launch).
|
||||
/// FoldReset → `1/6`.
|
||||
///
|
||||
/// Producer-only after 1B-iii — diagnostic surface for HEALTH_DIAG /
|
||||
/// per-group attention monitoring. No consumer kernel reads slots
|
||||
/// [105..111) in this commit nor in 1B-iii (consumer attaches in a later
|
||||
/// `attention_monitor` upgrade).
|
||||
pub const VSN_MASK_GROUP_0_EMA_INDEX: usize = 105;
|
||||
pub const VSN_MASK_GROUP_1_EMA_INDEX: usize = 106;
|
||||
pub const VSN_MASK_GROUP_2_EMA_INDEX: usize = 107;
|
||||
pub const VSN_MASK_GROUP_3_EMA_INDEX: usize = 108;
|
||||
pub const VSN_MASK_GROUP_4_EMA_INDEX: usize = 109;
|
||||
pub const VSN_MASK_GROUP_5_EMA_INDEX: usize = 110;
|
||||
|
||||
/// ISV slot [111] — low 32 bits of the u64 layout fingerprint (stored as raw f32 bits).
|
||||
///
|
||||
/// Design note: this is NOT a version number. There is no ordered version space.
|
||||
/// The fingerprint is a structural hash that automatically changes whenever any
|
||||
@@ -626,15 +688,16 @@ pub const IQN_Q_P95_EMA_INDEX: usize = 102;
|
||||
/// Plan 3 Task 4 B.4, 76→80 in Plan 3 Task 7 C.3, 80→85 in Plan 3 Task 8 B.3,
|
||||
/// 85→90 in Plan 4 Task 5 Mode A, 90→94 in Plan 4 follow-up (target-drift
|
||||
/// EMA replacing legacy CPU-DtoH path), 94→97 in Plan 4 Task 2c.3c.5
|
||||
/// (H_S2_RMS_EMA producer slot), then 97→103 in Plan 4 Task 3 E.3 (IQN
|
||||
/// fixed-τ multi-quantile head, +4 diagnostic slots).
|
||||
pub const ISV_LAYOUT_FINGERPRINT_LO_INDEX: usize = 103;
|
||||
/// ISV slot [104] — high 32 bits of the u64 layout fingerprint (stored as raw f32 bits).
|
||||
/// (H_S2_RMS_EMA producer slot), 97→103 in Plan 4 Task 3 E.3 (IQN
|
||||
/// fixed-τ multi-quantile head, +4 diagnostic slots), then 103→111 in
|
||||
/// Plan 4 Task 1B-ii (VSN per-group mask EMA, +6 slots).
|
||||
pub const ISV_LAYOUT_FINGERPRINT_LO_INDEX: usize = 111;
|
||||
/// ISV slot [112] — high 32 bits of the u64 layout fingerprint (stored as raw f32 bits).
|
||||
/// Shifted 62→70 in Plan 3 Task 1, 70→74 in Plan 3 Task 3, 74→77 in Plan 3 Task 4 B.4,
|
||||
/// 77→81 in Plan 3 Task 7 C.3, 81→86 in Plan 3 Task 8 B.3, 86→91 in Plan 4 Task 5,
|
||||
/// 91→95 in Plan 4 follow-up, 95→98 in Plan 4 Task 2c.3c.5, then 98→104 in Plan 4
|
||||
/// Task 3 E.3.
|
||||
pub const ISV_LAYOUT_FINGERPRINT_HI_INDEX: usize = 104;
|
||||
/// 91→95 in Plan 4 follow-up, 95→98 in Plan 4 Task 2c.3c.5, 98→104 in Plan 4
|
||||
/// Task 3 E.3, then 104→112 in Plan 4 Task 1B-ii.
|
||||
pub const ISV_LAYOUT_FINGERPRINT_HI_INDEX: usize = 112;
|
||||
/// Canonical alias for the fingerprint slot (the low half).
|
||||
pub const ISV_LAYOUT_FINGERPRINT_INDEX: usize = ISV_LAYOUT_FINGERPRINT_LO_INDEX;
|
||||
|
||||
@@ -714,8 +777,10 @@ const fn layout_fingerprint_seed() -> &'static [u8] {
|
||||
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;\
|
||||
ISV_LAYOUT_FINGERPRINT_LO=103;ISV_LAYOUT_FINGERPRINT_HI=104;\
|
||||
ISV_TOTAL_DIM=105;\
|
||||
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;\
|
||||
ISV_LAYOUT_FINGERPRINT_LO=111;ISV_LAYOUT_FINGERPRINT_HI=112;\
|
||||
ISV_TOTAL_DIM=113;\
|
||||
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;\
|
||||
@@ -756,7 +821,13 @@ const fn layout_fingerprint_seed() -> &'static [u8] {
|
||||
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_TOTAL_TENSORS=95"
|
||||
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_TOTAL_TENSORS=119"
|
||||
}
|
||||
|
||||
/// Compile-time layout fingerprint. Burned into the binary at build time.
|
||||
@@ -1150,8 +1221,13 @@ impl Default for CausalInterventionConfig {
|
||||
/// + 8 multi-horizon value heads (5-bar and 20-bar)
|
||||
/// + 4 learned risk management (5th branch) = 68
|
||||
/// + 10 ISV encoder/conditioning + 2 feature gate + 2 temporal routing = 82
|
||||
/// + 4 trade plan head = 86.
|
||||
pub(crate) const NUM_WEIGHT_TENSORS: usize = 95;
|
||||
/// + 4 trade plan head = 86 → indices [0..95).
|
||||
/// + 24 Plan 4 Task 1B-ii VSN per-group MLP params (4 tensors × 6 groups —
|
||||
/// w1_g/b1_g/w2_g/b2_g per `FEATURE_GROUP_RANGES` entry, hidden dim
|
||||
/// `VSN_HIDDEN_DIM = 16`) → indices [95..119). Producer-only params in
|
||||
/// this commit; the `vsn_softmax_and_gate_forward` consumer kernel and
|
||||
/// the cuBLAS `Linear_1`/`Linear_2` wire-in land in 1B-iii.
|
||||
pub(crate) const NUM_WEIGHT_TENSORS: usize = 119;
|
||||
|
||||
/// Compute the size (element count) of each weight tensor.
|
||||
///
|
||||
@@ -1183,7 +1259,8 @@ pub(crate) fn compute_param_sizes(cfg: &GpuDqnTrainConfig) -> [usize; NUM_WEIGHT
|
||||
let bn_dim = cfg.bottleneck_dim;
|
||||
let market_dim = cfg.market_dim;
|
||||
|
||||
[
|
||||
let mut sizes = [0usize; NUM_WEIGHT_TENSORS];
|
||||
let core: [usize; 95] = [
|
||||
// ── h_s1 GRN block (Plan 4 Task 2c.3a) ───────────────────────
|
||||
// GRN(x) = LayerNorm(Linear_residual(x) + GLU(Linear_b(ELU(Linear_a(x))))).
|
||||
// h_s1 maps SD → SH1 (dims differ → Linear_residual is a real GEMM).
|
||||
@@ -1296,7 +1373,36 @@ pub(crate) fn compute_param_sizes(cfg: &GpuDqnTrainConfig) -> [usize; NUM_WEIGHT
|
||||
cfg.adv_h, // [92] b_plan_fc [AH]
|
||||
6 * cfg.adv_h, // [93] w_plan_out [6, AH]
|
||||
6, // [94] b_plan_out [6]
|
||||
]
|
||||
];
|
||||
sizes[..95].copy_from_slice(&core);
|
||||
|
||||
// ── Plan 4 Task 1B-ii: VSN per-group MLP params (24 tensors) ───
|
||||
// 4 tensors per group × 6 groups (`FEATURE_GROUP_RANGES`).
|
||||
// Per-group total = VSN_HIDDEN_DIM * group_dim_g + VSN_HIDDEN_DIM (b1)
|
||||
// + VSN_HIDDEN_DIM (w2 flat) + 1 (b2)
|
||||
// = 16 * group_dim_g + 33.
|
||||
// Sum over groups (market=42, ofi=32, tlob=16, mtf=16, portfolio=8,
|
||||
// plan_isv=7) = 16*121 + 6*33 = 1936 + 198 = 2134 floats.
|
||||
// No production callers in this commit — params consumed by 1B-iii
|
||||
// (forward orchestrator + Linear_1/Linear_2 cuBLAS wire-in) and
|
||||
// 1B-iv (backward orchestrator).
|
||||
let group_ranges = ml_core::state_layout::FEATURE_GROUP_RANGES;
|
||||
let mut idx = 95;
|
||||
let mut g = 0;
|
||||
while g < ml_core::state_layout::SL_NUM_FEATURE_GROUPS {
|
||||
let group_dim = group_ranges[g].1 - group_ranges[g].0;
|
||||
sizes[idx] = VSN_HIDDEN_DIM * group_dim; // vsn_w1_g{g} [VSN_HIDDEN_DIM, group_dim_g]
|
||||
sizes[idx + 1] = VSN_HIDDEN_DIM; // vsn_b1_g{g} [VSN_HIDDEN_DIM]
|
||||
sizes[idx + 2] = VSN_HIDDEN_DIM; // vsn_w2_g{g} [1, VSN_HIDDEN_DIM] (flat-stored)
|
||||
sizes[idx + 3] = 1; // vsn_b2_g{g} [1]
|
||||
idx += 4;
|
||||
g += 1;
|
||||
}
|
||||
debug_assert!(idx == NUM_WEIGHT_TENSORS,
|
||||
"compute_param_sizes: VSN tail fill ended at {} but NUM_WEIGHT_TENSORS={}",
|
||||
idx, NUM_WEIGHT_TENSORS);
|
||||
|
||||
sizes
|
||||
}
|
||||
|
||||
/// Align element count to 4-element boundary (16 bytes for f32).
|
||||
@@ -8985,6 +9091,21 @@ impl GpuDqnTrainer {
|
||||
*sig_ptr.add(IQN_Q_P25_EMA_INDEX) = 0.0_f32;
|
||||
*sig_ptr.add(IQN_Q_P75_EMA_INDEX) = 0.0_f32;
|
||||
*sig_ptr.add(IQN_Q_P95_EMA_INDEX) = 0.0_f32;
|
||||
/* Plan 4 Task 1B-ii: VSN per-group mask-EMA cold-start.
|
||||
* 1.0 / SL_NUM_FEATURE_GROUPS = 1/6 — uniform prior (no
|
||||
* group preference at init). The producer kernel
|
||||
* `vsn_mask_ema_update` (1B-i, launch site wired in
|
||||
* 1B-iii) overwrites these slots on every fire; until
|
||||
* 1B-iii lands, they stay at the uniform cold-start.
|
||||
* FoldReset reapplies the same 1/6 at fold boundary so
|
||||
* each fold begins with no inherited group bias. */
|
||||
let uniform_mask = 1.0_f32 / ml_core::state_layout::SL_NUM_FEATURE_GROUPS as f32;
|
||||
*sig_ptr.add(VSN_MASK_GROUP_0_EMA_INDEX) = uniform_mask;
|
||||
*sig_ptr.add(VSN_MASK_GROUP_1_EMA_INDEX) = uniform_mask;
|
||||
*sig_ptr.add(VSN_MASK_GROUP_2_EMA_INDEX) = uniform_mask;
|
||||
*sig_ptr.add(VSN_MASK_GROUP_3_EMA_INDEX) = uniform_mask;
|
||||
*sig_ptr.add(VSN_MASK_GROUP_4_EMA_INDEX) = uniform_mask;
|
||||
*sig_ptr.add(VSN_MASK_GROUP_5_EMA_INDEX) = uniform_mask;
|
||||
// Plan 2 C.1 Q-quantile bootstrap (2026-04-24):
|
||||
// q_p05 = v_min, q_p95 = v_max — matches the cold-start atom range
|
||||
// so the first update_eval_v_range call reads meaningful bounds.
|
||||
@@ -15565,7 +15686,8 @@ impl GpuDqnTrainer {
|
||||
// gamma, beta — 7 tensors; h_s2 GRN: Linear_a, Linear_b, gamma, beta —
|
||||
// 6 tensors since SH1==SH2 makes Linear_residual identity). Every
|
||||
// index ≥ 4 in the OLD layout shifts +9 in the NEW layout.
|
||||
let fan_dims: [(usize, usize); NUM_WEIGHT_TENSORS] = [
|
||||
let mut fan_dims: [(usize, usize); NUM_WEIGHT_TENSORS] = [(0, 0); NUM_WEIGHT_TENSORS];
|
||||
let core_fan: [(usize, usize); 95] = [
|
||||
// ── h_s1 GRN ──
|
||||
(cfg.shared_h1, s1_input_dim), // [0] w_a_h_s1(Kaiming/Xavier)
|
||||
(0, 0), // [1] b_a_h_s1 (zero)
|
||||
@@ -15669,6 +15791,27 @@ impl GpuDqnTrainer {
|
||||
(6, cfg.adv_h), // [93] w_plan_out (Xavier)
|
||||
(0, 0), // [94] b_plan_out (zero)
|
||||
];
|
||||
fan_dims[..95].copy_from_slice(&core_fan);
|
||||
|
||||
// ── Plan 4 Task 1B-ii: VSN per-group MLP fan dims (24 tensors) ─
|
||||
// Xavier on weights (`w1_g`, `w2_g`); zero on biases (`b1_g`, `b2_g`).
|
||||
// Initial logits ≈ 0 → softmax mask ≈ uniform 1/SL_NUM_FEATURE_GROUPS
|
||||
// per group at every sample (cold-start neutral, matches the ISV
|
||||
// mask-EMA cold-start of 1/6).
|
||||
{
|
||||
let group_ranges = ml_core::state_layout::FEATURE_GROUP_RANGES;
|
||||
let mut g = 0;
|
||||
let mut idx = 95;
|
||||
while g < ml_core::state_layout::SL_NUM_FEATURE_GROUPS {
|
||||
let group_dim = group_ranges[g].1 - group_ranges[g].0;
|
||||
fan_dims[idx] = (VSN_HIDDEN_DIM, group_dim); // w1_g [VSN_HIDDEN_DIM, group_dim_g] (Xavier)
|
||||
fan_dims[idx + 1] = (0, 0); // b1_g (zero)
|
||||
fan_dims[idx + 2] = (1, VSN_HIDDEN_DIM); // w2_g [1, VSN_HIDDEN_DIM] (Xavier)
|
||||
fan_dims[idx + 3] = (0, 0); // b2_g (zero)
|
||||
idx += 4;
|
||||
g += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Build flat host buffer: Xavier init for weights, zeros for biases + padding.
|
||||
let cutlass_tile_pad = 32 * cfg.adv_h.max(cfg.value_h);
|
||||
|
||||
@@ -300,6 +300,37 @@ impl StateResetRegistry {
|
||||
category: ResetCategory::FoldReset,
|
||||
description: "ISV[IQN_Q_P95_EMA_INDEX=102] — mean |Q| at IQN τ=0.95 EMA; GPU iqn_quantile_ema_update kernel fills (Plan 4 Task 3 E.3). Cold-start 0.0 reapplied at fold boundary. Diagnostic only",
|
||||
},
|
||||
// ───── Plan 4 Task 1B-ii: VSN per-group mask EMAs ──────────
|
||||
RegistryEntry {
|
||||
name: "isv_vsn_mask_g0_ema",
|
||||
category: ResetCategory::FoldReset,
|
||||
description: "ISV[VSN_MASK_GROUP_0_EMA_INDEX=105] — `market` group importance mask EMA (α=0.05); GPU vsn_mask_ema_update kernel fills (Plan 4 Task 1B-i kernel; launch site wired in 1B-iii). Cold-start 1.0/SL_NUM_FEATURE_GROUPS=1/6 (uniform prior — no group preference) reapplied at fold boundary",
|
||||
},
|
||||
RegistryEntry {
|
||||
name: "isv_vsn_mask_g1_ema",
|
||||
category: ResetCategory::FoldReset,
|
||||
description: "ISV[VSN_MASK_GROUP_1_EMA_INDEX=106] — `ofi` group importance mask EMA; same producer/contract as g0; cold-start 1/6",
|
||||
},
|
||||
RegistryEntry {
|
||||
name: "isv_vsn_mask_g2_ema",
|
||||
category: ResetCategory::FoldReset,
|
||||
description: "ISV[VSN_MASK_GROUP_2_EMA_INDEX=107] — `tlob` group importance mask EMA; same producer/contract as g0; cold-start 1/6",
|
||||
},
|
||||
RegistryEntry {
|
||||
name: "isv_vsn_mask_g3_ema",
|
||||
category: ResetCategory::FoldReset,
|
||||
description: "ISV[VSN_MASK_GROUP_3_EMA_INDEX=108] — `mtf` group importance mask EMA; same producer/contract as g0; cold-start 1/6",
|
||||
},
|
||||
RegistryEntry {
|
||||
name: "isv_vsn_mask_g4_ema",
|
||||
category: ResetCategory::FoldReset,
|
||||
description: "ISV[VSN_MASK_GROUP_4_EMA_INDEX=109] — `portfolio` group importance mask EMA; same producer/contract as g0; cold-start 1/6",
|
||||
},
|
||||
RegistryEntry {
|
||||
name: "isv_vsn_mask_g5_ema",
|
||||
category: ResetCategory::FoldReset,
|
||||
description: "ISV[VSN_MASK_GROUP_5_EMA_INDEX=110] — `plan_isv` group importance mask EMA; same producer/contract as g0; cold-start 1/6",
|
||||
},
|
||||
];
|
||||
Self { entries }
|
||||
}
|
||||
|
||||
@@ -4531,6 +4531,38 @@ impl DQNTrainer {
|
||||
fused.trainer().write_isv_signal_at(idx, 0.0);
|
||||
}
|
||||
}
|
||||
// Plan 4 Task 1B-ii: VSN per-group mask-EMA producer slots
|
||||
// (one per `FEATURE_GROUP_RANGES` entry). Reset at fold
|
||||
// boundary to the uniform prior `1.0/SL_NUM_FEATURE_GROUPS`
|
||||
// so each fold begins with no inherited group bias and the
|
||||
// first `vsn_mask_ema_update` fire on the new fold EMAs the
|
||||
// measured per-group mask toward 1/6 rather than carrying
|
||||
// forward a stale fold-N estimate. Diagnostic only — no
|
||||
// consumer kernel reads slots [105..111) in this commit nor
|
||||
// in 1B-iii (the consumer attaches in a later
|
||||
// `attention_monitor` upgrade).
|
||||
"isv_vsn_mask_g0_ema" | "isv_vsn_mask_g1_ema" | "isv_vsn_mask_g2_ema"
|
||||
| "isv_vsn_mask_g3_ema" | "isv_vsn_mask_g4_ema" | "isv_vsn_mask_g5_ema" => {
|
||||
if let Some(ref fused) = self.fused_ctx {
|
||||
let idx = match name {
|
||||
"isv_vsn_mask_g0_ema"
|
||||
=> crate::cuda_pipeline::gpu_dqn_trainer::VSN_MASK_GROUP_0_EMA_INDEX,
|
||||
"isv_vsn_mask_g1_ema"
|
||||
=> crate::cuda_pipeline::gpu_dqn_trainer::VSN_MASK_GROUP_1_EMA_INDEX,
|
||||
"isv_vsn_mask_g2_ema"
|
||||
=> crate::cuda_pipeline::gpu_dqn_trainer::VSN_MASK_GROUP_2_EMA_INDEX,
|
||||
"isv_vsn_mask_g3_ema"
|
||||
=> crate::cuda_pipeline::gpu_dqn_trainer::VSN_MASK_GROUP_3_EMA_INDEX,
|
||||
"isv_vsn_mask_g4_ema"
|
||||
=> crate::cuda_pipeline::gpu_dqn_trainer::VSN_MASK_GROUP_4_EMA_INDEX,
|
||||
"isv_vsn_mask_g5_ema"
|
||||
=> crate::cuda_pipeline::gpu_dqn_trainer::VSN_MASK_GROUP_5_EMA_INDEX,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let uniform = 1.0_f32 / ml_core::state_layout::SL_NUM_FEATURE_GROUPS as f32;
|
||||
fused.trainer().write_isv_signal_at(idx, uniform);
|
||||
}
|
||||
}
|
||||
"isv_gamma_dir_eff" | "isv_gamma_mag_eff" | "isv_gamma_ord_eff" | "isv_gamma_urg_eff" => {
|
||||
// D.2 per-branch gamma slots. Reset to 0.0 at fold boundary;
|
||||
// per_branch_gamma_update GPU kernel re-populates on next epoch-boundary launch.
|
||||
|
||||
@@ -291,6 +291,8 @@ Plan 4 Task 2c.3c.2 (2026-04-24): backward infrastructure additions — two help
|
||||
|
||||
Plan 4 Task 2c.3c.3 (2026-04-24): GRN trunk backward scratch buffer allocation on `GpuDqnTrainer`. 8 new `CudaSlice<f32>` device buffers added (4 per GRN block × 2 blocks for h_s1 + h_s2): `bw_grn_h_s2_d_pre_ln` [B, SH2], `bw_grn_h_s2_d_linear_b_out` [B, 2*SH2], `bw_grn_h_s2_d_elu_out` [B, SH2], `bw_grn_h_s2_d_linear_a_out` [B, SH2], plus the four h_s1 mirrors at [B, SH1] (and 2*SH1 for the linear_b_out scratch — GLU's pre-activation has 2× hidden width because value-path + gate_pre are concatenated). The `d_pre_LN` buffer aliases both `d_glu_out` and `d_residual` (pure pass-through under `GrnBlock::backward_raw_phase1`'s pointer aliasing convention from 2c.3c.1); `d_linear_b_out` carries the phase1 → caller's Linear_b backward GEMM gradient; `d_elu_out` carries Linear_b backward's output into phase2; `d_linear_a_out` carries phase2's output to the caller's Linear_a backward GEMM. Allocated alongside the existing `alloc_backward_scratch` call in the trainer constructor (mirrors that helper's `stream.alloc_zeros::<f32>(size).map_err(...)?` pattern). Total footprint at SH1=SH2=256, B=512: 8 × 5 × 256 × 4B = 5.0 MB per fold (negligible against existing ~32 MB per-branch cuBLAS workspaces). Each field is `#[allow(dead_code)]` until 2c.3c.4's `apply_grn_trunk_backward_raw` helper consumes them — the comment `wired by Task 2c.3c.4` annotates each suppression. **Additive only — ZERO production callers in this commit**; the three backward panic gates (`backward_full`, `apply_iqn_trunk_gradient`, `apply_ensemble_diversity_backward`) remain in place until 2c.3c.4. Adam state for the new GRN tensors is auto-allocated since `m_buf`/`v_buf` are sized to `total_params + cutlass_tile_pad` which already includes the 13 GRN tensors after 2c.3a. cargo check clean at 11 warnings (baseline preserved); cargo build compiles 80 cubins (unchanged — no new kernel). +57 LOC. No new module / kernel / ISV slot / Orphan row.
|
||||
|
||||
Plan 4 Task 1B-ii (2026-04-25): VSN params + ISV slots landing — additive, no callers (param tensors written but unread; ISV slots cold-started but unread). 24 new param tensors appended at indices `[95..119)` covering the per-group VSN MLP (`Linear_1_g`/`b1_g`/`Linear_2_g`/`b2_g`) for each `FEATURE_GROUP_RANGES` entry — one quad per group, `VSN_HIDDEN_DIM = 16` (the new compile-time constant alongside `H_S2_RMS_EMA_INDEX` at the head of `gpu_dqn_trainer.rs`). Per-group element count `16*group_dim_g + 16 + 16 + 1`; sum `16*(42+32+16+16+8+7) + 6*33 = 1936 + 198 = 2134` floats (≈8.5 KB on params, mirrored on `target_params_buf` and Adam `m_buf`/`v_buf` — all four buffers grow automatically since their allocations use `compute_total_params(cfg) + cutlass_tile_pad`). `NUM_WEIGHT_TENSORS` 95 → 119. `compute_param_sizes()` rewritten to build a `[usize; NUM_WEIGHT_TENSORS]` from the existing 95-entry array literal `core` plus a runtime `for g in 0..SL_NUM_FEATURE_GROUPS` loop reading `FEATURE_GROUP_RANGES` from `ml_core::state_layout` (per Task 1A's prerequisite); `xavier_init_params_buf()` follows the same `core_fan` + per-group loop pattern with `(VSN_HIDDEN_DIM, group_dim)` for `w1_g`, `(1, VSN_HIDDEN_DIM)` for `w2_g`, and `(0,0)` (zero) for both biases — initial logits `≈ 0` therefore yield `softmax ≈ 1/SL_NUM_FEATURE_GROUPS` per group at every sample (cold-start neutral). 6 new ISV slots tail-appended for the per-group importance-mask EMA: `VSN_MASK_GROUP_0_EMA_INDEX=105` (market) through `VSN_MASK_GROUP_5_EMA_INDEX=110` (plan_isv); fingerprint pair shifted 103/104 → 111/112; `ISV_TOTAL_DIM` 105 → 113; `layout_fingerprint_seed()` extended with `VSN_MASK_GROUP_{0..5}_EMA=105..110;ISV_LAYOUT_FINGERPRINT_LO=111;ISV_LAYOUT_FINGERPRINT_HI=112;ISV_TOTAL_DIM=113;` plus the 24 new `PARAM_VSN_W1_G{g}=…;PARAM_VSN_B1_G{g}=…;PARAM_VSN_W2_G{g}=…;PARAM_VSN_B2_G{g}=…;` entries terminating at `PARAM_TOTAL_TENSORS=119` — new `LAYOUT_FINGERPRINT_CURRENT = 0x1b28321bb816f246` (was `0x5789155b683ab59c`). Constructor cold-start writes `1.0/SL_NUM_FEATURE_GROUPS = 1/6` (uniform prior — no group preference at init) into all six new ISV slots, mirroring the `H_S2_RMS_EMA = 1.0` cold-start convention from 2c.3c.5. `StateResetRegistry` extended with 6 new `FoldReset` entries (`isv_vsn_mask_g{0..5}_ema`); `training_loop.rs::reset_named_state` adds a single match arm covering all six names that recomputes the same `1.0 / SL_NUM_FEATURE_GROUPS` uniform value — no hardcoded `1.0/6.0` per `feedback_isv_for_adaptive_bounds.md` (the constant comes from `ml_core::state_layout::SL_NUM_FEATURE_GROUPS` so a future group-count change propagates automatically). Two new `pub(crate) static` cubin refs `VSN_FEATURE_SELECTION_CUBIN` and `VSN_MASK_EMA_CUBIN` added alongside the existing Plan 4 cubin block; `#[allow(dead_code)]` annotated because the runtime `load_cubin(...)` + `load_function(...)` calls are deferred to 1B-iii's forward orchestrator wire-in (the static refs themselves are compile-time `include_bytes!()` only — adding them in this commit means 1B-iii has zero new cubin-side file-touches). Mirrors the 2c.3a pattern (param tensor reshuffle without runtime callers — that landed cleanly because the runtime sites were panic-gated; here, the runtime sites simply don't exist yet so no gating needed). **Checkpoint break by intent** — fingerprint shift invalidates pre-1B-ii checkpoints at constructor load (fail-fast, no migration path per `feedback_no_legacy_aliases.md`). **No production callers in this commit** — params are written but unread (no consumer reads `param_buf[95..119)`), ISV slots are cold-started but unread (no consumer reads `isv_signals[105..111)`), and neither cubin is loaded yet. The forward orchestrator + cuBLAS `Linear_1`/`Linear_2` wire-in lands in 1B-iii; the backward orchestrator + autograd integration lands in 1B-iv. cargo check clean at 11 warnings (workspace baseline preserved); cargo build compiles 62/62 cubins clean (unchanged — no new `.cu` files, only new `pub(crate) static` refs to the 1B-i cubins). Smoke deferred — behaviour byte-identical to the f3e3ac347 / `0x5789155b683ab59c` post-bottleneck-fix baseline (700.43s, 3 folds × 5 epochs, per-fold best train Sharpe 6.53 / 80.11 / 66.72, geom-mean 39.27) since no consumer reads the new params or ISV slots; smoke re-validation with real numbers lands at 1B-iii where the forward orchestrator wire-in actually validates something. 2 new `dead_code`-annotated cubin refs (will retire `dead_code` at 1B-iii); 0 new Orphan rows (the kernels themselves were registered at 1B-i and remain Orphan-by-design until 1B-iii). 0 panic gates added/removed.
|
||||
|
||||
Plan 4 Task 1B-i (2026-04-25): VSN kernel module landing — additive, no callers. Two new CUDA kernel files registered in `build.rs::kernels_with_common` (kernel count 60 → 62, all compile clean under nvcc sm_80). Forward kernel `vsn_feature_selection_kernel.cu` exposes `vsn_softmax_and_gate_forward` — single-block-per-sample (256 threads × B blocks), reads `logits [B, num_groups]` + `state_in [B, state_dim_padded]`, computes a numerically-stable softmax over the 6 feature groups in shared memory (`max_l`+`exp_g/sum_e` serial pass on thread 0, broadcast via shmem since `num_groups=6` makes a tree reduce wasteful), saves the resulting mask `mask [B, num_groups]` for backward, and per-feature gate-multiplies the row (`gated_state[..., i] = state_in[..., i] * mask[group_of(i)]`); indices outside any group's `[group_begins[g], group_ends[g])` (the 7-element padding tail past `SL_PADDING_START`) are passed through unchanged so the byte image of the padding bytes matches the raw input regardless of downstream consumer behaviour. Backward kernel `vsn_softmax_and_gate_backward` implements the FULL softmax-over-groups Jacobian — `d_dot[g] = sum_{i in group_g} d_gated[i] * state[i]` via per-block shmem tree reduction (one pass per group, scratch reused across passes, no atomicAdd per `feedback_no_atomicadd.md`), then `d_logit[g] = mask[g] * (d_dot[g] - sum_h(mask[h] * d_dot[h]))` for the softmax derivative, plus per-feature `d_state[..., i] = d_gated[..., i] * mask[group_of(i)]` with the same passthrough convention as forward. Producer-only ISV kernel `vsn_mask_ema_kernel.cu` mirrors `h_s2_rms_ema_kernel.cu`'s shmem-reduce pattern exactly: 256 threads × 1 block, 256 floats of scratchpad reused across `num_groups` separate batch-mean reductions, EMA-updates `num_groups` adjacent ISV slots in `[isv_first_index, isv_first_index + num_groups)` with the caller-supplied `ema_alpha` (per-call parameter, not hardcoded — matches the `h_s2_rms_ema` / `reward_component_ema` plumbing convention from `training_loop.rs`). Layout convention for both kernels: row-major `[B, state_dim_padded]` for state, row-major `[B, num_groups]` for the mask (matches `state_layout.cuh` + sample-major convention from `dt_layernorm_kernel`). All reductions GPU-side per `pearl_cold_path_no_exception_to_gpu_drives.md`. Per-group `Linear_1_g` / `ReLU` / `Linear_2_g` MLPs (the importance-score producers feeding `logits`) remain caller-side cuBLAS GEMMs + the existing ReLU primitive — no new kernels for them, mirroring the `gpu_grn.rs` contract pattern. The kernels rely on `state_layout.cuh`'s Task 1A constants (`SL_NUM_FEATURE_GROUPS=6`, `SL_*_GROUP_BEGIN/END`) for compile-time bounds (the `vsn_group_of` helper unrolls a `#pragma unroll` loop bounded by `SL_NUM_FEATURE_GROUPS`); runtime `num_groups` / `group_begins[]` / `group_ends[]` are still passed in as scalar / pointer args for graph-capture symmetry with the host launchers (mirroring `gpu_grn.rs`'s style of taking dimensions as arguments). Module is **additive — ZERO production callers in this commit**; classified Orphan-by-design. Tasks 1B-iii (forward orchestrator + cuBLAS Linear_1/Linear_2 wire-in + per-group ReLU dispatch) and 1B-iv (backward orchestrator + autograd integration) wire it into the pre-trunk forward + backward path; Task 1B-ii allocates the param tensors and the 6 new ISV mask-EMA slots that `vsn_mask_ema_update`'s `isv_first_index` will point at. No checkpoint break, no fingerprint change, no new param tensors, no new ISV slots in this commit. Kernel LOC: vsn_feature_selection_kernel.cu = 262 lines, vsn_mask_ema_kernel.cu = 78 lines (340 lines total). Cubin sizes (sm_80, -O3): vsn_feature_selection_kernel.cubin = 31,392 B, vsn_mask_ema_kernel.cubin = 6,688 B. cargo check clean at 11 warnings (workspace baseline preserved); cargo build compiles 62/62 cubins clean (was 60 — `vsn_feature_selection_kernel.cubin` and `vsn_mask_ema_kernel.cubin` added). Smoke deferred — no behaviour change is possible since neither kernel is loaded by any Rust code in this commit; `multi_fold_convergence` byte-identical to the post-bottleneck-fix baseline (700.43s, 3 folds × 5 epochs, per-fold best train Sharpe 6.53 / 80.11 / 66.72, geom-mean 39.27). Smoke saved for 1B-iii where the forward orchestrator wire-in actually validates something. 2 new Orphan-by-design rows (will retire at 1B-iii when the forward kernel acquires a production caller via the encoder pre-pass).
|
||||
|
||||
Bottleneck Linear runtime-indices + missing target/DDQN bn paths (2026-04-25, 2c.3a follow-up): three related bugs from the GRN trunk reshuffle that the runtime sites for the bottleneck Linear had silently inherited. (1) `launch_cublas_forward`'s online bottleneck GEMM and `launch_cublas_backward_to`'s `goff_w_bn` / `goff_b_bn` used stale legacy indices `on_w_ptrs[24]` / `padded_byte_offset(*, 24)` and `[25]`. After 2c.3a inserted 13 GRN tensors at indices [0..13), every legacy index ≥ 4 was supposed to migrate +9; these four bottleneck sites were missed. The post-2c.3a tensor at index 24 is `b_b1out` (153 floats = 3 × 51) and index 25 is `w_b2fc` (4288 floats = 64 × 67). The bottleneck Linear was therefore reading 153-float `b_b1out` as if it were the 672-float `w_bn` weight matrix and clipping after 153 floats, while the bias-add was treating the start of `w_b2fc` as a 16-float bias. The corresponding backward writes scribbled into the same wrong slots, so the gradient flow was self-consistent but acting on completely wrong tensors. The actual `w_bn` / `b_bn` tensors at the documented indices 33 / 34 sat untouched as zeros throughout training. Smoke tests passed despite this for ~10 commits because the GRN trunk's `Linear_residual` projection (which runs in parallel with the bottleneck path and reads raw states) provided a clean compensating pathway — the network was effectively training a residual-only encoder while the bottleneck slot accumulated gradient-corruption noise. Fix: replace 4 stale index references — forward `on_w_ptrs[24]` → `[33]`, forward `on_w_ptrs[25]` → `[34]`, backward `padded_byte_offset(.., 24)` → `(.., 33)`, backward `(.., 25)` → `(.., 34)`, with comments documenting the +9 migration that 2c.3a missed. (2) Target net's `forward_target_raw` passed raw `next_states_buf` directly to the encoder; the encoder expects `[B, s1_input_dim=102]` features (post-bottleneck shape), so it was reading the first 102 columns of `next_states_buf` (`[market | ofi | tlob | mtf]`) instead of `[bn_market_proj | portfolio]`. Fix: build a `tg_bn_concat_buf` mirroring the online inline-build but on `next_states_buf` with target weights `tg_w_ptrs[33]` / `tg_w_ptrs[34]`, and pass it as `tg_s1_input_ptr` to `forward_target_raw`. New buffer fields `tg_bn_hidden_buf` (`[B, bn_dim]`) + `tg_bn_concat_buf` (`[B, concat_dim]`) allocated alongside their online siblings. (3) DDQN argmax-pass `submit_forward_ops_ddqn` had the same shape mismatch and was running the online network on raw `next_states_buf`. Fix: build `on_next_bn_concat_buf` using **online** weights `on_w_ptrs[33]` / `[34]` (DDQN argmax uses online net on next_states, distinct from target's tg-on-next path); two new buffer fields `on_next_bn_hidden_buf` / `on_next_bn_concat_buf` allocated. The shared `bn_tanh_concat_kernel` is reused at all three call sites (online-on-states, target-on-next_states, ddqn-online-on-next_states); kernel itself unchanged. No fingerprint change (no ISV slot or param tensor added — pure runtime-index correction + new device buffers). Smoke (`cargo test … multi_fold_convergence --ignored --release`, 700.43s, 3 folds × 5 epochs): all 3 `dqn_fold{N}_best.safetensors` checkpoints written; per-fold best train Sharpe 6.53 / 80.11 / 66.72 at epochs 2 / 4 / 4; geom-mean 39.27 — the strongest smoke result of the Plan 4 chain to date (Task 3 was 20.03, 2c.3c.6 was 18.80). The Sharpe lift is consistent with the bottleneck Linear now seeing its actual weights and the target / DDQN nets seeing input-shape-consistent features for TD-target / argmax-action computations. cargo check clean at 11 warnings (baseline preserved); cargo build compiles all kernel cubins clean (no kernel changes). +166 / -9 LOC, all in `gpu_dqn_trainer.rs`. 0 panic gates added/removed. 0 stubs. No new module / kernel / ISV slot / Orphan row.
|
||||
|
||||
Reference in New Issue
Block a user