1 Commits

Author SHA1 Message Date
jgrusewski
1c645264e6 test(sp12): GPU oracle tests for the 3 reward math changes
Adds the GPU oracle test scaffold deferred from commit 17cfbb250.

Refactored 3 reward composition functions into device-inline functions
in trade_physics.cuh for testability without behavior drift:
  - compute_asymmetric_capped_pnl
  - compute_min_hold_penalty
  - compute_lump_sum_opp_cost

experience_kernels.cu's segment_complete branch now calls these helpers
in place of the inline math; the lump-sum opp_cost helper preserves the
production multiplication order via parens so the refactor is bit-
equivalent under f32 rounding to the inline computation it replaces.
The min-hold helper subsumes the original `if (hold_time < target)`
early-exit (returns 0 when at/past target — capped_pnl - shaping_scale
* 0 == capped_pnl).

New test kernel sp12_reward_math_test_kernel.cu exposes the device
functions for GPU oracle testing. The wrapper is single-thread / single-
block (the math is pure register arithmetic) and writes outputs to
mapped-pinned buffers with __threadfence_system() for host visibility,
matching the thompson_test_kernel / sp4_histogram_p99_test_kernel
pattern. Cubin registered in build.rs alongside the other test kernels.

New test module crates/ml/tests/sp12_reward_math_tests.rs covers all
three changes:

  Asymmetric cap (5 tests):
    - clips above pos_cap (+20 -> +5)
    - clips below neg_cap (-20 -> -10)
    - passes through zero
    - exact at pos_cap (+5 -> +5)
    - exact at neg_cap (-10 -> -10)

  Min-hold soft penalty (5 tests):
    - at target -> zero penalty
    - at hold=0,target=30,T=10,max=3 -> 2.25 (factor 30/40 = 0.75)
    - mid-deficit hold=15 -> 1.8 (factor 15/25 = 0.6)
    - past target -> no penalty (early-exit branch)
    - temperature smooths transition (T=10 -> 2.25 vs T=20 -> 1.8)

  Lump-sum opp_cost (4 tests):
    - exiting + position + hold_time -> -0.01
    - zero position -> zero cost
    - zero hold_time -> zero cost
    - negative position -> uses |position| (matches +0.5 magnitude)

Per feedback_no_cpu_test_fallbacks: GPU oracle pattern (synthetic
inputs through real device functions, verified against analytical
expected outputs). No CPU reference impl. Per
feedback_no_htod_htoh_only_mapped_pinned: every CPU<->GPU buffer is
a MappedF32Buffer; zero htod_copy / dtoh_sync_copy. Tests gated with
#[ignore = "requires GPU"] to match the existing sp4/sp5/sp11
producer-test convention.

Verified on RTX 3050 Ti (sm_86):
  SQLX_OFFLINE=true CUDA_COMPUTE_CAP=86 \
    cargo test -p ml --test sp12_reward_math_tests --features cuda \
      -- --ignored --nocapture
  -> 14 passed; 0 failed (3.32s).

Build: SQLX_OFFLINE=true cargo check -p ml --lib --features cuda clean.

Audit doc updated in docs/dqn-wire-up-audit.md (Invariant 7).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 19:32:18 +02:00