Commit Graph

3983 Commits

Author SHA1 Message Date
jgrusewski
aa43b6dddb cleanup: update scoreboard — iter 8 (4 resolved + Needs-human closed per user directive) 2026-04-20 23:46:24 +02:00
jgrusewski
298cc33fcb cleanup(fflag,dead): collapse flash_attention flags to unconditional — [DEAD-004]
FlashAttention3Config had four flags, all dead or with dead else-branches:
- use_sparse_patterns: write-only (sparse_pattern Mask is created
  unconditionally via create_sparse_mask)
- io_aware_tiling: always-true setter; the "else" branch called
  standard_attention which itself discarded all its QK/scale/mask work
  and called io_aware.compute_attention — pure dead code
- cuda_optimization: load_kernels() gate, always true in practice
- standard_attention method + mask parameter on forward(): entirely dead

Per user directive "all features enabled" / "should be used":
- Deleted 4 fields (use_sparse_patterns, io_aware_tiling, cuda_optimization, sparse_pattern_iterations) — note sparse_pattern (BlockSparsePattern) stays
- Collapsed forward() to unconditional io_aware.compute_attention, dropped mask param
- Removed 40-LOC standard_attention dead fallback
- Dropped AttentionStats.io_aware_enabled field + test assertion
- cuda_kernels load unconditionally
2026-04-20 23:45:21 +02:00
jgrusewski
647ff0997d cleanup(dead): delete orphan transformers/features.rs — [DEAD-003]
File was 126 LOC of dangling test code referencing types that don't exist
(FeatureConfig, MarketTick, MarketMicrostructure, TradeFlowFeatures,
FinancialFeatureExtractor, percentile). Not declared via `mod features;`
in transformers/mod.rs, so the compiler never saw it. Pure dead weight.
2026-04-20 22:55:16 +02:00
jgrusewski
967e56c64c cleanup(dead): remove dead opportunity_scores API — [DEAD-002]
MultiAssetPortfolioTracker.opportunity_scores field and its two public
methods (set_opportunity_scores, select_active_symbol) had zero callers
workspace-wide per rg. Also eliminates the FALLBACK-001 symbols[0]
default — that else branch was inside the now-removed dead method.

Deleted: field + 2 init sites + 2 pub methods + architecture doc line.
Per user directive "no deferred tasks, solve properly" — was previously
marked as DEFERRED (FALLBACK-001 false-positive flagging dead code).
2026-04-20 22:54:26 +02:00
jgrusewski
e8eee1ed87 cleanup(fflag): delete dead enable_action_masking chain — [FFLAG-013b]
The chain was: GpuDqnTrainer.enable_action_masking field (mod.rs:234) →
set via local `let enable_action_masking = true;` at constructor.rs:380
→ passed into GpuExperienceCollectorConfig.enable_action_masking at
training_loop.rs:1263. Zero conditional readers anywhere in cuda_pipeline
— the GPU kernel always filters invalid actions. Comment at
constructor.rs:379 says "action masking and entropy regularization
always active", confirming the flag is vestigial.

Deleted all 4 sites. Per user directive "no deferred tasks, solve
properly" — previously deferred as FFLAG-013b, now resolved.
2026-04-20 22:53:10 +02:00
jgrusewski
34b764a91d cleanup: update scoreboard — iter 7 (3 fixed, FFLAG-013b deferral noted + DEAD-003/004 added) 2026-04-20 22:49:57 +02:00
jgrusewski
be4f9932b0 cleanup(fflag): delete dead use_noisy_nets + use_distributional — [FFLAG-013a]
GpuExperienceCollectorConfig.use_noisy_nets and use_distributional were
both declared with "always enabled" comments on their default=true
setters. Zero conditional readers anywhere — the GPU kernel always
runs NoisyNet exploration and C51 distributional RL. Deleted fields +
defaults + training_loop setters. Kept noisy_sigma_init, num_atoms,
v_min, v_max since those are read.

enable_action_masking left for human review — has real gating chain
across collector config + trainer struct + training loop.
2026-04-20 22:49:00 +02:00
jgrusewski
3f4fa33b9b cleanup(fflag): delete 7 dead transformer flags — [FFLAG-016, FFLAG-017]
HFTTransformerConfig had seven write-only bool flags with zero conditional
readers anywhere in the workspace:
- FFLAG-017: use_market_microstructure, use_order_book_features,
  use_trade_flow_features
- FFLAG-016: use_flash_attention, use_sparse_attention, use_cuda_graphs,
  enable_profiling

Each was declared + defaulted + asserted in tests, but no code branched on
them. Deleted all 7 fields + Default init + production()/benchmark() setters
+ test assertions. Per feedback_no_feature_flags.md these were aspirational
knobs — the features they nominally gated have no implementation.
2026-04-20 22:45:51 +02:00
jgrusewski
e2a84c90c4 cleanup: update scoreboard — iter 6 (FFLAG-012, FFLAG-018a resolved; FFLAG-018b split) 2026-04-20 22:42:04 +02:00
jgrusewski
37451834aa cleanup(fflag): delete dead enable_quality_filtering + enable_augmentation — [FFLAG-018a]
Both flags in unified_data_loader.rs config structs were declared and
defaulted but never read anywhere workspace-wide:
- DataProcessingConfig.enable_quality_filtering: set true, zero readers
- TrainingDataConfig.enable_augmentation: set false, zero readers

Deleted field + default at each of 2 sites. use_unified_extractor left
alone — it actively gates Option<UnifiedFeatureExtractor> at line 360.
2026-04-20 22:41:30 +02:00
jgrusewski
9fa8c009f6 cleanup(fflag): delete dead enable_gradient_vaccine chain — [FFLAG-012]
Same dead-flag chain as FFLAG-011: GpuDqnTrainConfig.enable_gradient_vaccine
→ local vaccine_enabled → GpuDqnTrainer.enable_gradient_vaccine field.
Zero readers for self.enable_gradient_vaccine — the vaccine kernels
(vaccine_dot_kernel, vaccine_project_kernel) run unconditionally. Deleted
field + struct field + local + setter at fused_training, 3 sites total.
2026-04-20 22:39:10 +02:00
jgrusewski
6cece4c4e5 cleanup: update scoreboard — iter 5 (FFLAG-003, FFLAG-011 resolved; FFLAG-007 deferred) 2026-04-20 22:36:33 +02:00
jgrusewski
35ccc48c96 cleanup(fflag): delete dead enable_causal_intervention chain — [FFLAG-011]
Write-only flag chain: DqnTrainerConfig.enable_causal_intervention →
local causal_enabled → CausalInterventionConfig.enabled. Zero readers
anywhere — causal intervention buffers are unconditionally allocated
per the existing "ALWAYS allocated (one production path)" comment.
Deleted all three sites + 2 construction setters. Weight/interval/
market_dim fields retained (those are read).
2026-04-20 22:35:42 +02:00
jgrusewski
b0c0f6c511 cleanup: drop 'BUG #37 FIX' tracker preamble — normal code comment 2026-04-20 22:33:12 +02:00
jgrusewski
b78fd2f1f0 cleanup(fflag): remove enable_q_value_clipping — always clip — [FFLAG-003]
DQNConfig.enable_q_value_clipping was true at all 4 construction sites;
the else branch returned the pre-clamp tensor unchanged (dead identity
path). Q-value clipping is a BUG #37 fix — clipping happens after
forward pass so gradients still flow, and prevents Q-value explosions
from poisoning action selection. Per feedback_no_feature_flags.md, the
flag is gone — clipping is now unconditional. Kept q_value_clip_min /
q_value_clip_max since they're still passed to clamp().
2026-04-20 22:32:08 +02:00
jgrusewski
4f79572783 cleanup: update scoreboard — iter 4 (FFLAG-002, FFLAG-005 resolved) 2026-04-20 22:28:32 +02:00
jgrusewski
6090412572 cleanup(fflag): delete dead use_spectral_norm/use_residual in QNetworkConfig — [FFLAG-005]
QNetworkConfig had three write-only fields with zero workspace readers:
use_spectral_norm, spectral_norm_iterations, use_residual. Defaults set
at the single Default impl, never checked in forward/backward. Deleted
all three + their Default init. use_gpu kept — line 138 gates CUDA
construction and errors without it (genuine sanity check).
2026-04-20 22:28:03 +02:00
jgrusewski
39d18f582b cleanup(fflag): delete dead use_spectral_norm in RainbowNetworkConfig — [FFLAG-002]
RainbowNetworkConfig.use_spectral_norm + spectral_norm_iterations were
declared but never read anywhere in the workspace. Defaulted false, set
false at the sole construction site. Pure write-only pair. Deleted both
fields + 2 construction sites. Serde default (no deny_unknown_fields)
keeps old JSON configs deserializable.
2026-04-20 22:26:57 +02:00
jgrusewski
18261c85a3 feat(tuning): temporal amplification + snapshot-on-winrate + stronger barrier
Tuning pass on the adaptive mechanisms. Changes:

1. F5 barrier weight raised 0.05 → 0.20 base, amplified 1×..2× by meta-Q
   collapse prediction (proactive, not reactive). Old 0.05 couldn't escape
   the Q-uniform attractor locally.

2. last_meta_q_pred field added on GpuDqnTrainer with set/get accessors,
   wired from DQNTrainer's meta_q.predict() each epoch boundary. Aux-op
   kernels now have per-step access to temporal collapse prediction.

3. DISTILL_HEALTH_THRESHOLD raised 0.4 → 0.55 (fire earlier). Additional
   temporal trigger: distill also fires when meta_q_pred > 0.5.

4. SNAPSHOT_HEALTH_THRESHOLD lowered 0.7 → 0.65.

5. Snapshot-on-winrate fallback: when last_epoch_win_rate >= 0.45, inflate
   effective health to 0.75 so a snapshot IS taken even if the health EMA
   is stuck in the 0.48 trough. Without this, the good moments (WinRate 56%,
   49%) are never captured → distillation has nothing to pull toward.

Result on local E1: distill=on every epoch (was permanently off), D6
fires only on bad-outcome epochs (was firing on convergence). Q-gap
still collapsed — tuning alone won't fix the underlying attractor;
root cause investigation next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:24:37 +02:00
jgrusewski
e36f63d1d4 cleanup: update scoreboard — iter 3 (FFLAG-006, FFLAG-009 resolved; note user WIP blocker) 2026-04-20 22:24:01 +02:00
jgrusewski
a05cae1e40 cleanup(fflag): remove use_soft_updates — always EMA — [FFLAG-006]
DQNConfig.use_soft_updates was true at all 4 construction sites; the
else branch (legacy hard update) was unreachable. Deleted field + 4
setters + collapsed update_target_networks() to the unconditional
cosine-annealed EMA path. Hard-copy mode removed per
feedback_no_feature_flags.md.
2026-04-20 22:23:22 +02:00
jgrusewski
9c9c7c6d8c cleanup(fflag): delete dead use_regime_conditioning flag — [FFLAG-009]
DQNConfig.use_regime_conditioning was declared, defaulted true at 3
construction sites, but never read anywhere in the workspace. Classic
write-only flag (like use_different_seeds in DEAD-001). Deleted field +
docstring + 3 assignment sites. Regime conditioning is already
unconditional per the trainer — the flag was vestigial.
2026-04-20 22:21:09 +02:00
jgrusewski
045c6f24e1 cleanup: update scoreboard — iter 2 (UNWRAP-002, FFLAG-015 resolved; UNWRAP-001 false-positive; GPUSYNC-006/008 deferred) 2026-04-20 22:17:11 +02:00
jgrusewski
dd57a9a938 fix(D6): temporal outcome gate prevents destroying converged models
Previous D6 logic fired plasticity on any ensemble_collapse_score > 0.8,
which destroyed models at the exact moment they converged to a consistent
policy (low per-branch Q-gap variance means either collapse OR convergence —
the signal alone cannot distinguish them). Result: WinRate=46% epoch killed
by plasticity → WinRate=0.9% next epoch.

Added temporal outcome gate: require last_epoch_win_rate < 0.45 OR
last_meta_q_pred > 0.5 alongside the ensemble-collapse signal. Local
smoke-test run shows D6 correctly fires on WinRate 0% / 0.2% epochs but
spares WinRate 49% / 46% epochs.

The underlying Q-collapse attractor still wins on short (10-epoch) runs,
but the adaptive system is no longer destroying the rare good epochs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:16:27 +02:00
jgrusewski
0c5186cdaa cleanup(fflag): delete dead enable_network_diagnostics flag — [FFLAG-015]
LoggingConfig.enable_network_diagnostics was declared and defaulted but
never read anywhere (only test round-trip assertions existed). Deleted
field + Default init + two test assertions that validated the round-trip.
Zero production callers per rg.
2026-04-20 22:16:23 +02:00
jgrusewski
e508475f3c cleanup(unwrap): NaN-safe q_gap partial_cmp in snapshot ring — [UNWRAP-002]
Three sites compared f32 q_gap values via partial_cmp().unwrap() — any
NaN q_gap would panic the snapshot swap-out / best-pointer paths on the
training hot path. Replaced with partial_cmp().unwrap_or(Ordering::Equal)
and tightened the outer Option unwrap to expect() with the invariant text
(len >= MAX_SNAPSHOTS guarantees a worst element exists).
2026-04-20 22:14:26 +02:00
jgrusewski
ca6a1a7ba6 fix(F6): backtest evaluator missing contrarian_active arg — caused L40S segfault
F6 added contrarian_active as the final parameter to experience_action_select
but gpu_backtest_evaluator.rs's launch site at line 980-1002 wasn't updated,
so CUDA read garbage for the new int arg and crashed the eval forward pass.

Added .arg(&0i32) as the final arg in the backtest launch — contrarian is
always off during deterministic backtest evaluation.

Verified locally via test_adaptive_learning_no_collapse: 10 epochs + 10
validation backtests now complete without segfault.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:13:13 +02:00
jgrusewski
2153929ed3 cleanup: update scoreboard — iter 1 (DEAD-001, FFLAG-001 resolved) 2026-04-20 22:11:10 +02:00
jgrusewski
3c65696794 cleanup(fflag): remove use_percentage_pnl — always on — [FFLAG-001]
The field was gated by validate() to always be true (absolute-dollar mode
caused Q-value explosion ±50,000). All call sites already set true. Deleted:
- RewardConfig.use_percentage_pnl field + Default init
- Absolute-dollar branch in calculate_pnl_reward (dead)
- Validation guard against false
- RewardConfigBuilder field + builder method + build() init
- Constructor call site

Percentage-based P&L is now unconditional per feedback_no_feature_flags.md.
2026-04-20 22:10:32 +02:00
jgrusewski
4bbe1e5610 cleanup(dead): remove unused use_different_seeds field — [DEAD-001]
EnsembleConfig.use_different_seeds was set but never read anywhere in
the workspace. rg confirmed zero readers. Field removed from struct,
Default impl, and new() constructor (3 sites).
2026-04-20 22:07:54 +02:00
jgrusewski
cc96dd7fd3 fix(F4/F5/D6): kernel buffer-stride bug + D6 warmup gate
Root cause of L40S segfault (train-tgdlq workflow, commit 0d639dfe9):

1. F4 (IB) and F5 (barrier) gradient kernels indexed cql_d_adv_logits and
   on_b_logits_buf with stride b0_size (4) instead of total_actions (13).
   The buffer is sized [B, total_actions, num_atoms]. Writes for batch i>0
   landed in other actions'/batches' gradient slots — corrupting CQL gradient
   for every batch beyond the first. After cuBLAS backward, weights diverged
   in undefined ways; validation forward then segfaulted reading NaN-laced
   parameters in GpuBacktestEvaluator init.

   Fix: add `total_actions` kernel parameter (int), use it as the full stride
   for adv_row and d_adv_a pointer arithmetic; keep b0_size as the loop
   bound (direction-branch-only update). All three launch sites updated:
   inlined F5 launch in apply_cql_gradient, inlined F4 launch alongside,
   and the standalone inject_barrier_into_cql_d_logits method.

2. D6 ensemble oracle fired at epoch 0 because per-branch Q-gaps are all 0
   at random init (range = 0 → score = 1.0 → plasticity trigger).
   Shrink-and-perturb ran immediately, then again next epoch, etc. Gate
   behind `learning_health.epoch > 5` (3 warmup + 2 buffer epochs for Q to
   move) so the oracle only fires on post-warmup real collapse, not
   untrained networks.

Both bugs are regressions from today's work — F4/F5 introduced yesterday,
D6 became live after the ens_disagreement real-signal fix in d9d35b6fa.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 22:03:45 +02:00
jgrusewski
e45e58a41a docs(cleanup): ralph-loop prompt + scoreboard for DQN code cleanup sweep
Adds scoreboard-driven cleanup infrastructure for bounded-batch Ralph
iteration across crates/ml*, services/ml_training_service, backtesting,
data_acquisition, and bin/fxt. Ten smell categories (GPUSYNC, FFLAG,
FALLBACK, ACCOUNT, DIMMIX, UNWRAP, DEAD, MAGIC, PDRIFT, TESTROT) with
per-iter ceiling of 3 commits / 200 LOC / 10 files. Completion gate:
empty scoreboard + cargo check --workspace + cargo test -p ml-dqn --lib.
2026-04-20 22:03:14 +02:00
jgrusewski
0d639dfe97 feat(F8/G5): apply c51_budget as real grad scale — was accounting-only before
Add GpuDqnTrainer::apply_c51_budget_scale() which runs scale_f32_ungraphed
on grad_buf immediately after submit_forward_ops_main (C51 backward) and
before CQL/IQN SAXPYs in submit_aux_ops. Final master gradient is now a
weighted sum: c51_budget×C51 + cql_budget×CQL + iqn_budget×IQN.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:45:07 +02:00
jgrusewski
85b7c10421 feat(F4/D5): Information Bottleneck variance gradient — spreads Q within direction branch
Adds ib_gradient_direction CUDA kernel that fires when population variance
of Q(a) across b0 actions falls below min_var=0.01, pushing each Q(a) away
from mean_q. Piggybacked on cql_d_adv_logits / cql_d_value_logits (same
atomicAdd path as F5 barrier). ib_weight = 0.05 × (1−health) → zero-op when
network is healthy (health≈1). Wired inside apply_cql_gradient immediately
after the F5 barrier launch, flows through the existing CQL SAXPY + backward.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:40:54 +02:00
jgrusewski
5bc712478e refactor(A2): production α=0.1, tests opt in to α=0.3 via with_alpha()
LearningHealth::new() now uses α=0.1 (the production-appropriate ~10-sample
EMA window for 80-100 epoch runs). Tests that need to reach threshold
assertions inside a 5-iteration loop (warmup=3 + 2 EMA steps) use
LearningHealth::with_alpha(0.3) instead of bending production behaviour
to satisfy test convenience.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:38:53 +02:00
jgrusewski
b05036ab93 feat(F5/D2): Q-gap barrier gradient — piggybacks on CQL SAXPY path
Add `barrier_gradient_direction` CUDA kernel to c51_loss_kernel.cu that
computes barrier = max(0, 0.05*health - q_gap) from the direction branch
logits and ISV[12], then injects gradient via atomicAdd into the CQL
d-logit accumulator buffers. When barrier > 0, it raises Q(argmax) and
lowers Q(second_max) to widen the direction Q-gap.

Wire-up: `apply_cql_gradient` now accepts `barrier_weight` and inlines
the kernel launch after the CQL kernel but before `backward_full`, so
both CQL and barrier share one cuBLAS backward pass with no extra SAXPY.
`submit_aux_ops` passes `barrier_weight = 0.05` every step (kernel is
internally a no-op when q_gap >= min_req). D2/N2 epoch-boundary block
updated to reflect that gradient is now live.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:35:06 +02:00
jgrusewski
0bd5c68f56 feat(F3): spectral_gap via rolling Gram + power iteration (sigma_1/sigma_2)
Replace single-sample max/min proxy with mathematically proper sigma_1/sigma_2
ratio. Maintains a host-side VecDeque of up to 64 Q-value samples; once ≥ 8
samples are available computes the n_cols×n_cols Gram matrix X^T X, then
extracts the two largest singular values via power iteration + rank-one
deflation. Falls back to the coarse max/min ratio until the buffer fills.
compute_q_spectral_gap changed to &mut self; callers updated accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:25:55 +02:00
jgrusewski
bda319cfd7 feat(F2): true vector cosine grad_consistency — replaces scalar delta proxy (A3 follow-up)
Replaces HealthEmaTrackers scalar delta-ratio proxy with real vector cosine
similarity across aggregated Adam m-state buffers (q_attn, sel, denoise,
mamba2, ofi_embed). Scalar fallback retained for fused_ctx=None / readback
failure cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:20:30 +02:00
jgrusewski
3fa9923a39 refactor(F7): remove dead iqn/cql/ens_grad_budget config fields — superseded by adaptive budgets (B4/G5)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 21:16:52 +02:00
jgrusewski
edaa078a17 fix(F1): silence unsafe_code warning on snapshot alloc — SAFETY comment + allow attribute
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:14:29 +02:00
jgrusewski
782633a47e refactor(F6/D7): contrarian Q-gap uses sign-flipped Q for consistent conviction 2026-04-20 21:10:44 +02:00
jgrusewski
d9d35b6fad fix: address all 3 precommit LOW findings
1. D6 ensemble oracle no longer dormant — replaces hardcoded ens_disagreement=0.1
   with range of per-branch Q-gap EMAs (max - min over 4 branches). This gives a
   real signal that collapses to 0 when branches agree on uniform Q and expands
   to non-trivial values when branches preserve diverse action differentiation.
   D6's smoothstep(0.01, 0.1) window now actually moves.

2. HEALTH_DIAG defaults aligned with constructor inits — cql_budget default
   0.10→0.00, c51_budget 0.45→0.55. Only visible when fused_ctx is None
   (pre-first-step); eliminates the cosmetic log jump on the first real epoch.

3. reward_v8 smoke-test module changed from pub mod → mod, resolving the
   pre-existing unreachable_pub warning. No external consumers of the module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:07:45 +02:00
jgrusewski
d5fea00108 test(E1): collapse-recovery smoke test for LearningHealth adaptive system
Trains a 10-epoch DQN run on fxcache data and asserts q_gap_ema > 0.05 and
learning_health.value > 0.3 — regression guard against the Q-uniform collapse
attractor the entire adaptive-learning-dynamics spec is designed to prevent.

Run via:
  FOXHUNT_TEST_DATA=test_data/futures-baseline \\
    cargo test -p ml --lib -- test_adaptive_learning_no_collapse --ignored --nocapture

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 21:00:32 +02:00
jgrusewski
8be3d05ab7 fix(D7/N7): zero out_q_gaps during contrarian mode — prevents full-conviction sizing on argmin trades
Addresses reviewer's IMPORTANT flag: the conviction Q-gap was not sign-flipped when
contrarian_active, so downstream env_step would size contrarian (argmin) positions
with maximum conviction, amplifying losses. During contrarian override, set
out_q_gaps=0 so those trades size minimally — the override is deliberate and
temporary; we don't want to compound it with aggressive position sizing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:59:23 +02:00
jgrusewski
f0d01478ec chore(D8): derive Debug on MetaQNetwork/SimpleLcg — fixes missing_debug_implementations lint
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 20:58:20 +02:00
jgrusewski
d74b085191 feat(D8/N8): meta-Q network — predicts collapse K epochs ahead as leading indicator
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:56:54 +02:00
jgrusewski
598c1d57f7 feat(D7/N7 Part B): wire contrarian Q-negation into Boltzmann action selection
Add `contrarian_active` int parameter to `experience_action_select` kernel.
When non-zero, a `q_sign = -1.0f` multiplier is applied to Q values across
all 4 branches (direction/magnitude/order/urgency) before Boltzmann softmax,
converting argmax-favoring sampling to argmin-favoring without touching
temperature, epsilon, conviction filter, masking, or sampling logic.
When zero, q_sign = +1.0f — behavior is bit-identical to before.

Wire: GpuExperienceCollector gains `contrarian_active_cache: u8` field plus
`set_contrarian_active()` / `contrarian_active()` accessors. The flag is
appended as the last arg at the action_select launch site. training_loop.rs
propagates `self.contrarian_active` to the collector immediately after the
D7 Part A state machine updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:51:11 +02:00
jgrusewski
ac58b9d402 feat(D7/N7): contrarian override — argmin flag when WinRate<40% for 5+ epochs during collapse
Part A fully implemented: state machine tracks low_winrate_count across epochs,
activates contrarian_active for 2 epochs when WinRate<40% × 5 consecutive AND
health<0.3, deactivates on WinRate>=45% recovery. last_epoch_win_rate carries
financials.win_rate (f32 cast) from log_epoch_metrics_and_financials into next
process_epoch_boundary. HEALTH_DIAG already consumes last_contrarian_active as
contrarian=on/off. Part B (kernel argmin flip) deferred: direction selection uses
Boltzmann softmax, not simple argmax, making inversion non-trivial.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:45:56 +02:00
jgrusewski
af9373ced9 feat(D6/N6): ensemble as collapse oracle — pairwise Q-gap triggers plasticity
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:41:25 +02:00
jgrusewski
087c21050a feat(D5/N5): information bottleneck — scalar penalty visible in HEALTH_DIAG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 20:39:12 +02:00