1use crate::error::Result;
7use crate::multi_objective::ParetoFront;
8use crate::numeric::{count_as, scalar_as};
9use crate::EvaluationMetric;
10use scirs2_core::numeric::Float;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::fmt::Debug;
14use std::time::{Duration, Instant};
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct OptimizerArchitecture<T: Float + Debug + Send + Sync + 'static> {
19 pub components: Vec<String>,
20 pub parameters: HashMap<String, T>,
21 pub connections: Vec<(usize, usize)>,
22 pub metadata: HashMap<String, String>,
23 pub hyperparameters: HashMap<String, T>,
24 pub architecture_id: String,
25}
26
27#[derive(Debug, Clone)]
28pub struct OptimizerComponent<T: Float + Debug + Send + Sync + 'static> {
29 pub name: String,
30 pub parameters: HashMap<String, T>,
31}
32
33#[derive(Debug, Clone)]
34pub struct ConvergenceData<T: Float + Debug + Send + Sync + 'static> {
35 pub iteration: usize,
36 pub best_score: T,
37 pub convergence_rate: T,
38 pub stability_measure: T,
39 pub best_scores_over_time: Vec<T>,
40 pub diversity_over_time: Vec<T>,
41 pub convergence_generation: usize,
42 pub final_diversity: T,
43}
44
45#[derive(Debug, Clone)]
47pub struct SearchResult<T: Float + Debug + Send + Sync + 'static> {
48 pub architecture: OptimizerArchitecture<T>,
50
51 pub evaluation_results: EvaluationResults<T>,
53
54 pub generation: usize,
56
57 pub search_time: f64,
59
60 pub resource_usage: ResourceUsage<T>,
62
63 pub encoding: ArchitectureEncoding,
65
66 pub metadata: SearchResultMetadata,
68}
69
70#[derive(Debug, Clone)]
72pub struct EvaluationResults<T: Float + Debug + Send + Sync + 'static> {
73 pub metric_scores: HashMap<EvaluationMetric, T>,
75
76 pub overall_score: T,
78
79 pub confidence_intervals: HashMap<EvaluationMetric, (T, T)>,
81
82 pub evaluation_time: Duration,
84
85 pub success: bool,
87
88 pub error_message: Option<String>,
90
91 pub cv_results: Option<CrossValidationResults<T>>,
93
94 pub benchmark_results: HashMap<String, BenchmarkResult<T>>,
96
97 pub training_trajectory: Vec<TrainingSnapshot<T>>,
99}
100
101#[derive(Debug, Clone)]
103pub struct CrossValidationResults<T: Float + Debug + Send + Sync + 'static> {
104 pub fold_results: Vec<T>,
106
107 pub mean_score: T,
109
110 pub std_score: T,
112
113 pub confidence_interval: (T, T),
115
116 pub statistical_tests: HashMap<String, StatisticalTestResult<T>>,
118}
119
120#[derive(Debug, Clone)]
122pub struct StatisticalTestResult<T: Float + Debug + Send + Sync + 'static> {
123 pub test_statistic: T,
125
126 pub p_value: T,
128
129 pub critical_value: T,
131
132 pub reject_null: bool,
134
135 pub effect_size: Option<T>,
137}
138
139#[derive(Debug, Clone)]
141pub struct BenchmarkResult<T: Float + Debug + Send + Sync + 'static> {
142 pub benchmark_name: String,
144
145 pub primary_score: T,
147
148 pub all_scores: HashMap<EvaluationMetric, T>,
150
151 pub evaluation_time: Duration,
153
154 pub peak_memory_usage: usize,
156
157 pub success: bool,
159
160 pub metadata: HashMap<String, String>,
162}
163
164#[derive(Debug, Clone)]
166pub struct TrainingSnapshot<T: Float + Debug + Send + Sync + 'static> {
167 pub epoch: usize,
169
170 pub training_loss: T,
172
173 pub validation_loss: Option<T>,
175
176 pub training_metrics: HashMap<EvaluationMetric, T>,
178
179 pub validation_metrics: HashMap<EvaluationMetric, T>,
181
182 pub learning_rate: T,
184
185 pub timestamp: Duration,
187
188 pub memory_usage: usize,
190}
191
192#[derive(Debug, Clone)]
194pub struct ResourceUsage<T: Float + Debug + Send + Sync + 'static> {
195 pub memory_gb: T,
197
198 pub cpu_time_seconds: T,
200
201 pub gpu_time_seconds: T,
203
204 pub energy_kwh: T,
206
207 pub network_io_gb: T,
209
210 pub disk_io_gb: T,
212
213 pub peak_memory_gb: T,
215
216 pub efficiency_score: T,
218
219 pub cost_usd: T,
221
222 pub network_gb: T,
224}
225
226#[derive(Debug, Clone)]
228pub struct ArchitectureEncoding {
229 pub encoding_type: EncodingType,
231
232 pub binary_encoding: Option<Vec<u8>>,
234
235 pub string_encoding: Option<String>,
237
238 pub graph_encoding: Option<GraphEncoding>,
240
241 pub hash: u64,
243
244 pub metadata: EncodingMetadata,
246}
247
248#[derive(Debug, Clone, Copy)]
250pub enum EncodingType {
251 Binary,
252 String,
253 Graph,
254 Neural,
255 Hybrid,
256}
257
258#[derive(Debug, Clone)]
260pub struct GraphEncoding {
261 pub nodes: Vec<NodeEncoding>,
263
264 pub edges: Vec<EdgeEncoding>,
266
267 pub properties: GraphProperties,
269}
270
271#[derive(Debug, Clone)]
273pub struct NodeEncoding {
274 pub id: usize,
276
277 pub component_type: String,
279
280 pub parameters: HashMap<String, f64>,
282
283 pub properties: HashMap<String, String>,
285}
286
287#[derive(Debug, Clone)]
289pub struct EdgeEncoding {
290 pub source: usize,
292
293 pub target: usize,
295
296 pub connection_type: String,
298
299 pub weight: f64,
301
302 pub properties: HashMap<String, String>,
304}
305
306#[derive(Debug, Clone)]
308pub struct GraphProperties {
309 pub num_nodes: usize,
311
312 pub num_edges: usize,
314
315 pub density: f64,
317
318 pub average_degree: f64,
320
321 pub is_directed: bool,
323
324 pub has_cycles: bool,
326}
327
328#[derive(Debug, Clone)]
330pub struct EncodingMetadata {
331 pub version: String,
333
334 pub created_at: Instant,
336
337 pub size_bytes: usize,
339
340 pub compression_ratio: Option<f64>,
342
343 pub quality_score: Option<f64>,
345}
346
347#[derive(Debug, Clone)]
349pub struct SearchResultMetadata {
350 pub search_strategy: String,
352
353 pub controller_type: String,
355
356 pub evaluation_method: String,
358
359 pub search_phase: Option<String>,
361
362 pub parent_architectures: Vec<u64>,
364
365 pub operation_info: Option<OperationInfo>,
367
368 pub tags: HashMap<String, String>,
370}
371
372#[derive(Debug, Clone)]
374pub struct OperationInfo {
375 pub operation_type: String,
377
378 pub parameters: HashMap<String, f64>,
380
381 pub success: bool,
383
384 pub operation_time: Duration,
386}
387
388#[derive(Debug, Clone)]
390pub struct SearchStatistics<T: Float + Debug + Send + Sync + 'static> {
391 pub total_architectures_evaluated: usize,
393
394 pub total_evaluations: usize,
396
397 pub current_generation: usize,
399
400 pub population_diversity: T,
402
403 pub average_score: T,
405
406 pub successful_evaluations: usize,
408
409 pub failed_evaluations: usize,
411
412 pub best_score: Option<T>,
414
415 pub best_architecture_hash: Option<u64>,
417
418 pub average_evaluation_time: Duration,
420
421 pub total_search_time: Duration,
423
424 pub score_history: Vec<T>,
426
427 pub resource_stats: ResourceStatistics<T>,
429
430 pub convergence_metrics: ConvergenceMetrics<T>,
432
433 pub diversity_metrics: DiversityMetrics<T>,
435
436 pub efficiency_metrics: EfficiencyMetrics<T>,
438}
439
440#[derive(Debug, Clone)]
442pub struct ResourceStatistics<T: Float + Debug + Send + Sync + 'static> {
443 pub total_memory_gb: T,
445
446 pub total_cpu_hours: T,
448
449 pub total_gpu_hours: T,
451
452 pub total_energy_kwh: T,
454
455 pub peak_memory_gb: T,
457
458 pub average_utilization: T,
460
461 pub efficiency_score: T,
463}
464
465#[derive(Debug, Clone)]
467pub struct ConvergenceMetrics<T: Float + Debug + Send + Sync + 'static> {
468 pub generations_to_convergence: Option<usize>,
470
471 pub convergence_rate: Option<T>,
473
474 pub plateau_generations: usize,
476
477 pub recent_score_variance: T,
479
480 pub improvement_rate: T,
482
483 pub is_stagnating: bool,
485}
486
487#[derive(Debug, Clone)]
489pub struct DiversityMetrics<T: Float + Debug + Send + Sync + 'static> {
490 pub population_diversity: T,
492
493 pub uniqueness_ratio: T,
495
496 pub genotypic_diversity: T,
498
499 pub phenotypic_diversity: T,
501
502 pub diversity_history: Vec<T>,
504
505 pub diversity_maintenance: T,
507}
508
509#[derive(Debug, Clone)]
511pub struct EfficiencyMetrics<T: Float + Debug + Send + Sync + 'static> {
512 pub evaluations_per_improvement: T,
514
515 pub time_to_best: Duration,
517
518 pub resource_cost_per_improvement: T,
520
521 pub efficiency_score: T,
523
524 pub pareto_efficiency: Option<T>,
526
527 pub hypervolume: Option<T>,
529}
530
531#[derive(Debug, Clone)]
533pub struct SearchResults<T: Float + Debug + Send + Sync + 'static> {
534 pub best_architectures: Vec<SearchResult<T>>,
536
537 pub pareto_front: Option<ParetoFront<T>>,
539
540 pub search_history: Vec<SearchResult<T>>,
542
543 pub search_statistics: SearchStatistics<T>,
545
546 pub evaluation_summary: EvaluationSummary<T>,
548
549 pub search_configuration: SearchConfigSummary,
551
552 pub recommendations: Vec<SearchRecommendation>,
554
555 pub resource_usage_summary: ResourceUsageSummary<T>,
557
558 pub search_time_seconds: f64,
560
561 pub convergence_data: ConvergenceData<T>,
563
564 pub config: SearchConfigSummary,
566}
567
568#[derive(Debug, Clone)]
570pub struct ResourceUsageSummary<T: Float + Debug + Send + Sync + 'static> {
571 pub total_memory_gb: T,
573
574 pub total_cpu_hours: T,
576
577 pub total_gpu_hours: T,
579
580 pub total_energy_kwh: T,
582
583 pub total_cost_usd: T,
585
586 pub average_efficiency: T,
588}
589
590#[derive(Debug, Clone)]
592pub struct EvaluationSummary<T: Float + Debug + Send + Sync + 'static> {
593 pub total_evaluations: usize,
595
596 pub success_rate: T,
598
599 pub best_score: T,
601
602 pub score_statistics: ScoreStatistics<T>,
604
605 pub benchmark_summary: HashMap<String, BenchmarkSummary<T>>,
607
608 pub resource_summary: ResourceSummary<T>,
610}
611
612#[derive(Debug, Clone)]
614pub struct ScoreStatistics<T: Float + Debug + Send + Sync + 'static> {
615 pub mean: T,
617
618 pub median: T,
620
621 pub std_dev: T,
623
624 pub min: T,
626
627 pub max: T,
629
630 pub percentiles: HashMap<usize, T>,
632
633 pub histogram: Vec<(T, usize)>,
635}
636
637#[derive(Debug, Clone)]
639pub struct BenchmarkSummary<T: Float + Debug + Send + Sync + 'static> {
640 pub benchmark_name: String,
642
643 pub best_score: T,
645
646 pub average_score: T,
648
649 pub successful_evaluations: usize,
651
652 pub time_statistics: TimeStatistics,
654
655 pub improvement_trajectory: Vec<T>,
657}
658
659#[derive(Debug, Clone)]
661pub struct TimeStatistics {
662 pub mean: Duration,
664
665 pub median: Duration,
667
668 pub std_dev: Duration,
670
671 pub min: Duration,
673
674 pub max: Duration,
676}
677
678#[derive(Debug, Clone)]
680pub struct ResourceSummary<T: Float + Debug + Send + Sync + 'static> {
681 pub total_consumption: ResourceUsage<T>,
683
684 pub average_per_evaluation: ResourceUsage<T>,
686
687 pub peak_usage: ResourceUsage<T>,
689
690 pub efficiency_metrics: ResourceEfficiencyMetrics<T>,
692}
693
694#[derive(Debug, Clone)]
696pub struct ResourceEfficiencyMetrics<T: Float + Debug + Send + Sync + 'static> {
697 pub memory_efficiency: T,
699
700 pub cpu_efficiency: T,
702
703 pub gpu_efficiency: T,
705
706 pub energy_efficiency: T,
708
709 pub overall_efficiency: T,
711}
712
713#[derive(Debug, Clone)]
715pub struct SearchConfigSummary {
716 pub search_strategy: String,
718
719 pub population_size: usize,
721
722 pub search_budget: usize,
724
725 pub evaluation_config: String,
727
728 pub multi_objective_config: Option<String>,
730
731 pub resource_constraints: String,
733
734 pub key_hyperparameters: HashMap<String, String>,
736}
737
738#[derive(Debug, Clone)]
740pub struct SearchRecommendation {
741 pub recommendation_type: RecommendationType,
743
744 pub description: String,
746
747 pub priority: RecommendationPriority,
749
750 pub expected_improvement: Option<f64>,
752
753 pub implementation_effort: ImplementationEffort,
755
756 pub evidence: Vec<String>,
758}
759
760#[derive(Debug, Clone, Copy)]
762pub enum RecommendationType {
763 SearchStrategyChange,
764 PopulationSizeAdjustment,
765 BudgetReallocation,
766 EvaluationMethodImprovement,
767 ResourceOptimization,
768 HyperparameterTuning,
769 SearchSpaceModification,
770 DiversityImprovement,
771 ConvergenceAcceleration,
772}
773
774#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
776pub enum RecommendationPriority {
777 Low,
778 Medium,
779 High,
780 Critical,
781}
782
783#[derive(Debug, Clone, Copy)]
785pub enum ImplementationEffort {
786 Minimal,
787 Low,
788 Medium,
789 High,
790 Extensive,
791}
792
793impl<T: Float + Debug + Send + Sync + 'static> Default for SearchStatistics<T> {
795 fn default() -> Self {
796 Self {
797 total_architectures_evaluated: 0,
798 total_evaluations: 0,
799 current_generation: 0,
800 population_diversity: T::zero(),
801 average_score: T::zero(),
802 successful_evaluations: 0,
803 failed_evaluations: 0,
804 best_score: None,
805 best_architecture_hash: None,
806 average_evaluation_time: Duration::from_secs(0),
807 total_search_time: Duration::from_secs(0),
808 score_history: Vec::new(),
809 resource_stats: ResourceStatistics::default(),
810 convergence_metrics: ConvergenceMetrics::default(),
811 diversity_metrics: DiversityMetrics::default(),
812 efficiency_metrics: EfficiencyMetrics::default(),
813 }
814 }
815}
816
817impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceStatistics<T> {
818 fn default() -> Self {
819 Self {
820 total_memory_gb: T::zero(),
821 total_cpu_hours: T::zero(),
822 total_gpu_hours: T::zero(),
823 total_energy_kwh: T::zero(),
824 peak_memory_gb: T::zero(),
825 average_utilization: T::zero(),
826 efficiency_score: T::zero(),
827 }
828 }
829}
830
831impl<T: Float + Debug + Send + Sync + 'static> Default for ConvergenceMetrics<T> {
832 fn default() -> Self {
833 Self {
834 generations_to_convergence: None,
835 convergence_rate: None,
836 plateau_generations: 0,
837 recent_score_variance: T::zero(),
838 improvement_rate: T::zero(),
839 is_stagnating: false,
840 }
841 }
842}
843
844impl<T: Float + Debug + Send + Sync + 'static> Default for DiversityMetrics<T> {
845 fn default() -> Self {
846 Self {
847 population_diversity: T::one(),
848 uniqueness_ratio: T::one(),
849 genotypic_diversity: T::one(),
850 phenotypic_diversity: T::one(),
851 diversity_history: Vec::new(),
852 diversity_maintenance: T::one(),
853 }
854 }
855}
856
857impl<T: Float + Debug + Send + Sync + 'static> Default for EfficiencyMetrics<T> {
858 fn default() -> Self {
859 Self {
860 evaluations_per_improvement: T::one(),
861 time_to_best: Duration::from_secs(0),
862 resource_cost_per_improvement: T::zero(),
863 efficiency_score: T::zero(),
864 pareto_efficiency: None,
865 hypervolume: None,
866 }
867 }
868}
869
870impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceUsage<T> {
871 fn default() -> Self {
872 Self {
873 memory_gb: T::zero(),
874 cpu_time_seconds: T::zero(),
875 gpu_time_seconds: T::zero(),
876 energy_kwh: T::zero(),
877 network_io_gb: T::zero(),
878 disk_io_gb: T::zero(),
879 peak_memory_gb: T::zero(),
880 efficiency_score: T::zero(),
881 cost_usd: T::zero(),
882 network_gb: T::zero(),
883 }
884 }
885}
886
887impl Default for ArchitectureEncoding {
888 fn default() -> Self {
889 Self {
890 encoding_type: EncodingType::String,
891 binary_encoding: None,
892 string_encoding: None,
893 graph_encoding: None,
894 hash: 0,
895 metadata: EncodingMetadata::default(),
896 }
897 }
898}
899
900impl Default for EncodingMetadata {
901 fn default() -> Self {
902 Self {
903 version: "1.0".to_string(),
904 created_at: Instant::now(),
905 size_bytes: 0,
906 compression_ratio: None,
907 quality_score: None,
908 }
909 }
910}
911
912impl Default for SearchResultMetadata {
913 fn default() -> Self {
914 Self {
915 search_strategy: "Unknown".to_string(),
916 controller_type: "Unknown".to_string(),
917 evaluation_method: "Unknown".to_string(),
918 search_phase: None,
919 parent_architectures: Vec::new(),
920 operation_info: None,
921 tags: HashMap::new(),
922 }
923 }
924}
925
926impl Default for GraphProperties {
927 fn default() -> Self {
928 Self {
929 num_nodes: 0,
930 num_edges: 0,
931 density: 0.0,
932 average_degree: 0.0,
933 is_directed: true,
934 has_cycles: false,
935 }
936 }
937}
938
939impl Default for TimeStatistics {
940 fn default() -> Self {
941 Self {
942 mean: Duration::from_secs(0),
943 median: Duration::from_secs(0),
944 std_dev: Duration::from_secs(0),
945 min: Duration::from_secs(0),
946 max: Duration::from_secs(0),
947 }
948 }
949}
950
951impl<T: Float + Debug + Send + Sync + 'static> SearchStatistics<T> {
953 pub fn calculate_efficiency_score(&self) -> Result<T> {
966 if self.total_architectures_evaluated == 0 {
967 return Ok(T::zero());
968 }
969
970 let successes: T = count_as(self.successful_evaluations, "successful evaluations")?;
971 let attempts: T = count_as(
972 self.total_architectures_evaluated,
973 "evaluated architectures",
974 )?;
975 let success_rate = successes / attempts;
976
977 let elapsed_seconds = self.total_search_time.as_secs();
978 let time_efficiency = if elapsed_seconds > 0 {
979 let seconds: T = count_as(elapsed_seconds as usize, "elapsed search seconds")?;
980 successes / seconds
981 } else {
982 T::zero()
983 };
984
985 let two: T = scalar_as(2.0, "efficiency averaging divisor")?;
986 Ok((success_rate + time_efficiency) / two)
987 }
988
989 pub fn has_converged(&self, patience: usize, min_improvement: T) -> bool {
991 if self.score_history.len() < patience {
992 return false;
993 }
994
995 let recent_scores = &self.score_history[self.score_history.len() - patience..];
996 let max_score = recent_scores
997 .iter()
998 .fold(T::neg_infinity(), |a, &b| a.max(b));
999 let min_score = recent_scores.iter().fold(T::infinity(), |a, &b| a.min(b));
1000
1001 max_score - min_score < min_improvement
1002 }
1003
1004 pub fn get_improvement_trend(&self, window_size: usize) -> Option<T> {
1006 if self.score_history.len() < window_size * 2 {
1007 return None;
1008 }
1009
1010 let recent_scores = &self.score_history[self.score_history.len() - window_size..];
1011 let older_scores = &self.score_history
1012 [self.score_history.len() - window_size * 2..self.score_history.len() - window_size];
1013
1014 let recent_mean = recent_scores.iter().fold(T::zero(), |a, &b| a + b)
1015 / scirs2_core::numeric::NumCast::from(window_size).unwrap_or_else(|| T::zero());
1016 let older_mean = older_scores.iter().fold(T::zero(), |a, &b| a + b)
1017 / scirs2_core::numeric::NumCast::from(window_size).unwrap_or_else(|| T::zero());
1018
1019 Some(recent_mean - older_mean)
1020 }
1021}
1022
1023impl<T: Float + Debug + Send + Sync + 'static> ResourceUsage<T> {
1024 pub fn total_cost(&self, costs: &ResourceCosts<T>) -> T {
1026 self.memory_gb * costs.memory_cost_per_gb
1027 + self.cpu_time_seconds * costs.cpu_cost_per_second
1028 + self.gpu_time_seconds * costs.gpu_cost_per_second
1029 + self.energy_kwh * costs.energy_cost_per_kwh
1030 }
1031
1032 pub fn exceeds_limits(&self, limits: &ResourceLimits<T>) -> bool {
1034 self.memory_gb > limits.max_memory_gb
1035 || self.cpu_time_seconds > limits.max_cpu_seconds
1036 || self.gpu_time_seconds > limits.max_gpu_seconds
1037 || self.energy_kwh > limits.max_energy_kwh
1038 }
1039}
1040
1041#[derive(Debug, Clone)]
1043pub struct ResourceCosts<T: Float + Debug + Send + Sync + 'static> {
1044 pub memory_cost_per_gb: T,
1045 pub cpu_cost_per_second: T,
1046 pub gpu_cost_per_second: T,
1047 pub energy_cost_per_kwh: T,
1048}
1049
1050#[derive(Debug, Clone)]
1052pub struct ResourceLimits<T: Float + Debug + Send + Sync + 'static> {
1053 pub max_memory_gb: T,
1054 pub max_cpu_seconds: T,
1055 pub max_gpu_seconds: T,
1056 pub max_energy_kwh: T,
1057}
1058
1059impl<T: Float + Debug + Send + Sync + 'static> Default for ScoreStatistics<T> {
1062 fn default() -> Self {
1063 Self {
1064 mean: T::zero(),
1065 median: T::zero(),
1066 std_dev: T::zero(),
1067 min: T::zero(),
1068 max: T::zero(),
1069 percentiles: HashMap::new(),
1070 histogram: Vec::new(),
1071 }
1072 }
1073}
1074
1075impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceSummary<T> {
1076 fn default() -> Self {
1077 Self {
1078 total_consumption: ResourceUsage::default(),
1079 average_per_evaluation: ResourceUsage::default(),
1080 peak_usage: ResourceUsage::default(),
1081 efficiency_metrics: ResourceEfficiencyMetrics::default(),
1082 }
1083 }
1084}
1085
1086impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceEfficiencyMetrics<T> {
1087 fn default() -> Self {
1088 Self {
1089 cpu_efficiency: T::zero(),
1090 gpu_efficiency: T::zero(),
1091 memory_efficiency: T::zero(),
1092 energy_efficiency: T::zero(),
1093 overall_efficiency: T::zero(),
1094 }
1095 }
1096}
1097
1098#[cfg(test)]
1099mod tests {
1100 use super::*;
1101
1102 #[test]
1103 fn test_search_statistics_default() {
1104 let stats = SearchStatistics::<f32>::default();
1105 assert_eq!(stats.total_architectures_evaluated, 0);
1106 assert_eq!(stats.successful_evaluations, 0);
1107 assert_eq!(stats.failed_evaluations, 0);
1108 assert!(stats.best_score.is_none());
1109 }
1110
1111 #[test]
1112 fn test_efficiency_score_calculation() {
1113 let stats = SearchStatistics::<f32> {
1114 total_architectures_evaluated: 100,
1115 successful_evaluations: 80,
1116 total_search_time: Duration::from_secs(3600),
1117 ..Default::default()
1118 };
1119
1120 let efficiency = stats
1121 .calculate_efficiency_score()
1122 .expect("counts this small are representable in f32");
1123 assert!(efficiency > 0.0);
1124 assert!(efficiency <= 1.0);
1125
1126 let empty = SearchStatistics::<f32>::default();
1128 assert_eq!(
1129 empty
1130 .calculate_efficiency_score()
1131 .expect("an empty search has a defined score"),
1132 0.0
1133 );
1134
1135 let brief = SearchStatistics::<f32> {
1138 total_architectures_evaluated: 4,
1139 successful_evaluations: 4,
1140 total_search_time: Duration::from_millis(120),
1141 ..Default::default()
1142 };
1143 assert!(
1144 (brief
1145 .calculate_efficiency_score()
1146 .expect("a sub-second search has a defined score")
1147 - 0.5)
1148 .abs()
1149 < 1e-6
1150 );
1151 }
1152
1153 #[test]
1154 fn test_convergence_detection() {
1155 let stats = SearchStatistics::<f32> {
1156 score_history: vec![0.5, 0.6, 0.65, 0.66, 0.67, 0.67, 0.67, 0.68],
1157 ..Default::default()
1158 };
1159
1160 let converged = stats.has_converged(5, 0.05);
1161 assert!(converged);
1162
1163 let not_converged = stats.has_converged(5, 0.001);
1164 assert!(!not_converged);
1165 }
1166
1167 #[test]
1168 fn test_improvement_trend() {
1169 let stats = SearchStatistics::<f32> {
1170 score_history: vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8],
1171 ..Default::default()
1172 };
1173
1174 let trend = stats.get_improvement_trend(2);
1175 assert!(trend.is_some());
1176 assert!(trend.expect("unwrap failed") > 0.0); }
1178
1179 #[test]
1180 fn test_resource_usage_calculations() {
1181 let usage = ResourceUsage {
1182 memory_gb: 10.0,
1183 cpu_time_seconds: 3600.0,
1184 gpu_time_seconds: 1800.0,
1185 energy_kwh: 5.0,
1186 network_io_gb: 1.0,
1187 disk_io_gb: 2.0,
1188 peak_memory_gb: 12.0,
1189 efficiency_score: 0.8,
1190 cost_usd: 0.0,
1191 network_gb: 1.0,
1192 };
1193
1194 let costs = ResourceCosts {
1195 memory_cost_per_gb: 0.1,
1196 cpu_cost_per_second: 0.001,
1197 gpu_cost_per_second: 0.01,
1198 energy_cost_per_kwh: 0.2,
1199 };
1200
1201 let total_cost = usage.total_cost(&costs);
1202 assert!(total_cost > 0.0);
1203
1204 let limits = ResourceLimits {
1205 max_memory_gb: 8.0,
1206 max_cpu_seconds: 7200.0,
1207 max_gpu_seconds: 3600.0,
1208 max_energy_kwh: 10.0,
1209 };
1210
1211 let exceeds = usage.exceeds_limits(&limits);
1212 assert!(exceeds); }
1214
1215 #[test]
1216 fn test_architecture_encoding() {
1217 let encoding = ArchitectureEncoding::default();
1218 assert_eq!(encoding.hash, 0);
1219 assert!(matches!(encoding.encoding_type, EncodingType::String));
1220 }
1221}