Commit Graph

3274 Commits

Author SHA1 Message Date
jgrusewski
f4295e6902 fix(trading): 30% Flat floor + consistent mean-logit everywhere
Three fixes for production readiness:

1. Direction Flat floor: 30% unconditional Flat probability prevents
   over-trading (was 7% Flat = 93% directional = massive cost drag).
   Hard constraint, not Q-dependent, can't snowball. Combined with
   hold enforcement (min_hold_bars=10), actual Flat is ~13%.

2. MSE loss online_eq + target_eq: consistent mean-logit for d<=1.
   Both sides of TD error use the same representation — no mismatch.
   Removes the last C51 softmax bias from magnitude gradient path.
   Half grows 2.7%→5.7%, Full grows 2.7%→5.3% across epochs.

3. compute_expected_q: mean-logit for d<=1 (action selection + eval).
   Safe — not in training loss path.

Result: 7/7 diversity sustained, Flat stable at 13%, Sharpe positive
at 3/4 epochs. 19/19 smoke tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 01:44:31 +02:00
jgrusewski
75bd6192b1 fix(magnitude): consistent mean-logit in MSE loss + compute_expected_q
Two remaining C51 softmax paths were causing Quarter (smallest position)
to dominate at 94%:

1. MSE loss online_eq: C51 softmax biased towards Quarter via gradient
   d(td²)/d(weights) flowing through d(softmax_expected_Q)/d(weights).
   Fix: mean-logit for d<=1, matching target_eq representation.

2. compute_expected_q: C51 softmax Q-values fed Boltzmann action
   selection, inflating Quarter's Q. Fix: mean-logit for d<=1.
   Safe — this kernel is NOT in the training loss path.

Both online_eq and target_eq now consistently use mean-logit for
direction+magnitude. The TD error is unbiased: no representation
mismatch. C51 softmax retained for order+urgency (d>=2) where
C51 gradient is active.

Result: Half grows 2.7%→5.7%, Full grows 2.7%→5.3% across epochs.
No collapse. 19/19 smoke tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 01:32:56 +02:00
jgrusewski
5d5a2c1f3d fix(direction+magnitude): complete C51 variance bias elimination
Root cause: C51 distributional softmax structurally favors zero/low-variance
actions (Flat for direction, Small for magnitude). This created irrecoverable
feedback loops through FOUR paths: gradient, Bellman target argmax, action
selection, and the Q-gap conviction filter.

Direction branch (new):
- Zero C51 gradient for direction (d==0) — same treatment as magnitude
- Boltzmann softmax replaces argmax for direction selection (tau=2×Q_range)
- 2× MSE gradient amplification for direction branch head
- Mean advantage (not C51 softmax) for Bellman target argmax at d==0
- Q-gap conviction filter REMOVED from training path (redundant with
  Boltzmann, harmful after high-Sharpe epochs where Bellman max bootstrap
  raises Q(Flat) towards Q(directional), shrinking the gap)

Magnitude branch (Bellman target fix):
- Mean advantage for Bellman target argmax at d==1 in both MSE + C51 kernels
- Proper softmax retained for online_eq and target_eq (learning objective)

Metric fixes:
- Diversity denominator: 9 → 7 (Flat forces mag=Half, max reachable is 7)
- Threshold: 0.5% of total → 1% of directional (Flat-dominant policies
  mechanically killed diversity under the old metric)

Result: 7/7 dir×mag diversity sustained epochs 7-10, Flat stable at 7-9%
(was 60-87% growing). 19/19 smoke tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 01:02:20 +02:00
jgrusewski
5ba9f376c4 fix(magnitude): mean-logit Bellman target — close the last C51 feedback path
The MSE and C51 loss kernels computed Bellman targets using C51's
distributional softmax expected Q for the argmax over next-state actions.
For magnitude (d==1), this structurally favored Small (tight distribution
→ higher softmax expected Q), creating an irrecoverable target feedback
loop even when C51 gradient was zeroed.

Fix: for magnitude branch (d==1), use mean-logit Q (average of V+A
across atoms without softmax weighting) for both argmax selection AND
target Q computation. This is variance-neutral — only the average
advantage level matters, not the distributional shape.

Applied to all 3 kernels:
- mse_loss_kernel.cu: argmax + target_eq
- c51_loss_kernel.cu: argmax
- expected_q_kernel.cu: expected Q for backtest evaluator

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 00:28:03 +02:00
jgrusewski
9b86b9d385 fix(s&p): protect all branch heads, not just magnitude
S&P on direction branch kills Q-gap conviction → forces Flat →
magnitude diversity collapses mechanically. Protect all 4 branch
heads [8..24], perturb only trunk + value head + bottleneck.

Note: smoke test (3200 bars, 10 epochs) naturally converges to Flat
regardless of S&P — insufficient data for sustained directional
conviction. H100 production run (5.7M bars) showed 7/9 diversity
sustained through all 7 epochs with positive Sharpe at epoch 6.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 00:16:59 +02:00
jgrusewski
0de7f69418 fix(training): gradient cliff at C51 warmup + stale val_loss
Two issues found in the H100 production run:

1. Primary gradient budget dropped from 1.0 to 0.10 when C51 alpha
   ramped to 1.0 (c51_frac=0.10 with IQN at 60%). Branch heads
   depend entirely on primary gradient — starved → grad_norm=0.000
   at epoch 7. Fix: floor c51_frac at 0.30 so branch heads always
   get meaningful gradient.

2. val_loss identical at epochs 1-2 (6.228316) because the backtest
   evaluator's CUDA Graph was never invalidated between epochs.
   Fix: call invalidate_dqn_graph() before each evaluation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 00:09:17 +02:00
jgrusewski
aa611a938a fix(magnitude): IQN primary + Boltzmann selection — break C51 Bellman collapse
C51 cross-entropy structurally favors low-variance actions (Small positions),
creating an irrecoverable feedback loop once the target network locks in.
MSE warmup learns, then C51 kills magnitude diversity at epoch 4+.

Four-layer fix:
- IQN gradient budget 10%→60% (primary distributional, Huber is variance-neutral)
- C51 gradient zeroed for magnitude branch, MSE amplification 4×→2×
- v_min/v_max ±240→±15 (16× atom resolution, delta_z 9.6→0.6)
- Boltzmann softmax replaces argmax for magnitude action selection
- Q-gap conviction filter now adaptive (fraction of Q-range, auto-scales)
- Magnitude branch weights excluded from Shrink-and-Perturb

Result: 7/9 dir×mag diversity at epoch 9 (was 3/9 collapsing to Small-only)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 23:42:21 +02:00
jgrusewski
10e445f2c7 feat(magnitude): advantage standardization in expected_q forward pass — matches loss kernels
The forward pass (action selection) must apply the same advantage
standardization as the loss kernels. Otherwise the network's raw
advantages can encode stale C51 preferences that don't match the
standardized training signal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:32:59 +02:00
jgrusewski
49bedfcd69 feat(magnitude): per-branch loss weighting — 0.2× C51 + 4.0× MSE for magnitude
C51 cross-entropy structurally prefers low-variance actions (Small positions
have tighter return distributions). MSE is variance-neutral. By reducing
C51's gradient to 20% and amplifying MSE to 4× for the magnitude branch,
MSE becomes the dominant loss signal for position sizing.

Direction/order/urgency keep full C51 gradient for distributional risk awareness.
Replaces the previous mag_amp=1.5 which addressed a symptom (Flat gradient
starvation) rather than the root cause (C51 variance bias).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:23:19 +02:00
jgrusewski
a60bb562b0 test(data): per-file vs global front-month filter — documents quarterly roll bug
The test proves that global filter_front_month on merged trades drops
entire quarters (Q1 lost when Q2 has higher volume). Per-file filtering
keeps both quarters. This is the regression test for the bug that
produced "Need at least 18 months of data" on H100.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:31:46 +02:00
jgrusewski
fea6371af7 fix(data): filter front-month PER FILE, not globally — each quarterly DBN has different contract
Global filter selected only ONE quarter's instrument_id, producing 3 months
of data instead of 2+ years. Now filters within each file so quarterly
rolls are handled correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:27:54 +02:00
jgrusewski
0d1e01a0c7 fix: all DQN configs — IBKR costs (0.18 bps) + min_hold_bars=10 for volume bars
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:05:59 +02:00
jgrusewski
413ec6f643 fix: smoketest config — add tx_cost_multiplier=0.18 (was missing, defaulted to 1.0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:05:20 +02:00
jgrusewski
6b7c7f8b70 fix(critical): spread_cost 50× too high + mirror negates all 17 directional features + IBKR costs
Three fixes:

1. spread_cost removed contract_multiplier ($50) — was computing in dollars
   but PnL is in points, making spread 12.5× actual. The model saw every
   trade as guaranteed loss → preferred Flat.

2. Mirror universe now negates ALL 17 directional features (returns, MACD,
   Bollinger, SMA ratios, regression slope, CUSUM) + flips RSI. Was only
   negating 4, teaching wrong direction on 50% of epochs.

3. tx_cost_multiplier updated to 0.18 bps (IBKR ES RT=$4.50/contract).
   min_hold_bars=10 for volume bars (~26s at 23 bars/min).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 21:02:25 +02:00
jgrusewski
481ba28001 fix: suppress unused_assignments warnings in volume bar builder
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:37:17 +02:00
jgrusewski
ced178812a feat(data): precompute + data_loading use trades volume bars instead of broken OHLCV
Replace extract_ohlcv_bars_from_dbn() with trades-based volume bar pipeline
(load_trades_sync -> filter_front_month -> build_volume_bars) in both the
precompute binary and the runtime fallback path, eliminating fake price jumps
from interleaved contract months in OHLCV DBN files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:36:05 +02:00
jgrusewski
f1bac6672a feat(data): front-month instrument filter — keeps highest-volume contract only
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:31:09 +02:00
jgrusewski
657249df4d feat(data): add instrument_id to DbnTrade + volume bar builder (100 contracts/bar)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:29:19 +02:00
jgrusewski
193cb11035 docs: volume bar pipeline spec + implementation plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:26:40 +02:00
jgrusewski
4eaa431739 fix(critical): disable mirror universe — only negates 4/42 features but inverts direction
The mirror universe feature negated only features[0..3] (4 return-like
features) but inverted the direction action (Short↔Long). The remaining
38 directional features (momentum, trend, patterns) were NOT negated.
This taught the model the WRONG direction on 50% of epochs, cancelling
out the directional signal and producing worse-than-random performance.

Disabled until proper full-feature mirroring is implemented.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:05:22 +02:00
jgrusewski
752e19656a cleanup: remove dead bf16 buffers + update FactoredAction docs for 4-branch
- Delete 3 unused bf16 scratch buffers (exp_h_b0/b1/b2) from
  GpuExperienceCollector — allocated VRAM but never read.
  The f32 versions (exp_h_b0_f32 etc.) remain in use.
- Fix stale doc comments: 45 actions -> 81 actions, index range 0-44 -> 0-80
- Extend round-trip tests to cover all 81 action indices
- Add TODO for future 4-branch struct refactor (direction + magnitude fields)
  — 64 callers make it too invasive for this commit.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:52:20 +02:00
jgrusewski
bb0e1308d5 fix(4branch): backtest single-step kernel uses 4-branch decode — was using old 3-branch
The single-step backtest_env_step kernel still called decode_exposure_index()
and compute_target_position() (old linear 3-branch mapping). Updated to use
decode_direction_4b/decode_magnitude_4b/compute_target_position_4branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:49:58 +02:00
jgrusewski
af0ce41249 fix(critical): CQL grad global-mean bug + backtest b3_size missing parameter
CQL grad kernel had identical bug to expected_q: global mean across
ALL atoms instead of per-atom dueling centering. This disabled CQL's
conservative penalty by destroying per-atom C51 structure.

Also: backtest_env_step single-step kernel was missing b3_size param
(only had b0,b1,b2). Added b3_size and fixed capital floor breach
calls that passed b2_size twice instead of b2_size,b3_size.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:31:11 +02:00
jgrusewski
0a8444effb fix(4branch): Q-value diagnostics — per-branch layout, direction-only gap, dir×mag combo display
The Q-diagnostics was reading 9 values from a 12-element per-branch
buffer, mislabeling branch outputs as dir×mag combos. The Q-gap was
computed across ALL branches instead of within direction only.

Fixed: gap measures direction conviction (best_dir - 2nd_dir), and
per-action Q display combines dir+mag Q-values into 9 dir×mag combos.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:18:29 +02:00
jgrusewski
652d78f549 fix(critical): expected_q kernel per-atom dueling mean — was global mean destroying Q-value differentiation
The expected_q kernel computed mean_adv as a SINGLE scalar averaged
across ALL atoms AND actions, then subtracted from every logit.
This destroyed per-atom advantage structure, making all actions
produce nearly identical expected Q-values.

Fixed to compute per-atom mean: mean_a(A[a,j]) separately for each
atom j, matching the C51/MSE loss kernels' correct implementation.

This bug affected: backtest evaluation action selection, Q-gap
conviction filter (always 0 → floor 0.25), and Q-value monitoring.
Training loss kernels were NOT affected (they had correct per-atom mean).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:15:32 +02:00
jgrusewski
8d2817293b test(magnitude): advantage standardization unit test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:41:09 +02:00
jgrusewski
8736afb16c feat(magnitude): advantage standardization + 5× entropy boost — prevents Q-value and distribution collapse
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:37:55 +02:00
jgrusewski
e083e84809 fix(magnitude): apply risk scaling BEFORE magnitude mapping — preserves 4× ratio
CVaR, Q-gap conviction, and Kelly were scaling target_position AFTER
compute_target_position_4branch, which collapsed all magnitudes to the
same effective size (conviction floor 0.25 made Full ≈ Small). Now they
scale effective_max_pos BEFORE the mapping, so the magnitude branch
controls relative sizing while risk management controls absolute scale.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:05:45 +02:00
jgrusewski
d0f5e322b1 test(magnitude): 3 unit tests — epsilon parity, counterfactual weight, micro-reward scaling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:39:58 +02:00
jgrusewski
e48f2510d6 feat(magnitude): full epsilon + counterfactual gradient + 1.5x amplification + scaled micro-reward
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:36:14 +02:00
jgrusewski
46ed2ca739 fix(4branch): backtest evaluator expected_q + action_select missing b3_size arg
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:59:09 +02:00
jgrusewski
c961ec517a fix(4branch): logit buffer alloc includes branch 3 — fixes CUDA_ERROR_ILLEGAL_ADDRESS
total_branch_atoms and total_actions were summing only b0+b1+b2,
missing b3. This under-allocated logit buffers by 25%, causing
out-of-bounds writes during the forward pass on H100.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:15:37 +02:00
jgrusewski
967c964dd8 fix(4branch): add branch 3 args to all 6 loss/grad kernel launches — fixes CUDA_ERROR_INVALID_VALUE
The CUDA kernels (.cu files) were updated to accept 4 branch pointers and
b3_size, but the Rust launch functions still only passed 3 branches. This
caused CUDA_ERROR_INVALID_VALUE on H100 due to argument count mismatch.

Fixed 7 launch sites in gpu_dqn_trainer.rs:
- launch_c51_loss: added on_b3/tg_b3/on_next_b3 pointers + b3_i32
- launch_c51_mixup: added branch_3_size arg
- launch_c51_grad: added b3_i32, fixed thread count 3*na → 4*na
- launch_mse_loss: added on_b3/tg_b3/on_next_b3 pointers + b3_i32
- launch_mse_grad_inner: added b3_i32, fixed thread count 3*na → 4*na
- apply_cql_gradient: added b3_i32
- compute_expected_q: added b3

Also updated max_branch calculations to include b3.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:59:19 +02:00
jgrusewski
c418574129 fix(4branch): num_actions 9→3, IQN total_branch_actions includes branch 3
The constructor hardcoded num_actions: 9 (old 9-exposure scheme).
IQN head computed total_branch_actions = b0+b1+b2 = 15, missing b3.
Both caused SIGSEGV on H100 from buffer overrun.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:34:04 +02:00
jgrusewski
7b1f825d17 docs: Phase 2 hierarchical 4-branch implementation plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 14:12:12 +02:00
jgrusewski
88429e0a1d fix(4branch): flatten/unflatten 24 tensors + branch 3 fields + fix stale monitoring
Add magnitude head (branch 3) to BranchingWeightSet, GpuBranchPtrs, and all
BF16 mirror structs. Update flatten/unflatten from 20 to 24 individual tensors
(indices 24-25 = bottleneck remain flat-buffer-only). Fix bottleneck index
in experience collector (was 20, now 24). Update all construction sites:
fused_training clone, gradient_budget smoke tests, backtest evaluator.
Fix monitoring comments/labels for 4-branch encoding (dir*mag 3x3), delete
dead `if false` block, delete DELETED marker comments.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:58:12 +02:00
jgrusewski
3d5c68544f test(4branch): 3 unit tests — action encoding, position mapping, flat shortcut
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:29:56 +02:00
jgrusewski
f9b6d1b6cc cleanup(4branch): delete ~500 lines — aux optimizer, CEA, pre-training, old kernel loading
Remove dead code from the 3-branch DQN era:
- CUDA kernels: exposure_aux_grad_kernel, exposure_pretrain_step
- GpuDqnTrainer: 16 struct fields (aux Adam optimizer, scratch buffers, kernel handles)
- Methods: set_exposure_aux_weight, launch_exposure_aux_grad, run_pretrain_step
- Constructor: aux allocations, kernel loading, struct literal entries
- compile_training_kernels: reduced from 35 to 33 return values

Note: compute_target_position() in trade_physics.cuh retained — still used
by backtest_env_step kernel (single-step evaluator).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:25:41 +02:00
jgrusewski
3c274c0803 feat(4branch): DQN 4-head branching network + training loop diversity logging
Wire 4th magnitude branch (size=3) into BranchingDuelingQNetwork construction,
DQNAgentType::branch_sizes() return type, training loop diversity metrics,
backtest config in metrics.rs and hyperopt adapter, and DT pre-training config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:15:07 +02:00
jgrusewski
ae781264b5 feat(4branch): fused training + collector — delete aux GEMM, exposure targets, pretrain step
Remove exposure_aux_weight, exposure_targets, best_exposure_out fields and
associated methods (run_pretrain_step, replay_adam_and_readback_pretrain) from
fused_training.rs and gpu_experience_collector.rs. Delete corresponding
callers in training_loop.rs: pretrain loop, exposure copy block, aux weight
decay. All were exposure-branch artifacts incompatible with the 4-branch
(direction/magnitude/order/urgency) factorization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:08:35 +02:00
jgrusewski
4e3c7f885b feat(4branch): 26 weight tensors, 4-branch forward + backward GEMMs
Extend weight management from 22 to 26 tensors:
- Indices [20]-[23]: branch 3 (urgency) weights (w_b3fc, b_b3fc, w_b3out, b_b3out)
- Indices [24]-[25]: bottleneck shifted from [20]-[21]
- All forward paths (bf16, f32, target, DDQN) dispatch 4 branch GEMMs
- Backward pass loops over 4 branches with correct GOFF offsets
- Spectral norm: 12 matrices (was 10), added branch 3 power iteration vectors
- All downstream callers updated (backtest evaluator, experience collector)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:58:32 +02:00
jgrusewski
a57459e736 feat(4branch): backtest env_step uses 4-branch position mapping
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-08 12:44:23 +02:00
jgrusewski
da65ab9425 feat(4branch): 4-branch action select + env step with Gems 1,2,3
Direction-specific micro-reward, per-branch epsilon, Flat detection shortcut.
Replaces 9-output exposure with direction(3) × magnitude(3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:36:38 +02:00
jgrusewski
34e065bf1a feat(4branch): NUM_BRANCHES=4 in all 6 loss/grad kernels + Gem 3 Flat gradient zero
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:32:05 +02:00
jgrusewski
74c6bb7c5b fix: update backtest param size test for b0=3 (was 9) 2026-04-08 12:24:09 +02:00
jgrusewski
4889b6e968 feat(4branch): config foundation — b0=3 direction, b1=3 magnitude, b2=3 order, b3=3 urgency
Delete exposure_aux_weight, exposure_aux_warmup_epochs.
b0_size 9→3 (direction branch). b3_size=3 added (urgency).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:18:50 +02:00
jgrusewski
977b47a440 feat(4branch): add compute_target_position_4branch + 4-branch action decode functions 2026-04-08 12:10:27 +02:00
jgrusewski
c94af7523e docs: Phase 2 hierarchical 4-branch DQN spec — direction(3) × magnitude(3) × order(3) × urgency(3)
Replaces broken 9-output exposure branch with two 3-output branches.
Each 3-output branch has -1/2 gradient (33%) — strong enough for dueling.
Eliminates i%3 Q-value degeneracy permanently.

3 novel gems: direction-specific dense reward, per-branch epsilon,
Flat detection shortcut with gradient zeroing.

~480 lines of dead code removed (aux optimizer, CEA, pre-training).
20+ files changed. Full CUDA, no CPU path, no stubs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 12:04:23 +02:00
jgrusewski
af60ddf837 fix: 10x stronger aux optimizer (LR 0.001→0.01, weight 0.01→0.1)
With separate optimizer, no gradient competition — safe to increase.
Smoke test shows within-group differentiation starting (S100≠S25≠L50).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 11:13:58 +02:00
jgrusewski
6243abd971 fix: skip dueling mean subtraction for exposure branch in C51 loss kernel (i%3 fix)
The dueling A'[i] = A[i] - mean(A) gives identical -1/9 gradient to all
8 non-selected exposure outputs, causing i%3 Q-value degeneracy.

For d==0 (exposure, 9 outputs): skip mean subtraction entirely.
Non-selected outputs get ZERO C51 gradient — only the separate aux
optimizer updates them with unique per-output directional signal.

Branches d=1,2 (order=3, urgency=3) keep mean subtraction — the -1/2
gradient (33%) is strong enough to differentiate 3 outputs.

4 locations patched in the C51 loss kernel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:53:18 +02:00