84de278dfee5b1d12f480a2eaabc42e9e6213c19
2557 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
84de278dfe |
feat(sp14): B.2 — register 11 SP14 ISV slots for fold-boundary reset
Each EGF pearl EMA / state slot resets to its Pearl-A sentinel at fold
boundary, mirroring sp13_aux_dir_acc_short_ema / long_ema entries.
Atomic refactor (feedback_no_partial_refactor): both halves land
together — registry entry + reset_named_state dispatch arm.
Reset slots (11 total, sentinel in parens):
- Q_DISAGREEMENT_SHORT/LONG_EMA (slots 383, 384) → 0.5
- K_AUX_ADAPTIVE (385) → K_BASE_AUX = 20.0
- K_Q_ADAPTIVE (386) → K_BASE_Q = 15.0
- BETA_RATE_LIMITER_ADAPTIVE (387) → BETA_BASE = 0.5
- AUX_DIR_ACC_VARIANCE_EMA, Q_DISAGREEMENT_VARIANCE_EMA,
ALPHA_GRAD_RAW_VARIANCE_EMA (388, 389, 390) → 0.0
(initial k = k_base, β = β_base via ISV-driven controllers)
- GATE1_OPEN_STATE (391) → 0.0 (closed)
- ALPHA_GRAD_SMOOTHED (393) → 0.0
- AUX_DIR_ACC_POST_OPEN_MIN (394) → 1.0 (no min observed)
ALPHA_GRAD_RAW (slot 392, recomputed every step from variance EMAs)
and GRADIENT_HACK_LOCKOUT_REMAINING (slot 395, decays at epoch
boundary) are NOT in the fold-reset registry; both naturally
re-initialise without explicit reset.
Also corrects the isv-slots.md SP14 table: slots 392 and 395 were
incorrectly marked FoldReset in the B.1 entry; corrected to reflect
their actual reset semantics (NOT reset / epoch-boundary decay).
Producer + consumer wiring lands in subsequent tasks (B.3-B.12);
this commit is additive infrastructure only — no behavior change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
d63cb7992e |
feat(sp14): B.1 — sp14_isv_slots.rs with 13 new ISV slot constants
Allocates ISV slots [383..396) for the Aux→Q Wire + Earned Gradient Flow pearl (Layer B of SP14). Mirrors sp13_isv_slots.rs pattern. The plan originally documented [381..394), but Phase 0 verification found SP13 closeout added HOLD_RATE_TARGET_INDEX=381 and HOLD_RATE_OBSERVED_EMA_INDEX=382 after the plan was written, so the range shifts by +2. Slots fall into 4 functional groups: - Q-disagreement EMAs (short, long; K=4↔K=2 mapping with Hold/Flat masked) - Adaptive controllers (k_aux, k_q, β; variance-driven) - Welford variance EMAs (3, one per adaptive scalar) - Schmitt state + α_grad outputs + circuit breaker Plus 14 structural constants for numerical-stability anchors: K_BASE_*, K_MIN, VARIANCE_REF_*, BETA_BASE, BETA_MAX, SCHMITT_BAND, WARMUP_STEPS_FALLBACK, LOCKOUT_*, Q_DISAGREEMENT_BASELINE. Per feedback_isv_for_adaptive_bounds: adaptive bounds (k_*, β, post_open_min, lockout) live in ISV; numerical anchors live as structural constants. Per pearl_first_observation_bootstrap: all EMAs reset to sentinels and Pearl-A bootstraps on first observation. Producer + consumer wiring lands in subsequent tasks (B.2-B.12); this commit is additive infrastructure only — no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f75786fc5a |
fix(sp14): A.1 — C51 inv_a_std floor lift (1e-6 → 1e-3)
c51_grad_kernel.cu line 275: lift floor from 1e-6 to 1e-3 in \`inv_a_std = 1.0f / (a_std + 1e-3f)\`, capping the magnitude-branch gradient amplifier at 1000 instead of ~1e6 in the degenerate case. Why: Smoke A produced 1109 GRAD_CLIP_OUTLIER events with C51 grad reaching 9.5e6 — the SP7 budget controller saturated at the EPS_DIV floor instead of rebalancing proportionally. Phase-0 verification against the actual kernel found the amplifier is NOT the spec's claimed −log(p)/p divide (which does not exist; the kernel uses the CE-stable expf(lp) - proj form at line 81). The actual amplifier is inv_a_std = 1/(a_std + 1e-6) at line 275, gated by \`if (d == 1) grad_val *= inv_a_std\` at line 282. When magnitude advantage logits collapse near-uniform (Smoke A: var_q=9e-10), a_std → ~1e-9, so inv_a_std → ~1e6. Per feedback_isv_for_adaptive_bounds, this is a numerical-stability anchor (Invariant 1: prevent division-by-near-zero amplification), not a behavioural bound. ISV-driven bounds govern behavior; the existing 1e-12f floor on a_std at line 274 is also a structural anchor — same class of fix. Validation gate: Smoke A2-A GRAD_CLIP_OUTLIER count <100 in fold 2 (was 1109 pre-fix). The 3-order-of-magnitude reduction in worst- case amplification should bring C51 grad spikes back under SP7 budget controller authority. Audit doc: Fix 40 added (parallel to Fix 39 for A.2 and Fix 41 for A.3); the stale "A.1 deferred" note was removed in the A.3 commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1420383212 |
fix(sp14): A.3 — stagnation warmup gate at fold boundary
compute_aux_w_p0b's stagnation term was firing inappropriately at fold reset because Pearl-A first-observation bootstrap forces both EMAs equal: short_ema = sentinel 0.5 → first observation X long_ema = sentinel 0.5 → first observation X (same update) improvement = max(0, short - long) = 0 stagnation = (1 - 0/max(deficit, 0.005)) = 1.0 aux_w *= (1 - 0.7) = 0.3 → spurious decay on a non-stagnation Fix: add epochs_in_fold parameter; skip stagnation when < 1. Wait one full epoch for the α=0.3 vs α=0.05 short/long EMA timescale split to produce real improvement signal. Atomic refactor (feedback_no_partial_refactor): all 5 callers migrated — 1 production site (training_loop.rs:4257) plus 4 existing unit tests at trainers/dqn/trainer/tests.rs. Test: aux_w_stagnation_warmup_gate_epoch_0 verifies: - Epoch 0: stagnation = 0, aux_w = base × deficit_amp = 0.625 - Epoch 1: stagnation = 1.0, aux_w = floor 0.15 Combined with A.2 (clamp lift), Fold 2's aux_w should now hit 0.625 in epoch 0 (the controller's intended post-deficit-amp value) instead of being collapsed to 0.164. Audit doc: Fix 41 added; stale "A.1 deferred" note from a prior killed agent was also removed (A.1 lands in the next commit, not deferred per user direction). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
731cae4c80 |
fix(sp14): A.2 — lift set_aux_weight clamp to SP13 P0b range [0.15, 1.5]
The pre-fix clamp at gpu_dqn_trainer.rs:14722 was [0.05, 0.3] — the SP11-era cap. P0b's controller computes aux_w in [0.15, 1.5] (base 0.5 × [0.3, 3.0]) but the setter silently chopped everything above 0.3, masking the deficit-amplification term entirely. Smoke A trace confirmed: Fold 0/1: raw aux_w = 0.66-0.80 → clamped to 0.30 (deficit invisible) Fold 2: raw aux_w = 0.164 (stagnation; below clamp) → 45% deficit Post-fix: deficit-amp term `(1 + 5 × deficit)` actually expresses through to the trainer. Fold 2 stagnation will get the designed floor 0.15 instead of being capped at 0.3 — but the upper range 1.5 also opens, so deficit-amp can pull aux_w up when accuracy is below target. Constants imported from sp13_isv_slots.rs (AUX_W_BASE=0.5, AUX_W_HARD_FLOOR_RATIO=0.3, AUX_W_HARD_CEIL_RATIO=3.0). No new slots; existing constants exposed as the clamp bounds. Test: set_aux_weight_clamp_range verifies constants resolve correctly. A.1 (C51 atom-probability floor) deferred — Phase 0 verification found the spec's stated `−log(p)/p · ∂p/∂z` divide does NOT exist in c51_grad_kernel.cu at HEAD 037c24116; actual kernel uses the numerically-stable `expf(lp) - proj`. The 1109 GRAD_CLIP_OUTLIER events in Smoke A are real but their mechanism is different. Will be re-spec'd as a separate task post-SP14. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
6657e56265 |
feat(sp13): B1.1b — producer kernel + replay direct path + experience collector
Final piece of the SP13 Layer B chain. B1.1a flipped the aux head from K=1
MSE regression to K=2 softmax CE classification but aux_nb_label_buf was
zero-init — model was training on "all bars are class 0 (down)". B1.1b
lands the producer kernel that fills i32 -1/0/1 labels from the 30-bar
price trajectory, the replay direct-path 8th gather that carries those
labels into the trainer, and the experience collector hoist that ensures
bar_indices_pinned is always populated (producer + hindsight relabel both
consume it). Aux head finally trains on real classification signal.
Recovery commit: this completes a B1.1b agent dispatch that crashed
mid-edit. The implementer had landed ~95% of the cascade (kernel file,
build.rs, replay buffer signature + direct path, fused_training getter,
trainer accessor, both training_loop.rs callers, kernel field + cubin
loader on the experience collector) before being killed. The missing
pieces (experience collector launch + bar_indices_pinned hoist + 6
producer tests + audit doc) were completed manually post-crash and
verified end-to-end.
Three contracts (atomic single commit per feedback_no_partial_refactor):
1. NEW aux_sign_label_kernel.cu producer — pure per-thread O(1) map
reading targets[bar*6+2] (raw_close column) at bar and bar+lookahead,
writing -1 (skip if bar+lookahead >= total_bars), 0 (down/flat under
strict greater-than tie-break), or 1 (up). Replaces B0 alloc_zeros.
2. Replay direct-path 8th gather — set_trainer_buffers gains 8th arg
trainer_aux_sign_labels_ptr; direct branch in sample_proportional
adds gather_i32_scalar into the trainer ptr; fallback gather wrapped
in if !direct_to_trainer (avoids wasted DtoD). Direct-mode
GpuBatchPtrs return points aux_sign_labels_ptr at trainer ptr.
3. Experience collector bar_indices_pinned cpu-fill hoist — moved out
of if hindsight_fraction > 0.0 so producer + hindsight share it.
Files (9 total):
- crates/ml/src/cuda_pipeline/aux_sign_label_kernel.cu (NEW)
- crates/ml/build.rs (cubin registration)
- crates/ml/src/cuda_pipeline/gpu_experience_collector.rs (kernel
field + cubin loader + struct init + hoist + producer launch)
- crates/ml-dqn/src/gpu_replay_buffer.rs (8th arg + direct gather +
fallback skip + GpuBatchPtrs return)
- crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs (aux_nb_label_buf_ptr
accessor mirrors 6 existing trainer-buf accessors)
- crates/ml/src/trainers/dqn/fused_training.rs
(trainer_aux_sign_labels_buf_ptr getter)
- crates/ml/src/trainers/dqn/trainer/training_loop.rs (both
set_trainer_buffers callers updated)
- crates/ml/tests/sp13_layer_b_oracle_tests.rs (6 NEW producer tests)
- docs/dqn-wire-up-audit.md (B1.1b section)
Hard rules upheld:
- feedback_no_partial_refactor: every consumer of the 3 contracts
migrates atomically
- feedback_no_atomicadd: producer is pure map; no reductions
- feedback_cpu_is_read_only: producer GPU-only; only host work is
pre-existing bar_indices_pinned cpu-fill (hoisted unchanged)
- feedback_no_stubs: kernel output flows through real chain — ring
buffer → direct gather → aux_nb_label_buf → CE consumer
- feedback_no_legacy_aliases: 8-arg setter gets
#[allow(clippy::too_many_arguments)] not an alias shim
- feedback_no_htod_htoh_only_mapped_pinned: targets_buf and
bar_indices_pinned both pre-existing mapped-pinned
Build + test:
- cargo check --workspace clean (only pre-existing warnings)
- cargo check --workspace --tests clean
- 17 tests in sp13_layer_b_oracle_tests.rs:
- 2 CPU-only (fingerprint bump + HEALTH_DIAG snap stable) pass
- 15 GPU on RTX 3050 Ti pass (9 B1.1a + 6 new B1.1b producer):
aux_sign_label_monotone_up_all_ones
aux_sign_label_monotone_down_all_zeros
aux_sign_label_flat_all_zeros_strict_gt
aux_sign_label_last_30_bars_skip
aux_sign_label_boundary_first_valid_last_skip
aux_sign_label_multi_episode_per_episode_skip
Producer tests cover every edge case in the kernel:
- Monotone trajectories (label=1 / label=0 across all valid bars)
- Flat tie-break (strict greater-than means flat → 0)
- Skip sentinel for last lookahead bars
- First/last bar boundary (bar=0 valid, bar=L-1 skip)
- Multi-episode global skip semantics
Next: Smoke A — L40S 5-epoch validation of full SP13 stack
(P0a + P0b + B0 + B0.1 + B1.0 + B1.1a + B1.1b). Expected: aux head
trains on real K=2 softmax CE labels; aux_dir_acc_short_ema rises above
0.5 within first epoch (vs B1.1a degraded baseline at 0.5);
HEALTH_DIAG aux_b1_diag emits per-epoch with n_down/n_up/n_skip/mask_frac.
If aux_dir_acc_short_ema > 0.55 by epoch 5, B1.1b is validated and the
chain merges to main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
7d10ea8b3e |
feat(sp13): B1.1a — K=1→2 + softmax CE kernel rewrites + struct flips
Flips the aux next-bar head from K=1 MSE regression to K=2 softmax
cross-entropy classification. Kernel ABIs, struct fields, partial-buf
shapes, and per-step ISV producers all migrate atomically; the producer
that fills `aux_sign_labels` with real -1/0/1 from the price trajectory
lands separately in B1.1b.
Why split B1.1a from B1.1b: the original B1 brief was decomposed (B1.0
+ B1.1) after six full-B1 dispatches confirmed agent-session capacity
is the bottleneck, not cascade understanding. B1.1 itself is now further
split into B1.1a (kernel/struct/test cascade — this commit) and B1.1b
(producer kernel + replay direct path + experience collector hoist +
remaining tests + Smoke A). B1.1a is contract-consistent atomic
kernel-side; B1.1b lands the producer + smoke.
Why labels stay zero-init: B1.1a is producer-less by design. The
`aux_nb_label_buf: CudaSlice<i32>` is `alloc_zeros<i32>` so every
sample receives label 0 ("down"). The model converges on "predict
class 0 (down) everywhere" until B1.1b lands the producer kernel that
fills real -1/0/1 from the 30-bar price trajectory. This degraded
training behavior is intentional and known — the cascade is internally
consistent (every consumer of K-flip / softmax tile / CE / i32 label
migrates atomically per `feedback_no_partial_refactor`); the labels are
placeholder. Local unit tests (CE correctness, dir_acc correctness,
isv_tanh correctness, fingerprint bump) validate B1.1a in isolation;
no L40S smoke runs between B1.1a and B1.1b.
Four contracts (atomic in this commit):
1. K_NB flip 1 → 2: AUX_NEXT_BAR_K constant, compute_param_sizes
([121]/[122] grow), fingerprint seed rename
(PARAM_AUX_NB_W2/B2 → PARAM_AUX_NB_W2_K2/B2_K2 — bumps the hash),
forward + backward kernels, partial-buf allocs (nb_w2 [B,H]→[B,K,H],
nb_b2 [B]→[B,K]), saxpy spec table, max_aux_tensor_len,
aux_nb_pred_buf renamed to aux_nb_logits_buf per
feedback_no_legacy_aliases.
2. Softmax tile: aux_next_bar_forward writes [B, K] softmax via
in-kernel stable softmax (max-shift form, K=2 single-thread fanout
mirrors regime kernel); 4 consumers read the tile (loss, backward,
dir-acc, isv-tanh). NEW field aux_nb_softmax_buf [B, K].
3. MSE → CE: aux_next_bar_loss_reduce reads softmax + i32 labels,
masks -1, divides by B_valid (mean-over-valid-rows), writes loss +
B_valid scalar. aux_next_bar_backward reads B_valid so loss + grad
share the same divisor — derivatives of the same scalar function.
NEW field aux_nb_valid_count_buf [1]. All-skip batch produces
loss = 0 (no NaN; fmaxf(valid, 1) divisor) and zero gradients.
Numerical floor 1e-30 prevents -log(0) = +inf in extreme-logit path.
4. i32 label dtype: aux_nb_label_buf flipped f32 → i32; -1 mask
sentinel handled across loss + backward + dir-acc + isv-tanh.
The strided_gather of next_states[:, 0] retired entirely.
Cascade (atomic per feedback_no_partial_refactor):
- aux_heads_kernel.cu: aux_next_bar_forward gains K + softmax tile
output (via in-kernel stable softmax); aux_next_bar_loss_reduce
ABI flipped (softmax + i32 labels, mean-over-valid CE,
valid_count_out); aux_next_bar_backward ABI flipped (softmax +
i32 labels + valid_count, K-fanout d_logits, masked rows zero
across the K-vector)
- aux_dir_acc_reduce_kernel.cu: read softmax + i32 labels, argmax
over K, output grew 3 → 6 floats (added n_down/n_up/n_skip);
shmem 4 → 6 int arrays
- aux_pred_to_isv_tanh_kernel.cu: read softmax tile, compute
mean(softmax[:, 1] - softmax[:, 0]); tanh transcend retired
(structural [-1, +1] bound via softmax components per
pearl_bounded_modifier_outputs_require_structural_activation)
- gpu_aux_heads.rs: AUX_NEXT_BAR_K 1 → 2; forward_next_bar gains K +
logits_out + softmax_out args; next_bar_loss_reduce gains K +
valid_count_out; backward_next_bar gains softmax_in + labels_i32_in
+ valid_count_in + K
- gpu_dqn_trainer.rs: compute_param_sizes ([121]/[122]); fingerprint
seed rename (W2/B2 → W2_K2/B2_K2); struct fields (logits, softmax,
i32 label, valid_count); aux_dir_acc_buf 3 → 6 floats; partial-buf
allocs grow; max_aux_tensor_len extended; saxpy spec table updated;
orchestrator launchers (launch_aux_dir_acc_reduce,
launch_aux_pred_to_isv_tanh, launch_sp13_aux_dir_metrics) gain K
arg; strided_gather block deleted entirely
- training_loop.rs: aux_b1_diag HEALTH_DIAG line reads
aux_dir_acc_buf [3..6] for n_down / n_up / n_skip + mask_frac;
doc comment update for the per-step aux dir-metrics block
- tests/sp13_phase0_oracle_tests.rs: 6 dir_acc + 3 isv_tanh tests
rewritten in-place to new ABI (no shadow tests, per
feedback_no_legacy_aliases)
- tests/sp13_layer_b_oracle_tests.rs (NEW): 11 B1.1a tests — 5 CE
loss/backward (single-row, batch-mixed, all-skip-NaN, backward
single-row, backward batch-mixed); 2 dir_acc (handcrafted argmax,
all-skip NaN-safe); 2 isv_tanh (bounded fuzz, mean handcrafted);
2 layout regression (fingerprint bump, HEALTH_DIAG snap stable)
- docs/dqn-wire-up-audit.md: B1.1a section
Hard rules upheld:
- feedback_no_partial_refactor: every consumer of K-flip / softmax tile
/ CE / i32 labels migrates atomically — kernels + orchestrators +
struct + diag + existing oracle tests
- feedback_no_atomicadd: block tree-reduce only; CE loss reduce uses
2 parallel partial-reduction strips; CE backward uses existing
per-sample partial → final aux_param_grad_reduce pattern
- feedback_cpu_is_read_only: aux_nb_label_buf is GPU-resident
CudaSlice<i32>; HEALTH_DIAG aux_b1_diag reads via mapped-pinned
aux_dir_acc_buf (no DtoH)
- feedback_no_stubs: every new buffer + kernel arg wired through to
a real consumer; CE forward / loss / backward chain executes
end-to-end against placeholder labels (degraded behavior, not stub)
- feedback_no_legacy_aliases: aux_nb_pred_buf renamed in-place
(no shim); PARAM_AUX_NB_W2/B2 renamed to _W2_K2/_B2_K2 in seed
(no _DEPRECATED alias); 9 existing oracle tests rewritten in-place
- feedback_no_cpu_test_fallbacks: 9 GPU tests gated #[ignore]; 2
layout-regression tests are CPU-only (pub const + size_of)
- feedback_no_htod_htoh_only_mapped_pinned: every CPU↔GPU buffer
in tests + production is MappedF32Buffer / MappedI32Buffer
- feedback_isv_for_adaptive_bounds: no hardcoded thresholds added
(1e-30 numerical floor on log is stability epsilon, not tunable)
- feedback_trust_code_not_docs: 8/8 Phase 0 anchors verified at
HEAD
|
||
|
|
75e94858c5 |
feat(sp13): B1.0 — ISV[117] retirement + scale-free MSE bridge
Retires ISV[117]=AUX_LABEL_SCALE_EMA_INDEX together with its producer
kernel (aux_label_scale_ema_update), launch site, backward pass-through,
StateResetRegistry entry, HEALTH_DIAG snapshot field, and unit test.
Why: labels at the data layer are z-normalised, so the
mean(|label|) EMA tracked by ISV[117] sits at ~1.0 empirically.
Dividing by max(scale, 1e-6) before the residual `(pred - label)`
reduces to `(pred - label)` within rounding. The divisor was a
defensive scaffold from when the data layer carried mixed-scale
labels (1e-3 log returns vs 5000 raw prices); z-normalisation made
that scaffold redundant.
This is a numerical bridge, NOT the final fix. B1.1 lands on top:
- Aux head 1→2 dim (next-bar regression → 2-class direction logit)
- MSE → CE loss flip
- aux_dir_acc reads softmax over the 2 logits
- aux_pred_to_isv_tanh rewrite as logit-diff
- Producer kernel that fills aux_sign_labels with real -1/0/1 from
the 30-bar price trajectory (B0 plumbing currently zero-init)
- dqn_param_layout fingerprint bump (head dim changes)
- aux_b1_diag HEALTH_DIAG metric
- 17+ GPU oracle unit tests
Cascade (atomic per feedback_no_partial_refactor):
- aux_heads_kernel.cu: aux_next_bar_loss_reduce + aux_next_bar_backward
drop `isv` + `isv_label_scale_index` params; residual is (pred - label)
- aux_heads_loss_ema_kernel.cu: aux_label_scale_ema_update kernel deleted
- gpu_aux_heads.rs: kernel field/loader + launch_label_scale_ema +
isv_* args from next_bar_loss_reduce / backward_next_bar all dropped
- gpu_dqn_trainer.rs: Step 2b producer launch + ISV slot uses dropped;
AUX_LABEL_SCALE_EMA=117 line retained in fingerprint seed
(no fingerprint bump in B1.0; B1.1 will bump on head-dim flip)
- gpu_health_diag.rs + health_diag.rs: aux_label_scale snapshot field
dropped; aux block 4→3 floats, downstream offsets shift down by 1,
WORD_TOTAL 150→149, snapshot_size_is_stable test 150*4 → 149*4
- health_diag_kernel.cu: WORD_AUX_LABEL_SCALE removed, downstream
offsets shift, static_assert(WORD_TOTAL == 149)
- state_reset_registry.rs: isv_aux_label_scale_ema FoldReset dropped
- training_loop.rs: reset_named_state arm + HEALTH_DIAG read +
aux line label_scale field all dropped
- sp4_producer_unit_tests.rs: load_aux_label_scale_ema_kernel helper +
sp4_aux_label_scale_ema_writes_step_obs_via_pearl_a_then_converges_pearl_d
test dropped
Hard rules upheld:
- feedback_no_partial_refactor: every consumer of ISV[117] migrates
atomically — kernel + Rust orchestrator + producer launch + backward
+ HEALTH_DIAG + reset registry + unit test all in this commit
- feedback_no_stubs: not a stub — divisor is removed at every site,
not aliased through a 1.0_const shim
- feedback_no_legacy_aliases: no legacy AUX_LABEL_SCALE_EMA_INDEX → 1.0
alias function
- feedback_no_hiding: doc comments forward to B1.1 explicitly; no
underscore suppression or #[allow(dead_code)]
Build: cargo check --workspace --tests clean.
Tests: snapshot_size_is_stable passes at 149*4=596 bytes.
cargo test -p ml --lib + cargo test -p ml-dqn --lib compile.
Net delta: 10 files, −288 LOC.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
6a869ad366 |
fix(sp13): B0 cascade gap — 5 unaudited insert_batch call sites
The B0 audit (commit
|
||
|
|
62ab8ed850 |
feat(sp13): B0 — replay buffer i32 ring + GpuBatchPtrs plumbing
Pure plumbing: threads a new i32 column (aux_sign_labels) through every
layer of the replay path so B1 can wire the aux head's CrossEntropy
classification target without touching any aggregator or batch-shape
contract on its own. No consumer reads the column yet — all labels are
zero-initialized; smoke between B0 and B1 should be bit-identical to
parent
|
||
|
|
bdc5cb8bb2 |
feat(sp13): P0b — aux_w deficit+stagnation controller + Hold cost lift
P0a smoke (train-67gqb on
|
||
|
|
f934ea1719 |
feat(sp13): P0a atomic — Hold-pricing + dir_acc instrumentation (additive)
Tests user's hypothesis (Hold being FREE is the bug, not Hold itself) by
pricing Hold via ISV-driven adaptive controller targeting 20% Hold-rate.
11 new SP13 ISV slots [372..383). 5 new GPU kernels:
- aux_dir_acc_reduce_kernel.cu (correct/pos_pred/pos_label/valid → 3 scalars)
- hold_rate_observer_kernel.cu (packed batch_actions decode, count(Hold)/B)
- apply_fixed_alpha_ema_kernel.cu (preserves short/long timescale split that
Wiener-optimal apply_pearls_ad_kernel would collapse)
- aux_pred_to_isv_tanh_kernel.cu (mean(tanh(aux_pred)) → ISV[375])
- 3 reward-composition sites in experience_kernels.cu subtract isv[HOLD_COST]
on Hold actions (segment_complete pre-asymmetric-cap, positioned-non-event
per-bar, flat per-bar)
Host-side controller in training_loop.rs:
excess = max(0, observed - target)
hold_cost = HOLD_COST_BASE × (1 + 5 × excess), clamped [0.5×, 5.0×base]
Per-step observer + EMA chain in gpu_experience_collector.rs after
experience_action_select. Per-epoch HEALTH_DIAG emit:
aux_dir_acc target/short/long/pred_tanh
hold_pricing observed_rate/target/cost
4-way action space stays (ExposureLevel::Hold preserved). Replay buffer /
fxcache compatibility preserved. SP11 (11/11) + SP12 (14/14) tests no
regression. SP13 P0a oracle tests: 14/14 on RTX 3050 Ti.
Spec/plan: docs/superpowers/{specs,plans}/2026-05-04-sp13-redefine-success-for-predictive-skill.md (v3)
Audit: docs/dqn-wire-up-audit.md (SP13 P0a section appended)
v2 → v3 reframe: P0a.T3 v2 implementer's audit found DirectionAction enum
doesn't exist (codebase uses 8-variant fused ExposureLevel cascading through
77 files). v3 reframes from "eliminate Hold" (250 LOC + 32-test cascade) to
"price Hold" (additive, no contract change, no cross-crate cascade).
Tension with pearl_event_driven_reward_density_alignment acknowledged in spec
— per-bar Hold cost is exposure-NEGATIVE (pulls policy AWAY from Hold-default,
inverse of the pearl's failure mode), models real economic carry, ISV-bounded
by controller. Faithful reward modeling, not artificial shaping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
a1681abc46 |
Revert "exp(sp13): aux_w=1.0 override + directional accuracy metric for data investigation"
This reverts commit
|
||
|
|
d2a27a0042 |
exp(sp13): aux_w=1.0 override + directional accuracy metric for data investigation
One-shot diagnostic to answer the SP13 root question: does the data have
predictable directional signal at the bar level?
Wires the existing `aux_next_bar_loss_reduce` kernel (which already had a
4-strip shmem reduction emitting `[dir_acc, pos_pred_frac, pos_label_frac]`
into a 3-float output) end-to-end:
- `gpu_aux_heads.rs`: launcher takes `dir_acc_out_ptr`, allocates 4×AUX_BLOCK
shmem to back the four parallel reductions.
- `gpu_dqn_trainer.rs`: adds `aux_nb_dir_acc_buf` (3 f32 device buffer),
threads it through the loss-reduce launch, exposes `read_aux_dir_acc()`
accessor for once-per-epoch DtoH readback.
- `training_loop.rs`: pins `aux_w = 1.0` (instead of the ISV-driven 0.05–0.3
clamp) so the supervised aux head dominates the loss; emits a new
`HEALTH_DIAG[ep]: aux_dir_acc accuracy=… pos_pred_frac=… pos_label_frac=…`
line per epoch.
Verdict thresholds:
* dir_acc > 55% by ep 5 ⇒ data has signal, DQN failing to use it
* dir_acc ≈ 50% throughout ⇒ data lacks signal at this timescale
* dir_acc 60–70% ⇒ strong signal we're not using
EXPERIMENT BRANCH — revert this commit after the investigation reads back the
5-epoch table from smoke logs. All five touch points are tagged
"SP13 data-investigation" / "EXPERIMENT" for clean revert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
1c645264e6 |
test(sp12): GPU oracle tests for the 3 reward math changes
Adds the GPU oracle test scaffold deferred from commit
|
||
|
|
17cfbb2503 |
fix(sp12): per-trade event-driven reward composition
Three architectural changes in one atomic commit per spec |
||
|
|
b92dcc3dfc |
chore(sp11): remove reward-chain diagnostic instrumentation
Instrumentation from |
||
|
|
348f6078b8 |
fix(sp11): plan_isv symmetric clamp — 4 sites mirror reward-cap bug
Implementer of
|
||
|
|
35db310893 |
fix(sp11): symmetric reward cap — losses were unbounded
experience_kernels.cu:2788:
float capped_pnl = fminf(base_reward, 10.0f);
^^^^^^^^^^^^^ caps profits, NOT losses
Diagnostic instrumentation in smoke-test-k9drh on commit
|
||
|
|
774d7552a0 |
diag(sp11): instrument reward chain to find the 5000x inflater
Smoke smoke-test-gwfn8 on
|
||
|
|
fd24b53833 |
fix(sp11): B1b launch-order — reward_component_ema before mag-ratio canary
smoke-test-4rbv9 on
|
||
|
|
b3b4d02789 |
fix(sp11): B1b smoke-recovery — z-score normalization for mag-ratio canary
Linear magnitude ratios in reward_component_mag_ratio_compute_kernel amplified popart's intrinsic O(100) magnitude over the other 5 components' O(0.1-2) magnitudes, causing controller to saturate w_pop toward MAX_WEIGHT regardless of actual signal quality. Replaced with z-score: z[c] = mag[c] / max(sqrt(var[c]), EPS_DIV). 6 new ISV slots [361..367) for per-component variance EMAs computed via Welford's online algorithm in extended popart_component_ema_kernel and reward_component_ema_kernel. Atomic per feedback_no_partial_refactor: slot allocation + state-reset registry + 2 producer kernels + canary signature + launcher Pearls A+D + tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
61b2fa962b |
fix(sp11): B1b bug 3 — cf-component feedback loop in mag-ratio canary
Deep audit on |
||
|
|
b435d25bec |
fix(sp11): B1b bug-hunt fix-up — stale rc[] init + cf_flip ordering
Bug-hunt review on
|
||
|
|
5e16b67ca6 |
fix(sp11): B1b follow-up — add slot 360 for popart-component mag EMA
Per spec §4 amendment at
|
||
|
|
034ba16801 |
feat(sp11): B1b — structural reward composition refactor (production flip)
Per spec §3.5.3 amended at
|
||
|
|
d5e1214f25 |
fix(sp11): B1a — saboteur GPU multiplication + SimHash state_stride
Per feedback_cpu_is_read_only, saboteur effective scale now computed on-device: - saboteur_generate_params kernel signature gains isv ptr + saboteur_intensity_mult_slot parameter - Kernel reads `mult = fmaxf(isv[saboteur_intensity_mult_slot], SABOTEUR_MIN)` (sentinel-0 defense for cold-start before A2's controller first runs) and applies `effective_scale = base × mult` to perturbation generation - gpu_experience_collector.rs launcher updated; only one call site SimHash state_stride parameter added to lookup + update kernels — prepares for B1c replay-time curiosity wiring against trainer. states_buf which is STATE_DIM_PADDED=128-strided. Kernel inner loop reads `state[i × state_stride + d]` (was `i × 42 + d`). Proj-init kernel unchanged (writes projection, doesn't read states). Pre-requisite for B1c (curiosity wiring) and a small atomic step toward full SP11 production behavior. cargo check + build clean; 6/6 SP11 GPU tests + 14/14 contract tests still pass. |
||
|
|
302992f63a |
fix(sp11): B0 — controller renorm Σ=1 → mean=1 (post-A2 spec amendment)
Per spec §3.4.3 amended at
|
||
|
|
44fb4531a8 |
fix(sp11): A2 follow-up — delete dead launchers + expand XOR-fold rationale
Code-quality review on
|
||
|
|
25eba79ad5 |
feat(sp11): A2 — controller kernel + SimHash novelty buffer
reward_subsystem_controller_kernel: 5 canaries → 10 outputs, true Z-score (delta_ema/sqrt(var_ema)), sigmoid blending, weight renormalization to Σ=1, saboteur post-clamp, curiosity permanent floor (0.2 × bound). Pearls A+D chained on outputs per spec §3.4.1. novelty_simhash_kernel: 42×16 random projection → 16-bit SimHash code, 1M-slot bucket count table for novelty signal `1/sqrt(1+count)`. Race- tolerated update per feedback_no_atomicadd (under-counts bias novelty UPWARD — safe direction). novelty_simhash_proj_init_kernel: Philox-seeded GPU init for the projection matrix (CPU is read-only per feedback_no_cpu_forwards). HEALTH_DIAG `sp11_reward` line emits 10 outputs + improvement_z each epoch. Reset registry: novelty hash table reset arm wired (closes the A0 deferral); projection matrix is frozen at trainer init for run lifetime, not reset. All 20 SP11 slots populate every step. No consumer reads them yet — training behavior unchanged from A1. 3 new GPU oracle tests pass on RTX 3050 Ti (controller midpoint, weight renorm, saboteur clamp). Spec: docs/superpowers/specs/2026-05-04-sp11-reward-as-controlled-subsystem.md §3.4 §3.5.2 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
66f5fd8f00 |
fix(sp11): A1 follow-up — remove let _ + correct shmem in tests
Code-quality review on
|
||
|
|
91b48bc7a5 |
feat(sp11): A1 — three canary producer kernels (no behavior change)
Adds val_sharpe_delta + saboteur_engagement + reward_component_mag_ratio
GPU producers for the SP11 reward-as-controlled-subsystem chain. Each is
a single-block producer chained with apply_pearls_ad_kernel for Pearls
A+D smoothing per pearl_first_observation_bootstrap.md +
pearl_wiener_optimal_adaptive_alpha.md. All three write to slots in
[350..360) which no consumer reads yet — Layer A is additive; consumer
migration lands atomically in Layer B.
A1.1 — val_sharpe_delta_compute_kernel.cu
Two-pass: writes raw delta + (delta - prev_delta_ema)^2 to scratch.
Chained Pearls A+D (n_slots=2) → ISV[VAL_SHARPE_DELTA_EMA_INDEX=350,
VAL_SHARPE_VAR_EMA_INDEX=351]. Host writes val_sharpe to mapped-pinned
history[1]; rotation handled in training_loop.rs at val emit boundary
(a literal already-computed value — no host-side compute, no htod_copy).
A1.2 — saboteur_engagement_compute_kernel.cu
Per-bar |Δreward| > 0.01 × ISV[PNL_REWARD_MAGNITUDE_EMA_INDEX] check
with block tree-reduce (no atomicAdd per feedback_no_atomicadd). The
per-bar Δreward signal is produced by experience_env_step's saboteur
perturbation site as `traded × |reward| × max(|eff_spread − 1|,
|eff_slip − 1|)` — a structural proxy for the cost-differential the
saboteur imposed on bars where the model traded. Single kernel-side
emit (no parallel reward computation), per spec §3.3.1.
Chained Pearls A+D → ISV[SABOTEUR_ENGAGEMENT_RATE_INDEX=358].
A1.3 — reward_component_mag_ratio_compute_kernel.cu
Reads ISV[REWARD_POPART_EMA_INDEX..+6) (the SP4 reward-component
magnitude EMAs), normalises to ratios, and mirrors popart magnitude
into scratch[6] as a side-output. ONE non-pointer parameter
(popart_ema_base_slot) — no _unused param per feedback_no_stubs.
Two chained Pearls A+D launches:
n_slots=6 → ISV[REWARD_COMPONENT_MAG_RATIO_BASE..+6)
n_slots=1 → ISV[PNL_REWARD_MAGNITUDE_EMA_INDEX=359]
(slots non-contiguous: 352..358 then 359.)
Wire-up (per feedback_wire_everything_up):
- 3 cubin entries appended to crates/ml/build.rs
- 3 kernel handles + val_sharpe_history_pinned (MappedF32Buffer[2]) +
saboteur_delta_reward dev-ptr cache fields on GpuDqnTrainer
- 3 launchers (launch_sp11_*) + 1 setter (set_sp11_saboteur_delta_reward_buf)
- saboteur_delta_reward_per_sample buffer field on GpuExperienceCollector
- experience_env_step kernel signature extended with the new buffer arg;
every call site in the same commit per feedback_no_partial_refactor
- training_loop.rs init wires collector→trainer setter; val emit boundary
invokes launch_sp11_val_sharpe_delta_compute; per-epoch metrics block
invokes launch_sp11_mag_ratio_compute then
launch_sp11_saboteur_engagement_compute (mag_ratio first so the
signal-relative threshold base is populated before the saboteur reader)
- SP5_SCRATCH_TOTAL grown 266 → 276 (10 new scratch slots: 2+1+7)
- docs/isv-slots.md SP11 section updated to reflect A1 producers
3 GPU oracle tests in crates/ml/tests/sp11_producer_unit_tests.rs
pass on RTX 3050 Ti via MappedF32Buffer fixtures (zero htod_copy /
dtoh_sync_copy / alloc_zeros — feedback_no_htod_htoh_only_mapped_pinned
compliant).
Note on Step 8a path: the plan offered two routes for the saboteur
Δreward producer — in-kernel diff emission OR a small dedicated
reader-of-existing-buffers. The existing reward path emits ONE reward
(not both with/without), so the dedicated-reader alternative was
infeasible. The in-kernel emission landed as a small write site at the
END of experience_env_step (after total_reward_per_sample is finalised),
threading saboteur_eff_spread/saboteur_eff_slip from the perturbation
site forward to the END via stack vars. Single new kernel parameter,
single new GPU-only buffer, single existing call site updated.
Spec: docs/superpowers/specs/2026-05-04-sp11-reward-as-controlled-subsystem.md §5
Plan: docs/superpowers/plans/2026-05-04-sp11-reward-as-controlled-subsystem.md (Task A1)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
201b59dfbc |
fix(sp11): A0 sweep — eliminate remaining stale wiener-buffer refs
A0 follow-up (
|
||
|
|
1e5a65912c |
fix(sp11): A0 follow-up — update stale wiener-buffer + isv-slots header
Code-quality review on
|
||
|
|
bf3a32d63a |
feat(sp11): A0 — allocate 20 ISV slots [340..360) + 20 reset entries
Pure infrastructure. No producer kernels, no consumer reads. Existing training paths trace identically because no consumer reads slots [340..360) yet. Layout-fingerprint bumped to ISV_TOTAL_DIM=360. Spec: docs/superpowers/specs/2026-05-04-sp11-reward-as-controlled-subsystem.md |
||
|
|
e580c1388c |
fix(log): epoch summary Return uses scientific notation, fixes overflow display
`total_return` from financials.rs:80-94 is log-space cumulative growth
across every per-bar step_return. With ~4M step_returns in a fold-
convergence run, even sub-bps positive bars compound to absurd
magnitudes (observed: 1.93e37%) when displayed as `{:+.2}%`. Math is
correct; display needs scientific notation.
Surfaced in T10 train-multi-seed-xkjkb seed-0 ep3 epoch summary while
SP10 chain validates structural fixes. Cosmetic-only change; no
training-path impact. Audit doc updated with Cosmetic 38.1 entry.
|
||
|
|
a5457b0dc7 |
Revert "fix(log): epoch summary Return uses scientific notation, fixes overflow display"
This reverts commit
|
||
|
|
368454788a |
fix(log): epoch summary Return uses scientific notation, fixes overflow display
`total_return` from financials.rs:80-94 is the log-space cumulative growth
across every per-bar step_return: `exp(sum(ln(max(1+r, 1e-10)))) - 1`.
With ~4M per-bar step returns in a fold-convergence run, even sub-bps
positive bars compound to absurd magnitudes (observed: 1.93e37%) when
displayed as `{:+.2}%`. The math is correct; the display is broken.
Switching to `{:+.3e}%` shows the same magnitude compactly across the
full dynamic range without hiding the number. Comment added explaining
the underlying compute's HFT-inappropriate semantic so a future reader
knows the value is correct-but-meaningless rather than buggy.
Surfaced in T10 train-multi-seed-xkjkb seed-0 ep3 epoch summary while
the SP10 chain validates structural fixes. Cosmetic-only change; no
training-path impact.
|
||
|
|
920a2d0219 |
fix(sp10): unconditional Thompson selector + ISV-driven temperature (Fix 38)
T10 train-multi-seed-khr7c (commit
|
||
|
|
8a25b330fc |
fix(sp9): targets as Invariant-1 anchors + divergence sentinel handling
Two targeted fixes to the SP9 Kelly cold-start warmup floor (Fix 37,
commit `48a8b9ee7`) for quirks observed in smoke-test-wrwkz on a
5-epoch L40S smoke. Atomic per `feedback_no_partial_refactor`.
**Quirk 1 — EMA target saturation:** ep1 HEALTH_DIAG showed
`stat_count_tgt=419 div_tgt=105002 temp_tgt=1.0 conf [stat=1.000
bhv=0.333 tmp=1.000] floor=0.0`. Pearl A sentinel-bootstrap on the
3 EMA target updaters (`kelly_*_target_ema_kernel.cu`) caused the
first observation to replace the sentinel directly, so
`target = first_obs`, `current/target = 1.0`, `confidence = 1.0`,
`floor = base × (1 − 1.0) = 0` — defeating the cold-start mechanism.
The principled fix per `feedback_isv_for_adaptive_bounds.md`:
"sufficient" is defined in absolute terms via Invariant-1 numerical
anchors. The 3 target ISV slots become constructor-written constants
(written ONCE in trainer constructor, identical pattern to existing
`config.cql_alpha → ISV[CQL_ALPHA_INDEX=48]` from Plan 1 Task 12).
- ISV[KELLY_SAMPLE_COUNT_TARGET_INDEX=333] = 100.0 (trades)
- ISV[KELLY_DIVERGENCE_TARGET_INDEX=334] = 2.0 (ratio)
- ISV[KELLY_TEMPORAL_TARGET_INDEX=335] = 5.0 (epochs)
**Quirk 2 — divergence ratio explosion:** ep1 showed
`divergence=70005`. Algebraically `intent_f / max(eval_f, 1e-6) =
0.07 / 1e-6 = 70 000` because `ISV[EVAL_DIST_F_INDEX=338]` was still
at Pearl A sentinel 0 before the first val window populated it.
Algebraically the warmup-floor kernel still derived
`behavioral_conf = 0` (correct semantic), but the synthetic 70 000
in HEALTH_DIAG masked the real signal flow.
Fix: explicit sentinel detection in
`intent_eval_divergence_compute_kernel.cu`:
if (eval_f < SENTINEL_THRESHOLD=1e-5)
divergence = SENTINEL_DIVERGENCE=1e6 // forces behavioral_conf=0
else
divergence = intent_f / eval_f
Same `behavioral_conf = 0` outcome but with explicit provenance —
HEALTH_DIAG now reads `divergence=1e6` until eval_f matures.
**Atomic structure:**
- DELETE 3 EMA target updater kernels (`.cu` files)
- DELETE 3 entries from `crates/ml/build.rs::kernels_with_common`
- DELETE 3 cubin static byte arrays + 3 struct fields + 3 cubin
loaders + 3 fields in trainer construction tuple in
`gpu_dqn_trainer.rs`
- DELETE 3 launches in `launch_sp9_kelly_warmup_floor` (chain
shrinks 5 → 2: q_var_mag_ema + main warmup-floor)
- DELETE 3 scratch slots; SP5_SCRATCH_TOTAL 268 → 265;
SCRATCH_SP9_EVAL_DIST_BASE slides 265 → 262
- ADD constructor-write of 3 Invariant-1 anchors (100.0, 2.0, 5.0)
immediately before layout-fingerprint write
- UPDATE 3 dispatch arms in `reset_named_state` to rewrite the
Invariant-1 anchors at fold boundary (NOT sentinel 0) — these
are constants, not stateful EMAs
- UPDATE 3 registry descriptions to reflect Invariant-1 anchor
semantic + smoke-test-wrwkz evidence
- UPDATE `intent_eval_divergence_compute_kernel.cu` with
sentinel-detect branch
- ISV slot layout UNCHANGED (3 target slots @ ISV[333..336)
remain); Wiener-buffer linear span (`SP5_PRODUCER_COUNT=165`)
UNCHANGED — 3 wiener triples for deleted producers become
reserved-unused like Pearl 6's [525..543) carve-out
- audit doc Fix 37.1 entry with full provenance + smoke evidence
**Verification:**
- `SQLX_OFFLINE=true cargo check -p ml` — clean (only pre-existing
18 warnings; no new errors or warnings).
- `SQLX_OFFLINE=true cargo test -p ml --lib state_reset` — 4/4 pass
including `every_fold_and_soft_reset_entry_has_dispatch_arm`.
- `SQLX_OFFLINE=true cargo test -p ml --lib sp5_isv_slots` — 9/9
pass including `sp9_slots_contiguous_above_sp8_block`.
**Expected smoke signature post-fix:**
- floor: starts ≈ base_floor × 1.0, decays as confidence accumulates
- divergence: 1e6 until first val window, small (≤ 5) afterward
- conf: stat=count/100, bhv=0 until eval_dist matures, tmp=ep/5
- combined_conf reaches 1.0 only when at least one axis genuinely
matures (typically temporal at epoch 5 in fold 0)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
48a8b9ee7c |
fix(sp9): Kelly cold-start ISV-driven warmup floor (Fix 37)
Per pearl_cold_start_exit_signal_or.md + pearl_controller_anchors_isv_driven.md:
the Kelly cap's `warmup_floor` and its release condition were both
regime-encoded constants. Single-axis statistical cold-start exit
(`total_trades >= 10` in trade_physics.cuh::kelly_position_cap) created
a chicken-and-egg deadlock: cap closed → no trades → no Kelly samples →
cap stays closed. T10 wsnc6 ep1-3 evidence (post-Fix 36):
intent_dist_f rising to 0.47 while eval_dist_f pinned to 0.00 with
kelly_f=0 across all epochs — train-vs-eval divergence as direct
symptom of the cold-start deadlock.
SP9 lifts the floor and its release condition fully onto ISV. The exit
condition is OR'd across statistical / behavioral / temporal axes per
the pearl — any single axis firing exits cold-start.
Atomic commit per feedback_no_partial_refactor:
- 9 new ISV slots @ [330..339):
- KELLY_WARMUP_FLOOR_INDEX (330) — adaptive floor consumed by
unified_env_step_core
- Q_VAR_MAG_EMA_INDEX (331) — historical EMA baseline for the
self-relative `base_floor` ratio (eliminates 0.5 hardcoded
half-Kelly)
- INTENT_EVAL_DIVERGENCE_INDEX (332) — behavioral-axis numerator
- 3 EMA targets (sample_count / divergence / temporal)
- 3 EVAL_DIST_{Q,H,F}_INDEX — replaces DtoH
read_eval_intent_magnitude_distribution()
- ISV_TOTAL_DIM 330 → 339; SP5_PRODUCER_COUNT linear span 156 → 165
- 9 new scratch slots @ [259..268); SP5_SCRATCH_TOTAL 259 → 268
- 7 new producer kernels (single-block cold-path, chained through
apply_pearls_ad_kernel for Pearl A bootstrap + Pearl D Wiener-α):
- eval_intent_dist_compute_kernel.cu — GPU reduction of
intent_mag_buf, replaces DtoH path per
feedback_no_cpu_compute_strict.md
- intent_eval_divergence_compute_kernel.cu
- q_var_mag_ema_compute_kernel.cu
- kelly_sample_count_target_ema_kernel.cu
- kelly_divergence_target_ema_kernel.cu
- kelly_temporal_target_ema_kernel.cu
- kelly_warmup_floor_compute_kernel.cu — main producer combining
all 8 ISV inputs
- consumer migration in trade_physics.cuh::unified_env_step_core:
health_safety_sp9 = fmaxf(health_safety_sp5,
isv[SP9_KELLY_WARMUP_FLOOR_INDEX])
threading through apply_kelly_cap's health_floor parameter; zero
sentinel → no-op via fmaxf so existing path's behavior is preserved
- DELETE read_eval_intent_magnitude_distribution() in
gpu_backtest_evaluator.rs (was DtoH read_all + host loop) per
feedback_no_htod_htoh_only_mapped_pinned.md; replaced by
intent_mag_dev_ptr_and_n() GPU accessor; metrics.rs:908 consumer
migrated from DtoH to launch + ISV mapped-pinned read
- 9 new state-reset registry FoldReset entries + 9 dispatch arms in
reset_named_state (Pearl A bootstrap on first observation)
- HEALTH_DIAG sp9_kelly_warmup line emits floor + divergence + 3
confidence axes + 3 EMA targets per feedback_no_hiding
- audit doc Fix 37 entry with full provenance, files touched, pearls
applied, verification
Constants: only KELLY_FLOOR_MIN_RATIO=0.25, KELLY_FLOOR_MAX_RATIO=1.0,
EPS_DIV=1e-6 — Invariant 1 numerical-stability anchors per
feedback_isv_for_adaptive_bounds.md. All thresholds, targets, and
the floor itself are signal-driven via ISV.
Pre-existing 18 cargo warnings unchanged. State-reset contract test
`every_fold_and_soft_reset_entry_has_dispatch_arm` passes for all 9
new entries; SP5 ISV slot layout test
`sp9_slots_contiguous_above_sp8_block` passes;
`SQLX_OFFLINE=true cargo check -p ml` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
e0dbae3c99 |
fix(sp7): ISV-driven MAX_BUDGET via GPU train_active_frac canary (Fix 36)
Per pearl_controller_anchors_isv_driven.md: Fix 35 (CQL formula direction
flip) is necessary but insufficient — `const float MAX_BUDGET = 1.0f;` at
line 113 of loss_balance_controller_kernel.cu is regime-encoded the same
way the original CQL target_ratio formula was. Under healthy training
the cap is fine; under val-Flat-collapse it lets the controller saturate
budgets to 1.0 while the model is regressing into Flat.
The pearl prescribes the structural fix: pick the canary that fires
under the failure mode → train_active_frac (Long+Short / total during
training rollout). Lift it onto ISV with Pearls A+D smoothing; route it
through a producer kernel that derives per-(head, branch) cap via linear
interpolation FLOOR + active_frac × (CEIL - FLOOR); read the cap from
ISV in the controller kernel (no more hardcoded 1.0).
Atomic commit per feedback_no_partial_refactor:
- new kernel: train_active_frac_compute_kernel.cu — single-thread
reduction over monitoring_summary[5..17), writes scratch[250]; chained
apply_pearls_ad → ISV[TRAIN_ACTIVE_FRAC_INDEX=321]
- new kernel: loss_balance_max_budget_compute_kernel.cu — 8 threads
(2 heads × 4 branches), reads ISV[TRAIN_ACTIVE_FRAC_INDEX], writes
scratch[251..259); chained apply_pearls_ad ×8 →
ISV[LB_MAX_BUDGET_{CQL,C51}_BASE..+4)
- consumer: loss_balance_controller_kernel.cu — replace
`const float MAX_BUDGET = 1.0f;` with per-(head, branch) ISV reads,
defensive Pearl A bootstrap clamp
- 9 new ISV slots @ [321..330); ISV_TOTAL_DIM 321 → 330;
SP5_PRODUCER_COUNT linear span 147 → 156
- 9 new scratch slots @ [250..259); SP5_SCRATCH_TOTAL 250 → 259
- delete CPU train_active_frac compute (training_loop.rs:3392-3396) per
feedback_no_cpu_compute_strict; delete host field
`last_train_active_frac: f32`; replace with accessor reading from ISV
- 3 new state-reset registry entries (FoldReset; Pearl A bootstrap on
first observation) + dispatch arms in reset_named_state
- audit doc: Fix 36 entry with full provenance, files touched, pearls
applied, verification
Pre-existing 18 cargo warnings unchanged. State-reset contract test
`every_fold_and_soft_reset_entry_has_dispatch_arm` passes for all 3 new
entries; SP5 ISV slot layout test
`sp8_max_budget_slots_contiguous_and_above_activation_block` passes;
`SQLX_OFFLINE=true cargo check -p ml` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
65c3083dec |
fix(sp7): flip CQL target_ratio direction — death spiral on magnitude
`target_ratio[CQL][b] = ANCHOR_CQL_RATIO * (1 - flatness[b])` had the sign inverted: it pushed CQL HIGH when Q was flat (the collapse state) and LOW when Q had variance. Per pearl_2_budget_kernel.cu, `flatness = var_q / σ²`, so flatness HIGH means Q has variance — exactly when overconfidence-risk-driven CQL pressure should kick in. Symptom (T10-v3 train-multi-seed-x7sl2 logs): Once IQN Fix 34 woke IQN-mag/ord/urg branches and produced real Q-targets, the controller's inverted formula sent cql_budget to MAX_BUDGET=1.0 saturation on every branch, the conservative pull kept Q flat, the model collapsed to single-Flat-action eval (val_active_frac=0, dir_entropy=0, trade_count=1, sharpe=0). Fix is one operational character: `(1 - flatness)` → `flatness`. C51 formula was already correctly aligned (`flatness * ANCHOR_C51_RATIO`) and unchanged. Per pearl_controller_anchors_isv_driven.md: the inverted formula was masked for months because the IQN-dead regime (Fix 34) suppressed cql_raw on mag/ord/urg, never letting the controller engage with the inverted target. Fixing the IQN regime exposed the dormant formula bug. ANCHOR_CQL_RATIO=2.0 and MAX_BUDGET=1.0 remain hardcoded; Fix 36 will make those ISV-driven via a GPU train_active_frac canary signal per the pearl's "pick the canary that fires under the pathology" rule. |
||
|
|
9b5296b2f0 |
fix(iqn): set_cached_target_h_s2 per branch — fixes silent 3/4 IQN branch dropout
execute_training_pipeline consumes cached_target_h_s2_ptr via .take(), so
the SP6 Pearl 5 per-branch loop (4 sequential iqn calls per step) only
worked for branch 0. Branches 1, 2, 3 saw cached_ptr=None, returned a
"non-fatal" Err logged as a warning, and silently skipped apply_iqn_
trunk_gradient — only the direction branch's IQN quantile signal ever
reached the trunk, for months.
Surfaced in T10-retry train-multi-seed-ksjcm logs as repeated:
WARN: IQN parallel branch 1 step failed (non-fatal):
WARN: IQN parallel branch 2 step failed (non-fatal):
WARN: IQN parallel branch 3 step failed (non-fatal):
cached_target_h_s2_ptr is None.
Pre-existing since SP6 Pearl 5 (P4.T3-era multi-quantile IQN per-branch
τ schedules) — masked by the non-fatal warning classification.
Likely explains:
- cql_mag persistence at SP7 smoke (cql_mag=0.07 vs cql_dir=1.0):
CQL was the only learning signal mag had because IQN-mag was dead.
- SP7 c51 1000:1 controller suppression on mag/ord/urg may stabilize
at non-collapse values once IQN signal returns to those branches.
Fix is local: re-set cached ptr inside both 4-branch loops (parallel arm
near line 2123, sequential arm near line 2308). The ptr is the same
value (target_h_s2 is per-step, not per-branch), but the .take()
contract requires set-per-call. Defensive single-shot semantic preserved
— if a future bug skips the set, IQN errors loud rather than reusing
stale ptr.
Per feedback_no_partial_refactor: SP6 changed the call pattern 1→4 per
step but didn't migrate the cache contract. Per feedback_no_hiding: the
non-fatal classification hid the structural bug; escalation to hard
error deferred to next commit after post-fix T10 validates the warnings
disappear.
Audit doc updated with Fix 34 entry.
|
||
|
|
e3d0829680 |
fix(build): cargo:rerun-if-env-changed=CUDA_COMPUTE_CAP for cubin arch
Without this trigger, cargo treated CUDA_COMPUTE_CAP-driven cubin
compilation as cached output of a non-tracked env var. The cargo-target
PVC is shared across H100 (sm_90) and L40S (sm_89) training jobs, so
swapping --gpu-pool between archs left stale cubins from the previous
build cached for any future sm_X variation.
Symptom (T10 train-multi-seed-rn559 today):
Failed to create DQN: rmsnorm cubin load: DriverError(
CUDA_ERROR_NO_BINARY_FOR_GPU,
"no kernel image is available for execution on the device")
The training pod scheduled on L40S (sm_89), the binary cache served
sm_90 cubins from a prior H100 build, and the rmsnorm cubin had no
sm_89 entrypoint.
Fix is one line: tell cargo to invalidate when CUDA_COMPUTE_CAP changes.
First post-fix build will rebuild all cubins (cargo conservatively
reruns when a new rerun-if-env-changed trigger is added without prior
state). Subsequent builds rebuild only on actual env changes.
Generalises the same lesson as the recent SP7 host-branch-in-captured-graph
fix: env-var-conditional code that doesn't declare its dependencies
freezes at first observation regardless of runtime input.
|
||
|
|
07f5ed5d74 |
fix(sp7): GPU dispatch for cql/c51 budget — eliminate capture-time host-branch freeze
The SP7 controller wrote real budgets to ISV[BUDGET_CQL_BASE/C51_BASE] and
the activation flag correctly transitioned 0→1, but downstream SAXPY ops
still saw exactly 0.02/0.05 every step. Root cause: compute_adaptive_budgets
ran host-side Rust with `if cql_active >= 0.5 { real } else { bootstrap }`,
captured into the aux_child CUDA Graph at step 0 of each fold (when
cql_active is FoldReset sentinel 0.0). The bootstrap branch resolves to
literal 0.02/0.05 and freezes into kernel arg buffers; ~1000 graph replays
per fold use frozen scalars regardless of runtime ISV state.
Same disease as SP4 host-side EMA elimination 2026-05-01. Fix:
- New consume_lb_budget_kernel.cu (8 threads, 1 block): reads ISV slots
for activation flag + controller budget per (head, branch); applies
if/else dispatch on-device; writes trunk_mean + per-branch correction
to mapped-pinned scratch buf [10] = (cql_trunk + cql_corr×4 + c51_trunk
+ c51_corr×4). Same cubin houses dqn_{scale,saxpy}_f32_dev_ptr_kernel
variants reading alpha from a device pointer (no kernel-arg freeze).
- Buffer + launcher + cubin loading + 4 dev-ptr accessor methods
in gpu_dqn_trainer.rs.
- apply_c51_budget_scale / apply_cql_saxpy + per-branch variants take
alpha_dev_ptr: u64 instead of scalar f32. The "skip when ≈ 1.0"
optimization is dropped — the value lives on-device. Existing scalar
dqn_{saxpy,scale}_f32_kernel are NOT modified — they remain reused
by distill, VSN dW dilution, etc.
- compute_adaptive_budgets refactored to launch the dispatch kernel
inline (captured along with consumers; replay-time fresh values
every step) and stop returning host-resolved CQL/C51 scalars.
IQN/ENS keep their host-side resolution (out-of-scope per change
scope — no activation-flag dispatch).
- HEALTH_DIAG reads from lb_budget_effective_buf via new accessor
methods on FusedTrainingCtx; dead caches on GpuDqnTrainer
(last_{cql,c51}_budget_eff / _per_branch) deleted.
- Audit doc Fix 33. Memory pearl out-of-tree.
Bootstrap byte-equivalence preserved: kernel-arg constants
cql_bootstrap=0.02 / c51_bootstrap=0.05 remain byte-identical to the
prior host-side CQL_BOOTSTRAP_BUDGET / C51_BOOTSTRAP_BUDGET literals
and to loss_balance_controller_kernel.cu's COLD_START_FLOOR_* anchors.
Files: consume_lb_budget_kernel.cu (new), build.rs, gpu_dqn_trainer.rs,
fused_training.rs, training_loop.rs, dqn-wire-up-audit.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
14af4854fb |
fix(merge): resolve sp5→main merge fixups — DevicePtrMut import + adam_step 7-arg signature
After sp5 merge with -X theirs, two compile errors needed manual fix: - gpu_her.rs lost the DevicePtrMut trait import - gpu_tlob.rs Fix 20 regression test used the 4-arg adam_step signature before sp5's Pearl 4 added β1/β2/ε params. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
aef2003db2 |
Merge: sp5-magnitude-differentiation → main (SP4+SP5+SP6+SP7 + observability + ISV bus fix)
Conflicts auto-resolved with -X theirs strategy. sp5 has more evolved MappedF32Buffer API + more recent work; main's via-pinned cleanup landed in parallel as a lateral migration. Taking sp5's side preserves the cleaner consolidated upload pattern. |
||
|
|
ad4a2de12a |
Merge: fix(tlob) align dW layout + fuse Q/K/V SGEMMs
# Conflicts: # crates/ml/src/cuda_pipeline/gpu_tlob.rs |
||
|
|
32ccf963dc | Merge: refactor delete via_pinned helpers, migrate 22 callers |