use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CostPerformancePoint {
pub id: String,
pub performance: f64,
pub cost: f64,
pub energy_joules: f64,
pub latency_ms: Option<f64>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CostPerformanceBenchmark {
pub name: String,
pub points: Vec<CostPerformancePoint>,
pareto_frontier: Option<Vec<usize>>,
}
impl CostPerformanceBenchmark {
pub fn new(name: impl Into<String>) -> Self {
Self { name: name.into(), points: Vec::new(), pareto_frontier: None }
}
pub fn add_point(&mut self, point: CostPerformancePoint) {
self.points.push(point);
self.pareto_frontier = None; }
pub fn compute_pareto_frontier(&mut self) -> &[usize] {
if let Some(ref frontier) = self.pareto_frontier {
return frontier;
}
let mut frontier = Vec::new();
for (i, point) in self.points.iter().enumerate() {
let mut is_dominated = false;
for (j, other) in self.points.iter().enumerate() {
if i == j {
continue;
}
let other_better_perf = other.performance >= point.performance;
let other_better_cost = other.cost <= point.cost;
let other_strictly_better =
other.performance > point.performance || other.cost < point.cost;
if other_better_perf && other_better_cost && other_strictly_better {
is_dominated = true;
break;
}
}
if !is_dominated {
frontier.push(i);
}
}
frontier.sort_by(|&a, &b| {
self.points[b]
.performance
.partial_cmp(&self.points[a].performance)
.unwrap_or(std::cmp::Ordering::Equal)
});
self.pareto_frontier = Some(frontier);
self.pareto_frontier.as_ref().expect("just assigned Some above")
}
pub fn pareto_optimal_points(&mut self) -> Vec<&CostPerformancePoint> {
let frontier = self.compute_pareto_frontier().to_vec();
frontier.iter().map(|&i| &self.points[i]).collect()
}
pub fn best_within_budget(&mut self, max_cost: f64) -> Option<&CostPerformancePoint> {
self.compute_pareto_frontier();
self.pareto_frontier
.as_ref()
.expect("compute_pareto_frontier ensures Some")
.iter()
.map(|&i| &self.points[i])
.filter(|p| p.cost <= max_cost)
.max_by(|a, b| {
a.performance.partial_cmp(&b.performance).unwrap_or(std::cmp::Ordering::Equal)
})
}
pub fn efficiency_scores(&self) -> Vec<(usize, f64)> {
self.points
.iter()
.enumerate()
.map(|(i, p)| {
let efficiency = if p.cost > 0.0 { p.performance / p.cost } else { f64::INFINITY };
(i, efficiency)
})
.collect()
}
}