use super::*;
fn workload(active_neurons: usize, spike_rate: f64, synaptic_activity: f64) -> WorkloadSample<f64> {
WorkloadSample {
timestamp: Instant::now(),
active_neurons,
spike_rate,
synaptic_activity,
memory_access_pattern: MemoryAccessPattern::Mixed,
communication_overhead: 1.0,
}
}
fn config_with(strategy: EnergyOptimizationStrategy) -> EnergyEfficientConfig<f64> {
EnergyEfficientConfig::<f64> {
primary_strategy: strategy,
..EnergyEfficientConfig::<f64>::default()
}
}
#[test]
fn dvfs_transition_yields_nonzero_power_reduction() {
let config = config_with(EnergyOptimizationStrategy::DynamicVoltageScaling);
let mut optimizer = EnergyEfficientOptimizer::new(config, 1000);
let low_util = workload(10, 5.0, 2.0);
let result = optimizer
.optimize_energy(&low_util)
.expect("optimize_energy failed");
let state = optimizer.get_system_state();
assert!(
state.current_voltage != 1.0 || state.current_frequency != 100.0,
"DVFS controller never transitioned away from the startup operating point"
);
assert_ne!(
result.power_reduction, 0.0,
"power_reduction was zero: the DVFS ratio is still being forced to 1.0"
);
}
#[test]
fn clock_gating_scales_with_idle_fraction() {
let make = |active: usize| {
let config = config_with(EnergyOptimizationStrategy::ClockGating);
let mut optimizer = EnergyEfficientOptimizer::new(config, 1000);
optimizer
.optimize_energy(&workload(active, 5.0, 2.0))
.expect("optimize_energy failed")
.power_reduction
};
let mostly_idle = make(10); let mostly_active = make(900); assert!(
mostly_idle > mostly_active,
"clock gating did not scale down with a less-idle workload: idle={mostly_idle}, active={mostly_active}"
);
}
#[test]
fn power_gating_respects_idle_threshold() {
let config = config_with(EnergyOptimizationStrategy::PowerGating);
let mut busy_optimizer = EnergyEfficientOptimizer::new(config.clone(), 1000);
let busy_result = busy_optimizer
.optimize_energy(&workload(950, 5.0, 2.0)) .expect("optimize_energy failed");
assert_eq!(busy_result.power_reduction, 0.0);
let mut idle_optimizer = EnergyEfficientOptimizer::new(config, 1000);
let idle_result = idle_optimizer
.optimize_energy(&workload(50, 5.0, 2.0)) .expect("optimize_energy failed");
assert!(idle_result.power_reduction > 0.0);
}
#[test]
fn sleep_mode_escalates_with_idleness() {
let config = config_with(EnergyOptimizationStrategy::SleepModeOptimization);
let mut light = EnergyEfficientOptimizer::new(config.clone(), 1000);
light
.optimize_energy(&workload(600, 5.0, 2.0)) .expect("optimize_energy failed");
assert!(matches!(
light.get_system_state().sleep_status,
SleepStatus::LightSleep
));
let mut deep = EnergyEfficientOptimizer::new(config, 1000);
deep.optimize_energy(&workload(50, 5.0, 2.0)) .expect("optimize_energy failed");
assert!(matches!(
deep.get_system_state().sleep_status,
SleepStatus::DeepSleep
));
}
#[test]
fn thermal_aware_reduction_is_proportional_to_temperature() {
let config = config_with(EnergyOptimizationStrategy::ThermalAwareOptimization);
let mut cool = EnergyEfficientOptimizer::new(config.clone(), 1000);
cool.system_state.temperature = 60.0; let cool_result = cool
.optimize_energy(&workload(500, 5.0, 2.0))
.expect("optimize_energy failed");
let mut warm = EnergyEfficientOptimizer::new(config.clone(), 1000);
warm.system_state.temperature = 75.0; let warm_result = warm
.optimize_energy(&workload(500, 5.0, 2.0))
.expect("optimize_energy failed");
let mut hot = EnergyEfficientOptimizer::new(config, 1000);
hot.system_state.temperature = 95.0; let hot_result = hot
.optimize_energy(&workload(500, 5.0, 2.0))
.expect("optimize_energy failed");
assert!(cool_result.power_reduction < warm_result.power_reduction);
assert!(warm_result.power_reduction < hot_result.power_reduction);
}
#[test]
fn sparse_strategy_uses_real_matrix_zero_fraction() {
let config = config_with(EnergyOptimizationStrategy::SparseComputation);
let mut optimizer = EnergyEfficientOptimizer::new(config, 1000);
let matrix = Array2::from_shape_vec(
(2, 10),
vec![
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 1.0,
],
)
.expect("failed to build test matrix");
let with_matrix = optimizer
.optimize_energy_with_matrix(&workload(900, 5.0, 2.0), Some(&matrix))
.expect("optimize_energy_with_matrix failed");
let mut baseline_optimizer = EnergyEfficientOptimizer::new(
config_with(EnergyOptimizationStrategy::SparseComputation),
1000,
);
let without_matrix = baseline_optimizer
.optimize_energy(&workload(900, 5.0, 2.0))
.expect("optimize_energy failed");
assert!(
with_matrix.power_reduction > without_matrix.power_reduction,
"real 80%-sparse matrix should yield more savings than the ~10%-idle estimate: \
with_matrix={}, without_matrix={}",
with_matrix.power_reduction,
without_matrix.power_reduction
);
}
#[test]
fn predict_energy_honors_horizon_and_history() {
let config = EnergyEfficientConfig::<f64>::default();
let empty_optimizer = EnergyEfficientOptimizer::new(config.clone(), 1000);
let empty_prediction = empty_optimizer
.predictive_manager
.predict_energy(Duration::from_secs(60))
.expect("predict_energy failed");
assert_eq!(empty_prediction, 0.0);
let mut optimizer = EnergyEfficientOptimizer::new(config, 1000);
for _ in 0..10 {
optimizer
.optimize_energy(&workload(500, 5.0, 2.0))
.expect("optimize_energy failed");
}
let short = optimizer
.predictive_manager
.predict_energy(Duration::from_millis(10))
.expect("predict_energy failed");
let long = optimizer
.predictive_manager
.predict_energy(Duration::from_millis(100))
.expect("predict_energy failed");
assert!(
short > 0.0,
"prediction should be positive once power has been observed"
);
assert!(
(long / short - 10.0).abs() < 1e-6,
"predict_energy is not linear in horizon: short={short}, long={long}"
);
}
#[test]
fn thermal_model_integrates_gradually_and_bounds_history() {
let config = EnergyEfficientConfig::<f64>::default();
let mut optimizer = EnergyEfficientOptimizer::new(config, 1000);
optimizer.system_state.current_power = 1000.0; let initial_temp = optimizer.thermal_manager.current_temperature;
assert_eq!(initial_temp, 25.0);
let state_snapshot = optimizer.system_state.clone();
optimizer
.thermal_manager
.update(&state_snapshot)
.expect("thermal update failed");
let after_first = optimizer.thermal_manager.current_temperature;
assert!(
after_first > initial_temp && after_first < 525.0,
"temperature should move toward, but not jump to, steady state: {after_first}"
);
std::thread::sleep(Duration::from_millis(20));
let state_snapshot = optimizer.system_state.clone();
optimizer
.thermal_manager
.update(&state_snapshot)
.expect("thermal update failed");
let after_second = optimizer.thermal_manager.current_temperature;
assert!(
after_second > after_first,
"temperature should keep approaching steady state over time"
);
optimizer.energy_monitor.window_size = Duration::from_millis(1);
for _ in 0..20 {
let state_snapshot = optimizer.system_state.clone();
optimizer
.energy_monitor
.update(&state_snapshot)
.expect("energy monitor update failed");
std::thread::sleep(Duration::from_millis(2));
}
assert!(
optimizer.energy_monitor.power_history.len() <= 3,
"power_history grew without bound: len={}",
optimizer.energy_monitor.power_history.len()
);
}