diff --git a/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs b/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs index 885f3ce81..4a0b2f57e 100644 --- a/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs +++ b/crates/ml/src/trainers/dqn/smoke_tests/training_stability.rs @@ -313,9 +313,11 @@ fn test_50_epoch_convergence() -> anyhow::Result<()> { // ═══════════════════════════════════════════════════════════ let avg_q = metrics.additional_metrics.get("avg_q_value").copied().unwrap_or(0.0); assert_finite(avg_q, "avg_q_value"); + // v_range is now ±240 (computed from reward_scale=10, gamma=0.95). + // Q-values should stay within the C51 support range with some margin. assert!( - avg_q.abs() < 2.5, - "ANOMALY 3: Q-value {:.4} saturating v_range ±2.0. C51 atoms exhausted.", + avg_q.abs() < 300.0, + "ANOMALY 3: Q-value {:.4} exceeding v_range ±240. C51 atoms exhausted.", avg_q ); diff --git a/crates/ml/tests/dqn_action_collapse_fix_test.rs b/crates/ml/tests/dqn_action_collapse_fix_test.rs index d11a6268b..e350c56a1 100644 --- a/crates/ml/tests/dqn_action_collapse_fix_test.rs +++ b/crates/ml/tests/dqn_action_collapse_fix_test.rs @@ -217,31 +217,24 @@ fn test_hyperopt_26d_includes_cql_alpha() { use ml::hyperopt::traits::ParameterSpace; let bounds = DQNParams::continuous_bounds(); - assert_eq!( - bounds.len(), - 39, - "Search space should be 39D (31 base + 8 reward/policy weights), got {}D", + assert!( + bounds.len() >= 39, + "Search space should be >= 39D, got {}D", bounds.len() ); let names = DQNParams::param_names(); assert_eq!( names.len(), - 39, - "param_names should return 39 entries, got {}", - names.len() + bounds.len(), + "param_names length should match continuous_bounds length: {} vs {}", + names.len(), bounds.len() ); - // cql_alpha at index 22 - assert_eq!( - names.get(22).copied(), - Some("cql_alpha"), - "Parameter 22 should be cql_alpha, got {:?}", - names.get(22) - ); - - // cql_alpha bounds: (0.0, 1.0) — full offline-RL range - let (lo, hi) = bounds.get(22).copied().unwrap_or((0.0, 0.0)); + // cql_alpha must exist in search space with bounds (0.0, 1.0) + let cql_idx = names.iter().position(|&n| n == "cql_alpha") + .expect("cql_alpha should be in param_names"); + let (lo, hi) = bounds[cql_idx]; assert!( (lo - 0.0).abs() < 1e-6 && (hi - 1.0).abs() < 1e-6, "cql_alpha bounds should be (0.0, 1.0), got ({}, {})", @@ -289,7 +282,8 @@ fn test_hyperopt_cql_alpha_round_trip() { params.cql_alpha = 0.25; let continuous = params.to_continuous(); - assert_eq!(continuous.len(), 39); + let bounds = DQNParams::continuous_bounds(); + assert_eq!(continuous.len(), bounds.len(), "to_continuous length should match bounds"); let reconstructed = DQNParams::from_continuous(&continuous).expect("from_continuous should succeed");