1use scirs2_core::ndarray::Array1;
8use scirs2_core::numeric::Float;
9use std::collections::{HashMap, VecDeque};
10use std::time::{Duration, Instant};
11
12use crate::error::Result;
13use crate::utils::scalar_or;
14
15#[derive(Debug, Clone)]
17pub enum PerformanceMetric<A: Float + Send + Sync> {
18 Loss(A),
19 Accuracy(A),
20 F1Score(A),
21 AUC(A),
22 Custom { name: String, value: A },
23}
24
25#[derive(Debug, Clone)]
27pub struct EnhancedAdaptiveLRController<A: Float + Send + Sync> {
28 current_lr: A,
30
31 base_lr: A,
33
34 adaptation_strategy: MultiSignalAdaptationStrategy<A>,
36
37 gradient_adapter: GradientBasedAdapter<A>,
39
40 performance_adapter: PerformanceBasedAdapter<A>,
42
43 drift_adapter: DriftAwareAdapter<A>,
45
46 resource_adapter: ResourceAwareAdapter<A>,
48
49 meta_optimizer: MetaOptimizer<A>,
51
52 adaptation_history: VecDeque<AdaptationEvent<A>>,
54
55 config: AdaptiveLRConfig<A>,
57}
58
59#[derive(Debug, Clone)]
61pub struct AdaptiveLRConfig<A: Float + Send + Sync> {
62 pub base_lr: A,
64
65 pub min_lr: A,
67
68 pub max_lr: A,
70
71 pub enable_gradient_adaptation: bool,
73
74 pub enable_performance_adaptation: bool,
76
77 pub enable_drift_adaptation: bool,
79
80 pub enable_resource_adaptation: bool,
82
83 pub enable_meta_learning: bool,
85
86 pub history_window_size: usize,
88
89 pub adaptation_frequency: usize,
91
92 pub adaptation_sensitivity: A,
94
95 pub use_ensemble_voting: bool,
97
98 pub step_time_budget: Option<Duration>,
102
103 pub memory_budget_mb: Option<f64>,
105}
106
107#[derive(Debug, Clone)]
109pub struct MultiSignalAdaptationStrategy<A: Float + Send + Sync> {
110 pub(crate) signal_weights: HashMap<AdaptationSignalType, A>,
112
113 pub(crate) voting_history: VecDeque<SignalVote<A>>,
115
116 pub(crate) conflict_resolution: ConflictResolution,
118
119 pub(crate) signal_reliability: HashMap<AdaptationSignalType, A>,
121
122 pub(crate) last_decision: Option<AdaptationDecision<A>>,
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
128pub enum AdaptationSignalType {
129 GradientMagnitude,
130 GradientVariance,
131 LossProgression,
132 AccuracyTrend,
133 ConceptDrift,
134 ResourceUtilization,
135 ModelComplexity,
136 DataQuality,
137}
138
139#[derive(Debug, Clone)]
141pub struct SignalVote<A: Float + Send + Sync> {
142 signal_type: AdaptationSignalType,
143 recommended_lr_change: A, confidence: A,
145 reasoning: String,
146 timestamp: Instant,
147}
148
149impl<A: Float + Send + Sync> SignalVote<A> {
150 pub fn signal_type(&self) -> AdaptationSignalType {
152 self.signal_type
153 }
154
155 pub fn recommended_lr_change(&self) -> A {
157 self.recommended_lr_change
158 }
159
160 pub fn confidence(&self) -> A {
162 self.confidence
163 }
164
165 pub fn reasoning(&self) -> &str {
171 &self.reasoning
172 }
173
174 pub fn timestamp(&self) -> Instant {
176 self.timestamp
177 }
178}
179
180#[derive(Debug, Clone, Copy)]
182pub enum ConflictResolution {
183 WeightedAverage,
185 HighestConfidence,
187 MajorityVote { threshold: f64 },
189 Conservative,
191 MetaLearned,
193}
194
195#[derive(Debug, Clone)]
197pub struct AdaptationDecision<A: Float + Send + Sync> {
198 new_lr: A,
199 lr_multiplier: A,
200 contributing_signals: Vec<AdaptationSignalType>,
201 confidence: A,
202 rationale: String,
203 timestamp: Instant,
204}
205
206impl<A: Float + Send + Sync> AdaptationDecision<A> {
207 pub fn new_lr(&self) -> A {
209 self.new_lr
210 }
211
212 pub fn lr_multiplier(&self) -> A {
214 self.lr_multiplier
215 }
216
217 pub fn contributing_signals(&self) -> &[AdaptationSignalType] {
219 &self.contributing_signals
220 }
221
222 pub fn confidence(&self) -> A {
224 self.confidence
225 }
226
227 pub fn rationale(&self) -> &str {
230 &self.rationale
231 }
232
233 pub fn timestamp(&self) -> Instant {
235 self.timestamp
236 }
237}
238
239#[derive(Debug, Clone)]
241pub struct GradientBasedAdapter<A: Float + Send + Sync> {
242 magnitude_history: VecDeque<A>,
244
245 direction_variance_history: VecDeque<A>,
247
248 norm_statistics: GradientNormStatistics<A>,
250
251 snr_estimator: SignalToNoiseEstimator<A>,
253
254 staleness_detector: GradientStalenessDetector,
256}
257
258#[derive(Debug, Clone)]
260pub struct PerformanceBasedAdapter<A: Float + Send + Sync> {
261 metric_history: HashMap<String, VecDeque<A>>,
263
264 trend_analyzer: PerformanceTrendAnalyzer<A>,
266
267 plateau_detector: PlateauDetector<A>,
269
270 overfitting_detector: OverfittingDetector<A>,
272
273 efficiency_tracker: LearningEfficiencyTracker<A>,
275}
276
277#[derive(Debug, Clone)]
279pub struct DriftAwareAdapter<A: Float + Send + Sync> {
280 drift_detectors: Vec<ConceptDriftDetector<A>>,
282
283 distribution_tracker: DistributionTracker<A>,
285
286 adaptation_speed: AdaptationSpeedController<A>,
288
289 drift_severity: DriftSeverityAssessor<A>,
291}
292
293#[derive(Debug, Clone)]
295pub struct ResourceAwareAdapter<A: Float + Send + Sync> {
296 memory_tracker: MemoryUsageTracker,
298
299 compute_tracker: ComputationTimeTracker,
301
302 energy_tracker: EnergyConsumptionTracker,
304
305 throughput_requirements: ThroughputRequirements<A>,
307
308 budget_manager: ResourceBudgetManager<A>,
310}
311
312#[derive(Debug, Clone)]
314pub struct MetaOptimizer<A: Float + Send + Sync> {
315 optimization_history: VecDeque<HyperparameterUpdate<A>>,
317
318 exploration_strategy: ExplorationStrategy<A>,
320
321 transfer_learner: TransferLearner<A>,
323}
324
325#[derive(Debug, Clone)]
327pub struct AdaptationEvent<A: Float + Send + Sync> {
328 timestamp: Instant,
329 old_lr: A,
330 new_lr: A,
331 trigger_signals: Vec<AdaptationSignalType>,
332 effectiveness_score: Option<A>, }
334
335#[derive(Debug, Clone)]
337pub struct GradientNormStatistics<A: Float + Send + Sync> {
338 mean: A,
339 variance: A,
340 skewness: A,
341 kurtosis: A,
342 percentiles: Vec<A>, autocorrelation: A,
344}
345
346#[derive(Debug, Clone)]
348pub struct SignalToNoiseEstimator<A: Float + Send + Sync> {
349 signal_estimate: A,
350 noise_estimate: A,
351 snr_history: VecDeque<A>,
352}
353
354#[derive(Debug, Clone, Copy)]
355pub enum SNREstimationMethod {
356 MovingAverage,
357 ExponentialSmoothing,
358 RobustEstimation,
359 WaveletDenoising,
360}
361
362#[derive(Debug, Clone, Default)]
364pub struct GradientStalenessDetector {
365 gradient_timestamps: VecDeque<Instant>,
366}
367
368#[derive(Debug, Clone)]
370pub struct PerformanceTrendAnalyzer<A: Float + Send + Sync> {
371 trend_detection_window: usize,
372 trend_types: Vec<TrendType>,
373 trend_strength: A,
374}
375
376#[derive(Debug, Clone, Copy)]
377pub enum TrendType {
378 Improving,
379 Degrading,
380 Oscillating,
381 Plateau,
382 Volatile,
383}
384
385#[derive(Debug, Clone)]
387pub struct PlateauDetector<A: Float + Send + Sync> {
388 plateau_threshold: A,
389 min_plateau_duration: usize,
390 current_plateau_length: usize,
391 plateau_confidence: A,
392}
393
394#[derive(Debug, Clone)]
396pub struct OverfittingDetector<A: Float + Send + Sync> {
397 train_loss_history: VecDeque<A>,
398 val_loss_history: VecDeque<A>,
399}
400
401#[derive(Debug, Clone)]
403pub struct LearningEfficiencyTracker<A: Float + Send + Sync> {
404 loss_reduction_per_step: VecDeque<A>,
405 efficiency_score: A,
406 efficiency_trend: TrendType,
407}
408
409#[derive(Debug, Clone)]
416pub struct ConceptDriftDetector<A: Float + Send + Sync> {
417 pub(crate) detection_method: DriftDetectionMethod,
418 pub(crate) drift_threshold: A,
419 pub(crate) window_size: usize,
420 pub(crate) drift_confidence: A,
421 pub(crate) last_drift_time: Option<Instant>,
422 pub(crate) inner: LossDriftDetector<A>,
423}
424
425#[derive(Debug, Clone, Copy)]
426pub enum DriftDetectionMethod {
427 ADWIN,
428 DDM,
429 EDDM,
430 PageHinkley,
431 KSWIN,
432 Statistical,
433}
434
435#[derive(Debug, Clone)]
437pub struct DistributionTracker<A: Float + Send + Sync> {
438 feature_distributions: HashMap<usize, FeatureDistribution<A>>,
439 distribution_drift_score: A,
440}
441
442#[derive(Debug, Clone)]
443pub struct FeatureDistribution<A: Float + Send + Sync> {
444 mean: A,
445 variance: A,
446 histogram: Vec<A>,
447 last_update: Instant,
448}
449
450#[derive(Debug, Clone)]
452pub struct AdaptationSpeedController<A: Float + Send + Sync> {
453 base_adaptation_rate: A,
454 current_adaptation_rate: A,
455 acceleration_factor: A,
456 deceleration_factor: A,
457 momentum: A,
458}
459
460#[derive(Debug, Clone)]
462pub struct DriftSeverityAssessor<A: Float + Send + Sync> {
463 severity_levels: Vec<DriftSeverityLevel<A>>,
464 current_severity: DriftSeverityLevel<A>,
465 severity_history: VecDeque<DriftSeverityLevel<A>>,
466}
467
468#[derive(Debug, Clone)]
469pub struct DriftSeverityLevel<A: Float + Send + Sync> {
470 level: DriftSeverity,
471 recommended_lr_adjustment: A,
472}
473
474#[derive(Debug, Clone, Copy, PartialEq)]
475pub enum DriftSeverity {
476 None,
477 Mild,
478 Moderate,
479 Severe,
480 Critical,
481}
482
483#[derive(Debug, Clone, Default)]
485pub struct MemoryUsageTracker {
486 pub(crate) current_usage_mb: f64,
487 pub(crate) peak_usage_mb: f64,
488 pub(crate) usage_history: VecDeque<f64>,
489 pub(crate) memory_pressure: Option<f64>,
494}
495
496#[derive(Debug, Clone, Default)]
497pub struct ComputationTimeTracker {
498 pub(crate) step_times: VecDeque<Duration>,
499 pub(crate) average_step_time: Duration,
500 pub(crate) time_budget: Option<Duration>,
501 pub(crate) time_pressure: Option<f64>,
503}
504
505#[derive(Debug, Clone, Default)]
511pub struct EnergyConsumptionTracker {
512 pub(crate) energy_per_step: VecDeque<f64>,
513 pub(crate) cumulative_energy: f64,
514 pub(crate) energy_efficiency: Option<f64>,
515}
516
517#[derive(Debug, Clone)]
518pub struct ThroughputRequirements<A: Float + Send + Sync> {
519 min_samples_per_second: A,
520 current_throughput: A,
521 throughput_deficit: A,
522}
523
524#[derive(Debug, Clone)]
525pub struct ResourceBudgetManager<A: Float + Send + Sync> {
526 memory_budget_mb: f64,
527 compute_budget_seconds: f64,
528 budget_utilization: A,
529 budget_violations: usize,
530}
531
532#[derive(Debug, Clone)]
534pub struct HyperparameterUpdate<A: Float + Send + Sync> {
535 features: Array1<A>,
536 reward: A, }
538
539#[derive(Debug, Clone)]
541pub struct ExplorationStrategy<A: Float + Send + Sync> {
542 exploration_rate: A,
543 arm_rewards: HashMap<usize, A>,
544 arm_counts: HashMap<usize, usize>,
545}
546
547#[derive(Debug, Clone, Copy)]
548pub enum ExplorationStrategyType {
549 EpsilonGreedy,
550 UCB1,
551 ThompsonSampling,
552 LinUCB,
553 ContextualBandit,
554}
555
556#[derive(Debug, Clone)]
558pub struct TransferLearner<A: Float + Send + Sync> {
559 source_task_data: Vec<TaskData<A>>,
560 transfer_confidence: A,
561}
562
563#[derive(Debug, Clone)]
564pub struct TaskData<A: Float + Send + Sync> {
565 optimal_lr_sequence: Vec<A>,
566}
567
568#[derive(Debug, Clone, Default)]
570pub struct AdaptationStatistics<A: Float + Send + Sync> {
571 pub total_adaptations: usize,
573
574 pub successful_adaptations: usize,
576
577 pub avg_adaptation_frequency: A,
579
580 pub lr_volatility: A,
582
583 pub signal_reliability_scores: HashMap<AdaptationSignalType, A>,
585
586 pub signal_effectiveness: HashMap<AdaptationSignalType, A>,
588
589 pub resource_efficiency_gains: A,
591
592 pub convergence_speed_improvement: A,
594}
595
596impl<A: Float + Default + Clone + std::iter::Sum + Send + Sync> EnhancedAdaptiveLRController<A> {
597 pub fn new(config: AdaptiveLRConfig<A>) -> Result<Self> {
599 let adaptation_strategy = MultiSignalAdaptationStrategy::new(&config)?;
600 let gradient_adapter = GradientBasedAdapter::new(&config)?;
601 let performance_adapter = PerformanceBasedAdapter::new(&config)?;
602 let drift_adapter = DriftAwareAdapter::new(&config)?;
603 let resource_adapter = ResourceAwareAdapter::new(&config)?;
604 let meta_optimizer = MetaOptimizer::new(&config)?;
605
606 Ok(Self {
607 current_lr: config.base_lr,
608 base_lr: config.base_lr,
609 adaptation_strategy,
610 gradient_adapter,
611 performance_adapter,
612 drift_adapter,
613 resource_adapter,
614 meta_optimizer,
615 adaptation_history: VecDeque::with_capacity(config.history_window_size),
616 config,
617 })
618 }
619
620 pub fn record_memory_usage_mb(&mut self, usage_mb: f64) {
623 self.resource_adapter
624 .record_memory_usage(usage_mb, self.config.memory_budget_mb);
625 }
626
627 pub fn record_energy_sample(&mut self, joules: f64) {
629 self.resource_adapter.record_energy(joules);
630 }
631
632 pub fn record_throughput(&mut self, samples_per_second: A) {
635 self.resource_adapter.record_throughput(samples_per_second);
636 }
637
638 pub fn add_source_task(&mut self, task: TaskData<A>) {
641 self.meta_optimizer.add_source_task(task);
642 }
643
644 pub fn update_learning_rate(
646 &mut self,
647 gradients: &Array1<A>,
648 loss: A,
649 metrics: &HashMap<String, A>,
650 step: usize,
651 ) -> Result<A> {
652 let step_started = Instant::now();
653
654 let frequency = self.config.adaptation_frequency.max(1);
658 if !step.is_multiple_of(frequency) {
659 self.gradient_adapter.observe_only(gradients);
660 self.performance_adapter.observe_only(loss);
661 self.resource_adapter
662 .record_step_time(step_started.elapsed(), self.config.step_time_budget);
663 return Ok(self.current_lr);
664 }
665
666 let mut signals = Vec::new();
668
669 if self.config.enable_gradient_adaptation {
670 if let Ok(signal) = self.gradient_adapter.generate_signal(gradients, step) {
671 signals.push(signal);
672 }
673 }
674
675 if self.config.enable_performance_adaptation {
676 if let Ok(signal) = self
677 .performance_adapter
678 .generate_signal(loss, metrics, step)
679 {
680 signals.push(signal);
681 }
682 }
683
684 if self.config.enable_drift_adaptation {
685 if let Ok(signal) = self.drift_adapter.generate_signal(gradients, step) {
686 signals.push(signal);
687 }
688 }
689
690 if self.config.enable_resource_adaptation {
691 if let Ok(signal) = self.resource_adapter.generate_signal(step) {
692 signals.push(signal);
693 }
694 }
695
696 let previous_lr = self.current_lr;
701 let decision = self.adaptation_strategy.resolve_signals(
702 signals,
703 previous_lr,
704 self.config.adaptation_sensitivity,
705 self.config.use_ensemble_voting,
706 step,
707 )?;
708
709 if self.config.enable_meta_learning {
711 let meta_adjustment = self.meta_optimizer.meta_optimize(&decision, step)?;
712 self.current_lr = self.apply_meta_adjustment(decision.new_lr, meta_adjustment);
713 } else {
714 self.current_lr = decision.new_lr;
715 }
716
717 self.current_lr = self
719 .current_lr
720 .clamp(self.config.min_lr, self.config.max_lr);
721
722 let event = AdaptationEvent {
724 timestamp: Instant::now(),
725 old_lr: previous_lr,
728 new_lr: self.current_lr,
729 trigger_signals: decision.contributing_signals,
730 effectiveness_score: None, };
732
733 self.adaptation_history.push_back(event);
734 if self.adaptation_history.len() > self.config.history_window_size {
735 self.adaptation_history.pop_front();
736 }
737
738 self.resource_adapter
739 .record_step_time(step_started.elapsed(), self.config.step_time_budget);
740
741 Ok(self.current_lr)
742 }
743
744 pub fn previous_lr(&self) -> Option<A> {
746 self.adaptation_history.back().map(|event| event.old_lr)
747 }
748
749 pub fn last_decision(&self) -> Option<&AdaptationDecision<A>> {
751 self.adaptation_strategy.last_decision.as_ref()
752 }
753
754 pub fn voting_history(&self) -> &VecDeque<SignalVote<A>> {
756 &self.adaptation_strategy.voting_history
757 }
758
759 pub fn get_current_lr(&self) -> A {
761 self.current_lr
762 }
763
764 pub fn get_adaptation_statistics(&self) -> AdaptationStatistics<A> {
766 let total_adaptations = self.adaptation_history.len();
767 let successful_adaptations = self
768 .adaptation_history
769 .iter()
770 .filter(|event| {
771 event
772 .effectiveness_score
773 .is_some_and(|score| score > A::zero())
774 })
775 .count();
776
777 let lr_volatility = if !self.adaptation_history.is_empty() {
778 let lr_values: Vec<A> = self
779 .adaptation_history
780 .iter()
781 .map(|event| event.new_lr)
782 .collect();
783
784 let mean_lr = lr_values.iter().fold(A::zero(), |acc, &lr| acc + lr)
785 / scalar_or(lr_values.len(), A::one());
786
787 let variance = lr_values
788 .iter()
789 .map(|&lr| {
790 let diff = lr - mean_lr;
791 diff * diff
792 })
793 .fold(A::zero(), |acc, var| acc + var)
794 / scalar_or(lr_values.len(), A::one());
795
796 variance.sqrt()
797 } else {
798 A::zero()
799 };
800
801 let signal_reliability_scores = self.adaptation_strategy.signal_reliability.clone();
804 let mut signal_effectiveness: HashMap<AdaptationSignalType, A> = HashMap::new();
805 let mut signal_counts: HashMap<AdaptationSignalType, usize> = HashMap::new();
806 for event in &self.adaptation_history {
807 let Some(score) = event.effectiveness_score else {
808 continue;
809 };
810 for signal_type in &event.trigger_signals {
811 let count = signal_counts.entry(*signal_type).or_insert(0);
812 *count += 1;
813 let steps = A::from(*count).unwrap_or_else(A::one);
814 let entry = signal_effectiveness
815 .entry(*signal_type)
816 .or_insert_with(A::zero);
817 *entry = *entry + (score - *entry) / steps;
818 }
819 }
820
821 let avg_adaptation_frequency = if let (Some(first), Some(last)) = (
822 self.adaptation_history.front(),
823 self.adaptation_history.back(),
824 ) {
825 let span = last.timestamp.saturating_duration_since(first.timestamp);
826 if span.as_secs_f64() > 0.0 {
827 A::from(total_adaptations as f64 / span.as_secs_f64()).unwrap_or_else(A::zero)
828 } else {
829 A::zero()
830 }
831 } else {
832 A::zero()
833 };
834
835 let convergence_speed_improvement = {
836 let scores: Vec<A> = self
837 .adaptation_history
838 .iter()
839 .filter_map(|event| event.effectiveness_score)
840 .collect();
841 if scores.is_empty() {
842 A::zero()
843 } else {
844 let count = A::from(scores.len()).unwrap_or_else(A::one);
845 scores.iter().fold(A::zero(), |acc, score| acc + *score) / count
846 }
847 };
848
849 AdaptationStatistics {
850 total_adaptations,
851 successful_adaptations,
852 avg_adaptation_frequency,
853 lr_volatility,
854 signal_reliability_scores,
855 signal_effectiveness,
856 resource_efficiency_gains: A::from(self.resource_adapter.budget_violations() as f64)
857 .map(|violations| A::zero() - violations)
858 .unwrap_or_else(A::zero),
859 convergence_speed_improvement,
860 }
861 }
862
863 fn apply_meta_adjustment(&self, base_lr: A, meta_adjustment: A) -> A {
865 let alpha = scalar_or(0.7, A::zero()); let beta = scalar_or(0.3, A::zero()); alpha * base_lr + beta * meta_adjustment
870 }
871
872 pub fn evaluate_adaptation_effectiveness(&mut self, performance_improvement: A) {
874 let mut signals = Vec::new();
875 if let Some(last_event) = self.adaptation_history.back_mut() {
876 last_event.effectiveness_score = Some(performance_improvement);
877 signals = last_event.trigger_signals.clone();
878 }
879 for signal_type in signals {
881 self.adaptation_strategy
882 .update_signal_reliability(signal_type, performance_improvement);
883 }
884 self.meta_optimizer.record_reward(performance_improvement);
887 }
888
889 pub fn meta_arm_rewards(&self) -> &HashMap<usize, A> {
891 self.meta_optimizer.arm_rewards()
892 }
893
894 pub fn meta_arm_counts(&self) -> &HashMap<usize, usize> {
896 self.meta_optimizer.arm_counts()
897 }
898
899 pub fn signal_reliability(&self) -> &HashMap<AdaptationSignalType, A> {
901 &self.adaptation_strategy.signal_reliability
902 }
903
904 pub fn reset(&mut self) {
906 self.current_lr = self.base_lr;
907 self.adaptation_history.clear();
908 self.gradient_adapter.reset();
909 self.performance_adapter.reset();
910 self.drift_adapter.reset();
911 self.resource_adapter.reset();
912 self.meta_optimizer.reset();
913 }
914}
915
916mod meta;
917mod signals;
918
919#[cfg(test)]
920mod adaptive_lr_tests;
921
922pub(crate) use signals::LossDriftDetector;
923
924impl<A: Float + Default + Send + Sync + Send + Sync> Default for GradientNormStatistics<A> {
926 fn default() -> Self {
927 Self {
928 mean: A::default(),
929 variance: A::default(),
930 skewness: A::default(),
931 kurtosis: A::default(),
932 percentiles: vec![A::default(); 5],
933 autocorrelation: A::default(),
934 }
935 }
936}
937
938impl<A: Float + Default + Send + Sync + Send + Sync> Default for SignalToNoiseEstimator<A> {
939 fn default() -> Self {
940 Self {
941 signal_estimate: A::default(),
942 noise_estimate: A::default(),
943 snr_history: VecDeque::new(),
944 }
945 }
946}
947
948impl<A: Float + Default + Send + Sync + Send + Sync> Default for PerformanceTrendAnalyzer<A> {
949 fn default() -> Self {
950 Self {
951 trend_detection_window: 10,
952 trend_types: vec![],
953 trend_strength: A::default(),
954 }
955 }
956}
957
958impl<A: Float + Default + Send + Sync + Send + Sync> Default for PlateauDetector<A> {
959 fn default() -> Self {
960 Self {
961 plateau_threshold: A::default(),
962 min_plateau_duration: 5,
963 current_plateau_length: 0,
964 plateau_confidence: A::default(),
965 }
966 }
967}
968
969impl<A: Float + Default + Send + Sync + Send + Sync> Default for OverfittingDetector<A> {
970 fn default() -> Self {
971 Self {
972 train_loss_history: VecDeque::new(),
973 val_loss_history: VecDeque::new(),
974 }
975 }
976}
977
978impl<A: Float + Default + Send + Sync + Send + Sync> Default for LearningEfficiencyTracker<A> {
979 fn default() -> Self {
980 Self {
981 loss_reduction_per_step: VecDeque::new(),
982 efficiency_score: A::default(),
983 efficiency_trend: TrendType::Improving,
984 }
985 }
986}
987
988impl<A: Float + Default + Send + Sync + Send + Sync> Default for DistributionTracker<A> {
989 fn default() -> Self {
990 Self {
991 feature_distributions: HashMap::new(),
992 distribution_drift_score: A::default(),
993 }
994 }
995}
996
997impl<A: Float + Default + Send + Sync + Send + Sync> Default for AdaptationSpeedController<A> {
998 fn default() -> Self {
999 Self {
1000 base_adaptation_rate: A::from(0.1).unwrap_or_default(),
1001 current_adaptation_rate: A::from(0.1).unwrap_or_default(),
1002 acceleration_factor: A::from(1.1).unwrap_or_default(),
1003 deceleration_factor: A::from(0.9).unwrap_or_default(),
1004 momentum: A::default(),
1005 }
1006 }
1007}
1008
1009impl<A: Float + Default + Send + Sync + Send + Sync> Default for DriftSeverityAssessor<A> {
1010 fn default() -> Self {
1011 Self {
1012 severity_levels: vec![],
1013 current_severity: DriftSeverityLevel::default(),
1014 severity_history: VecDeque::new(),
1015 }
1016 }
1017}
1018
1019impl<A: Float + Default + Send + Sync + Send + Sync> Default for DriftSeverityLevel<A> {
1020 fn default() -> Self {
1021 Self {
1022 level: DriftSeverity::None,
1023 recommended_lr_adjustment: A::one(),
1024 }
1025 }
1026}
1027
1028impl<A: Float + Default + Send + Sync + Send + Sync> Default for ExplorationStrategy<A> {
1029 fn default() -> Self {
1030 Self {
1031 exploration_rate: A::from(0.1).unwrap_or_default(),
1032 arm_rewards: HashMap::new(),
1033 arm_counts: HashMap::new(),
1034 }
1035 }
1036}
1037
1038impl<A: Float + Default + Send + Sync + Send + Sync> Default for TransferLearner<A> {
1039 fn default() -> Self {
1040 Self {
1041 source_task_data: vec![],
1042 transfer_confidence: A::default(),
1043 }
1044 }
1045}
1046
1047#[cfg(test)]
1048mod tests {
1049 use super::*;
1050 use scirs2_core::ndarray::Array1;
1051
1052 #[test]
1053 fn test_enhanced_adaptive_lr_controller_creation() {
1054 let config = AdaptiveLRConfig {
1055 base_lr: 0.01,
1056 min_lr: 1e-6,
1057 max_lr: 1.0,
1058 enable_gradient_adaptation: true,
1059 enable_performance_adaptation: true,
1060 enable_drift_adaptation: false,
1061 enable_resource_adaptation: false,
1062 enable_meta_learning: false,
1063 history_window_size: 100,
1064 adaptation_frequency: 10,
1065 adaptation_sensitivity: 0.1,
1066 use_ensemble_voting: true,
1067 step_time_budget: None,
1068 memory_budget_mb: None,
1069 };
1070
1071 let controller = EnhancedAdaptiveLRController::<f32>::new(config);
1072 assert!(controller.is_ok());
1073 }
1074
1075 #[test]
1076 fn test_learning_rate_update() {
1077 let config = AdaptiveLRConfig {
1078 base_lr: 0.01,
1079 min_lr: 1e-6,
1080 max_lr: 1.0,
1081 enable_gradient_adaptation: true,
1082 enable_performance_adaptation: true,
1083 enable_drift_adaptation: false,
1084 enable_resource_adaptation: false,
1085 enable_meta_learning: false,
1086 history_window_size: 100,
1087 adaptation_frequency: 10,
1088 adaptation_sensitivity: 0.1,
1089 use_ensemble_voting: true,
1090 step_time_budget: None,
1091 memory_budget_mb: None,
1092 };
1093
1094 let mut controller =
1095 EnhancedAdaptiveLRController::<f32>::new(config).expect("unwrap failed");
1096 let gradients = Array1::from_vec(vec![0.1, 0.2, 0.05]);
1097 let loss = 0.5;
1098 let metrics = HashMap::new();
1099
1100 let new_lr = controller.update_learning_rate(&gradients, loss, &metrics, 1);
1101 assert!(new_lr.is_ok());
1102 assert!(new_lr.expect("unwrap failed") > 0.0);
1103 }
1104
1105 #[test]
1106 fn test_adaptation_statistics() {
1107 let config = AdaptiveLRConfig {
1108 base_lr: 0.01,
1109 min_lr: 1e-6,
1110 max_lr: 1.0,
1111 enable_gradient_adaptation: true,
1112 enable_performance_adaptation: true,
1113 enable_drift_adaptation: false,
1114 enable_resource_adaptation: false,
1115 enable_meta_learning: false,
1116 history_window_size: 100,
1117 adaptation_frequency: 10,
1118 adaptation_sensitivity: 0.1,
1119 use_ensemble_voting: true,
1120 step_time_budget: None,
1121 memory_budget_mb: None,
1122 };
1123
1124 let controller = EnhancedAdaptiveLRController::<f32>::new(config).expect("unwrap failed");
1125 let stats = controller.get_adaptation_statistics();
1126
1127 assert_eq!(stats.total_adaptations, 0);
1128 assert_eq!(stats.successful_adaptations, 0);
1129 }
1130}