use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum MetricType {
ExecutionTime,
MemoryUsage,
SolutionQuality,
EnergyEfficiency,
Throughput,
Scalability,
CacheEfficiency,
ConvergenceRate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BenchmarkMetrics {
pub problem_size: usize,
pub problem_density: f64,
pub timings: TimingMetrics,
pub memory: MemoryMetrics,
pub quality: QualityMetrics,
pub utilization: UtilizationMetrics,
pub custom: HashMap<String, f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimingMetrics {
pub total_time: Duration,
pub setup_time: Duration,
pub compute_time: Duration,
pub postprocess_time: Duration,
pub time_per_sample: Duration,
pub time_to_solution: Option<Duration>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MemoryMetrics {
pub peak_memory: usize,
pub avg_memory: usize,
pub allocated: usize,
pub deallocated: usize,
pub cache_misses: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QualityMetrics {
pub best_energy: f64,
pub avg_energy: f64,
pub energy_std: f64,
pub success_probability: f64,
pub time_to_target: Option<Duration>,
pub unique_solutions: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UtilizationMetrics {
pub cpu_usage: f64,
pub gpu_usage: Option<f64>,
pub memory_bandwidth: f64,
pub cache_hit_rate: Option<f64>,
pub power_consumption: Option<f64>,
}
impl BenchmarkMetrics {
pub fn new(problem_size: usize, problem_density: f64) -> Self {
Self {
problem_size,
problem_density,
timings: TimingMetrics::default(),
memory: MemoryMetrics::default(),
quality: QualityMetrics::default(),
utilization: UtilizationMetrics::default(),
custom: HashMap::new(),
}
}
pub fn calculate_efficiency(&self) -> EfficiencyMetrics {
EfficiencyMetrics {
samples_per_second: self.calculate_throughput(),
energy_per_sample: self.calculate_energy_efficiency(),
memory_efficiency: self.calculate_memory_efficiency(),
scalability_factor: self.calculate_scalability(),
}
}
fn calculate_throughput(&self) -> f64 {
if self.timings.total_time.as_secs_f64() > 0.0 {
1.0 / self.timings.time_per_sample.as_secs_f64()
} else {
0.0
}
}
fn calculate_energy_efficiency(&self) -> Option<f64> {
self.utilization
.power_consumption
.map(|power| power * self.timings.time_per_sample.as_secs_f64())
}
fn calculate_memory_efficiency(&self) -> f64 {
if self.memory.peak_memory > 0 {
(self.problem_size as f64) / (self.memory.peak_memory as f64)
} else {
0.0
}
}
fn calculate_scalability(&self) -> f64 {
let expected_time = self.problem_size as f64 * 1e-6; let actual_time = self.timings.compute_time.as_secs_f64();
if actual_time > 0.0 {
expected_time / actual_time
} else {
0.0
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EfficiencyMetrics {
pub samples_per_second: f64,
pub energy_per_sample: Option<f64>,
pub memory_efficiency: f64,
pub scalability_factor: f64,
}
impl Default for TimingMetrics {
fn default() -> Self {
Self {
total_time: Duration::ZERO,
setup_time: Duration::ZERO,
compute_time: Duration::ZERO,
postprocess_time: Duration::ZERO,
time_per_sample: Duration::ZERO,
time_to_solution: None,
}
}
}
impl Default for QualityMetrics {
fn default() -> Self {
Self {
best_energy: f64::INFINITY,
avg_energy: 0.0,
energy_std: 0.0,
success_probability: 0.0,
time_to_target: None,
unique_solutions: 0,
}
}
}
impl Default for UtilizationMetrics {
fn default() -> Self {
Self {
cpu_usage: 0.0,
gpu_usage: None,
memory_bandwidth: 0.0,
cache_hit_rate: None,
power_consumption: None,
}
}
}
pub mod aggregation {
use super::*;
pub fn aggregate_metrics(metrics: &[BenchmarkMetrics]) -> AggregatedMetrics {
if metrics.is_empty() {
return AggregatedMetrics::default();
}
let mut aggregated = AggregatedMetrics {
num_runs: metrics.len(),
problem_sizes: metrics.iter().map(|m| m.problem_size).collect(),
..Default::default()
};
let total_times: Vec<f64> = metrics
.iter()
.map(|m| m.timings.total_time.as_secs_f64())
.collect();
aggregated.avg_total_time =
Duration::from_secs_f64(total_times.iter().sum::<f64>() / total_times.len() as f64);
aggregated.min_total_time =
Duration::from_secs_f64(total_times.iter().copied().fold(f64::INFINITY, f64::min));
aggregated.max_total_time =
Duration::from_secs_f64(total_times.iter().copied().fold(0.0, f64::max));
aggregated.best_energy_overall = metrics
.iter()
.map(|m| m.quality.best_energy)
.fold(f64::INFINITY, f64::min);
aggregated.avg_success_rate = metrics
.iter()
.map(|m| m.quality.success_probability)
.sum::<f64>()
/ metrics.len() as f64;
aggregated.peak_memory_overall = metrics
.iter()
.map(|m| m.memory.peak_memory)
.max()
.unwrap_or(0);
aggregated
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct AggregatedMetrics {
pub num_runs: usize,
pub problem_sizes: Vec<usize>,
pub avg_total_time: Duration,
pub min_total_time: Duration,
pub max_total_time: Duration,
pub best_energy_overall: f64,
pub avg_success_rate: f64,
pub peak_memory_overall: usize,
}
}
pub mod statistics {
use super::*;
pub fn calculate_statistics(values: &[f64]) -> Statistics {
if values.is_empty() {
return Statistics::default();
}
let n = values.len() as f64;
let mean = values.iter().sum::<f64>() / n;
let variance = values.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (n - 1.0).max(1.0);
let std_dev = variance.sqrt();
let mut sorted = values.to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let median = if sorted.len() % 2 == 0 {
f64::midpoint(sorted[sorted.len() / 2 - 1], sorted[sorted.len() / 2])
} else {
sorted[sorted.len() / 2]
};
Statistics {
mean,
median,
std_dev,
min: sorted[0],
max: sorted[sorted.len() - 1],
percentile_25: percentile(&sorted, 0.25),
percentile_75: percentile(&sorted, 0.75),
percentile_95: percentile(&sorted, 0.95),
}
}
fn percentile(sorted: &[f64], p: f64) -> f64 {
let k = (sorted.len() as f64 - 1.0) * p;
let f = k.floor() as usize;
let c = f + 1;
if c >= sorted.len() {
sorted[sorted.len() - 1]
} else {
(k - f as f64).mul_add(sorted[c] - sorted[f], sorted[f])
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Statistics {
pub mean: f64,
pub median: f64,
pub std_dev: f64,
pub min: f64,
pub max: f64,
pub percentile_25: f64,
pub percentile_75: f64,
pub percentile_95: f64,
}
}