use crate::error::Result;
use crate::multi_objective::ParetoFront;
use crate::numeric::{count_as, scalar_as};
use crate::EvaluationMetric;
use scirs2_core::numeric::Float;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt::Debug;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptimizerArchitecture<T: Float + Debug + Send + Sync + 'static> {
pub components: Vec<String>,
pub parameters: HashMap<String, T>,
pub connections: Vec<(usize, usize)>,
pub metadata: HashMap<String, String>,
pub hyperparameters: HashMap<String, T>,
pub architecture_id: String,
}
#[derive(Debug, Clone)]
pub struct OptimizerComponent<T: Float + Debug + Send + Sync + 'static> {
pub name: String,
pub parameters: HashMap<String, T>,
}
#[derive(Debug, Clone)]
pub struct ConvergenceData<T: Float + Debug + Send + Sync + 'static> {
pub iteration: usize,
pub best_score: T,
pub convergence_rate: T,
pub stability_measure: T,
pub best_scores_over_time: Vec<T>,
pub diversity_over_time: Vec<T>,
pub convergence_generation: usize,
pub final_diversity: T,
}
#[derive(Debug, Clone)]
pub struct SearchResult<T: Float + Debug + Send + Sync + 'static> {
pub architecture: OptimizerArchitecture<T>,
pub evaluation_results: EvaluationResults<T>,
pub generation: usize,
pub search_time: f64,
pub resource_usage: ResourceUsage<T>,
pub encoding: ArchitectureEncoding,
pub metadata: SearchResultMetadata,
}
#[derive(Debug, Clone)]
pub struct EvaluationResults<T: Float + Debug + Send + Sync + 'static> {
pub metric_scores: HashMap<EvaluationMetric, T>,
pub overall_score: T,
pub confidence_intervals: HashMap<EvaluationMetric, (T, T)>,
pub evaluation_time: Duration,
pub success: bool,
pub error_message: Option<String>,
pub cv_results: Option<CrossValidationResults<T>>,
pub benchmark_results: HashMap<String, BenchmarkResult<T>>,
pub training_trajectory: Vec<TrainingSnapshot<T>>,
}
#[derive(Debug, Clone)]
pub struct CrossValidationResults<T: Float + Debug + Send + Sync + 'static> {
pub fold_results: Vec<T>,
pub mean_score: T,
pub std_score: T,
pub confidence_interval: (T, T),
pub statistical_tests: HashMap<String, StatisticalTestResult<T>>,
}
#[derive(Debug, Clone)]
pub struct StatisticalTestResult<T: Float + Debug + Send + Sync + 'static> {
pub test_statistic: T,
pub p_value: T,
pub critical_value: T,
pub reject_null: bool,
pub effect_size: Option<T>,
}
#[derive(Debug, Clone)]
pub struct BenchmarkResult<T: Float + Debug + Send + Sync + 'static> {
pub benchmark_name: String,
pub primary_score: T,
pub all_scores: HashMap<EvaluationMetric, T>,
pub evaluation_time: Duration,
pub peak_memory_usage: usize,
pub success: bool,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct TrainingSnapshot<T: Float + Debug + Send + Sync + 'static> {
pub epoch: usize,
pub training_loss: T,
pub validation_loss: Option<T>,
pub training_metrics: HashMap<EvaluationMetric, T>,
pub validation_metrics: HashMap<EvaluationMetric, T>,
pub learning_rate: T,
pub timestamp: Duration,
pub memory_usage: usize,
}
#[derive(Debug, Clone)]
pub struct ResourceUsage<T: Float + Debug + Send + Sync + 'static> {
pub memory_gb: T,
pub cpu_time_seconds: T,
pub gpu_time_seconds: T,
pub energy_kwh: T,
pub network_io_gb: T,
pub disk_io_gb: T,
pub peak_memory_gb: T,
pub efficiency_score: T,
pub cost_usd: T,
pub network_gb: T,
}
#[derive(Debug, Clone)]
pub struct ArchitectureEncoding {
pub encoding_type: EncodingType,
pub binary_encoding: Option<Vec<u8>>,
pub string_encoding: Option<String>,
pub graph_encoding: Option<GraphEncoding>,
pub hash: u64,
pub metadata: EncodingMetadata,
}
#[derive(Debug, Clone, Copy)]
pub enum EncodingType {
Binary,
String,
Graph,
Neural,
Hybrid,
}
#[derive(Debug, Clone)]
pub struct GraphEncoding {
pub nodes: Vec<NodeEncoding>,
pub edges: Vec<EdgeEncoding>,
pub properties: GraphProperties,
}
#[derive(Debug, Clone)]
pub struct NodeEncoding {
pub id: usize,
pub component_type: String,
pub parameters: HashMap<String, f64>,
pub properties: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct EdgeEncoding {
pub source: usize,
pub target: usize,
pub connection_type: String,
pub weight: f64,
pub properties: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct GraphProperties {
pub num_nodes: usize,
pub num_edges: usize,
pub density: f64,
pub average_degree: f64,
pub is_directed: bool,
pub has_cycles: bool,
}
#[derive(Debug, Clone)]
pub struct EncodingMetadata {
pub version: String,
pub created_at: Instant,
pub size_bytes: usize,
pub compression_ratio: Option<f64>,
pub quality_score: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct SearchResultMetadata {
pub search_strategy: String,
pub controller_type: String,
pub evaluation_method: String,
pub search_phase: Option<String>,
pub parent_architectures: Vec<u64>,
pub operation_info: Option<OperationInfo>,
pub tags: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct OperationInfo {
pub operation_type: String,
pub parameters: HashMap<String, f64>,
pub success: bool,
pub operation_time: Duration,
}
#[derive(Debug, Clone)]
pub struct SearchStatistics<T: Float + Debug + Send + Sync + 'static> {
pub total_architectures_evaluated: usize,
pub total_evaluations: usize,
pub current_generation: usize,
pub population_diversity: T,
pub average_score: T,
pub successful_evaluations: usize,
pub failed_evaluations: usize,
pub best_score: Option<T>,
pub best_architecture_hash: Option<u64>,
pub average_evaluation_time: Duration,
pub total_search_time: Duration,
pub score_history: Vec<T>,
pub resource_stats: ResourceStatistics<T>,
pub convergence_metrics: ConvergenceMetrics<T>,
pub diversity_metrics: DiversityMetrics<T>,
pub efficiency_metrics: EfficiencyMetrics<T>,
}
#[derive(Debug, Clone)]
pub struct ResourceStatistics<T: Float + Debug + Send + Sync + 'static> {
pub total_memory_gb: T,
pub total_cpu_hours: T,
pub total_gpu_hours: T,
pub total_energy_kwh: T,
pub peak_memory_gb: T,
pub average_utilization: T,
pub efficiency_score: T,
}
#[derive(Debug, Clone)]
pub struct ConvergenceMetrics<T: Float + Debug + Send + Sync + 'static> {
pub generations_to_convergence: Option<usize>,
pub convergence_rate: Option<T>,
pub plateau_generations: usize,
pub recent_score_variance: T,
pub improvement_rate: T,
pub is_stagnating: bool,
}
#[derive(Debug, Clone)]
pub struct DiversityMetrics<T: Float + Debug + Send + Sync + 'static> {
pub population_diversity: T,
pub uniqueness_ratio: T,
pub genotypic_diversity: T,
pub phenotypic_diversity: T,
pub diversity_history: Vec<T>,
pub diversity_maintenance: T,
}
#[derive(Debug, Clone)]
pub struct EfficiencyMetrics<T: Float + Debug + Send + Sync + 'static> {
pub evaluations_per_improvement: T,
pub time_to_best: Duration,
pub resource_cost_per_improvement: T,
pub efficiency_score: T,
pub pareto_efficiency: Option<T>,
pub hypervolume: Option<T>,
}
#[derive(Debug, Clone)]
pub struct SearchResults<T: Float + Debug + Send + Sync + 'static> {
pub best_architectures: Vec<SearchResult<T>>,
pub pareto_front: Option<ParetoFront<T>>,
pub search_history: Vec<SearchResult<T>>,
pub search_statistics: SearchStatistics<T>,
pub evaluation_summary: EvaluationSummary<T>,
pub search_configuration: SearchConfigSummary,
pub recommendations: Vec<SearchRecommendation>,
pub resource_usage_summary: ResourceUsageSummary<T>,
pub search_time_seconds: f64,
pub convergence_data: ConvergenceData<T>,
pub config: SearchConfigSummary,
}
#[derive(Debug, Clone)]
pub struct ResourceUsageSummary<T: Float + Debug + Send + Sync + 'static> {
pub total_memory_gb: T,
pub total_cpu_hours: T,
pub total_gpu_hours: T,
pub total_energy_kwh: T,
pub total_cost_usd: T,
pub average_efficiency: T,
}
#[derive(Debug, Clone)]
pub struct EvaluationSummary<T: Float + Debug + Send + Sync + 'static> {
pub total_evaluations: usize,
pub success_rate: T,
pub best_score: T,
pub score_statistics: ScoreStatistics<T>,
pub benchmark_summary: HashMap<String, BenchmarkSummary<T>>,
pub resource_summary: ResourceSummary<T>,
}
#[derive(Debug, Clone)]
pub struct ScoreStatistics<T: Float + Debug + Send + Sync + 'static> {
pub mean: T,
pub median: T,
pub std_dev: T,
pub min: T,
pub max: T,
pub percentiles: HashMap<usize, T>,
pub histogram: Vec<(T, usize)>,
}
#[derive(Debug, Clone)]
pub struct BenchmarkSummary<T: Float + Debug + Send + Sync + 'static> {
pub benchmark_name: String,
pub best_score: T,
pub average_score: T,
pub successful_evaluations: usize,
pub time_statistics: TimeStatistics,
pub improvement_trajectory: Vec<T>,
}
#[derive(Debug, Clone)]
pub struct TimeStatistics {
pub mean: Duration,
pub median: Duration,
pub std_dev: Duration,
pub min: Duration,
pub max: Duration,
}
#[derive(Debug, Clone)]
pub struct ResourceSummary<T: Float + Debug + Send + Sync + 'static> {
pub total_consumption: ResourceUsage<T>,
pub average_per_evaluation: ResourceUsage<T>,
pub peak_usage: ResourceUsage<T>,
pub efficiency_metrics: ResourceEfficiencyMetrics<T>,
}
#[derive(Debug, Clone)]
pub struct ResourceEfficiencyMetrics<T: Float + Debug + Send + Sync + 'static> {
pub memory_efficiency: T,
pub cpu_efficiency: T,
pub gpu_efficiency: T,
pub energy_efficiency: T,
pub overall_efficiency: T,
}
#[derive(Debug, Clone)]
pub struct SearchConfigSummary {
pub search_strategy: String,
pub population_size: usize,
pub search_budget: usize,
pub evaluation_config: String,
pub multi_objective_config: Option<String>,
pub resource_constraints: String,
pub key_hyperparameters: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct SearchRecommendation {
pub recommendation_type: RecommendationType,
pub description: String,
pub priority: RecommendationPriority,
pub expected_improvement: Option<f64>,
pub implementation_effort: ImplementationEffort,
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Copy)]
pub enum RecommendationType {
SearchStrategyChange,
PopulationSizeAdjustment,
BudgetReallocation,
EvaluationMethodImprovement,
ResourceOptimization,
HyperparameterTuning,
SearchSpaceModification,
DiversityImprovement,
ConvergenceAcceleration,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RecommendationPriority {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Copy)]
pub enum ImplementationEffort {
Minimal,
Low,
Medium,
High,
Extensive,
}
impl<T: Float + Debug + Send + Sync + 'static> Default for SearchStatistics<T> {
fn default() -> Self {
Self {
total_architectures_evaluated: 0,
total_evaluations: 0,
current_generation: 0,
population_diversity: T::zero(),
average_score: T::zero(),
successful_evaluations: 0,
failed_evaluations: 0,
best_score: None,
best_architecture_hash: None,
average_evaluation_time: Duration::from_secs(0),
total_search_time: Duration::from_secs(0),
score_history: Vec::new(),
resource_stats: ResourceStatistics::default(),
convergence_metrics: ConvergenceMetrics::default(),
diversity_metrics: DiversityMetrics::default(),
efficiency_metrics: EfficiencyMetrics::default(),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceStatistics<T> {
fn default() -> Self {
Self {
total_memory_gb: T::zero(),
total_cpu_hours: T::zero(),
total_gpu_hours: T::zero(),
total_energy_kwh: T::zero(),
peak_memory_gb: T::zero(),
average_utilization: T::zero(),
efficiency_score: T::zero(),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for ConvergenceMetrics<T> {
fn default() -> Self {
Self {
generations_to_convergence: None,
convergence_rate: None,
plateau_generations: 0,
recent_score_variance: T::zero(),
improvement_rate: T::zero(),
is_stagnating: false,
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for DiversityMetrics<T> {
fn default() -> Self {
Self {
population_diversity: T::one(),
uniqueness_ratio: T::one(),
genotypic_diversity: T::one(),
phenotypic_diversity: T::one(),
diversity_history: Vec::new(),
diversity_maintenance: T::one(),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for EfficiencyMetrics<T> {
fn default() -> Self {
Self {
evaluations_per_improvement: T::one(),
time_to_best: Duration::from_secs(0),
resource_cost_per_improvement: T::zero(),
efficiency_score: T::zero(),
pareto_efficiency: None,
hypervolume: None,
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceUsage<T> {
fn default() -> Self {
Self {
memory_gb: T::zero(),
cpu_time_seconds: T::zero(),
gpu_time_seconds: T::zero(),
energy_kwh: T::zero(),
network_io_gb: T::zero(),
disk_io_gb: T::zero(),
peak_memory_gb: T::zero(),
efficiency_score: T::zero(),
cost_usd: T::zero(),
network_gb: T::zero(),
}
}
}
impl Default for ArchitectureEncoding {
fn default() -> Self {
Self {
encoding_type: EncodingType::String,
binary_encoding: None,
string_encoding: None,
graph_encoding: None,
hash: 0,
metadata: EncodingMetadata::default(),
}
}
}
impl Default for EncodingMetadata {
fn default() -> Self {
Self {
version: "1.0".to_string(),
created_at: Instant::now(),
size_bytes: 0,
compression_ratio: None,
quality_score: None,
}
}
}
impl Default for SearchResultMetadata {
fn default() -> Self {
Self {
search_strategy: "Unknown".to_string(),
controller_type: "Unknown".to_string(),
evaluation_method: "Unknown".to_string(),
search_phase: None,
parent_architectures: Vec::new(),
operation_info: None,
tags: HashMap::new(),
}
}
}
impl Default for GraphProperties {
fn default() -> Self {
Self {
num_nodes: 0,
num_edges: 0,
density: 0.0,
average_degree: 0.0,
is_directed: true,
has_cycles: false,
}
}
}
impl Default for TimeStatistics {
fn default() -> Self {
Self {
mean: Duration::from_secs(0),
median: Duration::from_secs(0),
std_dev: Duration::from_secs(0),
min: Duration::from_secs(0),
max: Duration::from_secs(0),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> SearchStatistics<T> {
pub fn calculate_efficiency_score(&self) -> Result<T> {
if self.total_architectures_evaluated == 0 {
return Ok(T::zero());
}
let successes: T = count_as(self.successful_evaluations, "successful evaluations")?;
let attempts: T = count_as(
self.total_architectures_evaluated,
"evaluated architectures",
)?;
let success_rate = successes / attempts;
let elapsed_seconds = self.total_search_time.as_secs();
let time_efficiency = if elapsed_seconds > 0 {
let seconds: T = count_as(elapsed_seconds as usize, "elapsed search seconds")?;
successes / seconds
} else {
T::zero()
};
let two: T = scalar_as(2.0, "efficiency averaging divisor")?;
Ok((success_rate + time_efficiency) / two)
}
pub fn has_converged(&self, patience: usize, min_improvement: T) -> bool {
if self.score_history.len() < patience {
return false;
}
let recent_scores = &self.score_history[self.score_history.len() - patience..];
let max_score = recent_scores
.iter()
.fold(T::neg_infinity(), |a, &b| a.max(b));
let min_score = recent_scores.iter().fold(T::infinity(), |a, &b| a.min(b));
max_score - min_score < min_improvement
}
pub fn get_improvement_trend(&self, window_size: usize) -> Option<T> {
if self.score_history.len() < window_size * 2 {
return None;
}
let recent_scores = &self.score_history[self.score_history.len() - window_size..];
let older_scores = &self.score_history
[self.score_history.len() - window_size * 2..self.score_history.len() - window_size];
let recent_mean = recent_scores.iter().fold(T::zero(), |a, &b| a + b)
/ scirs2_core::numeric::NumCast::from(window_size).unwrap_or_else(|| T::zero());
let older_mean = older_scores.iter().fold(T::zero(), |a, &b| a + b)
/ scirs2_core::numeric::NumCast::from(window_size).unwrap_or_else(|| T::zero());
Some(recent_mean - older_mean)
}
}
impl<T: Float + Debug + Send + Sync + 'static> ResourceUsage<T> {
pub fn total_cost(&self, costs: &ResourceCosts<T>) -> T {
self.memory_gb * costs.memory_cost_per_gb
+ self.cpu_time_seconds * costs.cpu_cost_per_second
+ self.gpu_time_seconds * costs.gpu_cost_per_second
+ self.energy_kwh * costs.energy_cost_per_kwh
}
pub fn exceeds_limits(&self, limits: &ResourceLimits<T>) -> bool {
self.memory_gb > limits.max_memory_gb
|| self.cpu_time_seconds > limits.max_cpu_seconds
|| self.gpu_time_seconds > limits.max_gpu_seconds
|| self.energy_kwh > limits.max_energy_kwh
}
}
#[derive(Debug, Clone)]
pub struct ResourceCosts<T: Float + Debug + Send + Sync + 'static> {
pub memory_cost_per_gb: T,
pub cpu_cost_per_second: T,
pub gpu_cost_per_second: T,
pub energy_cost_per_kwh: T,
}
#[derive(Debug, Clone)]
pub struct ResourceLimits<T: Float + Debug + Send + Sync + 'static> {
pub max_memory_gb: T,
pub max_cpu_seconds: T,
pub max_gpu_seconds: T,
pub max_energy_kwh: T,
}
impl<T: Float + Debug + Send + Sync + 'static> Default for ScoreStatistics<T> {
fn default() -> Self {
Self {
mean: T::zero(),
median: T::zero(),
std_dev: T::zero(),
min: T::zero(),
max: T::zero(),
percentiles: HashMap::new(),
histogram: Vec::new(),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceSummary<T> {
fn default() -> Self {
Self {
total_consumption: ResourceUsage::default(),
average_per_evaluation: ResourceUsage::default(),
peak_usage: ResourceUsage::default(),
efficiency_metrics: ResourceEfficiencyMetrics::default(),
}
}
}
impl<T: Float + Debug + Send + Sync + 'static> Default for ResourceEfficiencyMetrics<T> {
fn default() -> Self {
Self {
cpu_efficiency: T::zero(),
gpu_efficiency: T::zero(),
memory_efficiency: T::zero(),
energy_efficiency: T::zero(),
overall_efficiency: T::zero(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_search_statistics_default() {
let stats = SearchStatistics::<f32>::default();
assert_eq!(stats.total_architectures_evaluated, 0);
assert_eq!(stats.successful_evaluations, 0);
assert_eq!(stats.failed_evaluations, 0);
assert!(stats.best_score.is_none());
}
#[test]
fn test_efficiency_score_calculation() {
let stats = SearchStatistics::<f32> {
total_architectures_evaluated: 100,
successful_evaluations: 80,
total_search_time: Duration::from_secs(3600),
..Default::default()
};
let efficiency = stats
.calculate_efficiency_score()
.expect("counts this small are representable in f32");
assert!(efficiency > 0.0);
assert!(efficiency <= 1.0);
let empty = SearchStatistics::<f32>::default();
assert_eq!(
empty
.calculate_efficiency_score()
.expect("an empty search has a defined score"),
0.0
);
let brief = SearchStatistics::<f32> {
total_architectures_evaluated: 4,
successful_evaluations: 4,
total_search_time: Duration::from_millis(120),
..Default::default()
};
assert!(
(brief
.calculate_efficiency_score()
.expect("a sub-second search has a defined score")
- 0.5)
.abs()
< 1e-6
);
}
#[test]
fn test_convergence_detection() {
let stats = SearchStatistics::<f32> {
score_history: vec![0.5, 0.6, 0.65, 0.66, 0.67, 0.67, 0.67, 0.68],
..Default::default()
};
let converged = stats.has_converged(5, 0.05);
assert!(converged);
let not_converged = stats.has_converged(5, 0.001);
assert!(!not_converged);
}
#[test]
fn test_improvement_trend() {
let stats = SearchStatistics::<f32> {
score_history: vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8],
..Default::default()
};
let trend = stats.get_improvement_trend(2);
assert!(trend.is_some());
assert!(trend.expect("unwrap failed") > 0.0); }
#[test]
fn test_resource_usage_calculations() {
let usage = ResourceUsage {
memory_gb: 10.0,
cpu_time_seconds: 3600.0,
gpu_time_seconds: 1800.0,
energy_kwh: 5.0,
network_io_gb: 1.0,
disk_io_gb: 2.0,
peak_memory_gb: 12.0,
efficiency_score: 0.8,
cost_usd: 0.0,
network_gb: 1.0,
};
let costs = ResourceCosts {
memory_cost_per_gb: 0.1,
cpu_cost_per_second: 0.001,
gpu_cost_per_second: 0.01,
energy_cost_per_kwh: 0.2,
};
let total_cost = usage.total_cost(&costs);
assert!(total_cost > 0.0);
let limits = ResourceLimits {
max_memory_gb: 8.0,
max_cpu_seconds: 7200.0,
max_gpu_seconds: 3600.0,
max_energy_kwh: 10.0,
};
let exceeds = usage.exceeds_limits(&limits);
assert!(exceeds); }
#[test]
fn test_architecture_encoding() {
let encoding = ArchitectureEncoding::default();
assert_eq!(encoding.hash, 0);
assert!(matches!(encoding.encoding_type, EncodingType::String));
}
}