Skip to main content

quantrs2_device/unified_benchmarking/
optimization.rs

1//! Optimization engine for performance and cost optimization
2
3use serde::{Deserialize, Serialize};
4use std::collections::{HashMap, VecDeque};
5use std::time::SystemTime;
6
7use super::results::{OptimizationResult, UnifiedBenchmarkResult};
8
9/// Optimization engine for performance and cost optimization
10pub struct OptimizationEngine {
11    objective_functions: HashMap<String, Box<dyn Fn(&UnifiedBenchmarkResult) -> f64 + Send + Sync>>,
12    optimization_history: VecDeque<OptimizationResult>,
13    current_strategy: OptimizationStrategy,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct OptimizationStrategy {
18    pub strategy_name: String,
19    pub parameters: HashMap<String, f64>,
20    pub last_updated: SystemTime,
21    pub effectiveness_score: f64,
22}
23
24impl Default for OptimizationEngine {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl OptimizationEngine {
31    pub fn new() -> Self {
32        Self {
33            objective_functions: HashMap::new(),
34            optimization_history: VecDeque::new(),
35            current_strategy: OptimizationStrategy {
36                strategy_name: "default".to_string(),
37                parameters: HashMap::new(),
38                last_updated: SystemTime::now(),
39                effectiveness_score: 0.0,
40            },
41        }
42    }
43
44    /// Register an objective function that maps a benchmark result to a scalar score.
45    ///
46    /// The function should return larger values for *better* configurations.
47    pub fn register_objective<F>(&mut self, name: impl Into<String>, f: F)
48    where
49        F: Fn(&UnifiedBenchmarkResult) -> f64 + Send + Sync + 'static,
50    {
51        self.objective_functions.insert(name.into(), Box::new(f));
52    }
53
54    /// Run one optimisation cycle over the supplied benchmark result.
55    ///
56    /// For each registered objective function the engine evaluates the current
57    /// result, compares the score against the best score seen in the history,
58    /// and records an `OptimizationResult`.  The overall strategy effectiveness
59    /// is updated as the mean improvement across all objectives.
60    ///
61    /// Returns the list of `OptimizationResult` values produced this cycle.
62    pub fn optimize(&mut self, result: &UnifiedBenchmarkResult) -> Vec<OptimizationResult> {
63        let mut new_results: Vec<OptimizationResult> = Vec::new();
64
65        let start = std::time::Instant::now();
66
67        for (name, func) in &self.objective_functions {
68            let score = func(result);
69
70            // Find the best historical score for this objective.
71            let best_historical = self
72                .optimization_history
73                .iter()
74                .filter(|r| r.objective_function == *name)
75                .map(|r| r.optimal_value)
76                .fold(f64::NEG_INFINITY, f64::max);
77
78            let improvement = if best_historical.is_finite() {
79                score - best_historical
80            } else {
81                0.0
82            };
83
84            let opt_result = OptimizationResult {
85                objective_function: name.clone(),
86                optimal_solution: vec![score],
87                optimal_value: score,
88                convergence_history: vec![score],
89                optimization_time: start.elapsed(),
90            };
91            new_results.push(opt_result.clone());
92            self.optimization_history.push_back(opt_result);
93        }
94
95        // Keep history bounded.
96        while self.optimization_history.len() > 10_000 {
97            self.optimization_history.pop_front();
98        }
99
100        // Update effectiveness score: mean improvement this cycle.
101        let n = new_results.len();
102        if n > 0 {
103            let total_improvement: f64 = new_results.iter().map(|r| r.optimal_value).sum();
104            self.current_strategy.effectiveness_score = total_improvement / n as f64;
105            self.current_strategy.last_updated = SystemTime::now();
106        }
107
108        new_results
109    }
110
111    /// Return a summary of the current optimisation strategy.
112    pub fn current_strategy(&self) -> &OptimizationStrategy {
113        &self.current_strategy
114    }
115
116    /// Return the full optimisation history.
117    pub fn history(&self) -> &VecDeque<OptimizationResult> {
118        &self.optimization_history
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_new_engine_has_empty_history() {
128        let engine = OptimizationEngine::new();
129        assert!(
130            engine.history().is_empty(),
131            "new engine must have empty history"
132        );
133    }
134
135    #[test]
136    fn test_default_engine_matches_new() {
137        let e1 = OptimizationEngine::new();
138        let e2 = OptimizationEngine::default();
139        assert_eq!(
140            e1.current_strategy().strategy_name,
141            e2.current_strategy().strategy_name,
142        );
143    }
144
145    #[test]
146    fn test_register_objective_increases_objective_count() {
147        let mut engine = OptimizationEngine::new();
148        assert_eq!(engine.objective_functions.len(), 0);
149        engine.register_objective("test_obj", |_| 1.0);
150        assert_eq!(engine.objective_functions.len(), 1);
151    }
152
153    #[test]
154    fn test_current_strategy_initial_effectiveness() {
155        let engine = OptimizationEngine::new();
156        assert_eq!(
157            engine.current_strategy().effectiveness_score,
158            0.0,
159            "initial effectiveness_score must be 0"
160        );
161    }
162}