1use super::{NeuromorphicMetrics, ThermalManagementConfig};
8use crate::error::Result;
9use scirs2_core::ndarray::{Array2, ArrayBase, Data, Dimension};
10use scirs2_core::numeric::Float;
11use std::collections::{HashMap, VecDeque};
12use std::fmt::Debug;
13use std::time::{Duration, Instant};
14
15#[inline]
20fn to_t_or<T: Float>(value: f64, fallback: T) -> T {
21 T::from(value).unwrap_or(fallback)
22}
23
24const NOMINAL_VOLTAGE: f64 = 1.0;
40const NOMINAL_FREQUENCY_MHZ: f64 = 1000.0;
41
42const STATIC_POWER_PER_NEURON_NW: f64 = 0.05;
45const DYNAMIC_ENERGY_PER_SPIKE_NJ: f64 = 0.02;
47const DYNAMIC_POWER_PER_SYNAPTIC_ACTIVITY_NW: f64 = 5.0;
49const DYNAMIC_POWER_PER_COMM_OVERHEAD_NW: f64 = 2.0;
51
52const GATING_DOMAINS: usize = 8;
55const GATING_IDLE_THRESHOLD: f64 = 0.15;
58const CLOCK_GATING_EFFICIENCY: f64 = 0.9;
61const LIGHT_SLEEP_SAVINGS: f64 = 0.4;
64const DEEP_SLEEP_SAVINGS: f64 = 0.85;
65const DEEP_SLEEP_IDLE_THRESHOLD: f64 = 0.7;
67const THERMAL_SAFE_TEMP_C: f64 = 60.0;
71const THERMAL_CRITICAL_TEMP_C: f64 = 90.0;
72const THERMAL_MIN_REDUCTION: f64 = 0.05;
73const THERMAL_MAX_REDUCTION: f64 = 0.6;
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77pub enum EnergyOptimizationStrategy {
78 DynamicVoltageScaling,
80
81 PowerGating,
83
84 ClockGating,
86
87 AdaptivePrecision,
89
90 SparseComputation,
92
93 EventDrivenProcessing,
95
96 SleepModeOptimization,
98
99 ThermalAwareOptimization,
101
102 MultiLevel,
104}
105
106#[derive(Debug, Clone)]
108pub struct EnergyBudget<T: Float + Debug + Send + Sync + 'static> {
109 pub total_budget: T,
111
112 pub current_consumption: T,
114
115 pub per_operation_budget: T,
117
118 pub component_allocation: HashMap<EnergyComponent, T>,
120
121 pub efficiency_targets: EnergyEfficiencyTargets<T>,
123
124 pub emergency_reserves: T,
126
127 pub monitoring_frequency: Duration,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
133pub enum EnergyComponent {
134 SynapticOps,
136
137 MembraneDynamics,
139
140 SpikeGeneration,
142
143 PlasticityUpdates,
145
146 MemoryAccess,
148
149 Communication,
151
152 ControlLogic,
154
155 ThermalManagement,
157}
158
159#[derive(Debug, Clone)]
161pub struct EnergyEfficiencyTargets<T: Float + Debug + Send + Sync + 'static> {
162 pub ops_per_joule: T,
164
165 pub spikes_per_joule: T,
167
168 pub synaptic_updates_per_joule: T,
170
171 pub memory_bandwidth_efficiency: T,
173
174 pub thermal_efficiency: T,
176}
177
178#[derive(Debug, Clone)]
180pub struct EnergyEfficientConfig<T: Float + Debug + Send + Sync + 'static> {
181 pub primary_strategy: EnergyOptimizationStrategy,
183
184 pub fallback_strategies: Vec<EnergyOptimizationStrategy>,
186
187 pub energy_budget: EnergyBudget<T>,
189
190 pub adaptive_strategy_switching: bool,
192
193 pub strategy_switching_threshold: T,
195
196 pub predictive_energy_management: bool,
198
199 pub prediction_horizon: T,
201
202 pub energy_harvesting: bool,
204
205 pub harvesting_efficiency: T,
207
208 pub distributed_energy_management: bool,
210
211 pub real_time_monitoring: bool,
213
214 pub monitoring_resolution: T,
216
217 pub energy_aware_load_balancing: bool,
219
220 pub optimization_aggressiveness: T,
222}
223
224impl<T: Float + Debug + Send + Sync + 'static> Default for EnergyEfficientConfig<T> {
225 fn default() -> Self {
226 let mut component_allocation = HashMap::new();
227 component_allocation.insert(
228 EnergyComponent::SynapticOps,
229 T::from(0.4).unwrap_or_else(|| T::zero()),
230 );
231 component_allocation.insert(
232 EnergyComponent::MembraneDynamics,
233 T::from(0.2).unwrap_or_else(|| T::zero()),
234 );
235 component_allocation.insert(
236 EnergyComponent::SpikeGeneration,
237 T::from(0.15).unwrap_or_else(|| T::zero()),
238 );
239 component_allocation.insert(
240 EnergyComponent::PlasticityUpdates,
241 T::from(0.1).unwrap_or_else(|| T::zero()),
242 );
243 component_allocation.insert(
244 EnergyComponent::MemoryAccess,
245 T::from(0.1).unwrap_or_else(|| T::zero()),
246 );
247 component_allocation.insert(
248 EnergyComponent::Communication,
249 T::from(0.05).unwrap_or_else(|| T::zero()),
250 );
251
252 Self {
253 primary_strategy: EnergyOptimizationStrategy::DynamicVoltageScaling,
254 fallback_strategies: vec![
255 EnergyOptimizationStrategy::PowerGating,
256 EnergyOptimizationStrategy::ClockGating,
257 EnergyOptimizationStrategy::SparseComputation,
258 ],
259 energy_budget: EnergyBudget {
260 total_budget: T::from(1000.0).unwrap_or_else(|| T::zero()), current_consumption: T::zero(),
262 per_operation_budget: T::from(10.0).unwrap_or_else(|| T::zero()), component_allocation,
264 efficiency_targets: EnergyEfficiencyTargets {
265 ops_per_joule: T::from(1e12).unwrap_or_else(|| T::zero()), spikes_per_joule: T::from(1e9).unwrap_or_else(|| T::zero()), synaptic_updates_per_joule: T::from(1e10).unwrap_or_else(|| T::zero()), memory_bandwidth_efficiency: T::from(1e6).unwrap_or_else(|| T::zero()),
269 thermal_efficiency: T::from(1e9).unwrap_or_else(|| T::zero()),
270 },
271 emergency_reserves: T::from(100.0).unwrap_or_else(|| T::zero()), monitoring_frequency: Duration::from_micros(100),
273 },
274 adaptive_strategy_switching: true,
275 strategy_switching_threshold: T::from(0.1).unwrap_or_else(|| T::zero()), predictive_energy_management: true,
277 prediction_horizon: T::from(10.0).unwrap_or_else(|| T::zero()), energy_harvesting: false,
279 harvesting_efficiency: T::from(0.1).unwrap_or_else(|| T::zero()),
280 distributed_energy_management: false,
281 real_time_monitoring: true,
282 monitoring_resolution: T::from(1.0).unwrap_or_else(|| T::zero()), energy_aware_load_balancing: true,
284 optimization_aggressiveness: T::from(0.7).unwrap_or_else(|| T::zero()),
285 }
286 }
287}
288
289#[derive(Debug, Clone)]
291struct EnergyMonitor<
292 T: Float
293 + Debug
294 + scirs2_core::ndarray::ScalarOperand
295 + std::fmt::Debug
296 + std::iter::Sum
297 + Send
298 + Sync,
299> {
300 consumption_history: VecDeque<(Instant, T)>,
302
303 power_history: VecDeque<(Instant, T)>,
305
306 current_power: T,
308
309 peak_power: T,
311
312 average_power: T,
314
315 last_update: Instant,
317
318 window_size: Duration,
320}
321
322#[derive(Debug, Clone)]
324struct DVFSController<T: Float + Debug + Send + Sync + 'static> {
325 voltage_levels: Vec<T>,
327
328 frequency_levels: Vec<T>,
330
331 current_voltage_idx: usize,
333
334 current_frequency_idx: usize,
336}
337
338impl<T: Float + Debug + Send + Sync + 'static> DVFSController<T> {
339 fn new() -> Self {
340 Self {
341 voltage_levels: vec![
342 to_t_or(0.7, T::one()),
343 to_t_or(0.9, T::one()),
344 to_t_or(1.0, T::one()),
345 to_t_or(1.2, T::one()),
346 ],
347 frequency_levels: vec![
348 to_t_or(500.0, T::one()),
349 to_t_or(1000.0, T::one()),
350 to_t_or(1500.0, T::one()),
351 to_t_or(2000.0, T::one()),
352 ],
353 current_voltage_idx: 2,
354 current_frequency_idx: 2,
355 }
356 }
357
358 fn compute_optimal_levels(
363 &mut self,
364 workload: &WorkloadSample<T>,
365 total_neurons: usize,
366 ) -> Result<(T, T)> {
367 let total = to_t_or(total_neurons.max(1) as f64, T::one());
368 let utilization = (to_t_or(workload.active_neurons as f64, T::zero()) / total)
369 .max(T::zero())
370 .min(T::one());
371 let idx = (utilization * to_t_or((self.voltage_levels.len() - 1) as f64, T::zero()))
372 .to_usize()
373 .unwrap_or(2);
374
375 self.current_voltage_idx = idx.min(self.voltage_levels.len() - 1);
376 self.current_frequency_idx = idx.min(self.frequency_levels.len() - 1);
377
378 Ok((
379 self.voltage_levels[self.current_voltage_idx],
380 self.frequency_levels[self.current_frequency_idx],
381 ))
382 }
383}
384
385#[derive(Debug, Clone)]
387struct PowerGatingController<T: Float + Debug + Send + Sync + 'static> {
388 gated_groups: HashMap<usize, GatedGroup>,
390
391 gate_overhead_energy: f64,
393
394 total_neurons: usize,
396
397 _phantom: std::marker::PhantomData<T>,
399}
400
401impl<T: Float + Debug + Send + Sync + 'static> PowerGatingController<T> {
402 fn new(total_neurons: usize) -> Self {
403 Self {
404 gated_groups: HashMap::new(),
405 gate_overhead_energy: 0.001,
406 total_neurons: total_neurons.max(1),
407 _phantom: std::marker::PhantomData,
408 }
409 }
410
411 fn gate_region(&mut self, region_id: usize, idle_fraction: T) -> Result<T> {
416 let neurons_per_domain = (self.total_neurons / GATING_DOMAINS.max(1)).max(1);
417 let domain_static_power = to_t_or(
418 neurons_per_domain as f64 * STATIC_POWER_PER_NEURON_NW,
419 T::zero(),
420 );
421 let saved = domain_static_power * idle_fraction;
422
423 self.gated_groups
427 .insert(region_id, GatedGroup { is_gated: true });
428
429 Ok(saved)
430 }
431
432 fn identify_gatable_regions(&self, idle_fraction: T) -> Vec<usize> {
437 if idle_fraction < to_t_or(GATING_IDLE_THRESHOLD, T::zero()) {
438 return Vec::new();
439 }
440 let fraction = idle_fraction.to_f64().unwrap_or(0.0).clamp(0.0, 1.0);
441 let gatable_domains =
442 ((fraction * GATING_DOMAINS as f64).round() as usize).clamp(1, GATING_DOMAINS);
443 (0..gatable_domains).collect()
444 }
445}
446
447#[derive(Debug, Clone)]
449struct GatedGroup {
450 is_gated: bool,
452}
453
454#[derive(Debug, Clone)]
456struct SparseComputationOptimizer<T: Float + Debug + Send + Sync + 'static> {
457 sparsity_threshold: T,
459
460 total_neurons: usize,
463}
464
465impl<T: Float + Debug + Send + Sync + 'static> SparseComputationOptimizer<T> {
466 fn new(total_neurons: usize) -> Self {
467 Self {
468 sparsity_threshold: to_t_or(0.01, T::zero()),
469 total_neurons: total_neurons.max(1),
470 }
471 }
472
473 fn analyze_sparsity<S, Dm>(
480 &mut self,
481 workload: &WorkloadSample<T>,
482 matrix: Option<&ArrayBase<S, Dm>>,
483 ) -> Result<SparsityAnalysis<T>>
484 where
485 S: Data<Elem = T>,
486 Dm: Dimension,
487 {
488 let sparsity_ratio = if let Some(m) = matrix {
489 let total = m.len().max(1);
490 let zero_count = m
491 .iter()
492 .filter(|&&v| v.abs() <= self.sparsity_threshold)
493 .count();
494 to_t_or(zero_count as f64 / total as f64, T::zero())
495 } else {
496 let total = to_t_or(self.total_neurons as f64, T::one());
497 let active = to_t_or(workload.active_neurons as f64, T::zero());
498 (T::one() - (active / total)).max(T::zero()).min(T::one())
499 };
500
501 Ok(SparsityAnalysis { sparsity_ratio })
502 }
503
504 fn apply_compression(&mut self, analysis: &SparsityAnalysis<T>) -> Result<T> {
511 Ok(analysis.sparsity_ratio * to_t_or(SPARSE_SAVING_EFFICIENCY, T::zero()))
512 }
513
514 fn apply_sparse_optimizations(&mut self, analysis: &SparsityAnalysis<T>) -> Result<T> {
515 let compression_savings = self.apply_compression(analysis)?;
517 Ok(compression_savings)
518 }
519}
520
521const SPARSE_SAVING_EFFICIENCY: f64 = 0.8;
524
525#[derive(Debug, Clone)]
526struct SparsityAnalysis<T: Float + Debug + Send + Sync + 'static> {
527 sparsity_ratio: T,
529}
530
531pub struct EnergyEfficientOptimizer<
533 T: Float
534 + Debug
535 + scirs2_core::ndarray::ScalarOperand
536 + std::fmt::Debug
537 + std::iter::Sum
538 + Send
539 + Sync,
540> {
541 config: EnergyEfficientConfig<T>,
543
544 energy_monitor: EnergyMonitor<T>,
546
547 dvfs_controller: DVFSController<T>,
549
550 power_gating_controller: PowerGatingController<T>,
552
553 sparse_optimizer: SparseComputationOptimizer<T>,
555
556 thermal_manager: ThermalManager<T>,
558
559 predictive_manager: PredictiveEnergyManager<T>,
561
562 current_strategy: EnergyOptimizationStrategy,
564
565 strategy_effectiveness: HashMap<EnergyOptimizationStrategy, T>,
567
568 system_state: EnergySystemState<T>,
570
571 metrics: NeuromorphicMetrics<T>,
573}
574
575#[derive(Debug, Clone)]
577pub struct EnergySystemState<T: Float + Debug + Send + Sync + 'static> {
578 pub current_energy: T,
580
581 pub current_power: T,
583
584 pub temperature: T,
586
587 pub active_neurons: usize,
589
590 pub active_synapses: usize,
592
593 pub current_voltage: T,
595
596 pub current_frequency: T,
598
599 pub gated_regions: Vec<usize>,
601
602 pub sleep_status: SleepStatus,
604}
605
606#[derive(Debug, Clone, Copy)]
607pub enum SleepStatus {
608 Active,
609 LightSleep,
610 DeepSleep,
611 Hibernation,
612}
613
614#[derive(Debug, Clone)]
616struct ThermalManager<T: Float + Debug + Send + Sync + 'static> {
617 current_temperature: T,
619
620 temperature_history: VecDeque<(Instant, T)>,
622
623 thermal_model: ThermalModel<T>,
625
626 last_update: Instant,
629}
630
631impl<T: Float + Debug + Send + Sync + 'static> ThermalManager<T> {
632 fn new(_config: ThermalManagementConfig<T>) -> Self {
635 Self {
636 current_temperature: to_t_or(25.0, T::zero()),
637 temperature_history: VecDeque::new(),
638 thermal_model: ThermalModel {
639 time_constant: to_t_or(10.0, T::one()),
640 thermal_resistance: to_t_or(0.5, T::zero()),
641 ambient_temperature: to_t_or(25.0, T::zero()),
642 },
643 last_update: Instant::now(),
644 }
645 }
646
647 fn update(&mut self, system_state: &EnergySystemState<T>) -> Result<()> {
654 let now = Instant::now();
655 let dt_seconds = to_t_or(
656 now.duration_since(self.last_update).as_secs_f64().max(1e-6),
657 to_t_or(1e-3, T::one()),
658 );
659 self.last_update = now;
660
661 let steady_state = system_state.current_power * self.thermal_model.thermal_resistance
662 + self.thermal_model.ambient_temperature;
663 let tau = if self.thermal_model.time_constant > T::zero() {
664 self.thermal_model.time_constant
665 } else {
666 T::one()
667 };
668 self.current_temperature = self.current_temperature
669 + (dt_seconds / tau) * (steady_state - self.current_temperature);
670
671 self.temperature_history
672 .push_back((now, self.current_temperature));
673 while self.temperature_history.len() > 100 {
675 self.temperature_history.pop_front();
676 }
677 Ok(())
678 }
679}
680
681#[derive(Debug, Clone)]
683struct ThermalModel<T: Float + Debug + Send + Sync + 'static> {
684 time_constant: T,
686
687 thermal_resistance: T,
689
690 ambient_temperature: T,
692}
693
694#[derive(Debug, Clone)]
696struct PredictiveEnergyManager<T: Float + Debug + Send + Sync + 'static> {
697 workload_history: VecDeque<WorkloadSample<T>>,
699
700 power_history: VecDeque<(Instant, T)>,
705}
706
707impl<T: Float + Debug + Send + Sync + 'static> PredictiveEnergyManager<T> {
708 fn new() -> Self {
709 Self {
710 workload_history: VecDeque::new(),
711 power_history: VecDeque::new(),
712 }
713 }
714
715 fn record_sample(&mut self, workload: WorkloadSample<T>, power: T) {
718 self.power_history.push_back((Instant::now(), power));
719 while self.power_history.len() > 100 {
720 self.power_history.pop_front();
721 }
722 self.workload_history.push_back(workload);
723 while self.workload_history.len() > 100 {
724 self.workload_history.pop_front();
725 }
726 }
727
728 fn predict_energy(&self, horizon: Duration) -> Result<T> {
734 if self.power_history.is_empty() {
735 return Ok(T::zero());
736 }
737 let sum: T = self
738 .power_history
739 .iter()
740 .map(|(_, power)| *power)
741 .fold(T::zero(), |acc, x| acc + x);
742 let avg_power = sum / to_t_or(self.power_history.len() as f64, T::one());
743
744 let horizon_ms = to_t_or(horizon.as_secs_f64() * 1000.0, T::zero());
747 let predicted = avg_power * horizon_ms / to_t_or(1000.0, T::one());
748
749 Ok(predicted)
750 }
751}
752
753#[derive(Debug, Clone)]
755pub struct WorkloadSample<T: Float + Debug + Send + Sync + 'static> {
756 pub timestamp: Instant,
758
759 pub active_neurons: usize,
761
762 pub spike_rate: T,
764
765 pub synaptic_activity: T,
767
768 pub memory_access_pattern: MemoryAccessPattern,
770
771 pub communication_overhead: T,
773}
774
775#[derive(Debug, Clone, Copy)]
776pub enum MemoryAccessPattern {
777 Sequential,
778 Random,
779 Sparse,
780 Burst,
781 Mixed,
782}
783
784impl<
785 T: Float
786 + Debug
787 + Send
788 + Sync
789 + scirs2_core::ndarray::ScalarOperand
790 + std::fmt::Debug
791 + std::iter::Sum,
792 > EnergyEfficientOptimizer<T>
793{
794 pub fn new(_config: EnergyEfficientConfig<T>, numneurons: usize) -> Self {
796 Self {
797 config: _config.clone(),
798 energy_monitor: EnergyMonitor::new(_config.energy_budget.monitoring_frequency),
799 dvfs_controller: DVFSController::new(),
800 power_gating_controller: PowerGatingController::new(numneurons),
801 sparse_optimizer: SparseComputationOptimizer::new(numneurons),
802 thermal_manager: ThermalManager::new(ThermalManagementConfig::default()),
803 predictive_manager: PredictiveEnergyManager::new(),
804 current_strategy: _config.primary_strategy,
805 strategy_effectiveness: HashMap::new(),
806 system_state: EnergySystemState {
807 current_energy: T::zero(),
808 current_power: T::zero(),
809 temperature: T::from(25.0).unwrap_or_else(|| T::zero()), active_neurons: numneurons,
811 active_synapses: numneurons * numneurons,
812 current_voltage: T::from(1.0).unwrap_or_else(|| T::zero()), current_frequency: T::from(100.0).unwrap_or_else(|| T::zero()), gated_regions: Vec::new(),
815 sleep_status: SleepStatus::Active,
816 },
817 metrics: NeuromorphicMetrics::default(),
818 }
819 }
820
821 pub fn optimize_energy(
823 &mut self,
824 workload: &WorkloadSample<T>,
825 ) -> Result<EnergyOptimizationResult<T>> {
826 self.optimize_energy_impl(workload, None::<&Array2<T>>)
827 }
828
829 pub fn optimize_energy_with_matrix<S, Dm>(
835 &mut self,
836 workload: &WorkloadSample<T>,
837 matrix: Option<&ArrayBase<S, Dm>>,
838 ) -> Result<EnergyOptimizationResult<T>>
839 where
840 S: Data<Elem = T>,
841 Dm: Dimension,
842 {
843 self.optimize_energy_impl(workload, matrix)
844 }
845
846 fn optimize_energy_impl<S, Dm>(
847 &mut self,
848 workload: &WorkloadSample<T>,
849 matrix: Option<&ArrayBase<S, Dm>>,
850 ) -> Result<EnergyOptimizationResult<T>>
851 where
852 S: Data<Elem = T>,
853 Dm: Dimension,
854 {
855 self.energy_monitor.update(&self.system_state)?;
857
858 let _prediction = if self.config.predictive_energy_management {
862 self.predictive_manager
863 .predict_energy(Duration::from_secs(60))?
864 } else {
865 T::zero()
866 };
867
868 let optimization_result = match self.current_strategy {
870 EnergyOptimizationStrategy::DynamicVoltageScaling => {
871 self.apply_dvfs_optimization(workload)?
872 }
873 EnergyOptimizationStrategy::PowerGating => {
874 self.apply_power_gating_optimization(workload)?
875 }
876 EnergyOptimizationStrategy::ClockGating => {
877 self.apply_clock_gating_optimization(workload)?
878 }
879 EnergyOptimizationStrategy::SparseComputation => {
880 self.apply_sparse_computation_optimization(workload, matrix)?
881 }
882 EnergyOptimizationStrategy::SleepModeOptimization => {
883 self.apply_sleep_mode_optimization(workload)?
884 }
885 EnergyOptimizationStrategy::ThermalAwareOptimization => {
886 self.apply_thermal_aware_optimization(workload)?
887 }
888 EnergyOptimizationStrategy::MultiLevel => {
889 self.apply_multi_level_optimization(workload)?
890 }
891 _ => {
892 self.apply_default_optimization(workload)?
894 }
895 };
896
897 self.predictive_manager
901 .record_sample(workload.clone(), self.system_state.current_power);
902
903 self.evaluate_strategy_effectiveness(&optimization_result);
905
906 if self.config.adaptive_strategy_switching {
908 self.consider_strategy_switch()?;
909 }
910
911 self.thermal_manager.update(&self.system_state)?;
916 self.system_state.temperature = self.thermal_manager.current_temperature;
917
918 self.update_metrics(&optimization_result);
920
921 Ok(optimization_result)
922 }
923
924 fn idle_fraction(&self, workload: &WorkloadSample<T>) -> T {
930 let total = to_t_or(self.system_state.active_neurons.max(1) as f64, T::one());
931 let active = to_t_or(workload.active_neurons as f64, T::zero());
932 (T::one() - (active / total)).max(T::zero()).min(T::one())
933 }
934
935 fn estimate_workload_power(&self, workload: &WorkloadSample<T>) -> T {
942 let static_power = to_t_or(
948 self.system_state.active_neurons as f64 * STATIC_POWER_PER_NEURON_NW,
949 T::zero(),
950 );
951 let spike_power = workload.spike_rate * to_t_or(DYNAMIC_ENERGY_PER_SPIKE_NJ, T::zero());
952 let synaptic_power =
953 workload.synaptic_activity * to_t_or(DYNAMIC_POWER_PER_SYNAPTIC_ACTIVITY_NW, T::zero());
954 let comm_power = workload.communication_overhead
955 * to_t_or(DYNAMIC_POWER_PER_COMM_OVERHEAD_NW, T::zero());
956 let dynamic_baseline = spike_power + synaptic_power + comm_power;
957
958 let v_nom = to_t_or(NOMINAL_VOLTAGE, T::one());
959 let f_nom = to_t_or(NOMINAL_FREQUENCY_MHZ, T::one());
960 let voltage_ratio = if v_nom > T::zero() {
961 self.system_state.current_voltage / v_nom
962 } else {
963 T::one()
964 };
965 let freq_ratio = if f_nom > T::zero() {
966 self.system_state.current_frequency / f_nom
967 } else {
968 T::one()
969 };
970
971 static_power + dynamic_baseline * voltage_ratio * voltage_ratio * freq_ratio
972 }
973
974 fn apply_dvfs_optimization(
976 &mut self,
977 workload: &WorkloadSample<T>,
978 ) -> Result<EnergyOptimizationResult<T>> {
979 let initial_power = self.estimate_workload_power(workload);
981
982 let v_old = self.system_state.current_voltage;
987 let f_old = self.system_state.current_frequency;
988
989 let (optimal_voltage, optimal_frequency) = self
991 .dvfs_controller
992 .compute_optimal_levels(workload, self.system_state.active_neurons)?;
993
994 let power_reduction =
995 self.calculate_power_reduction(v_old, f_old, optimal_voltage, optimal_frequency);
996 let performance_impact = self.calculate_performance_impact(f_old, optimal_frequency);
997 let new_power = initial_power * power_reduction;
998 let thermal_impact = self.calculate_thermal_impact(initial_power, new_power);
999
1000 self.system_state.current_voltage = optimal_voltage;
1002 self.system_state.current_frequency = optimal_frequency;
1003 self.system_state.current_power = new_power;
1004
1005 let time_delta = to_t_or(1.0, T::one());
1007 let energy_delta = new_power * time_delta / to_t_or(1000.0, T::one());
1008 self.system_state.current_energy = self.system_state.current_energy + energy_delta;
1009
1010 Ok(EnergyOptimizationResult {
1011 strategy_used: EnergyOptimizationStrategy::DynamicVoltageScaling,
1012 energy_saved: (initial_power - new_power).max(T::zero()) * time_delta
1013 / to_t_or(1000.0, T::one()),
1014 power_reduction: initial_power - new_power,
1015 performance_impact,
1016 thermal_impact,
1017 optimization_overhead: to_t_or(0.1, T::zero()), })
1019 }
1020
1021 fn apply_power_gating_optimization(
1023 &mut self,
1024 workload: &WorkloadSample<T>,
1025 ) -> Result<EnergyOptimizationResult<T>> {
1026 let initial_power = self.estimate_workload_power(workload);
1027 let idle_fraction = self.idle_fraction(workload);
1028
1029 let gatable_regions = self
1031 .power_gating_controller
1032 .identify_gatable_regions(idle_fraction);
1033
1034 let mut total_power_saved = T::zero();
1035 for region_id in gatable_regions {
1036 let power_saved = self
1037 .power_gating_controller
1038 .gate_region(region_id, idle_fraction)?;
1039 total_power_saved = total_power_saved + power_saved;
1040 if !self.system_state.gated_regions.contains(®ion_id) {
1041 self.system_state.gated_regions.push(region_id);
1042 }
1043 }
1044
1045 let new_power = (initial_power - total_power_saved).max(T::zero());
1046 self.system_state.current_power = new_power;
1047
1048 let time_delta = to_t_or(1.0, T::one());
1049 let energy_saved = total_power_saved * time_delta / to_t_or(1000.0, T::one());
1050 let overhead = to_t_or(self.power_gating_controller.gate_overhead_energy, T::zero())
1051 * to_t_or(self.system_state.gated_regions.len() as f64, T::zero());
1052
1053 Ok(EnergyOptimizationResult {
1054 strategy_used: EnergyOptimizationStrategy::PowerGating,
1055 energy_saved,
1056 power_reduction: total_power_saved,
1057 performance_impact: T::zero(), thermal_impact: total_power_saved * to_t_or(0.8, T::zero()),
1059 optimization_overhead: overhead,
1060 })
1061 }
1062
1063 fn apply_sparse_computation_optimization<S, Dm>(
1067 &mut self,
1068 workload: &WorkloadSample<T>,
1069 matrix: Option<&ArrayBase<S, Dm>>,
1070 ) -> Result<EnergyOptimizationResult<T>>
1071 where
1072 S: Data<Elem = T>,
1073 Dm: Dimension,
1074 {
1075 let initial_power = self.estimate_workload_power(workload);
1076
1077 let sparsity_analysis = self.sparse_optimizer.analyze_sparsity(workload, matrix)?;
1079
1080 let energy_savings = self
1082 .sparse_optimizer
1083 .apply_sparse_optimizations(&sparsity_analysis)?;
1084
1085 let new_power = initial_power * (T::one() - energy_savings);
1087 self.system_state.current_power = new_power;
1088
1089 Ok(EnergyOptimizationResult {
1090 strategy_used: EnergyOptimizationStrategy::SparseComputation,
1091 energy_saved: initial_power * energy_savings,
1092 power_reduction: initial_power - new_power,
1093 performance_impact: energy_savings * to_t_or(0.1, T::zero()), thermal_impact: (initial_power - new_power) * to_t_or(0.9, T::zero()),
1095 optimization_overhead: to_t_or(0.2, T::zero()), })
1097 }
1098
1099 fn apply_multi_level_optimization(
1101 &mut self,
1102 workload: &WorkloadSample<T>,
1103 ) -> Result<EnergyOptimizationResult<T>> {
1104 let mut total_result = EnergyOptimizationResult {
1105 strategy_used: EnergyOptimizationStrategy::MultiLevel,
1106 energy_saved: T::zero(),
1107 power_reduction: T::zero(),
1108 performance_impact: T::zero(),
1109 thermal_impact: T::zero(),
1110 optimization_overhead: T::zero(),
1111 };
1112
1113 let strategies = [
1115 EnergyOptimizationStrategy::SparseComputation,
1116 EnergyOptimizationStrategy::DynamicVoltageScaling,
1117 EnergyOptimizationStrategy::PowerGating,
1118 ];
1119
1120 for strategy in &strategies {
1121 let prev_strategy = self.current_strategy;
1122 self.current_strategy = *strategy;
1123
1124 let result = match strategy {
1125 EnergyOptimizationStrategy::SparseComputation => {
1126 self.apply_sparse_computation_optimization(workload, None::<&Array2<T>>)?
1127 }
1128 EnergyOptimizationStrategy::DynamicVoltageScaling => {
1129 self.apply_dvfs_optimization(workload)?
1130 }
1131 EnergyOptimizationStrategy::PowerGating => {
1132 self.apply_power_gating_optimization(workload)?
1133 }
1134 _ => continue,
1135 };
1136
1137 total_result.energy_saved = total_result.energy_saved + result.energy_saved;
1139 total_result.power_reduction = total_result.power_reduction + result.power_reduction;
1140 total_result.performance_impact =
1141 total_result.performance_impact + result.performance_impact;
1142 total_result.thermal_impact = total_result.thermal_impact + result.thermal_impact;
1143 total_result.optimization_overhead =
1144 total_result.optimization_overhead + result.optimization_overhead;
1145
1146 self.current_strategy = prev_strategy;
1147 }
1148
1149 Ok(total_result)
1150 }
1151
1152 fn apply_default_optimization(
1154 &mut self,
1155 _workload: &WorkloadSample<T>,
1156 ) -> Result<EnergyOptimizationResult<T>> {
1157 Ok(EnergyOptimizationResult {
1159 strategy_used: self.current_strategy,
1160 energy_saved: T::zero(),
1161 power_reduction: T::zero(),
1162 performance_impact: T::zero(),
1163 thermal_impact: T::zero(),
1164 optimization_overhead: to_t_or(0.01, T::zero()),
1165 })
1166 }
1167
1168 fn apply_clock_gating_optimization(
1173 &mut self,
1174 workload: &WorkloadSample<T>,
1175 ) -> Result<EnergyOptimizationResult<T>> {
1176 let initial_power = self.estimate_workload_power(workload);
1177 let idle_fraction = self.idle_fraction(workload);
1178 let reduction_factor = idle_fraction * to_t_or(CLOCK_GATING_EFFICIENCY, T::zero());
1179 let new_power = initial_power * (T::one() - reduction_factor);
1180
1181 self.system_state.current_power = new_power;
1182
1183 Ok(EnergyOptimizationResult {
1184 strategy_used: EnergyOptimizationStrategy::ClockGating,
1185 energy_saved: initial_power * reduction_factor,
1186 power_reduction: initial_power - new_power,
1187 performance_impact: T::zero(), thermal_impact: (initial_power - new_power) * to_t_or(0.8, T::zero()),
1189 optimization_overhead: to_t_or(0.05, T::zero()),
1190 })
1191 }
1192
1193 fn apply_sleep_mode_optimization(
1197 &mut self,
1198 workload: &WorkloadSample<T>,
1199 ) -> Result<EnergyOptimizationResult<T>> {
1200 let initial_power = self.estimate_workload_power(workload);
1201 let idle_fraction = self.idle_fraction(workload);
1202
1203 let deep_sleep_threshold = to_t_or(DEEP_SLEEP_IDLE_THRESHOLD, T::one());
1204 let (status, base_savings, wakeup_latency) = if idle_fraction >= deep_sleep_threshold {
1205 (SleepStatus::DeepSleep, DEEP_SLEEP_SAVINGS, 0.2)
1206 } else {
1207 (SleepStatus::LightSleep, LIGHT_SLEEP_SAVINGS, 0.1)
1208 };
1209 self.system_state.sleep_status = status;
1210
1211 let reduction_factor = idle_fraction * to_t_or(base_savings, T::zero());
1212 let new_power = initial_power * (T::one() - reduction_factor);
1213 self.system_state.current_power = new_power;
1214
1215 Ok(EnergyOptimizationResult {
1216 strategy_used: EnergyOptimizationStrategy::SleepModeOptimization,
1217 energy_saved: initial_power * reduction_factor,
1218 power_reduction: initial_power - new_power,
1219 performance_impact: to_t_or(wakeup_latency, T::zero()),
1220 thermal_impact: (initial_power - new_power) * to_t_or(0.95, T::zero()),
1221 optimization_overhead: to_t_or(0.1, T::zero()),
1222 })
1223 }
1224
1225 fn apply_thermal_aware_optimization(
1230 &mut self,
1231 workload: &WorkloadSample<T>,
1232 ) -> Result<EnergyOptimizationResult<T>> {
1233 let initial_power = self.estimate_workload_power(workload);
1234
1235 let safe_temp = to_t_or(THERMAL_SAFE_TEMP_C, T::zero());
1236 let critical_temp = to_t_or(THERMAL_CRITICAL_TEMP_C, T::one());
1237 let min_reduction = to_t_or(THERMAL_MIN_REDUCTION, T::zero());
1238 let max_reduction = to_t_or(THERMAL_MAX_REDUCTION, T::zero());
1239
1240 let span = (critical_temp - safe_temp).max(to_t_or(1e-6, T::one()));
1241 let overshoot = ((self.system_state.temperature - safe_temp) / span)
1242 .max(T::zero())
1243 .min(T::one());
1244 let reduction_factor = min_reduction + (max_reduction - min_reduction) * overshoot;
1245
1246 let new_power = initial_power * (T::one() - reduction_factor);
1247 self.system_state.current_power = new_power;
1248
1249 Ok(EnergyOptimizationResult {
1250 strategy_used: EnergyOptimizationStrategy::ThermalAwareOptimization,
1251 energy_saved: initial_power * reduction_factor,
1252 power_reduction: initial_power - new_power,
1253 performance_impact: reduction_factor * to_t_or(0.5, T::zero()),
1254 thermal_impact: initial_power - new_power,
1255 optimization_overhead: to_t_or(0.15, T::zero()),
1256 })
1257 }
1258
1259 fn calculate_power_reduction(&self, v_old: T, f_old: T, v_new: T, f_new: T) -> T {
1264 let denom = v_old * v_old * f_old;
1265 if denom <= T::zero() {
1266 return T::one();
1267 }
1268 (v_new * v_new * f_new) / denom
1269 }
1270
1271 fn calculate_performance_impact(&self, old_frequency: T, new_frequency: T) -> T {
1274 if old_frequency <= T::zero() {
1275 return T::zero();
1276 }
1277 (old_frequency - new_frequency) / old_frequency
1278 }
1279
1280 fn calculate_thermal_impact(&self, old_power: T, newpower: T) -> T {
1283 let power_reduction = old_power - newpower;
1284 power_reduction * self.thermal_manager.thermal_model.thermal_resistance
1285 }
1286
1287 fn evaluate_strategy_effectiveness(&mut self, result: &EnergyOptimizationResult<T>) {
1289 let effectiveness =
1291 result.energy_saved / (result.optimization_overhead + to_t_or(1e-6, T::zero()));
1292
1293 *self
1295 .strategy_effectiveness
1296 .entry(result.strategy_used)
1297 .or_insert(T::zero()) = effectiveness;
1298 }
1299
1300 fn consider_strategy_switch(&mut self) -> Result<()> {
1302 if let Some(¤t_effectiveness) =
1303 self.strategy_effectiveness.get(&self.current_strategy)
1304 {
1305 if let Some((&best_strategy, &best_effectiveness)) = self
1307 .strategy_effectiveness
1308 .iter()
1309 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
1310 {
1311 if current_effectiveness > T::zero() {
1314 let improvement =
1315 (best_effectiveness - current_effectiveness) / current_effectiveness;
1316 if improvement > self.config.strategy_switching_threshold {
1317 self.current_strategy = best_strategy;
1318 }
1319 } else if best_effectiveness > T::zero() {
1320 self.current_strategy = best_strategy;
1321 }
1322 }
1323 }
1324
1325 Ok(())
1326 }
1327
1328 fn update_metrics(&mut self, _result: &EnergyOptimizationResult<T>) {
1330 self.metrics.energy_consumption = self.system_state.current_energy;
1331 self.metrics.power_consumption = self.system_state.current_power;
1332 let ambient = to_t_or(25.0, T::one());
1333 self.metrics.thermal_efficiency = if self.system_state.temperature > T::zero() {
1334 ambient / self.system_state.temperature
1335 } else {
1336 T::one()
1337 };
1338 }
1339
1340 pub fn get_energy_budget_status(&self) -> EnergyBudgetStatus<T> {
1342 let remaining_budget =
1343 self.config.energy_budget.total_budget - self.system_state.current_energy;
1344 let budget_utilization =
1345 self.system_state.current_energy / self.config.energy_budget.total_budget;
1346
1347 EnergyBudgetStatus {
1348 total_budget: self.config.energy_budget.total_budget,
1349 current_consumption: self.system_state.current_energy,
1350 remaining_budget,
1351 budget_utilization,
1352 emergency_reserve_available: remaining_budget
1353 > self.config.energy_budget.emergency_reserves,
1354 }
1355 }
1356
1357 pub fn get_metrics(&self) -> &NeuromorphicMetrics<T> {
1359 &self.metrics
1360 }
1361
1362 pub fn gated_domain_count(&self) -> usize {
1370 self.power_gating_controller
1371 .gated_groups
1372 .values()
1373 .filter(|group| group.is_gated)
1374 .count()
1375 }
1376
1377 pub fn get_system_state(&self) -> &EnergySystemState<T> {
1378 &self.system_state
1379 }
1380}
1381
1382#[derive(Debug, Clone)]
1384pub struct EnergyOptimizationResult<T: Float + Debug + Send + Sync + 'static> {
1385 pub strategy_used: EnergyOptimizationStrategy,
1387
1388 pub energy_saved: T,
1390
1391 pub power_reduction: T,
1393
1394 pub performance_impact: T,
1396
1397 pub thermal_impact: T,
1399
1400 pub optimization_overhead: T,
1402}
1403
1404#[derive(Debug, Clone)]
1406pub struct EnergyBudgetStatus<T: Float + Debug + Send + Sync + 'static> {
1407 pub total_budget: T,
1409
1410 pub current_consumption: T,
1412
1413 pub remaining_budget: T,
1415
1416 pub budget_utilization: T,
1418
1419 pub emergency_reserve_available: bool,
1421}
1422
1423impl<
1424 T: Float
1425 + Debug
1426 + Send
1427 + Sync
1428 + scirs2_core::ndarray::ScalarOperand
1429 + std::fmt::Debug
1430 + std::iter::Sum,
1431 > EnergyMonitor<T>
1432{
1433 fn new(_monitoringfrequency: Duration) -> Self {
1434 Self {
1435 consumption_history: VecDeque::new(),
1436 power_history: VecDeque::new(),
1437 current_power: T::zero(),
1438 peak_power: T::zero(),
1439 average_power: T::zero(),
1440 last_update: Instant::now(),
1441 window_size: Duration::from_secs(1),
1442 }
1443 }
1444
1445 fn update(&mut self, systemstate: &EnergySystemState<T>) -> Result<()> {
1446 let now = Instant::now();
1447 self.consumption_history
1448 .push_back((now, systemstate.current_energy));
1449 self.power_history
1450 .push_back((now, systemstate.current_power));
1451
1452 while let Some(&(time_, _)) = self.consumption_history.front() {
1455 if now.duration_since(time_) > self.window_size {
1456 self.consumption_history.pop_front();
1457 } else {
1458 break;
1459 }
1460 }
1461 while let Some(&(time_, _)) = self.power_history.front() {
1462 if now.duration_since(time_) > self.window_size {
1463 self.power_history.pop_front();
1464 } else {
1465 break;
1466 }
1467 }
1468
1469 self.current_power = systemstate.current_power;
1471 self.peak_power = self.peak_power.max(systemstate.current_power);
1472
1473 if !self.power_history.is_empty() {
1475 let sum: T = self.power_history.iter().map(|(_, power)| *power).sum();
1476 self.average_power = sum / to_t_or(self.power_history.len() as f64, T::one());
1477 }
1478
1479 self.last_update = now;
1480 Ok(())
1481 }
1482}
1483
1484#[cfg(test)]
1485#[path = "energy_efficient_tests.rs"]
1486mod tests;