feat(sp20): c51_loss launcher aux_conf arg + Phase 5 gate tests

Threads `self.aux_conf_at_state_buf` into the `c51_loss_batched` launch
in `GpuDqnTrainer::launch_c51_loss`. Position matches the kernel's
appended trailing arg from the previous commit.

Tests added in `crates/ml-dqn/src/gpu_replay_buffer.rs::tests`:

  - `aux_gate_high_confidence_passes_full_target` (CPU pure-math):
    gate(aux_conf=0.5, threshold=0.10, temp=0.05) > 0.99 proves
    high-confidence reward pass-through.
  - `aux_gate_low_confidence_attenuates_reward` (CPU pure-math):
    gate(aux_conf=0.02, threshold=0.10, temp=0.05) < 0.20 proves
    the uncertain-state neutralizer semantic.
  - `aux_gate_temp_floor_keeps_gate_finite` (CPU pure-math):
    sweeps {temp, aux_conf, threshold} and asserts finite gate ∈ [0,1]
    across the ISV-controllable parameter range — proves the
    fmaxf(temp, 1e-3) floor keeps the kernel numerically safe.
  - `aux_conf_direct_to_trainer_gather_populates_destination` (GPU
    behavioral): wires a fresh CudaSlice<f32> as the trainer
    destination, inserts 8 transitions with strictly-positive distinct
    aux_conf values, samples 1, asserts the trainer destination
    buffer post-sample holds a value from the inserted set (NOT the
    alloc_zeros sentinel) — proves the direct-gather wiring actually
    populates the trainer buffer with non-trivial data.

All 3 CPU math tests + 1 GPU integration test pass on RTX 3050.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-10 14:50:11 +02:00
parent 96b76d9298
commit ab4a7db33c
3 changed files with 240 additions and 0 deletions

View File

@@ -30691,6 +30691,13 @@ impl GpuDqnTrainer {
.arg(&cvar_alpha_buf_ptr)
// ── B3/G4: ISV signals for health-scaled Expected SARSA temperature ──
.arg(&self.isv_signals_dev_ptr)
// ── SP20 Phase 5: per-sample aux_conf at sampled state ──
// PER's `gather_f32_scalar` writes here every step (when
// `set_trainer_aux_conf_ptr` is wired at trainer init);
// c51 reads it for the reward gate at the Bellman target.
// NULL-tolerant in-kernel: gate=1.0 → identity if either
// aux_conf or isv_signals is NULL.
.arg(&self.aux_conf_at_state_buf)
.launch(LaunchConfig {
grid_dim: (b as u32, 1, 1),
block_dim: (256, 1, 1),