use scirs2_core::numeric::Float;
use std::collections::HashMap;
use std::fmt::Debug;
use std::time::Instant;
use super::benchmark::{BenchmarkSuite, TestResult};
use super::cache::EvaluationCache;
use super::predictor::PerformancePredictor;
use super::resource::ResourceMonitor;
use super::statistical::StatisticalAnalyzer;
use crate::error::Result;
use crate::nas_engine::results::EvaluationResults;
use crate::{EvaluationConfig, EvaluationMetric, OptimizerArchitecture};
pub struct PerformanceEvaluator<T: Float + Debug + Send + Sync + 'static> {
config: EvaluationConfig,
benchmark_suite: BenchmarkSuite<T>,
predictor: Option<PerformancePredictor<T>>,
evaluation_cache: EvaluationCache<T>,
statistical_analyzer: StatisticalAnalyzer<T>,
resource_monitor: ResourceMonitor<T>,
}
impl<T: Float + Debug + Default + Clone + Send + Sync + std::fmt::Debug + std::iter::Sum>
PerformanceEvaluator<T>
{
pub fn new(config: EvaluationConfig) -> Result<Self> {
Ok(Self {
benchmark_suite: BenchmarkSuite::new()?,
predictor: None,
evaluation_cache: EvaluationCache::new(),
statistical_analyzer: StatisticalAnalyzer::new(),
resource_monitor: ResourceMonitor::new(),
config,
})
}
pub fn initialize(&mut self) -> Result<()> {
self.benchmark_suite.initialize(&self.config)?;
if self.config.performance_prediction {
self.predictor = Some(PerformancePredictor::new(&self.config)?);
}
self.resource_monitor.start_monitoring()?;
Ok(())
}
pub fn evaluate_architecture(
&mut self,
architecture: &OptimizerArchitecture,
) -> Result<EvaluationResults<T>> {
let start_time = Instant::now();
let cache_key = self.generate_cache_key(architecture);
if let Some(cached_result) = self.evaluation_cache.get(&cache_key) {
return Ok(cached_result.results.clone());
}
let benchmark_results = self.benchmark_suite.run_benchmarks(architecture)?;
let mut metric_scores = HashMap::new();
let overall_score = self.aggregate_benchmark_scores(&benchmark_results)?;
metric_scores.insert(EvaluationMetric::FinalPerformance, overall_score);
let convergence_speed = self.compute_convergence_speed(&benchmark_results)?;
metric_scores.insert(EvaluationMetric::ConvergenceSpeed, convergence_speed);
let stability = self.compute_stability(&benchmark_results)?;
metric_scores.insert(EvaluationMetric::TrainingStability, stability);
let memory_efficiency = self.compute_memory_efficiency(&benchmark_results)?;
let computational_efficiency = self.compute_computational_efficiency(&benchmark_results)?;
metric_scores.insert(EvaluationMetric::MemoryEfficiency, memory_efficiency);
metric_scores.insert(
EvaluationMetric::ComputationalEfficiency,
computational_efficiency,
);
let confidence_intervals = self
.statistical_analyzer
.compute_confidence_intervals(&benchmark_results)?;
let evaluation_time = start_time.elapsed();
let results = EvaluationResults {
metric_scores,
overall_score,
confidence_intervals,
evaluation_time,
success: true,
error_message: None,
cv_results: None,
benchmark_results: std::collections::HashMap::new(),
training_trajectory: Vec::new(),
};
self.evaluation_cache.insert(cache_key, results.clone());
Ok(results)
}
fn generate_cache_key(&self, architecture: &OptimizerArchitecture) -> String {
format!("arch_{}", architecture.structure.len())
}
fn aggregate_benchmark_scores(&self, results: &[TestResult<T>]) -> Result<T> {
if results.is_empty() {
return Ok(T::zero());
}
let sum: T = results.iter().map(|r| r.normalized_score).sum();
Ok(sum / T::from(results.len()).expect("conversion failed"))
}
fn compute_convergence_speed(&self, results: &[TestResult<T>]) -> Result<T> {
let avg_time: f64 = results
.iter()
.map(|r| r.execution_time.as_secs_f64())
.sum::<f64>()
/ results.len() as f64;
Ok(T::from(1.0 / (avg_time + 1e-6)).expect("conversion failed"))
}
fn compute_stability(&self, results: &[TestResult<T>]) -> Result<T> {
if results.len() < 2 {
return Ok(T::one());
}
let scores: Vec<T> = results.iter().map(|r| r.score).collect();
let mean =
scores.iter().cloned().sum::<T>() / T::from(scores.len()).expect("conversion failed");
let variance = scores.iter().map(|&s| (s - mean) * (s - mean)).sum::<T>()
/ T::from(scores.len()).expect("conversion failed");
let std_dev = variance.sqrt();
let cv = std_dev
/ mean
.abs()
.max(scirs2_core::numeric::NumCast::from(1e-6).unwrap_or_else(|| T::zero()));
Ok(
T::one()
/ (cv + scirs2_core::numeric::NumCast::from(1e-6).unwrap_or_else(|| T::zero())),
)
}
fn compute_memory_efficiency(&self, results: &[TestResult<T>]) -> Result<T> {
let avg_memory = results
.iter()
.map(|r| r.resource_usage.memory_gb)
.sum::<f64>()
/ results.len() as f64;
let efficiency = 1.0 / (avg_memory + 1e-6);
Ok(scirs2_core::numeric::NumCast::from(efficiency).unwrap_or_else(|| T::zero()))
}
fn compute_computational_efficiency(&self, results: &[TestResult<T>]) -> Result<T> {
let avg_cpu_time = results
.iter()
.map(|r| r.resource_usage.cpu_time_seconds)
.sum::<f64>()
/ results.len() as f64;
let efficiency = 1.0 / (avg_cpu_time + 1e-6);
Ok(scirs2_core::numeric::NumCast::from(efficiency).unwrap_or_else(|| T::zero()))
}
pub fn cache(&self) -> &EvaluationCache<T> {
&self.evaluation_cache
}
pub fn analyzer(&self) -> &StatisticalAnalyzer<T> {
&self.statistical_analyzer
}
pub fn resource_monitor(&self) -> &ResourceMonitor<T> {
&self.resource_monitor
}
pub fn config(&self) -> &EvaluationConfig {
&self.config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_performance_evaluator_creation() {
}
}