Files
foxhunt/docs
jgrusewski b7c4f84ea0 feat(sp21): T2.2 Phase 4 — wire E4 branch_lr_scale → per-branch Adam (atomic)
Wires EnrichmentResult::branch_lr_scale (E4's [f32; 4] clamped [0.5,
2.0] per-branch LR multiplier) into the DQN Adam optimizer. Splits
the existing single DqnBranches Adam sub-launch into 4 per-branch
sub-launches, each consuming its own LR scale from ISV[521..525).

Plan amendment from on-paper design:
- Plan said "via existing per-group Adam infrastructure" but per-group
  operates at PARAM_GROUP granularity (8 groups, all 4 action branches
  lumped into DqnBranches). E4 needs per-action-BRANCH granularity.
- Resolution: split DqnBranches sub-launch into 4 per-branch sub-launches
  using the already-canonical branch byte ranges (4 param tensors per
  branch: Dir [17..21), Mag [21..25), Order [25..29), Urgency [29..33)).
  Coverage invariant updated to 7-way (was 4-way).
- Pearl C engagement tracking on branches DEFERRED to Phase 4.5: the
  shared DqnBranches engagement counter offset would collide on writes
  if 4 sub-launches use the same offset. Pearl C is a diagnostic system
  (not load-bearing) so its temporary unavailability for branches is
  acceptable; Trunk + Value + Trunk-extras Pearl C still active.
  Phase 4.5 follow-up will re-instate via ParamGroup expansion or
  sub-block offsetting scheme.

ISV slot allocation:
- BRANCH_LR_SCALE_{DIR,MAG,ORDER,URGENCY}_INDEX = 521..525
- ISV_TOTAL_DIM 521 → 525 (bus extension)
- Layout fingerprint adds 4 SLOT entries
- New branch_lr_scale_index(branch_idx) accessor for clean mapping
- Cold-start floor: launcher reads ISV, floors at 1.0 if at sentinel
  0.0 (per pearl_first_observation_bootstrap — first emit replaces
  directly with no intermediate state)

ABI surgery:
- dqn_adam_update_kernel: ONE new arg float lr_scale at end of
  signature; lr = *lr_ptr * lr_scale inside kernel
- 5 callers migrated atomically per feedback_no_partial_refactor:
  - launch_adam_update (main DQN): 7 sub-launches with per-branch
    lr_scale (Trunk + Value + 4 branch + Trunk-extras)
  - 4 post-aux launchers (ofi_embed/aux_trunk/denoise/sel) pass 1.0
  - decision_transformer Adam launch passes 1.0

Producer wireup (training_loop.rs post-enrichment block):
```rust
for (branch_idx, &scale) in result.branch_lr_scale.iter().enumerate() {
    fused.trainer().write_isv_signal_at(
        branch_lr_scale_index(branch_idx),
        scale,
    );
}
```

Files changed:
- crates/ml/src/cuda_pipeline/sp21_isv_slots.rs: +4 slots + accessor + tests
- crates/ml/src/cuda_pipeline/gpu_dqn_trainer.rs: ISV_TOTAL_DIM bump +
  fingerprint + 7-way Adam split with per-branch lr_scale
- crates/ml/src/cuda_pipeline/dqn_utility_kernels.cu: lr_scale arg + apply
- crates/ml/src/cuda_pipeline/decision_transformer.rs: lr_scale=1.0
- crates/ml/src/trainers/dqn/trainer/training_loop.rs: write 4 ISV slots
- docs/dqn-wire-up-audit.md: 2026-05-11 audit entry

Verification (passing):
- cargo check -p ml --tests --features cuda: 0 errors
- cargo test -p ml --lib sp21_isv_slots --features cuda: 3/3 (bus bounds
  + slot uniqueness + branch index mapping)
- sp20_aggregate_inputs_test: 12/12
- sp20_phase1_4_wireup_test: 2/2
- sp20_emas_compute_test: 4/4
- sp20_controllers_compute_test: 7/7
- sp21_per_trade_predicted_q_test: 3/3
Total: 31 tests, 0 failures. Behavioral gate (per-branch LR divergence)
is the upcoming smoke training run.

After this commit (T2.2 Phases 5-7 + 8 + 4.5):
- Phase 5: E6 winner indices → PER priority bumps
- Phase 6: E7 hindsight → replay buffer injection
- Phase 7: E8 curriculum weights → segment sampling
- Phase 8: signal-drive remaining controller GAINS
- Phase 4.5: re-instate Pearl C engagement tracking for branches

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 22:44:43 +02:00
..