docs(sp5): Task A5 — correct pearl_5_iqn_tau header comment direction

Code-quality review caught that the kernel header described the τ
shift as 'toward the dense tail' but the actual math
(`t = default + skew × SKEW_SHIFT`) shifts τ in the SAME direction
as the skew sign — i.e. toward the LONG (sparse) tail, not the dense
region.

For a left-skewed distribution (long left tail, mode + dense mass on
the right), skew < 0 → τ shifts down toward 0 → IQN samples more of
the LEFT tail (sparse). The intent is risk-aware quantile coverage:
when there's tail mass that the symmetric 5-tuple under-represents,
lean the grid toward that tail. This matches risk-aware IQN where
financial risk modeling cares about downside coverage.

The math is correct and faithful to the plan's formula; only the
prose description was reversed. Replaced the one-liner with a 7-line
explanation that names the actual semantic clearly ('leaning the
quantile grid INTO the asymmetry direction').

Comment-only change. Cubin rebuild clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-05-01 22:59:46 +02:00
parent b1ef312a40
commit 1aaa1cb0bc
2 changed files with 9 additions and 1 deletions

View File

@@ -5,7 +5,13 @@
// Reads q_skew_per_branch[b] from scratch_buf at skew_idx_base + b (written by
// q_skew_kurtosis_update). Computes per-branch 5-quantile τ schedule:
// - Symmetric default: {0.05, 0.25, 0.5, 0.75, 0.95}
// - Skewed: shift each τ by skew × SKEW_SHIFT toward the dense tail
// - Skewed: shift each τ in the SAME DIRECTION as the skew sign — i.e.
// left-skewed Q (skew<0, long left tail) shifts every τ toward 0, giving
// the IQN sampler more resolution over the long left tail; right-skewed
// Q (skew>0) shifts τ toward 1 to oversample the long right tail. This
// leans the quantile grid INTO the asymmetry direction, which gives more
// IQN samples where the distribution has tail mass that the symmetric
// 5-tuple under-represents.
// Clamps every τ to the structural envelope [0.01, 0.99].
//
// 4-thread single-block (one per branch); each thread emits 5 contiguous

View File

@@ -2,6 +2,8 @@
**Status:** Populated during Plan 1 Task 6 (A.5 orphan audit). Updated on every commit per Invariant 7.
SP5 Task A5 fix-up — pearl_5_iqn_tau header comment corrected (2026-05-01): code-quality review caught that the kernel header described the τ shift as "toward the dense tail" but the actual math (`t = default + skew × 0.05`) shifts τ in the SAME direction as the skew sign — i.e. toward the LONG (sparse) tail, not the dense region. The math is correct and faithful to the plan's formula; only the prose was reversed. Replaced the misleading one-liner with a 7-line explanation: left-skewed Q (skew<0) shifts τ toward 0, right-skewed (skew>0) shifts τ toward 1, "leaning the quantile grid INTO the asymmetry direction" so the IQN sampler gets more resolution over the long tail (where the symmetric 5-tuple under-represents tail mass). Comment-only change; behavior unchanged. Touched: `crates/ml/src/cuda_pipeline/pearl_5_iqn_tau_kernel.cu` (header rewrite, +7/-1 LOC). Cubin rebuild clean.
SP5 Task A5 — Pearl 5 per-branch IQN τ schedule GPU producer (Layer A, 2026-05-01): two new CUDA kernels (`q_skew_kurtosis_kernel.cu`, `pearl_5_iqn_tau_kernel.cu`) land as Layer A additive producers that feed ISV slots [250..270) with per-branch IQN quantile-τ schedules. No IQN τ-selection consumer migration in this commit — Layer B wires the τ consumer when distributional action-selection is migrated. `q_skew_kurtosis_update`: single-block 4-thread kernel (one thread per branch), reads `save_q_online[B × 13]` row-major; two-pass central moments (mean then m2/m3/m4); computes `skew = m3 / (m2^1.5 + EPS_DIV)` and `ex_kurt = m4 / (m2^2 + EPS_DIV) - 3`; EPS_DIV=1e-12 is an Invariant 1 anchor (numerical stability for degenerate constant-Q distribution); clamps skew to [-3,+3] and ex_kurt to [-3,+30] (structural envelopes); writes skew[4] to scratch[171..175) and ex_kurt[4] to scratch[175..179); `__threadfence_system()` after writes. No atomicAdd — one thread per branch with independent scratch writes. `pearl_5_iqn_tau_update`: single-block 4-thread kernel, reads scratch_buf[skew_idx_base + b]; shifts symmetric default schedule {0.05,0.25,0.5,0.75,0.95} by `skew × SKEW_SHIFT=0.05` (Invariant 1 anchor — modest schedule shift, max ±0.15 at clamped skew=±3); clamps every τ to structural envelope [0.01,0.99] (Invariant 1 anchor — IQN requires τ strictly in (0,1)); writes 20 τ floats to scratch_out[tau_idx_base + b×5 + q] at scratch[179..199). `launch_sp5_pearl_5_iqn_tau` fires both kernels sequentially then 20 `launch_apply_pearls` calls (4 branches × 5 quantiles → ISV[IQN_TAU_BASE=250..270)). ALPHA_META=1e-3 (same as SP4 default; Pearl 5's τ signal is direct per-step output, not an EMA-of-EMA, so slower smoothing relative to Pearl 4's 5e-4 not needed). Wiener offset formula: `base_wiener_offset + (isv_slot - SP5_SLOT_BASE) × 3` where `base_wiener_offset = SP4_PRODUCER_COUNT × 3 = 213`; for ISV slot 250 (first IQN_TAU slot): wiener_off = 213 + (250-174)×3 = 441. Buffer growth: `producer_step_scratch_buf` 171→199 slots (SP5_SCRATCH_TOTAL); 3 new scratch constants `SCRATCH_PEARL_5_SKEW=171`, `SCRATCH_PEARL_5_EX_KURT=175`, `SCRATCH_PEARL_5_TAU=179`. wiener_state_buf stays at 543 (SP5_WIENER_TOTAL_FLOATS — sized at Task A1 for all 110 SP5 slots, no growth). StateResetRegistry gains 1 new FoldReset entry: `sp5_iqn_tau` (ISV[250..270)) — Pearl A sentinel 0 at fold boundary triggers first-observation replacement to zero-skew symmetric default {0.05,0.25,0.5,0.75,0.95}. Wire-up in training_loop.rs: `launch_sp5_pearl_5_iqn_tau()` called immediately after `launch_sp5_pearl_4_adam_hparams()` with `tracing::warn!(error = %e, ...)` on error. Two GPU-only `#[ignore = "requires GPU"]` unit tests: (10) constant Q input → zero skew → symmetric default τ (chains both kernels); (11) injected skew=-1.0 → τ shifted down by 0.05, τ[0] clamped to 0.01 floor (exercises SKEW_SHIFT mechanism and structural envelope clamp). No CPU compute, no HtoD/HtoH, no atomicAdd, no stubs, no consumer migration. Touched: `cuda_pipeline/q_skew_kurtosis_kernel.cu` (new), `cuda_pipeline/pearl_5_iqn_tau_kernel.cu` (new), `build.rs` (+2 cubin entries), `cuda_pipeline/gpu_dqn_trainer.rs` (SP5_SCRATCH_TOTAL 171→199 + 3 new scratch constants + 2 static cubins + 2 struct fields + cubin loaders + struct initializer + launch method), `trainers/dqn/state_reset_registry.rs` (+1 FoldReset entry), `trainers/dqn/trainer/training_loop.rs` (+5 LOC after Pearl 4 launch), `tests/sp5_producer_unit_tests.rs` (+2 GPU-only tests + 2 cubin constants + 2 loader helpers + module docstring). Hard rules: feedback_no_cpu_compute_strict, feedback_no_atomicadd, feedback_no_htod_htoh_only_mapped_pinned, feedback_no_stubs, feedback_no_partial_refactor (no consumer migration — Layer A additive only).
SP5 docs/comment-only fix-up — close two minor review findings before Layer A Task A5 (2026-05-01): two minor code-review nits accumulated across A1-A4 reviews; closed in a single docs/comment-only commit. (1) `tests/sp5_producer_unit_tests.rs` module docstring (A4 review) updated to enumerate all 9 SP5 Layer A tests (q_branch_stats + pearl_1 + pearl_3 × 2 + pearl_2 × 2 + pearl_4 × 2 + grad_cosine_sim) so a reader cold to the file can locate each producer's coverage without scanning the body. (2) `gpu_dqn_trainer.rs:2854` stale field comment `// [B, TOTAL_ACTIONS(11)]` (A1 review) updated to `// [B, TOTAL_ACTIONS(13)] — 4 direction + 3 magnitude + 3 order + 3 urgency` so the field declaration matches the SP5 producer docstrings (e.g. q_branch_stats_kernel comments at line 277/282) and the 4-branch action layout established in commit `2fb30f098`. Comment-only changes; no behavior change. cargo check + cargo test --no-run both clean (11 pre-existing warnings, none new).