Skip to main content

quantrs2_anneal/universal_annealing_compiler/
execution.rs

1//! Execution result types.
2//!
3//! This module contains types for representing execution results,
4//! predictions, and metadata.
5
6use std::collections::HashMap;
7use std::time::{Duration, Instant};
8
9use super::compilation::CompilationResult;
10use super::config::{OptimizationLevel, ResourceAllocationStrategy, SchedulingPriority};
11use super::platform::QuantumPlatform;
12
13/// Universal execution result
14#[derive(Debug, Clone)]
15pub struct UniversalExecutionResult {
16    /// Problem identifier
17    pub problem_id: String,
18    /// Selected optimal platform
19    pub optimal_platform: QuantumPlatform,
20    /// Compilation results for all platforms
21    pub compilation_results: HashMap<QuantumPlatform, CompilationResult>,
22    /// Performance predictions
23    pub performance_predictions: HashMap<QuantumPlatform, PlatformPerformancePrediction>,
24    /// Execution result
25    pub execution_result: PlatformExecutionResult,
26    /// Total execution time
27    pub total_time: Duration,
28    /// Execution metadata
29    pub metadata: UniversalExecutionMetadata,
30}
31
32/// Platform performance prediction
33#[derive(Debug, Clone)]
34pub struct PlatformPerformancePrediction {
35    /// Target platform
36    pub platform: QuantumPlatform,
37    /// Predicted performance
38    pub predicted_performance: PredictedPerformance,
39    /// Confidence in prediction
40    pub confidence_score: f64,
41    /// Prediction metadata
42    pub prediction_metadata: PredictionMetadata,
43}
44
45/// Predicted performance
46#[derive(Debug, Clone)]
47pub struct PredictedPerformance {
48    /// Execution time
49    pub execution_time: Duration,
50    /// Solution quality
51    pub solution_quality: f64,
52    /// Success probability
53    pub success_probability: f64,
54    /// Cost
55    pub cost: f64,
56    /// Reliability score
57    pub reliability_score: f64,
58}
59
60/// Prediction metadata
61#[derive(Debug, Clone)]
62pub struct PredictionMetadata {
63    /// Model version
64    pub model_version: String,
65    /// Prediction timestamp
66    pub prediction_timestamp: Instant,
67    /// Features used
68    pub features_used: Vec<String>,
69    /// Model accuracy
70    pub model_accuracy: f64,
71}
72
73/// Optimal platform selection
74#[derive(Debug, Clone)]
75pub struct OptimalPlatformSelection {
76    /// Selected platform
77    pub platform: QuantumPlatform,
78    /// Selection score
79    pub selection_score: f64,
80    /// Selection rationale
81    pub selection_rationale: String,
82    /// Alternative platforms
83    pub alternatives: Vec<QuantumPlatform>,
84    /// Selection metadata
85    pub selection_metadata: SelectionMetadata,
86}
87
88/// Selection metadata
89#[derive(Debug, Clone)]
90pub struct SelectionMetadata {
91    /// Selection timestamp
92    pub selection_timestamp: Instant,
93    /// Strategy used
94    pub strategy_used: ResourceAllocationStrategy,
95    /// Confidence
96    pub confidence: f64,
97}
98
99/// Execution plan
100#[derive(Debug, Clone)]
101pub struct ExecutionPlan {
102    /// Target platform
103    pub platform: QuantumPlatform,
104    /// Scheduled start time
105    pub scheduled_start_time: Instant,
106    /// Estimated duration
107    pub estimated_duration: Duration,
108    /// Resource allocation
109    pub resource_allocation: PlatformResourceAllocation,
110    /// Execution parameters
111    pub execution_parameters: ExecutionParameters,
112}
113
114/// Platform resource allocation
115#[derive(Debug, Clone)]
116pub struct PlatformResourceAllocation {
117    /// Allocated qubits
118    pub qubits: Vec<usize>,
119    /// Execution priority
120    pub execution_priority: SchedulingPriority,
121    /// Resource reservation
122    pub resource_reservation: ResourceReservationInfo,
123}
124
125/// Resource reservation information
126#[derive(Debug, Clone)]
127pub struct ResourceReservationInfo {
128    /// Reservation identifier
129    pub reservation_id: String,
130    /// Reserved until
131    pub reserved_until: Instant,
132}
133
134/// Execution parameters
135#[derive(Debug, Clone)]
136pub struct ExecutionParameters {
137    /// Number of shots
138    pub shots: usize,
139    /// Optimization level
140    pub optimization_level: OptimizationLevel,
141    /// Error mitigation enabled
142    pub error_mitigation: bool,
143}
144
145/// Platform execution result
146#[derive(Debug, Clone)]
147pub struct PlatformExecutionResult {
148    /// Platform used
149    pub platform: QuantumPlatform,
150    /// Execution identifier
151    pub execution_id: String,
152    /// Solution found
153    pub solution: Vec<i32>,
154    /// Objective value
155    pub objective_value: f64,
156    /// Execution time
157    pub execution_time: Duration,
158    /// Success indicator
159    pub success: bool,
160    /// Quality metrics
161    pub quality_metrics: ExecutionQualityMetrics,
162    /// Resource usage
163    pub resource_usage: ExecutionResourceUsage,
164    /// Execution metadata
165    pub metadata: ExecutionMetadata,
166}
167
168/// Execution quality metrics
169#[derive(Debug, Clone)]
170pub struct ExecutionQualityMetrics {
171    /// Solution quality
172    pub solution_quality: f64,
173    /// Fidelity
174    pub fidelity: f64,
175    /// Success probability
176    pub success_probability: f64,
177}
178
179/// Execution resource usage
180#[derive(Debug, Clone)]
181pub struct ExecutionResourceUsage {
182    /// Qubits used
183    pub qubits_used: usize,
184    /// Shots executed
185    pub shots_executed: usize,
186    /// Classical compute time
187    pub classical_compute_time: Duration,
188    /// Cost incurred
189    pub cost_incurred: f64,
190}
191
192/// Execution metadata
193#[derive(Debug, Clone)]
194pub struct ExecutionMetadata {
195    /// Execution timestamp
196    pub execution_timestamp: Instant,
197    /// Platform version
198    pub platform_version: String,
199    /// Execution environment
200    pub execution_environment: String,
201}
202
203/// Universal execution metadata
204#[derive(Debug, Clone)]
205pub struct UniversalExecutionMetadata {
206    /// Compiler version
207    pub compiler_version: String,
208    /// Platforms considered
209    pub platforms_considered: usize,
210    /// Optimization level used
211    pub optimization_level: OptimizationLevel,
212    /// Cost savings achieved
213    pub cost_savings: f64,
214    /// Performance improvement
215    pub performance_improvement: f64,
216}
217
218/// Performance predictor backed by a real, growing history of observed
219/// [`PlatformExecutionResult`]s per platform.
220///
221/// Rather than emitting fixed confidence/accuracy constants regardless of
222/// input, [`Self::predict`]/[`Self::model_accuracy`]/[`Self::confidence_score`]
223/// derive their outputs from whatever execution history has actually been
224/// recorded via [`Self::record_result`] for that platform. With no history
225/// for a platform, prediction honestly returns `None` rather than a
226/// fabricated guess.
227///
228/// Note: this crate does not yet wire `record_result`/`predict` into
229/// [`super::compiler::UniversalAnnealingCompiler`]'s `predict_performance` /
230/// `update_performance_models`, which still construct
231/// [`PlatformPerformancePrediction`] with fixed constants; see the crate's
232/// TODOs for that remaining integration.
233#[derive(Debug, Default)]
234pub struct PerformancePredictor {
235    /// Observed execution results, keyed by platform.
236    history: HashMap<QuantumPlatform, Vec<PlatformExecutionResult>>,
237}
238
239impl PerformancePredictor {
240    /// Create a new performance predictor with empty history.
241    #[must_use]
242    pub fn new() -> Self {
243        Self {
244            history: HashMap::new(),
245        }
246    }
247
248    /// Record a real execution outcome, growing this platform's history.
249    pub fn record_result(&mut self, result: &PlatformExecutionResult) {
250        self.history
251            .entry(result.platform.clone())
252            .or_default()
253            .push(result.clone());
254    }
255
256    /// Number of recorded results for `platform`.
257    #[must_use]
258    pub fn sample_count(&self, platform: &QuantumPlatform) -> usize {
259        self.history.get(platform).map_or(0, Vec::len)
260    }
261
262    /// Predict performance for `platform` from its real recorded history.
263    /// Returns `None` if nothing has been recorded for this platform yet.
264    #[must_use]
265    pub fn predict(&self, platform: &QuantumPlatform) -> Option<PredictedPerformance> {
266        let results = self.history.get(platform)?;
267        if results.is_empty() {
268            return None;
269        }
270        let n = results.len() as f64;
271
272        let mean_time_secs = results
273            .iter()
274            .map(|r| r.execution_time.as_secs_f64())
275            .sum::<f64>()
276            / n;
277        let mean_quality = results
278            .iter()
279            .map(|r| r.quality_metrics.solution_quality)
280            .sum::<f64>()
281            / n;
282        let mean_success_probability = results
283            .iter()
284            .map(|r| r.quality_metrics.success_probability)
285            .sum::<f64>()
286            / n;
287        let mean_cost = results
288            .iter()
289            .map(|r| r.resource_usage.cost_incurred)
290            .sum::<f64>()
291            / n;
292        let success_rate = results.iter().filter(|r| r.success).count() as f64 / n;
293
294        Some(PredictedPerformance {
295            execution_time: Duration::from_secs_f64(mean_time_secs.max(0.0)),
296            solution_quality: mean_quality,
297            success_probability: mean_success_probability,
298            cost: mean_cost,
299            reliability_score: success_rate,
300        })
301    }
302
303    /// Real model accuracy for `platform`: `1 - coefficient_of_variation` of
304    /// the recorded solution-quality samples, clamped to `[0, 1]`. A
305    /// platform whose real outcomes are consistent scores near 1; one whose
306    /// outcomes vary wildly scores low. Returns `0.0` (honestly "no
307    /// evidence yet") when fewer than two samples have been recorded.
308    #[must_use]
309    pub fn model_accuracy(&self, platform: &QuantumPlatform) -> f64 {
310        let Some(results) = self.history.get(platform) else {
311            return 0.0;
312        };
313        if results.len() < 2 {
314            return 0.0;
315        }
316        let n = results.len() as f64;
317        let mean = results
318            .iter()
319            .map(|r| r.quality_metrics.solution_quality)
320            .sum::<f64>()
321            / n;
322        if mean.abs() < 1e-12 {
323            return 0.0;
324        }
325        let variance = results
326            .iter()
327            .map(|r| (r.quality_metrics.solution_quality - mean).powi(2))
328            .sum::<f64>()
329            / n;
330        let coefficient_of_variation = variance.sqrt() / mean.abs();
331        (1.0 - coefficient_of_variation).clamp(0.0, 1.0)
332    }
333
334    /// Real confidence score for `platform`: grows monotonically with the
335    /// amount of real recorded evidence (`n / (n + 5)`), rather than a fixed
336    /// constant regardless of how much (or how little) history exists.
337    #[must_use]
338    pub fn confidence_score(&self, platform: &QuantumPlatform) -> f64 {
339        let n = self.sample_count(platform) as f64;
340        n / (n + 5.0)
341    }
342}
343
344/// Cost optimizer backed by a real, growing history of observed platform
345/// costs, rather than emitting fabricated recommendations.
346#[derive(Debug, Default)]
347pub struct CostOptimizer {
348    /// Observed incurred costs, keyed by platform.
349    cost_history: HashMap<QuantumPlatform, Vec<f64>>,
350}
351
352impl CostOptimizer {
353    /// Create a new cost optimizer with empty history.
354    #[must_use]
355    pub fn new() -> Self {
356        Self {
357            cost_history: HashMap::new(),
358        }
359    }
360
361    /// Record a real observed cost for `platform`.
362    pub fn record_cost(&mut self, platform: QuantumPlatform, cost: f64) {
363        self.cost_history.entry(platform).or_default().push(cost);
364    }
365
366    /// Mean of the real recorded costs for `platform`, or `None` if nothing
367    /// has been recorded yet.
368    #[must_use]
369    pub fn estimate_cost(&self, platform: &QuantumPlatform) -> Option<f64> {
370        let costs = self.cost_history.get(platform)?;
371        if costs.is_empty() {
372            return None;
373        }
374        Some(costs.iter().sum::<f64>() / costs.len() as f64)
375    }
376
377    /// Recommend the platform among `candidates` with the lowest real mean
378    /// recorded cost. Candidates with no recorded history are skipped
379    /// (rather than fabricating a cost for them); returns `None` if none of
380    /// the candidates have any recorded history.
381    #[must_use]
382    pub fn recommend_cheapest<'a>(
383        &self,
384        candidates: &'a [QuantumPlatform],
385    ) -> Option<&'a QuantumPlatform> {
386        candidates
387            .iter()
388            .filter_map(|platform| self.estimate_cost(platform).map(|cost| (platform, cost)))
389            .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
390            .map(|(platform, _)| platform)
391    }
392}
393
394#[cfg(test)]
395mod tests {
396    use super::*;
397
398    fn make_result(
399        platform: QuantumPlatform,
400        quality: f64,
401        cost: f64,
402        success: bool,
403    ) -> PlatformExecutionResult {
404        PlatformExecutionResult {
405            platform,
406            execution_id: "test".to_string(),
407            solution: vec![1, 0, 1],
408            objective_value: -1.0,
409            execution_time: Duration::from_millis(100),
410            success,
411            quality_metrics: ExecutionQualityMetrics {
412                solution_quality: quality,
413                fidelity: 0.95,
414                success_probability: if success { 0.9 } else { 0.1 },
415            },
416            resource_usage: ExecutionResourceUsage {
417                qubits_used: 4,
418                shots_executed: 100,
419                classical_compute_time: Duration::from_millis(10),
420                cost_incurred: cost,
421            },
422            metadata: ExecutionMetadata {
423                execution_timestamp: Instant::now(),
424                platform_version: "1.0".to_string(),
425                execution_environment: "test".to_string(),
426            },
427        }
428    }
429
430    #[test]
431    fn performance_predictor_has_no_prediction_without_real_history() {
432        let predictor = PerformancePredictor::new();
433        assert!(predictor.predict(&QuantumPlatform::DWave).is_none());
434        assert_eq!(predictor.model_accuracy(&QuantumPlatform::DWave), 0.0);
435        assert_eq!(predictor.confidence_score(&QuantumPlatform::DWave), 0.0);
436    }
437
438    #[test]
439    fn performance_predictor_derives_real_predictions_from_recorded_history() {
440        let mut predictor = PerformancePredictor::new();
441        predictor.record_result(&make_result(QuantumPlatform::DWave, 0.8, 1.0, true));
442        predictor.record_result(&make_result(QuantumPlatform::DWave, 0.9, 2.0, true));
443        predictor.record_result(&make_result(QuantumPlatform::DWave, 0.7, 3.0, false));
444
445        let prediction = predictor
446            .predict(&QuantumPlatform::DWave)
447            .expect("prediction should exist once history has been recorded");
448
449        assert!((prediction.solution_quality - 0.8).abs() < 1e-9);
450        assert!((prediction.cost - 2.0).abs() < 1e-9);
451        // 2 of 3 recorded runs succeeded -> real reliability, not a fixed 0.9.
452        assert!((prediction.reliability_score - (2.0 / 3.0)).abs() < 1e-9);
453
454        // Confidence must grow with recorded evidence rather than stay fixed.
455        let confidence_after_3 = predictor.confidence_score(&QuantumPlatform::DWave);
456        predictor.record_result(&make_result(QuantumPlatform::DWave, 0.85, 1.5, true));
457        let confidence_after_4 = predictor.confidence_score(&QuantumPlatform::DWave);
458        assert!(confidence_after_4 > confidence_after_3);
459    }
460
461    #[test]
462    fn performance_predictor_accuracy_reflects_real_outcome_consistency() {
463        let mut consistent = PerformancePredictor::new();
464        consistent.record_result(&make_result(QuantumPlatform::IBM, 0.9, 1.0, true));
465        consistent.record_result(&make_result(QuantumPlatform::IBM, 0.91, 1.0, true));
466        consistent.record_result(&make_result(QuantumPlatform::IBM, 0.89, 1.0, true));
467
468        let mut erratic = PerformancePredictor::new();
469        erratic.record_result(&make_result(QuantumPlatform::IBM, 0.1, 1.0, true));
470        erratic.record_result(&make_result(QuantumPlatform::IBM, 0.9, 1.0, true));
471        erratic.record_result(&make_result(QuantumPlatform::IBM, 0.2, 1.0, false));
472
473        let consistent_accuracy = consistent.model_accuracy(&QuantumPlatform::IBM);
474        let erratic_accuracy = erratic.model_accuracy(&QuantumPlatform::IBM);
475
476        assert!(
477            consistent_accuracy > erratic_accuracy,
478            "a platform with consistent real outcomes must score higher accuracy than an erratic one \
479             (consistent={consistent_accuracy}, erratic={erratic_accuracy})"
480        );
481    }
482
483    #[test]
484    fn cost_optimizer_recommends_the_real_cheapest_platform() {
485        let mut optimizer = CostOptimizer::new();
486        optimizer.record_cost(QuantumPlatform::DWave, 5.0);
487        optimizer.record_cost(QuantumPlatform::DWave, 7.0);
488        optimizer.record_cost(QuantumPlatform::IBM, 1.0);
489        optimizer.record_cost(QuantumPlatform::IBM, 2.0);
490
491        assert!((optimizer.estimate_cost(&QuantumPlatform::DWave).unwrap() - 6.0).abs() < 1e-9);
492        assert!((optimizer.estimate_cost(&QuantumPlatform::IBM).unwrap() - 1.5).abs() < 1e-9);
493
494        let candidates = vec![QuantumPlatform::DWave, QuantumPlatform::IBM];
495        let cheapest = optimizer
496            .recommend_cheapest(&candidates)
497            .expect("a cheapest platform should be found");
498        assert_eq!(*cheapest, QuantumPlatform::IBM);
499    }
500
501    #[test]
502    fn cost_optimizer_skips_platforms_with_no_recorded_history() {
503        let mut optimizer = CostOptimizer::new();
504        optimizer.record_cost(QuantumPlatform::IBM, 3.0);
505
506        assert!(optimizer.estimate_cost(&QuantumPlatform::DWave).is_none());
507
508        let candidates = vec![QuantumPlatform::DWave, QuantumPlatform::IBM];
509        let cheapest = optimizer
510            .recommend_cheapest(&candidates)
511            .expect("should still find the one platform with real history");
512        assert_eq!(*cheapest, QuantumPlatform::IBM);
513    }
514}