Wave 10 Summary: - A1-A4: Architecture upgrades (4x network, LeakyReLU, Xavier init, diagnostics) - A5-A6: Integration testing and production validation - A7: Research hyperopt vs manual tuning (manual recommended) - A8-A12: HOLD penalty tuning and critical bug fixes Architecture Changes: - Network expansion: [128,64,32] → [256,128,64] (2.5x parameters) - LeakyReLU activation (alpha=0.01) to prevent dead neurons - Xavier/Glorot initialization for better gradient flow - Real-time diagnostic monitoring (Q-values, dead neurons, gradients) Critical Bugs Fixed: - Bug #1: HOLD penalty not wired to reward calculation - Bug #2: Zero price error in calculate_hold_reward (velocity-based fix) - Huber loss default enabled (Wave 9) - Shape mismatch fix (Wave 8) Test Results: - Integration tests: 149/152 passing (98%) - New tests: 40+ tests added across 15 files - Xavier init: 5/5 tests passing - HOLD penalty wiring: 4/4 tests passing - Zero price fix: 4/4 tests passing Known Issues: - HOLD bias persists at ~100% despite penalties - Gradient collapse: 217 instances per training run (norm=0.0) - Reversed penalty effect: Higher penalties → worse Q-spread - Root cause: Gradient clipping bottleneck (max_norm=10.0 vs penalty signal) Phase 1 Trials (all completed without crashes): - Penalty 0.5: Q-spread 250 pts, HOLD 100% - Penalty 1.0: Q-spread 251 pts, HOLD 100% - Penalty 2.0: Q-spread 255 pts, HOLD 100% (+ Q-value explosion) Next Steps: Architectural investigation via parallel agent debugging 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
580 lines
19 KiB
Rust
580 lines
19 KiB
Rust
//! DQN Hyperopt Multi-Objective Function Tests
|
|
//!
|
|
//! This module tests the multi-objective optimization function that combines:
|
|
//! - Reward component (80% weight): Primary metric - episode reward
|
|
//! - Diversity penalty (10% weight): Action distribution entropy
|
|
//! - Stability penalty (5% weight): Q-value health and gradient norms
|
|
//! - Completion penalty (5% weight): Epochs completed vs target
|
|
//!
|
|
//! ## Test Philosophy
|
|
//!
|
|
//! These tests validate the objective function BEFORE implementation (TDD).
|
|
//! They will fail initially and pass once the implementation is complete.
|
|
//!
|
|
//! ## Expected Behavior
|
|
//!
|
|
//! 1. **Hard Constraints** (trial rejection → objective = +1e6):
|
|
//! - Q-value floor: > -10.0
|
|
//! - Training loss ceiling: < 1000.0
|
|
//! - Minimum epochs: ≥ 20
|
|
//!
|
|
//! 2. **Soft Penalties** (weighted composite):
|
|
//! - Reward: -avg_episode_reward (negated for minimization)
|
|
//! - Diversity: Entropy-based action distribution penalty
|
|
//! - Stability: Q-value range + gradient norm penalties
|
|
//! - Completion: Penalty for early stopping
|
|
//!
|
|
//! ## Note on DQNMetrics Structure
|
|
//!
|
|
//! This test file uses the PROPOSED DQNMetrics structure with added fields:
|
|
//! - `action_distribution: [f64; 3]` - [BUY%, SELL%, HOLD%]
|
|
//! - `avg_gradient_norm: f64` - Average gradient norm during training
|
|
//!
|
|
//! These fields need to be added to `ml/src/hyperopt/adapters/dqn.rs` before
|
|
//! the tests can compile. See `DQN_HYPEROPT_OBJECTIVE_ANALYSIS.md` Section 6.
|
|
|
|
// NOTE: This will not compile until DQNMetrics is extended with:
|
|
// - action_distribution: [f64; 3]
|
|
// - avg_gradient_norm: f64
|
|
//
|
|
// Temporary struct definition for test-driven development
|
|
#[derive(Debug, Clone)]
|
|
struct DQNMetrics {
|
|
pub train_loss: f64,
|
|
pub val_loss: f64,
|
|
pub avg_q_value: f64,
|
|
pub final_epsilon: f64,
|
|
pub epochs_completed: usize,
|
|
pub avg_episode_reward: f64,
|
|
pub action_distribution: [f64; 3], // ✅ NEW: [BUY%, SELL%, HOLD%]
|
|
pub avg_gradient_norm: f64, // ✅ NEW: Average gradient norm
|
|
}
|
|
|
|
// ============================================================================
|
|
// Test Helpers
|
|
// ============================================================================
|
|
|
|
/// Create test DQNMetrics with default "healthy" values
|
|
fn create_healthy_metrics() -> DQNMetrics {
|
|
DQNMetrics {
|
|
train_loss: 0.1,
|
|
val_loss: 0.15,
|
|
avg_q_value: 5.0, // Healthy Q-value (0.5 to 10.0)
|
|
final_epsilon: 0.01,
|
|
epochs_completed: 50, // Full training
|
|
avg_episode_reward: 0.001, // Positive reward
|
|
action_distribution: [0.33, 0.33, 0.34], // Balanced actions
|
|
avg_gradient_norm: 2.0, // Healthy gradient norm (< 10.0)
|
|
}
|
|
}
|
|
|
|
/// Create test DQNMetrics with Q-value collapse
|
|
fn create_q_collapse_metrics() -> DQNMetrics {
|
|
DQNMetrics {
|
|
train_loss: 0.1,
|
|
val_loss: 0.15,
|
|
avg_q_value: -681.92, // CATASTROPHIC collapse
|
|
final_epsilon: 0.01,
|
|
epochs_completed: 10, // Early stop
|
|
avg_episode_reward: 0.001,
|
|
action_distribution: [0.994, 0.003, 0.003], // Degenerate (99.4% BUY)
|
|
avg_gradient_norm: 0.01,
|
|
}
|
|
}
|
|
|
|
/// Create test DQNMetrics with high loss (numerical explosion)
|
|
fn create_high_loss_metrics() -> DQNMetrics {
|
|
DQNMetrics {
|
|
train_loss: 5000.0, // EXPLOSION
|
|
val_loss: 5500.0,
|
|
avg_q_value: 5.0,
|
|
final_epsilon: 0.01,
|
|
epochs_completed: 5, // Early stop due to explosion
|
|
avg_episode_reward: 0.0001,
|
|
action_distribution: [0.50, 0.30, 0.20],
|
|
avg_gradient_norm: 150.0, // Gradient explosion
|
|
}
|
|
}
|
|
|
|
/// Create test DQNMetrics with early stopping
|
|
fn create_early_stop_metrics() -> DQNMetrics {
|
|
DQNMetrics {
|
|
train_loss: 0.1,
|
|
val_loss: 0.15,
|
|
avg_q_value: 5.0,
|
|
final_epsilon: 0.50, // High epsilon (not enough decay)
|
|
epochs_completed: 15, // Stopped at 15/50 epochs
|
|
avg_episode_reward: 0.0005,
|
|
action_distribution: [0.40, 0.35, 0.25],
|
|
avg_gradient_norm: 3.0,
|
|
}
|
|
}
|
|
|
|
/// Create test DQNMetrics with extreme HOLD bias
|
|
fn create_hold_bias_metrics() -> DQNMetrics {
|
|
DQNMetrics {
|
|
train_loss: 0.05,
|
|
val_loss: 0.06,
|
|
avg_q_value: 3.0,
|
|
final_epsilon: 0.01,
|
|
epochs_completed: 50,
|
|
avg_episode_reward: 0.0003, // Low reward (agent not acting)
|
|
action_distribution: [0.05, 0.05, 0.90], // 90% HOLD
|
|
avg_gradient_norm: 1.5,
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Component Tests (6 tests)
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_reward_component_normalization() {
|
|
// Test that reward component is correctly negated and scaled
|
|
let metrics = create_healthy_metrics();
|
|
|
|
// Reward component should be -avg_episode_reward
|
|
// For avg_episode_reward = 0.001, reward_component = -0.001
|
|
let reward_component = calculate_reward_component(&metrics);
|
|
|
|
assert_eq!(
|
|
reward_component,
|
|
-0.001,
|
|
"Reward component should be negated avg_episode_reward"
|
|
);
|
|
|
|
// Test with higher reward
|
|
let high_reward_metrics = DQNMetrics {
|
|
avg_episode_reward: 0.005,
|
|
..create_healthy_metrics()
|
|
};
|
|
let high_reward_component = calculate_reward_component(&high_reward_metrics);
|
|
|
|
assert_eq!(
|
|
high_reward_component,
|
|
-0.005,
|
|
"Higher reward should result in more negative component (better for minimization)"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_diversity_penalty_balanced_actions() {
|
|
// Test that balanced action distribution (33/33/33) results in zero/low penalty
|
|
let metrics = create_healthy_metrics();
|
|
|
|
let penalty = calculate_diversity_penalty(&metrics);
|
|
|
|
// Balanced distribution should have entropy close to max_entropy = ln(3) ≈ 1.099
|
|
// Diversity score = entropy / max_entropy ≈ 1.0
|
|
// Penalty = 1.0 - diversity_score ≈ 0.0
|
|
assert!(
|
|
penalty < 0.05,
|
|
"Balanced action distribution should have low penalty: {}",
|
|
penalty
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_diversity_penalty_extreme_hold() {
|
|
// Test that extreme HOLD bias (5/5/90) results in high penalty
|
|
let metrics = create_hold_bias_metrics();
|
|
|
|
let penalty = calculate_diversity_penalty(&metrics);
|
|
|
|
// Degenerate distribution (90% HOLD) should have low entropy
|
|
// Diversity score should be low
|
|
// Penalty should be high (close to 1.0)
|
|
assert!(
|
|
penalty > 0.5,
|
|
"Extreme HOLD bias should have high penalty: {}",
|
|
penalty
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_stability_penalty_good_qvalues() {
|
|
// Test that Q-values in healthy range (0.5 to 10.0) result in zero penalty
|
|
let metrics = create_healthy_metrics();
|
|
|
|
let penalty = calculate_stability_penalty(&metrics);
|
|
|
|
// Q-value = 5.0 is in healthy range [0.5, 10.0]
|
|
// Gradient norm = 2.0 is below threshold (10.0)
|
|
// Penalty should be zero or very low
|
|
assert!(
|
|
penalty < 0.1,
|
|
"Healthy Q-values and gradients should have low penalty: {}",
|
|
penalty
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_stability_penalty_gradient_explosion() {
|
|
// Test that gradient explosion (grad_norm > 10) results in high penalty
|
|
let metrics = create_high_loss_metrics();
|
|
|
|
let penalty = calculate_stability_penalty(&metrics);
|
|
|
|
// Gradient norm = 150.0 >> 10.0 threshold
|
|
// Should result in significant penalty
|
|
assert!(
|
|
penalty > 5.0,
|
|
"Gradient explosion should have high penalty: {}",
|
|
penalty
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_completion_penalty_early_stop() {
|
|
// Test that early stopping (30/100 epochs) results in penalty
|
|
let metrics = create_early_stop_metrics();
|
|
|
|
let penalty = calculate_completion_penalty(&metrics, 50);
|
|
|
|
// Completed 15/50 epochs = 30% completion
|
|
// Should have penalty proportional to missing epochs
|
|
assert!(
|
|
penalty > 0.5,
|
|
"Early stopping should have significant penalty: {}",
|
|
penalty
|
|
);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Integration Tests (3 tests)
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_multiobjective_optimal_trial() {
|
|
// Test that a "perfect" trial gets a good (low) objective score
|
|
let metrics = create_healthy_metrics();
|
|
|
|
let objective = calculate_multiobjective(&metrics, 50);
|
|
|
|
// Expected calculation:
|
|
// reward_component = -0.001
|
|
// diversity_penalty = ~0.0 (balanced actions)
|
|
// stability_penalty = ~0.0 (healthy Q-values)
|
|
// completion_penalty = ~0.0 (50/50 epochs)
|
|
//
|
|
// objective = 0.8 * (-0.001) + 0.1 * 0.0 + 0.05 * 0.0 + 0.05 * 0.0
|
|
// = -0.0008
|
|
|
|
assert!(
|
|
objective < -0.0005,
|
|
"Optimal trial should have low (negative) objective: {}",
|
|
objective
|
|
);
|
|
|
|
// Verify all constraints pass
|
|
assert!(
|
|
!violates_hard_constraints(&metrics),
|
|
"Optimal trial should not violate hard constraints"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_multiobjective_broken_trial() {
|
|
// Test that a broken trial (99% HOLD) gets a bad (high) objective score
|
|
let metrics = create_hold_bias_metrics();
|
|
|
|
let objective = calculate_multiobjective(&metrics, 50);
|
|
|
|
// Expected calculation:
|
|
// reward_component = -0.0003 (low reward due to inaction)
|
|
// diversity_penalty = ~0.6 (90% HOLD bias)
|
|
// stability_penalty = ~0.0 (Q-values OK)
|
|
// completion_penalty = ~0.0 (50/50 epochs)
|
|
//
|
|
// objective = 0.8 * (-0.0003) + 0.1 * 0.6 + 0.05 * 0.0 + 0.05 * 0.0
|
|
// = -0.00024 + 0.06
|
|
// = +0.05976
|
|
|
|
assert!(
|
|
objective > 0.03,
|
|
"Broken trial (HOLD bias) should have high (positive) objective: {}",
|
|
objective
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_weight_sensitivity() {
|
|
// Test that weight changes affect trial ranking correctly
|
|
|
|
let good_metrics = create_healthy_metrics();
|
|
let hold_bias_metrics = create_hold_bias_metrics();
|
|
|
|
// Calculate objectives with standard weights (reward=80%, diversity=10%, stability=5%, completion=5%)
|
|
let good_objective = calculate_multiobjective(&good_metrics, 50);
|
|
let hold_objective = calculate_multiobjective(&hold_bias_metrics, 50);
|
|
|
|
// Good trial should have LOWER objective (minimization)
|
|
assert!(
|
|
good_objective < hold_objective,
|
|
"Good trial (balanced actions) should rank better than HOLD-biased trial: {} vs {}",
|
|
good_objective,
|
|
hold_objective
|
|
);
|
|
|
|
// Verify ranking is correct
|
|
let ranking_correct = good_objective < hold_objective;
|
|
assert!(
|
|
ranking_correct,
|
|
"Weight sensitivity failed: good={}, hold={}",
|
|
good_objective,
|
|
hold_objective
|
|
);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Hard Constraint Tests (3 tests)
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_hard_constraint_q_value_floor() {
|
|
// Test that Q-value < -10.0 triggers hard constraint violation
|
|
let metrics = create_q_collapse_metrics();
|
|
|
|
let objective = calculate_multiobjective(&metrics, 50);
|
|
|
|
// Hard constraint violation should return penalty = +1e6
|
|
assert_eq!(
|
|
objective,
|
|
1e6,
|
|
"Q-value floor violation should return penalty objective"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_hard_constraint_training_loss_ceiling() {
|
|
// Test that training loss > 1000.0 triggers hard constraint violation
|
|
let metrics = create_high_loss_metrics();
|
|
|
|
let objective = calculate_multiobjective(&metrics, 50);
|
|
|
|
// Hard constraint violation should return penalty = +1e6
|
|
assert_eq!(
|
|
objective,
|
|
1e6,
|
|
"Training loss ceiling violation should return penalty objective"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_hard_constraint_minimum_epochs() {
|
|
// Test that epochs_completed < 20 triggers hard constraint violation
|
|
let metrics = DQNMetrics {
|
|
epochs_completed: 15, // Below minimum of 20
|
|
..create_healthy_metrics()
|
|
};
|
|
|
|
let objective = calculate_multiobjective(&metrics, 50);
|
|
|
|
// Hard constraint violation should return penalty = +1e6
|
|
assert_eq!(
|
|
objective,
|
|
1e6,
|
|
"Minimum epochs violation should return penalty objective"
|
|
);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Edge Case Tests (3 additional tests)
|
|
// ============================================================================
|
|
|
|
#[test]
|
|
fn test_edge_case_zero_entropy() {
|
|
// Test that 100% single action (zero entropy) results in maximum penalty
|
|
let metrics = DQNMetrics {
|
|
train_loss: 0.1,
|
|
val_loss: 0.15,
|
|
avg_q_value: 5.0,
|
|
final_epsilon: 0.01,
|
|
epochs_completed: 50,
|
|
avg_episode_reward: 0.001,
|
|
action_distribution: [1.0, 0.0, 0.0], // 100% BUY (degenerate)
|
|
avg_gradient_norm: 2.0,
|
|
};
|
|
|
|
let penalty = calculate_diversity_penalty(&metrics);
|
|
|
|
// Zero entropy → diversity_score = 0 → penalty = 1.0 (maximum)
|
|
assert!(
|
|
penalty > 0.95,
|
|
"Zero entropy should have maximum penalty: {}",
|
|
penalty
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_edge_case_q_value_boundaries() {
|
|
// Test Q-value penalty at exact boundaries (0.5 and 10.0)
|
|
|
|
// Test 1: Q-value = 0.5 (lower boundary of healthy range)
|
|
let metrics_lower = DQNMetrics {
|
|
avg_q_value: 0.5,
|
|
avg_gradient_norm: 2.0,
|
|
..create_healthy_metrics()
|
|
};
|
|
let penalty_lower = calculate_stability_penalty(&metrics_lower);
|
|
assert!(
|
|
penalty_lower < 0.01,
|
|
"Q-value at lower boundary (0.5) should have minimal penalty: {}",
|
|
penalty_lower
|
|
);
|
|
|
|
// Test 2: Q-value = 10.0 (upper boundary of healthy range)
|
|
let metrics_upper = DQNMetrics {
|
|
avg_q_value: 10.0,
|
|
avg_gradient_norm: 2.0,
|
|
..create_healthy_metrics()
|
|
};
|
|
let penalty_upper = calculate_stability_penalty(&metrics_upper);
|
|
assert!(
|
|
penalty_upper < 0.01,
|
|
"Q-value at upper boundary (10.0) should have minimal penalty: {}",
|
|
penalty_upper
|
|
);
|
|
|
|
// Test 3: Q-value = 0.49 (just below lower boundary)
|
|
let metrics_below = DQNMetrics {
|
|
avg_q_value: 0.49,
|
|
avg_gradient_norm: 2.0,
|
|
..create_healthy_metrics()
|
|
};
|
|
let penalty_below = calculate_stability_penalty(&metrics_below);
|
|
assert!(
|
|
penalty_below > 0.0,
|
|
"Q-value below lower boundary (0.49) should have penalty: {}",
|
|
penalty_below
|
|
);
|
|
|
|
// Test 4: Q-value = 10.01 (just above upper boundary)
|
|
let metrics_above = DQNMetrics {
|
|
avg_q_value: 10.01,
|
|
avg_gradient_norm: 2.0,
|
|
..create_healthy_metrics()
|
|
};
|
|
let penalty_above = calculate_stability_penalty(&metrics_above);
|
|
assert!(
|
|
penalty_above > 0.0,
|
|
"Q-value above upper boundary (10.01) should have penalty: {}",
|
|
penalty_above
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_edge_case_gradient_norm_threshold() {
|
|
// Test gradient norm penalty at exact threshold (10.0)
|
|
|
|
// Test 1: grad_norm = 10.0 (at threshold)
|
|
let metrics_at = DQNMetrics {
|
|
avg_q_value: 5.0,
|
|
avg_gradient_norm: 10.0,
|
|
..create_healthy_metrics()
|
|
};
|
|
let penalty_at = calculate_stability_penalty(&metrics_at);
|
|
assert!(
|
|
penalty_at < 0.1,
|
|
"Gradient norm at threshold (10.0) should have minimal penalty: {}",
|
|
penalty_at
|
|
);
|
|
|
|
// Test 2: grad_norm = 9.99 (just below threshold)
|
|
let metrics_below = DQNMetrics {
|
|
avg_q_value: 5.0,
|
|
avg_gradient_norm: 9.99,
|
|
..create_healthy_metrics()
|
|
};
|
|
let penalty_below = calculate_stability_penalty(&metrics_below);
|
|
assert!(
|
|
penalty_below < 0.1,
|
|
"Gradient norm below threshold (9.99) should have minimal penalty: {}",
|
|
penalty_below
|
|
);
|
|
|
|
// Test 3: grad_norm = 10.01 (just above threshold)
|
|
let metrics_above = DQNMetrics {
|
|
avg_q_value: 5.0,
|
|
avg_gradient_norm: 10.01,
|
|
..create_healthy_metrics()
|
|
};
|
|
let penalty_above = calculate_stability_penalty(&metrics_above);
|
|
assert!(
|
|
penalty_above > 0.0,
|
|
"Gradient norm above threshold (10.01) should have penalty: {}",
|
|
penalty_above
|
|
);
|
|
}
|
|
|
|
// ============================================================================
|
|
// Component Helper Functions (NOT IMPLEMENTED YET)
|
|
// ============================================================================
|
|
|
|
/// Calculate reward component (negated for minimization)
|
|
///
|
|
/// **NOT IMPLEMENTED**: This function is a placeholder for the actual implementation.
|
|
/// It will be implemented in `ml/src/hyperopt/adapters/dqn.rs` as part of the multi-objective function.
|
|
fn calculate_reward_component(_metrics: &DQNMetrics) -> f64 {
|
|
panic!("calculate_reward_component() not implemented yet - implement in ml/src/hyperopt/adapters/dqn.rs");
|
|
}
|
|
|
|
/// Calculate diversity penalty using entropy
|
|
///
|
|
/// Entropy = -Σ(p_i * ln(p_i)) for i in [BUY, SELL, HOLD]
|
|
/// Max entropy = ln(3) ≈ 1.099 (uniform distribution)
|
|
/// Diversity score = entropy / max_entropy
|
|
/// Penalty = 1.0 - diversity_score
|
|
///
|
|
/// **NOT IMPLEMENTED**: This function is a placeholder for the actual implementation.
|
|
fn calculate_diversity_penalty(_metrics: &DQNMetrics) -> f64 {
|
|
panic!("calculate_diversity_penalty() not implemented yet - implement in ml/src/hyperopt/adapters/dqn.rs");
|
|
}
|
|
|
|
/// Calculate stability penalty from Q-value health and gradient norms
|
|
///
|
|
/// Q-value penalty:
|
|
/// - If Q < 0.5: penalty = |0.5 - Q|
|
|
/// - If Q > 10.0: penalty = (Q - 10.0) * 0.1
|
|
/// - Else: penalty = 0.0
|
|
///
|
|
/// Gradient penalty:
|
|
/// - If grad_norm > 10.0: penalty = (grad_norm - 10.0) * 0.5
|
|
/// - Else: penalty = 0.0
|
|
///
|
|
/// **NOT IMPLEMENTED**: This function is a placeholder for the actual implementation.
|
|
fn calculate_stability_penalty(_metrics: &DQNMetrics) -> f64 {
|
|
panic!("calculate_stability_penalty() not implemented yet - implement in ml/src/hyperopt/adapters/dqn.rs");
|
|
}
|
|
|
|
/// Calculate completion penalty from epochs completed vs target
|
|
///
|
|
/// Penalty = (target_epochs - completed_epochs) / target_epochs
|
|
///
|
|
/// **NOT IMPLEMENTED**: This function is a placeholder for the actual implementation.
|
|
fn calculate_completion_penalty(_metrics: &DQNMetrics, _target_epochs: usize) -> f64 {
|
|
panic!("calculate_completion_penalty() not implemented yet - implement in ml/src/hyperopt/adapters/dqn.rs");
|
|
}
|
|
|
|
/// Check if metrics violate any hard constraints
|
|
///
|
|
/// Hard constraints:
|
|
/// 1. Q-value floor: avg_q_value > -10.0
|
|
/// 2. Training loss ceiling: train_loss < 1000.0
|
|
/// 3. Minimum epochs: epochs_completed >= 20
|
|
///
|
|
/// **NOT IMPLEMENTED**: This function is a placeholder for the actual implementation.
|
|
fn violates_hard_constraints(_metrics: &DQNMetrics) -> bool {
|
|
panic!("violates_hard_constraints() not implemented yet - implement in ml/src/hyperopt/adapters/dqn.rs");
|
|
}
|
|
|
|
/// Calculate multi-objective function
|
|
///
|
|
/// If hard constraints violated: return +1e6
|
|
/// Else: return weighted composite of components
|
|
///
|
|
/// **NOT IMPLEMENTED**: This function is a placeholder for the actual implementation.
|
|
fn calculate_multiobjective(_metrics: &DQNMetrics, _target_epochs: usize) -> f64 {
|
|
panic!("calculate_multiobjective() not implemented yet - implement in ml/src/hyperopt/adapters/dqn.rs");
|
|
}
|