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>
95 lines
2.5 KiB
Rust
95 lines
2.5 KiB
Rust
//! Standalone test for GAE module
|
|
//! Run with: cargo test --test gae_standalone_test
|
|
|
|
use ml::dqn::{GAECalculator, GAEConfig};
|
|
|
|
#[test]
|
|
fn test_gae_basic_functionality() {
|
|
let gae = GAECalculator::new(0.99, 0.95);
|
|
|
|
let rewards = vec![1.0, 2.0, 3.0];
|
|
let values = vec![0.5, 0.6, 0.7];
|
|
let dones = vec![false, false, true];
|
|
|
|
let returns = gae.compute_returns(&rewards, &values, &dones);
|
|
|
|
assert_eq!(returns.len(), 3);
|
|
for r in &returns {
|
|
assert!(r.is_finite());
|
|
}
|
|
|
|
println!("✓ GAE basic test passed: returns = {:?}", returns);
|
|
}
|
|
|
|
#[test]
|
|
fn test_gae_from_config() {
|
|
let config = GAEConfig {
|
|
gamma: 0.98,
|
|
lambda: 0.9,
|
|
};
|
|
let gae = GAECalculator::from_config(&config);
|
|
|
|
assert_eq!(gae.gamma(), 0.98);
|
|
assert_eq!(gae.lambda(), 0.9);
|
|
|
|
println!("✓ GAE config test passed");
|
|
}
|
|
|
|
#[test]
|
|
fn test_gae_advantages_separate() {
|
|
let gae = GAECalculator::new(0.99, 0.95);
|
|
|
|
let rewards = vec![1.0, 2.0];
|
|
let values = vec![0.5, 0.6];
|
|
let dones = vec![false, false];
|
|
|
|
let advantages = gae.compute_advantages(&rewards, &values, &dones);
|
|
let returns = gae.compute_returns(&rewards, &values, &dones);
|
|
|
|
assert_eq!(advantages.len(), returns.len());
|
|
|
|
// Verify: returns = advantages + values
|
|
for i in 0..advantages.len() {
|
|
assert!((returns[i] - (advantages[i] + values[i])).abs() < 1e-6);
|
|
}
|
|
|
|
println!("✓ GAE advantages test passed");
|
|
}
|
|
|
|
#[test]
|
|
fn test_gae_lambda_zero_equals_td() {
|
|
// Lambda = 0 should give TD(0) returns
|
|
let gae = GAECalculator::new(0.99, 0.0);
|
|
let rewards = vec![1.0, 2.0, 3.0];
|
|
let values = vec![0.5, 0.6, 0.7];
|
|
let dones = vec![false, false, false];
|
|
|
|
let returns = gae.compute_returns(&rewards, &values, &dones);
|
|
|
|
// With λ=0, GAE reduces to TD(0): A_t = δ_t
|
|
assert_eq!(returns.len(), 3);
|
|
assert!((returns[2] - 3.0).abs() < 1e-6);
|
|
assert!((returns[1] - 2.693).abs() < 1e-6);
|
|
assert!((returns[0] - 1.594).abs() < 1e-6);
|
|
|
|
println!("✓ GAE lambda=0 test passed");
|
|
}
|
|
|
|
#[test]
|
|
fn test_gae_episode_boundary() {
|
|
let gae = GAECalculator::new(0.99, 0.95);
|
|
let rewards = vec![1.0, 2.0, 3.0];
|
|
let values = vec![0.5, 0.6, 0.7];
|
|
let dones = vec![false, true, false]; // Episode ends at step 1
|
|
|
|
let returns = gae.compute_returns(&rewards, &values, &dones);
|
|
assert_eq!(returns.len(), 3);
|
|
|
|
// All returns should be finite
|
|
for r in &returns {
|
|
assert!(r.is_finite());
|
|
}
|
|
|
|
println!("✓ GAE episode boundary test passed: returns = {:?}", returns);
|
|
}
|