feat(sp6): Pearl 2 — SAXPY per-branch loss budgets (correction-factor pattern)
SP6 sub-project 1 (Pearl 2): converts the 4 SAXPY launchers from a single
scalar budget (mean of 4 ISV branch slots) to per-branch differentiated
scaling via the correction-factor pattern.
Problem: SP5 Layer B read ISV[190..210) per-branch budget slots but
collapsed them to a scalar via sum/4.0, then passed the single scalar to
apply_c51_budget_scale / apply_cql_saxpy / apply_iqn_trunk_gradient.
Branch HEAD parameters received the same budget as trunk, defeating
per-branch differentiation.
Fix: compute_adaptive_budgets() now returns ([f32;4], [f32;4], [f32;4],
[f32;4], f32, f32, f32, f32) — four per-branch arrays + four trunk-mean
scalars. The trunk mean (D3 decision) is used for the full-buffer trunk/value
SAXPY call (preserving SP5 Layer B behavior for shared params). Branch HEAD
parameter slices receive a correction sub-launch:
correction = branch_budget[b] / trunk_mean (skip if |correction-1| <= 1e-6)
After both launches, branch HEAD slice is effectively scaled by
branch_budget[b], trunk/value is scaled by trunk_mean. No double-scaling.
New helpers added to GpuDqnTrainer:
apply_c51_budget_scale_branch(branch_idx, correction): scale_f32_ungraphed
on branch-slice [f32 elements], offset via padded_byte_offset.
apply_cql_saxpy_branch(branch_idx, correction): saxpy_f32_aux on
branch-slice of both grad_buf and cql_grad_scratch.
IQN trunk gradient: uses iqn_trunk (mean of 4 branch IQN budgets) — IQN
backward flows through trunk only; per-branch IQN routing is beyond SP6 scope.
HEALTH_DIAG: three new per-epoch info! lines emit per-branch c51/iqn/cql
budget arrays (dir/mag/ord/urg) after the intent_dist line.
State: per-branch budget arrays cached on GpuDqnTrainer
(last_*_budget_per_branch: [f32;4]) and on DqnTrainer
(last_*_budget_per_branch: Option<[f32;4]>) for diagnostics.
docs/isv-slots.md: updated Pearl 2 slot rows to reflect SP6 consumer wiring.
Verification: cargo check + release build clean (13 warnings, pre-existing).
13 sp5+sp4+state_reset_registry lib tests pass. sp5_producer_unit_tests
--no-run clean. Sanity grep for old sum/4.0 averaging pattern: empty.
Files changed (6): gpu_dqn_trainer.rs, fused_training.rs, constructor.rs,
mod.rs, training_loop.rs, docs/isv-slots.md. No Pearl 3 or Pearl 5 files touched.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4829,6 +4829,12 @@ pub struct GpuDqnTrainer {
|
||||
/// B4/G5: Adaptive gradient budget for ensemble (constant 0.05).
|
||||
/// Populated by `FusedTrainingCtx::compute_adaptive_budgets`.
|
||||
pub(crate) last_ens_budget_eff: f32,
|
||||
/// SP6 Pearl 2: per-branch budget arrays — set by compute_adaptive_budgets.
|
||||
/// Index order: dir=0, mag=1, ord=2, urg=3.
|
||||
pub(crate) last_c51_budget_per_branch: [f32; 4],
|
||||
pub(crate) last_iqn_budget_per_branch: [f32; 4],
|
||||
pub(crate) last_cql_budget_per_branch: [f32; 4],
|
||||
pub(crate) last_ens_budget_per_branch: [f32; 4],
|
||||
|
||||
/// D1/N1: Ring buffer of best-health weight snapshots for temporal self-distillation.
|
||||
pub(crate) q_snapshots: crate::cuda_pipeline::q_snapshot::SnapshotRing,
|
||||
@@ -9461,6 +9467,46 @@ impl GpuDqnTrainer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// SP6 Pearl 2: scale only branch `branch_idx` parameter slice of grad_buf by `budget`.
|
||||
///
|
||||
/// Called after `apply_c51_budget_scale(trunk_mean)` has scaled the entire grad_buf.
|
||||
/// The correction factor is `branch_budget / trunk_mean`, so the branch slice ends up
|
||||
/// scaled by `branch_budget` rather than `trunk_mean`.
|
||||
///
|
||||
/// Branch b owns tensors `(8+4b)..(12+4b)` in the padded flat layout.
|
||||
/// Padding bytes in `grad_buf` are always zero so scaling them is a no-op.
|
||||
pub fn apply_c51_budget_scale_branch(&mut self, branch_idx: usize, budget: f32) -> Result<(), MLError> {
|
||||
if (budget - 1.0).abs() < 1e-6 {
|
||||
return Ok(());
|
||||
}
|
||||
let f32_size = std::mem::size_of::<f32>();
|
||||
let param_sizes = compute_param_sizes(&self.config);
|
||||
let first_tensor = 8 + branch_idx * 4;
|
||||
let start_bytes = padded_byte_offset(¶m_sizes, first_tensor) as usize;
|
||||
let end_bytes = padded_byte_offset(¶m_sizes, first_tensor + 4) as usize;
|
||||
let start_elems = (start_bytes / f32_size) as i32;
|
||||
let len_elems = ((end_bytes - start_bytes) / f32_size) as i32;
|
||||
if len_elems == 0 { return Ok(()); }
|
||||
let grad_slice_ptr = self.ptrs.grad_buf + (start_elems as u64) * (f32_size as u64);
|
||||
let blocks = ((len_elems as u32 + 255) / 256) as u32;
|
||||
unsafe {
|
||||
self.stream
|
||||
.launch_builder(&self.scale_f32_ungraphed)
|
||||
.arg(&grad_slice_ptr)
|
||||
.arg(&budget)
|
||||
.arg(&len_elems)
|
||||
.launch(LaunchConfig {
|
||||
grid_dim: (blocks, 1, 1),
|
||||
block_dim: (256, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
})
|
||||
.map_err(|e| MLError::ModelError(format!(
|
||||
"c51_budget_scale_branch[{branch_idx}]: {e}"
|
||||
)))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add CQL gradient scratch to grad_buf via plain SAXPY.
|
||||
///
|
||||
/// Called after `apply_cql_gradient` populated `cql_grad_scratch`.
|
||||
@@ -9493,6 +9539,45 @@ impl GpuDqnTrainer {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// SP6 Pearl 2: SAXPY only branch `branch_idx` parameter slice:
|
||||
/// grad_buf[branch_slice] += budget * cql_grad_scratch[branch_slice]
|
||||
///
|
||||
/// Called after `apply_cql_saxpy(trunk_mean)` has performed the full-buffer SAXPY.
|
||||
/// The correction factor is `branch_budget / trunk_mean`, so the branch slice ends up
|
||||
/// accumulating `branch_budget * cql_scratch` rather than `trunk_mean * cql_scratch`.
|
||||
///
|
||||
/// Branch b owns tensors `(8+4b)..(12+4b)` in the padded flat layout.
|
||||
pub fn apply_cql_saxpy_branch(&mut self, branch_idx: usize, budget: f32) -> Result<(), MLError> {
|
||||
let f32_size = std::mem::size_of::<f32>();
|
||||
let param_sizes = compute_param_sizes(&self.config);
|
||||
let first_tensor = 8 + branch_idx * 4;
|
||||
let start_bytes = padded_byte_offset(¶m_sizes, first_tensor) as usize;
|
||||
let end_bytes = padded_byte_offset(¶m_sizes, first_tensor + 4) as usize;
|
||||
let start_elems = (start_bytes / f32_size) as i32;
|
||||
let len_elems = ((end_bytes - start_bytes) / f32_size) as i32;
|
||||
if len_elems == 0 { return Ok(()); }
|
||||
let grad_ptr = self.ptrs.grad_buf + (start_elems as u64) * (f32_size as u64);
|
||||
let scratch_ptr = self.ptrs.cql_grad_scratch + (start_elems as u64) * (f32_size as u64);
|
||||
let blocks = ((len_elems as u32 + 255) / 256) as u32;
|
||||
unsafe {
|
||||
self.stream
|
||||
.launch_builder(&self.saxpy_f32_aux)
|
||||
.arg(&grad_ptr)
|
||||
.arg(&scratch_ptr)
|
||||
.arg(&budget)
|
||||
.arg(&len_elems)
|
||||
.launch(LaunchConfig {
|
||||
grid_dim: (blocks, 1, 1),
|
||||
block_dim: (256, 1, 1),
|
||||
shared_mem_bytes: 0,
|
||||
})
|
||||
.map_err(|e| MLError::ModelError(format!(
|
||||
"cql_saxpy_branch[{branch_idx}]: {e}"
|
||||
)))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// F5/D2: Inject the Q-gap barrier gradient into cql_d_adv_logits and cql_d_value_logits
|
||||
/// (direction branch b0 only) via atomicAdd.
|
||||
///
|
||||
@@ -16943,6 +17028,10 @@ impl GpuDqnTrainer {
|
||||
last_cql_budget_eff: 0.00,
|
||||
last_c51_budget_eff: 0.55,
|
||||
last_ens_budget_eff: 0.05,
|
||||
last_c51_budget_per_branch: [0.55; 4],
|
||||
last_iqn_budget_per_branch: [0.40; 4],
|
||||
last_cql_budget_per_branch: [0.00; 4],
|
||||
last_ens_budget_per_branch: [0.05; 4],
|
||||
q_snapshots: crate::cuda_pipeline::q_snapshot::SnapshotRing::new(
|
||||
stream_for_snapshots,
|
||||
total_params,
|
||||
|
||||
@@ -1878,8 +1878,9 @@ impl FusedTrainingCtx {
|
||||
// not as norm clips. This preserves gradient magnitude variation while steering
|
||||
// component balance. Safety is still handled by the single global clip in Adam.
|
||||
|
||||
// B4/G5: Compute adaptive budgets once per step from ISV health/regime signals.
|
||||
let (c51_budget, iqn_budget, cql_budget, _ens_budget) = self.compute_adaptive_budgets();
|
||||
// SP6 Pearl 2: Compute adaptive budgets — returns per-branch arrays + trunk means.
|
||||
let (c51_branch, iqn_branch, cql_branch, _ens_branch,
|
||||
c51_trunk, iqn_trunk, cql_trunk, _ens_trunk) = self.compute_adaptive_budgets();
|
||||
|
||||
// F8/G5: Scale C51 contribution in grad_buf by c51_budget.
|
||||
// C51 backward already wrote into grad_buf during submit_forward_ops_main (Phase 2).
|
||||
@@ -1891,8 +1892,18 @@ impl FusedTrainingCtx {
|
||||
// When `c51_budget ≈ 1.0` the scale is a no-op and delta = 0.0.
|
||||
self.trainer.grad_decomp_snapshot_c51_bs()
|
||||
.map_err(|e| anyhow::anyhow!("Task 2.0 grad_decomp_snapshot_c51_bs: {e}"))?;
|
||||
self.trainer.apply_c51_budget_scale(c51_budget)
|
||||
.map_err(|e| anyhow::anyhow!("c51_budget_scale: {e}"))?;
|
||||
// SP6 Pearl 2: trunk/value first (mean budget), then per-branch correction sub-launches.
|
||||
// Branch slice has already been scaled by c51_trunk; correction = branch[b]/trunk_mean
|
||||
// so branch slice ends up scaled by c51_branch[b].
|
||||
self.trainer.apply_c51_budget_scale(c51_trunk)
|
||||
.map_err(|e| anyhow::anyhow!("c51_budget_scale_trunk: {e}"))?;
|
||||
for b in 0..4_usize {
|
||||
let correction = if c51_trunk > 1e-6 { c51_branch[b] / c51_trunk } else { 1.0_f32 };
|
||||
if (correction - 1.0).abs() > 1e-6 {
|
||||
self.trainer.apply_c51_budget_scale_branch(b, correction)
|
||||
.map_err(|e| anyhow::anyhow!("c51_budget_scale_branch[{b}]: {e}"))?;
|
||||
}
|
||||
}
|
||||
self.trainer.grad_decomp_launch_c51_bs()
|
||||
.map_err(|e| anyhow::anyhow!("Task 2.0 grad_decomp c51_bs: {e}"))?;
|
||||
|
||||
@@ -2265,8 +2276,9 @@ impl FusedTrainingCtx {
|
||||
if iqn_ok {
|
||||
if let Some(ref mut iqn) = self.gpu_iqn {
|
||||
let d_h_s2_ptr = iqn.d_h_s2_raw_ptr();
|
||||
// SP6 Pearl 2: IQN operates on trunk only — use trunk mean (no per-branch sub-launch).
|
||||
self.trainer.apply_iqn_trunk_gradient(
|
||||
d_h_s2_ptr, &mut self.online_dueling, iqn_budget,
|
||||
d_h_s2_ptr, &mut self.online_dueling, iqn_trunk,
|
||||
).map_err(|e| anyhow::anyhow!("IQN trunk gradient: {e}"))?;
|
||||
|
||||
let tau = self.trainer.read_isv_signal_at(TAU_EFF_INDEX);
|
||||
@@ -2321,8 +2333,16 @@ impl FusedTrainingCtx {
|
||||
// `grad_buf`.
|
||||
self.trainer.grad_decomp_snapshot_cql_sx()
|
||||
.map_err(|e| anyhow::anyhow!("Task 2.0 grad_decomp_snapshot_cql_sx: {e}"))?;
|
||||
self.trainer.apply_cql_saxpy(cql_budget)
|
||||
.map_err(|e| anyhow::anyhow!("CQL SAXPY: {e}"))?;
|
||||
// SP6 Pearl 2: trunk/value SAXPY first (mean budget), then per-branch corrections.
|
||||
self.trainer.apply_cql_saxpy(cql_trunk)
|
||||
.map_err(|e| anyhow::anyhow!("CQL SAXPY trunk: {e}"))?;
|
||||
for b in 0..4_usize {
|
||||
let correction = if cql_trunk > 1e-6 { cql_branch[b] / cql_trunk } else { 1.0_f32 };
|
||||
if (correction - 1.0).abs() > 1e-6 {
|
||||
self.trainer.apply_cql_saxpy_branch(b, correction)
|
||||
.map_err(|e| anyhow::anyhow!("CQL SAXPY branch[{b}]: {e}"))?;
|
||||
}
|
||||
}
|
||||
self.trainer.grad_decomp_launch_cql_sx()
|
||||
.map_err(|e| anyhow::anyhow!("Task 2.0 grad_decomp cql_sx: {e}"))?;
|
||||
}
|
||||
@@ -3278,49 +3298,46 @@ impl FusedTrainingCtx {
|
||||
self.trainer.last_sarsa_tau_factor
|
||||
}
|
||||
|
||||
/// B4/G5: Compute adaptive gradient budgets from learning_health and regime_stability.
|
||||
/// Returns (c51_budget, iqn_budget, cql_budget, ens_budget). Caches to trainer fields
|
||||
/// for HEALTH_DIAG logging.
|
||||
/// SP6 Pearl 2: Compute adaptive gradient budgets from ISV[190..210).
|
||||
///
|
||||
/// SP5 Layer B (Pearl 2): reads per-branch budget signals from ISV[190..210)
|
||||
/// and collapses them to a single effective scale per loss component by
|
||||
/// averaging across the 4 branches. The Layer A producer (SP5 Task A3) runs
|
||||
/// the Wiener-optimal EMA (Pearls A+D) so the values already encode
|
||||
/// health/regime dynamics — no second-hand formula is needed here.
|
||||
/// Returns `(c51_branch, iqn_branch, cql_branch, ens_branch, c51_trunk, iqn_trunk,
|
||||
/// cql_trunk, ens_trunk)`. The per-branch arrays feed per-branch SAXPY correction
|
||||
/// sub-launches on branch HEAD parameter slices. The trunk scalars (mean of 4 branch
|
||||
/// budgets) feed the full-buf trunk/value scaling call — preserving SP5 Layer B
|
||||
/// behaviour for shared parameters (D3 decision in SP6 spec).
|
||||
///
|
||||
/// Cold-start floor: ISV reads 0 before the first observation; the
|
||||
/// normalisation `/ 4.0` and clamp ensure we never pass 0 to the loss
|
||||
/// scalers. The floor values (0.05 C51, 0.05 IQN, 0.02 CQL, 0.02 Ens)
|
||||
/// are Invariant 1 carve-outs (numerical-stability), not tuned constants.
|
||||
pub(crate) fn compute_adaptive_budgets(&mut self) -> (f32, f32, f32, f32) {
|
||||
// Mean of 4 per-branch slots → single effective scale.
|
||||
let c51_budget = ((0..4_usize)
|
||||
.map(|b| self.read_isv_signal_at(BUDGET_C51_BASE + b))
|
||||
.sum::<f32>()
|
||||
/ 4.0_f32)
|
||||
.max(0.05_f32);
|
||||
let iqn_budget = ((0..4_usize)
|
||||
.map(|b| self.read_isv_signal_at(BUDGET_IQN_BASE + b))
|
||||
.sum::<f32>()
|
||||
/ 4.0_f32)
|
||||
.max(0.05_f32);
|
||||
let cql_budget = ((0..4_usize)
|
||||
.map(|b| self.read_isv_signal_at(BUDGET_CQL_BASE + b))
|
||||
.sum::<f32>()
|
||||
/ 4.0_f32)
|
||||
.max(0.02_f32);
|
||||
let ens_budget = ((0..4_usize)
|
||||
.map(|b| self.read_isv_signal_at(BUDGET_ENS_BASE + b))
|
||||
.sum::<f32>()
|
||||
/ 4.0_f32)
|
||||
.max(0.02_f32);
|
||||
|
||||
self.trainer.last_iqn_budget_eff = iqn_budget;
|
||||
self.trainer.last_cql_budget_eff = cql_budget;
|
||||
self.trainer.last_c51_budget_eff = c51_budget;
|
||||
self.trainer.last_ens_budget_eff = ens_budget;
|
||||
|
||||
(c51_budget, iqn_budget, cql_budget, ens_budget)
|
||||
/// Cold-start floor: ISV slots read 0 before first observation. Floors (0.05 C51,
|
||||
/// 0.05 IQN, 0.02 CQL, 0.02 Ens) are Invariant 1 carve-outs (numerical-stability,
|
||||
/// not tuned constants).
|
||||
pub(crate) fn compute_adaptive_budgets(
|
||||
&mut self,
|
||||
) -> ([f32; 4], [f32; 4], [f32; 4], [f32; 4], f32, f32, f32, f32) {
|
||||
let mut c51 = [0.0_f32; 4];
|
||||
let mut iqn = [0.0_f32; 4];
|
||||
let mut cql = [0.0_f32; 4];
|
||||
let mut ens = [0.0_f32; 4];
|
||||
for b in 0..4_usize {
|
||||
c51[b] = self.read_isv_signal_at(BUDGET_C51_BASE + b).max(0.05_f32);
|
||||
iqn[b] = self.read_isv_signal_at(BUDGET_IQN_BASE + b).max(0.05_f32);
|
||||
cql[b] = self.read_isv_signal_at(BUDGET_CQL_BASE + b).max(0.02_f32);
|
||||
ens[b] = self.read_isv_signal_at(BUDGET_ENS_BASE + b).max(0.02_f32);
|
||||
}
|
||||
// Trunk/value use mean of 4 branch budgets (D3 decision in SP6 spec).
|
||||
let c51_trunk = c51.iter().sum::<f32>() / 4.0_f32;
|
||||
let iqn_trunk = iqn.iter().sum::<f32>() / 4.0_f32;
|
||||
let cql_trunk = cql.iter().sum::<f32>() / 4.0_f32;
|
||||
let ens_trunk = ens.iter().sum::<f32>() / 4.0_f32;
|
||||
// Cache per-branch arrays for HEALTH_DIAG logging.
|
||||
self.trainer.last_c51_budget_per_branch = c51;
|
||||
self.trainer.last_iqn_budget_per_branch = iqn;
|
||||
self.trainer.last_cql_budget_per_branch = cql;
|
||||
self.trainer.last_ens_budget_per_branch = ens;
|
||||
// Cache trunk scalars for backward-compat accessors.
|
||||
self.trainer.last_c51_budget_eff = c51_trunk;
|
||||
self.trainer.last_iqn_budget_eff = iqn_trunk;
|
||||
self.trainer.last_cql_budget_eff = cql_trunk;
|
||||
self.trainer.last_ens_budget_eff = ens_trunk;
|
||||
(c51, iqn, cql, ens, c51_trunk, iqn_trunk, cql_trunk, ens_trunk)
|
||||
}
|
||||
|
||||
/// B4/G5: Last adaptive IQN gradient budget (0.10 + 0.30×health).
|
||||
|
||||
@@ -786,6 +786,9 @@ impl DQNTrainer {
|
||||
last_iqn_budget_eff: None,
|
||||
last_cql_budget_eff: None,
|
||||
last_c51_budget_eff: None,
|
||||
last_c51_budget_per_branch: None,
|
||||
last_iqn_budget_per_branch: None,
|
||||
last_cql_budget_per_branch: None,
|
||||
last_tau_eff: None,
|
||||
last_sarsa_tau_factor: None,
|
||||
last_gamma_eff: None,
|
||||
|
||||
@@ -755,6 +755,10 @@ pub struct DQNTrainer {
|
||||
pub(crate) last_iqn_budget_eff: Option<f32>,
|
||||
pub(crate) last_cql_budget_eff: Option<f32>,
|
||||
pub(crate) last_c51_budget_eff: Option<f32>,
|
||||
/// SP6 Pearl 2 — per-branch budget arrays (dir=0, mag=1, ord=2, urg=3)
|
||||
pub(crate) last_c51_budget_per_branch: Option<[f32; 4]>,
|
||||
pub(crate) last_iqn_budget_per_branch: Option<[f32; 4]>,
|
||||
pub(crate) last_cql_budget_per_branch: Option<[f32; 4]>,
|
||||
/// B2/G3 — tau effective
|
||||
pub(crate) last_tau_eff: Option<f32>,
|
||||
/// B3/G4 — Expected SARSA temperature scaling factor
|
||||
|
||||
@@ -2749,6 +2749,10 @@ impl DQNTrainer {
|
||||
self.last_iqn_budget_eff = Some(fused.last_iqn_budget_eff());
|
||||
self.last_cql_budget_eff = Some(fused.last_cql_budget_eff());
|
||||
self.last_c51_budget_eff = Some(fused.last_c51_budget_eff());
|
||||
// SP6 Pearl 2: cache per-branch budget arrays.
|
||||
self.last_c51_budget_per_branch = Some(fused.trainer().last_c51_budget_per_branch);
|
||||
self.last_iqn_budget_per_branch = Some(fused.trainer().last_iqn_budget_per_branch);
|
||||
self.last_cql_budget_per_branch = Some(fused.trainer().last_cql_budget_per_branch);
|
||||
}
|
||||
|
||||
// Plan 2 D.2: propagate direction-branch gamma_eff from ISV for logging.
|
||||
@@ -3975,6 +3979,27 @@ impl DQNTrainer {
|
||||
self.last_eval_intent_magnitude_dist[2],
|
||||
);
|
||||
|
||||
// SP6 Pearl 2: per-branch budget HEALTH_DIAG lines — enables Layer C debugging
|
||||
// by showing how much each branch's loss budget differs from the trunk mean.
|
||||
if let Some(arr) = self.last_c51_budget_per_branch {
|
||||
tracing::info!(
|
||||
"HEALTH_DIAG[{}]: c51_budget_per_branch [dir={:.4} mag={:.4} ord={:.4} urg={:.4}]",
|
||||
epoch, arr[0], arr[1], arr[2], arr[3],
|
||||
);
|
||||
}
|
||||
if let Some(arr) = self.last_iqn_budget_per_branch {
|
||||
tracing::info!(
|
||||
"HEALTH_DIAG[{}]: iqn_budget_per_branch [dir={:.4} mag={:.4} ord={:.4} urg={:.4}]",
|
||||
epoch, arr[0], arr[1], arr[2], arr[3],
|
||||
);
|
||||
}
|
||||
if let Some(arr) = self.last_cql_budget_per_branch {
|
||||
tracing::info!(
|
||||
"HEALTH_DIAG[{}]: cql_budget_per_branch [dir={:.4} mag={:.4} ord={:.4} urg={:.4}]",
|
||||
epoch, arr[0], arr[1], arr[2], arr[3],
|
||||
);
|
||||
}
|
||||
|
||||
// C.2 Plan 3 Task 1 (spec §4.C.2): reward_split HEALTH_DIAG line.
|
||||
// Reads 6 ISV EMA slots updated by the GPU reward_component_ema kernel
|
||||
// launched just above. CPU-side code only reads; GPU wrote the values.
|
||||
|
||||
@@ -133,10 +133,10 @@ Constants live in `crates/ml/src/cuda_pipeline/sp5_isv_slots.rs`.
|
||||
| [178..182) | `ATOM_V_HALF_BASE` | per-branch [4] | Pearl 1 | C51 atom half-width |
|
||||
| [182..186) | `ATOM_HEADROOM_BASE` | per-branch [4] | Pearl 1 | C51 atom headroom |
|
||||
| [186..190) | `ATOM_CLIP_RATE_BASE` | per-branch [4] | Pearl 1 | C51 atom clip rate |
|
||||
| [190..194) | `BUDGET_C51_BASE` | per-branch [4] | Pearl 2 | C51 loss budget weight |
|
||||
| [194..198) | `BUDGET_IQN_BASE` | per-branch [4] | Pearl 2 | IQN loss budget weight |
|
||||
| [198..202) | `BUDGET_CQL_BASE` | per-branch [4] | Pearl 2 | CQL loss budget weight |
|
||||
| [202..206) | `BUDGET_ENS_BASE` | per-branch [4] | Pearl 2 | Ensemble loss budget weight |
|
||||
| [190..194) | `BUDGET_C51_BASE` | per-branch [4] | Pearl 2 | C51 loss budget weight. SP6 Pearl 2: `compute_adaptive_budgets()` reads individually, applies correction-factor sub-launches via `apply_c51_budget_scale_branch`. |
|
||||
| [194..198) | `BUDGET_IQN_BASE` | per-branch [4] | Pearl 2 | IQN loss budget weight. SP6 Pearl 2: used as trunk-mean only (`iqn_trunk`) — IQN backward targets trunk params exclusively. |
|
||||
| [198..202) | `BUDGET_CQL_BASE` | per-branch [4] | Pearl 2 | CQL loss budget weight. SP6 Pearl 2: `compute_adaptive_budgets()` reads individually, applies correction-factor sub-launches via `apply_cql_saxpy_branch`. |
|
||||
| [202..206) | `BUDGET_ENS_BASE` | per-branch [4] | Pearl 2 | Ensemble loss budget weight. SP6 Pearl 2: used as trunk-mean only. |
|
||||
| [206..210) | `FLATNESS_BASE` | per-branch [4] | Pearl 2 | Loss flatness diagnostic |
|
||||
| [210..214) | `NOISY_SIGMA_BASE` | per-branch [4] | Pearl 3 | NoisyNet σ level |
|
||||
| [214..218) | `SIGMA_FRACTION_BASE` | per-branch [4] | Pearl 3 | NoisyNet σ fraction |
|
||||
|
||||
Reference in New Issue
Block a user