From 7858ced5ff3ea2447e890f614293553c9ec0dced Mon Sep 17 00:00:00 2001 From: jgrusewski Date: Tue, 31 Mar 2026 09:00:04 +0200 Subject: [PATCH] fix: wire phase system into 14D hyperopt + Fast searches all dims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase system now controls which family intensities are searchable vs pinned in the 14D layout. Fast (default) searches ALL 14 dims — phased search was needed for old 30D space but 14D is within PSO's efficient range. Full/Reward/Risk phases remain for targeted refinement. Fix test_phase_fast_bounds to verify all dims searchable in Fast. Co-Authored-By: Claude Opus 4.6 (1M context) --- crates/ml/src/hyperopt/adapters/dqn.rs | 57 +++++++++++++++++++------- crates/ml/src/training_profile.rs | 16 ++++---- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/crates/ml/src/hyperopt/adapters/dqn.rs b/crates/ml/src/hyperopt/adapters/dqn.rs index 9bd127c39..ce730b285 100644 --- a/crates/ml/src/hyperopt/adapters/dqn.rs +++ b/crates/ml/src/hyperopt/adapters/dqn.rs @@ -230,21 +230,50 @@ impl ParameterSpace for DQNParams { fn continuous_bounds() -> Vec<(f64, f64)> { // 14D search space (consolidated from 30D via family intensity grouping) // 3 breakouts + 5 core families + 6 generalization families = 14D + // + // Phase system: certain dims are pinned to 1.0 (single-point bounds) + // depending on the active hyperopt phase. + use crate::training_profile::get_hyperopt_phase; + use crate::training_profile::HyperoptPhase; + + let phase = get_hyperopt_phase(); + let search = (0.0, 2.0); // searchable intensity range + let pinned = (1.0, 1.0); // fixed at neutral + + let (learning, exploration, replay, architecture, risk) = match &phase { + // Fast (default): search ALL 14 dims — 14D is well within PSO's efficient range. + // Phased search was needed for the old 30D space; with families it's one pass. + HyperoptPhase::Fast => (search, search, search, search, search), + // Full: search architecture + risk + gen families, pin dynamics from Phase 1 + HyperoptPhase::Full(_) => (pinned, pinned, pinned, search, search), + // Reward: pin everything except loss shaping + ensemble + HyperoptPhase::Reward(_) => (pinned, pinned, pinned, pinned, pinned), + // Risk: search risk, pin dynamics + architecture + HyperoptPhase::Risk(_) => (pinned, pinned, pinned, pinned, search), + }; + + let (adversarial, regularization, augmentation, loss_shaping, ensemble, causal) = match &phase { + HyperoptPhase::Fast => (search, search, search, search, search, search), + HyperoptPhase::Full(_) => (search, search, search, search, search, search), + HyperoptPhase::Reward(_) => (pinned, pinned, pinned, search, search, pinned), + HyperoptPhase::Risk(_) => (search, pinned, pinned, pinned, pinned, pinned), + }; + vec![ - (0.95, 0.999), // 0: gamma (breakout) - (0.0, 1.0), // 1: iqn_lambda (breakout) - (3.0, 15.0), // 2: c51_warmup_epochs (breakout) - (0.0, 2.0), // 3: learning_intensity (core family) - (0.0, 2.0), // 4: exploration_intensity (core family) - (0.0, 2.0), // 5: replay_intensity (core family) - (0.0, 2.0), // 6: architecture_intensity (core family) - (0.0, 2.0), // 7: risk_intensity (core family) - (0.0, 2.0), // 8: adversarial_intensity (gen family) - (0.0, 2.0), // 9: regularization_intensity (gen family) - (0.0, 2.0), // 10: augmentation_intensity (gen family) - (0.0, 2.0), // 11: loss_shaping_intensity (gen family) - (0.0, 2.0), // 12: ensemble_intensity (gen family) - (0.0, 2.0), // 13: causal_intensity (gen family) + (0.95, 0.999), // 0: gamma (breakout — always searchable) + (0.0, 1.0), // 1: iqn_lambda (breakout — always searchable) + (3.0, 15.0), // 2: c51_warmup_epochs (breakout — always searchable) + learning, // 3: learning_intensity + exploration, // 4: exploration_intensity + replay, // 5: replay_intensity + architecture, // 6: architecture_intensity + risk, // 7: risk_intensity + adversarial, // 8: adversarial_intensity + regularization, // 9: regularization_intensity + augmentation, // 10: augmentation_intensity + loss_shaping, // 11: loss_shaping_intensity + ensemble, // 12: ensemble_intensity + causal, // 13: causal_intensity ] } diff --git a/crates/ml/src/training_profile.rs b/crates/ml/src/training_profile.rs index bf8c473e4..5fa142788 100644 --- a/crates/ml/src/training_profile.rs +++ b/crates/ml/src/training_profile.rs @@ -1170,18 +1170,16 @@ mod tests { } #[test] - fn test_phase_fast_bounds_fix_architecture() { - // When phase=fast, architecture dims must become single-point bounds. + fn test_phase_fast_searches_all_14d() { + // Fast (default): all 14 dims searchable — 14D is within PSO's efficient range set_hyperopt_phase(HyperoptPhase::Fast); use crate::hyperopt::ParameterSpace; let bounds = crate::hyperopt::adapters::dqn::DQNParams::continuous_bounds(); - // 22D search space indices (from DQNParams::continuous_bounds): - // 10: dueling_hidden_dim, 13: num_atoms, 18: hidden_dim_base - assert_eq!(bounds[18].0, bounds[18].1, "hidden_dim_base must be fixed"); - assert_eq!(bounds[13].0, bounds[13].1, "num_atoms must be fixed"); - assert_eq!(bounds[10].0, bounds[10].1, "dueling_hidden_dim must be fixed"); - // Learning rate (index 0) must still be a range. - assert_ne!(bounds[0].0, bounds[0].1, "learning_rate must still be searchable"); + assert_eq!(bounds.len(), 14, "14D search space"); + // ALL dims must be searchable in Fast (single-pass) + for (i, (lo, hi)) in bounds.iter().enumerate() { + assert_ne!(lo, hi, "dim {} must be searchable in Fast phase", i); + } } #[test]