Files
foxhunt/docs/codebase-cleanup/wave26_p1.4_ensemble_uncertainty_changes.md
jgrusewski 2df1ea92e1 feat(ml): WAVE 29 DQN Codebase Cleanup & Refactoring Campaign
BREAKING CHANGES:
- Removed orphaned dqn.rs monolithic trainer (4,975 lines)
- Removed orphaned dqn_ensemble.rs module (816 lines)
- Removed orphaned tft.rs and tft_complete_int8_integration_test.rs
- TFT trainer split into modular directory structure

DQN Module Refactoring:
- Split trainers/dqn.rs into modular structure (config.rs, statistics.rs, trainer.rs)
- Fixed hyperopt 39D search space (continuous params only)
- Boolean flags (use_dueling, use_double_dqn, use_per, use_noisy_nets) are now FIXED architectural decisions
- use_distributional defaults to false (Candle BUG #36 - scatter_add gradient issues)

Clean Module Structure:
- ml/src/trainers/dqn/ directory with proper mod.rs exports
- ml/src/trainers/tft/ directory with config.rs, types.rs, model.rs, trainer.rs, tests.rs
- All P0 features validated: TD-error clamping, batch diversity, LR scheduler, priority staleness

Documentation:
- Added comprehensive docs in docs/codebase-cleanup/
- ADR-001 for DQN refactoring decisions
- Rainbow DQN component matrix and quick reference guides

Build Status: Compiles with zero errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 23:46:13 +01:00

7.1 KiB

WAVE 26 P1.4: Ensemble Uncertainty Hyperopt Integration

Summary

Added 6D ensemble uncertainty parameters to DQN hyperopt search space (22D → 27D).

Changes Made

1. DQNHyperparameters (ml/src/trainers/dqn/config.rs)

Lines 458-470: Added 6 new fields:

// WAVE 26 P1.4: Ensemble Uncertainty for Exploration (6D hyperopt expansion)
pub use_ensemble_uncertainty: bool,       // Enable/disable feature
pub ensemble_size: usize,                 // 3-10 Q-network heads
pub beta_variance: f64,                   // 0.1-1.0 variance penalty
pub beta_disagreement: f64,               // 0.1-1.0 disagreement penalty
pub beta_entropy: f64,                    // 0.05-0.5 entropy bonus
pub variance_cap: f64,                    // 0.1-2.0 cap for over-penalization

Lines 591-602: Added conservative defaults:

use_ensemble_uncertainty: false,      // Default: disabled (use noisy networks instead)
ensemble_size: 5,                     // Default: 5 Q-network heads
beta_variance: 0.5,                   // Default: 50% variance penalty
beta_disagreement: 0.5,               // Default: 50% disagreement penalty
beta_entropy: 0.1,                    // Default: 10% entropy bonus
variance_cap: 1.0,                    // Default: cap at 1.0

2. DQNParams (ml/src/hyperopt/adapters/dqn.rs)

Lines 258-269: Added 5 new fields (ensemble_size as f64 for hyperopt):

// WAVE 26 P1.4: Ensemble Uncertainty Exploration (5D hyperopt expansion)
pub use_ensemble_uncertainty: bool,
pub ensemble_size: f64,              // Stored as f64 for hyperopt, cast to usize
pub beta_variance: f64,
pub beta_disagreement: f64,
pub beta_entropy: f64,
// Note: variance_cap is NOT in search space, it's fixed in DQNHyperparameters

Lines 317-323: Added defaults matching conservative config:

use_ensemble_uncertainty: false,  // Default: disabled
ensemble_size: 5.0,               // Default: 5 heads
beta_variance: 0.5,               // Default: 50% variance penalty
beta_disagreement: 0.5,           // Default: 50% disagreement penalty
beta_entropy: 0.1,                // Default: 10% entropy bonus

3. Search Space Bounds (ml/src/hyperopt/adapters/dqn.rs)

Lines 373-378: Added 5 new continuous bounds (22D → 27D):

// WAVE 26 P1.4: Ensemble Uncertainty (22D → 27D)
(3.0, 10.0),        // 22: ensemble_size (will be rounded to int)
(0.1, 1.0),         // 23: beta_variance
(0.1, 1.0),         // 24: beta_disagreement
(0.05, 0.5),        // 25: beta_entropy
(0.1, 2.0),         // 26: variance_cap (fixed in DQNHyperparameters, not tuned per-trial)

Line 384: Updated validation to expect 27 parameters (was 22):

if x.len() != 27 {
    return Err(MLError::ConfigError {
        reason: format!("Expected 27 continuous parameters (WAVE 26 P1.4: added ensemble params), got {}", x.len()),
    });
}

4. Parameter Extraction (ml/src/hyperopt/adapters/dqn.rs)

Lines 423-428: Added ensemble parameter extraction with clamping:

// WAVE 26 P1.4: Extract ensemble uncertainty parameters
let ensemble_size = x[22].round().clamp(3.0, 10.0);
let beta_variance = x[23].clamp(0.1, 1.0);
let beta_disagreement = x[24].clamp(0.1, 1.0);
let beta_entropy = x[25].clamp(0.05, 0.5);
// Note: x[26] is variance_cap, but it's NOT in DQNParams (fixed in DQNHyperparameters)

Lines 484-488: Wired ensemble parameters into DQNParams construction:

// WAVE 26 P1.4: Ensemble uncertainty parameters
use_ensemble_uncertainty: false, // Boolean not in search space, hardcoded disabled
ensemble_size,
beta_variance,
beta_disagreement,
beta_entropy,

5. to_continuous() (ml/src/hyperopt/adapters/dqn.rs)

Lines 524-529: Added ensemble parameters to continuous representation:

// WAVE 26 P1.4: Ensemble uncertainty parameters (27D)
self.ensemble_size,
self.beta_variance,
self.beta_disagreement,
self.beta_entropy,
1.0, // variance_cap placeholder (not in DQNParams, fixed in DQNHyperparameters)

6. param_names() (ml/src/hyperopt/adapters/dqn.rs)

Lines 561-566: Added parameter names for logging:

// WAVE 26 P1.4: Ensemble uncertainty parameters (27D)
"ensemble_size",
"beta_variance",
"beta_disagreement",
"beta_entropy",
"variance_cap",

7. train_with_params() (ml/src/hyperopt/adapters/dqn.rs)

Lines 1943-1956: Wired ensemble parameters through to DQNHyperparameters:

// WAVE 26 P0.6: Learning Rate Scheduling (not in search space, fixed)
lr_decay_type: crate::trainers::dqn::lr_scheduler::LRDecayType::Constant,
min_learning_rate: 1e-6,

// WAVE 26 P1.4: Ensemble Uncertainty Exploration (tunable via hyperopt)
use_ensemble_uncertainty: params.use_ensemble_uncertainty,
ensemble_size: params.ensemble_size.round() as usize, // Cast f64 to usize
beta_variance: params.beta_variance,
beta_disagreement: params.beta_disagreement,
beta_entropy: params.beta_entropy,
variance_cap: 1.0, // Fixed in all trials (not in search space)

// WAVE 26 P1.8: Curiosity-Driven Exploration (not in search space, fixed)
curiosity_weight: 0.0, // Disabled for now

8. TDD Tests (ml/src/trainers/dqn/tests/ensemble_uncertainty_hyperopt_tests.rs)

Created comprehensive test suite with 8 tests:

  1. test_dqn_hyperparameters_has_ensemble_fields - Verify DQNHyperparameters has 6 new fields
  2. test_dqn_params_has_ensemble_fields - Verify DQNParams has 5 new fields
  3. test_ensemble_search_space_bounds - Verify search space bounds are correct
  4. test_from_continuous_validates_ensemble_params - Test parameter conversion
  5. test_from_continuous_clamps_ensemble_params - Test clamping behavior
  6. test_to_continuous_includes_ensemble_params - Verify continuous representation
  7. test_ensemble_size_rounds_to_integer - Test ensemble_size rounding
  8. Module registered in ml/src/trainers/dqn/tests/mod.rs (line 5)

Known Issues

⚠️ Compilation blocked: WorkingDQNConfig struct needs ensemble fields added. This is expected - the full ensemble implementation is in WAVE 25, we're just adding hyperopt search space here.

File Changes Summary

File Lines Changed Description
ml/src/trainers/dqn/config.rs 458-470, 591-602 Added 6 fields + defaults
ml/src/hyperopt/adapters/dqn.rs 258-269, 317-323, 373-378, 384, 423-428, 484-488, 524-529, 561-566, 1943-1956 Search space expansion 22D→27D
ml/src/trainers/dqn/tests/ensemble_uncertainty_hyperopt_tests.rs 1-250 (new file) TDD test suite
ml/src/trainers/dqn/tests/mod.rs 5 Registered test module

Search Space Expansion

  • Before (WAVE 19): 22D continuous space
  • After (WAVE 26 P1.4): 27D continuous space
  • New parameters: 5 (use_ensemble_uncertainty is boolean, not in search space)

Validation

All TDD tests written and module structure in place. Compilation will succeed once:

  1. WorkingDQNConfig adds ensemble fields (WAVE 25 implementation)
  2. Other compilation errors in codebase are fixed (unrelated to this change)

Next Steps

  1. Fix WorkingDQNConfig compilation error (add ensemble fields)
  2. Run full test suite: cargo test ensemble_uncertainty_hyperopt_tests
  3. Validate hyperopt can spawn trials with 27D parameter space
  4. Test ensemble_size rounding to usize works correctly