Skip to main content

quantrs2_device/unified_benchmarking/
system.rs

1//! Main unified benchmarking system implementation
2
3use std::collections::{HashMap, VecDeque};
4use std::sync::{mpsc, Arc, Mutex, RwLock};
5use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7use super::analysis::{
8    compute_cost_analysis, compute_cost_metrics, compute_cross_platform_analysis,
9    compute_performance_metrics, compute_reliability_metrics, compute_resource_analysis,
10    default_algorithm_level_results, default_circuit_level_results, default_gate_level_results,
11    default_scirs2_analysis, default_system_level_results,
12};
13use super::config::{
14    AlgorithmBenchmarkConfig, CircuitBenchmarkConfig, GateBenchmarkConfig, SystemBenchmarkConfig,
15    UnifiedBenchmarkConfig,
16};
17use super::events::BenchmarkEvent;
18use super::optimization::OptimizationEngine;
19use super::reporting::ReportGenerator;
20use super::results::{
21    AlgorithmLevelResults, CircuitLevelResults, CoherenceTimes, ConnectivityInfo,
22    CostAnalysisResult, CostMetrics, CrossPlatformAnalysis, DeviceInfo, DeviceSpecifications,
23    DeviceStatus, ExecutionMetadata, GateLevelResults, HistoricalComparisonResult,
24    OptimizationRecommendation, PlatformBenchmarkResult, PlatformPerformanceMetrics,
25    QuantumTechnology, ReliabilityMetrics, ResourceAnalysisResult, SciRS2AnalysisResult,
26    SystemLevelResults, TopologyType, UnifiedBenchmarkResult,
27};
28use super::types::{PerformanceBaseline, QuantumPlatform};
29
30use crate::{
31    advanced_benchmarking_suite::{AdvancedBenchmarkConfig, AdvancedHardwareBenchmarkSuite},
32    calibration::CalibrationManager,
33    cross_platform_benchmarking::{CrossPlatformBenchmarkConfig, CrossPlatformBenchmarker},
34    topology::HardwareTopology,
35    DeviceError, DeviceResult, QuantumDevice,
36};
37use quantrs2_core::error::{QuantRS2Error, QuantRS2Result};
38
39use scirs2_core::ndarray::Array2;
40
41/// Main unified benchmarking system
42pub struct UnifiedQuantumBenchmarkSystem {
43    /// Configuration
44    config: Arc<RwLock<UnifiedBenchmarkConfig>>,
45    /// Platform clients
46    platform_clients: Arc<RwLock<HashMap<QuantumPlatform, Box<dyn QuantumDevice + Send + Sync>>>>,
47    /// Cross-platform benchmarker
48    cross_platform_benchmarker: Arc<Mutex<CrossPlatformBenchmarker>>,
49    /// Advanced benchmarking suite
50    advanced_suite: Arc<Mutex<AdvancedHardwareBenchmarkSuite>>,
51    /// Calibration manager
52    calibration_manager: Arc<Mutex<CalibrationManager>>,
53    /// Historical data storage
54    historical_data: Arc<RwLock<VecDeque<UnifiedBenchmarkResult>>>,
55    /// Performance baselines
56    baselines: Arc<RwLock<HashMap<String, PerformanceBaseline>>>,
57    /// Real-time monitoring
58    monitoring_handle: Arc<Mutex<Option<std::thread::JoinHandle<()>>>>,
59    /// Event publisher
60    event_publisher: mpsc::Sender<BenchmarkEvent>,
61    /// Optimization engine
62    optimization_engine: Arc<Mutex<OptimizationEngine>>,
63    /// Report generator
64    report_generator: Arc<Mutex<ReportGenerator>>,
65}
66
67impl UnifiedQuantumBenchmarkSystem {
68    /// Create a new unified quantum benchmark system
69    pub async fn new(
70        config: UnifiedBenchmarkConfig,
71        calibration_manager: CalibrationManager,
72    ) -> DeviceResult<Self> {
73        let (event_publisher, _) = mpsc::channel();
74        let config = Arc::new(RwLock::new(config));
75
76        // Initialize platform clients
77        let platform_clients = Arc::new(RwLock::new(HashMap::new()));
78
79        // Initialize cross-platform benchmarker
80        let cross_platform_config = CrossPlatformBenchmarkConfig::default();
81        let cross_platform_benchmarker = Arc::new(Mutex::new(CrossPlatformBenchmarker::new(
82            cross_platform_config,
83            calibration_manager.clone(),
84        )));
85
86        // Initialize advanced benchmarking suite
87        let advanced_config = AdvancedBenchmarkConfig::default();
88        let topology = HardwareTopology::linear_topology(8); // Default topology
89        let advanced_suite = Arc::new(Mutex::new(
90            AdvancedHardwareBenchmarkSuite::new(
91                advanced_config,
92                calibration_manager.clone(),
93                topology,
94            )
95            .await?,
96        ));
97
98        let historical_data = Arc::new(RwLock::new(VecDeque::with_capacity(10000)));
99        let baselines = Arc::new(RwLock::new(HashMap::new()));
100        let monitoring_handle = Arc::new(Mutex::new(None));
101
102        let optimization_engine = Arc::new(Mutex::new(OptimizationEngine::new()));
103        let report_generator = Arc::new(Mutex::new(ReportGenerator::new()));
104
105        Ok(Self {
106            config,
107            platform_clients,
108            cross_platform_benchmarker,
109            advanced_suite,
110            calibration_manager: Arc::new(Mutex::new(calibration_manager)),
111            historical_data,
112            baselines,
113            monitoring_handle,
114            event_publisher,
115            optimization_engine,
116            report_generator,
117        })
118    }
119
120    /// Register a quantum platform for benchmarking
121    pub async fn register_platform(
122        &self,
123        platform: QuantumPlatform,
124        device: Box<dyn QuantumDevice + Send + Sync>,
125    ) -> DeviceResult<()> {
126        let mut clients = self
127            .platform_clients
128            .write()
129            .unwrap_or_else(|e| e.into_inner());
130        clients.insert(platform, device);
131        Ok(())
132    }
133
134    /// Run comprehensive unified benchmarks
135    pub async fn run_comprehensive_benchmark(&self) -> DeviceResult<UnifiedBenchmarkResult> {
136        let execution_id = self.generate_execution_id();
137        let start_time = SystemTime::now();
138
139        // Notify benchmark start
140        let config = self
141            .config
142            .read()
143            .unwrap_or_else(|e| e.into_inner())
144            .clone();
145        let _ = self.event_publisher.send(BenchmarkEvent::BenchmarkStarted {
146            execution_id: execution_id.clone(),
147            platforms: config.target_platforms.clone(),
148            timestamp: start_time,
149        });
150
151        // Execute benchmarks on all platforms
152        let mut platform_results = HashMap::new();
153
154        for platform in &config.target_platforms {
155            match self.run_platform_benchmark(platform, &execution_id).await {
156                Ok(result) => {
157                    let _ = self
158                        .event_publisher
159                        .send(BenchmarkEvent::PlatformBenchmarkCompleted {
160                            execution_id: execution_id.clone(),
161                            platform: platform.clone(),
162                            result: result.clone(),
163                            timestamp: SystemTime::now(),
164                        });
165                    platform_results.insert(platform.clone(), result);
166                }
167                Err(e) => {
168                    eprintln!("Platform benchmark failed for {platform:?}: {e}");
169                    // Continue with other platforms
170                }
171            }
172        }
173
174        // Perform analysis
175        let cross_platform_analysis = self
176            .perform_cross_platform_analysis(&platform_results)
177            .await?;
178        let scirs2_analysis = self.perform_scirs2_analysis(&platform_results).await?;
179        let resource_analysis = self.perform_resource_analysis(&platform_results).await?;
180        let cost_analysis = self.perform_cost_analysis(&platform_results).await?;
181
182        // Generate optimization recommendations
183        let optimization_recommendations = self
184            .generate_optimization_recommendations(
185                &platform_results,
186                &cross_platform_analysis,
187                &scirs2_analysis,
188            )
189            .await?;
190
191        // Perform historical comparison if available
192        let historical_comparison = self
193            .perform_historical_comparison(&platform_results)
194            .await?;
195
196        // Create execution metadata
197        let execution_metadata = ExecutionMetadata {
198            execution_start_time: start_time,
199            execution_end_time: SystemTime::now(),
200            total_duration: SystemTime::now()
201                .duration_since(start_time)
202                .unwrap_or(Duration::ZERO),
203            platforms_tested: config.target_platforms.clone(),
204            benchmarks_executed: platform_results.len(),
205            system_info: self.get_system_info(),
206        };
207
208        let result = UnifiedBenchmarkResult {
209            execution_id: execution_id.clone(),
210            timestamp: start_time,
211            config,
212            platform_results,
213            cross_platform_analysis,
214            scirs2_analysis,
215            resource_analysis,
216            cost_analysis,
217            optimization_recommendations,
218            historical_comparison,
219            execution_metadata,
220        };
221
222        // Store result in historical data
223        self.store_historical_result(&result).await;
224
225        // Update baselines if needed
226        self.update_baselines(&result).await;
227
228        // Trigger optimization if enabled
229        if result
230            .config
231            .optimization_config
232            .enable_intelligent_allocation
233        {
234            self.trigger_optimization(&result).await?;
235        }
236
237        // Generate automated reports if enabled
238        if result
239            .config
240            .reporting_config
241            .automated_reports
242            .enable_automated
243        {
244            self.generate_automated_reports(&result).await?;
245        }
246
247        // Notify benchmark completion
248        let _ = self
249            .event_publisher
250            .send(BenchmarkEvent::BenchmarkCompleted {
251                execution_id: execution_id.clone(),
252                result: result.clone(),
253                timestamp: SystemTime::now(),
254            });
255
256        Ok(result)
257    }
258
259    /// Run benchmark on a specific platform
260    async fn run_platform_benchmark(
261        &self,
262        platform: &QuantumPlatform,
263        execution_id: &str,
264    ) -> DeviceResult<PlatformBenchmarkResult> {
265        let config = self
266            .config
267            .read()
268            .unwrap_or_else(|e| e.into_inner())
269            .clone();
270
271        // Get device information
272        let device_info = self.get_device_info(platform).await?;
273
274        // Run benchmarks
275        let gate_level_results = self
276            .run_gate_level_benchmarks(platform, &config.benchmark_suite.gate_benchmarks)
277            .await?;
278        let circuit_level_results = self
279            .run_circuit_level_benchmarks(platform, &config.benchmark_suite.circuit_benchmarks)
280            .await?;
281        let algorithm_level_results = self
282            .run_algorithm_level_benchmarks(platform, &config.benchmark_suite.algorithm_benchmarks)
283            .await?;
284        let system_level_results = self
285            .run_system_level_benchmarks(platform, &config.benchmark_suite.system_benchmarks)
286            .await?;
287
288        // Calculate metrics
289        let performance_metrics = self
290            .calculate_platform_performance_metrics(
291                &gate_level_results,
292                &circuit_level_results,
293                &algorithm_level_results,
294                &system_level_results,
295            )
296            .await?;
297
298        let reliability_metrics = self
299            .calculate_reliability_metrics(
300                &gate_level_results,
301                &circuit_level_results,
302                &algorithm_level_results,
303            )
304            .await?;
305
306        let cost_metrics = self
307            .calculate_cost_metrics(
308                &gate_level_results,
309                &circuit_level_results,
310                &algorithm_level_results,
311            )
312            .await?;
313
314        Ok(PlatformBenchmarkResult {
315            platform: platform.clone(),
316            device_info,
317            gate_level_results,
318            circuit_level_results,
319            algorithm_level_results,
320            system_level_results,
321            performance_metrics,
322            reliability_metrics,
323            cost_metrics,
324        })
325    }
326
327    /// Generate unique execution ID
328    fn generate_execution_id(&self) -> String {
329        format!(
330            "unified_benchmark_{}",
331            SystemTime::now()
332                .duration_since(UNIX_EPOCH)
333                .unwrap_or(Duration::ZERO)
334                .as_millis()
335        )
336    }
337
338    /// Get device information for a platform
339    async fn get_device_info(&self, platform: &QuantumPlatform) -> DeviceResult<DeviceInfo> {
340        let (provider, technology) = match platform {
341            QuantumPlatform::IBMQuantum { .. } => {
342                ("IBM".to_string(), QuantumTechnology::Superconducting)
343            }
344            QuantumPlatform::AWSBraket { .. } => {
345                ("AWS".to_string(), QuantumTechnology::Superconducting)
346            }
347            QuantumPlatform::AzureQuantum { .. } => {
348                ("Microsoft".to_string(), QuantumTechnology::TrappedIon)
349            }
350            QuantumPlatform::IonQ { .. } => ("IonQ".to_string(), QuantumTechnology::TrappedIon),
351            QuantumPlatform::Rigetti { .. } => {
352                ("Rigetti".to_string(), QuantumTechnology::Superconducting)
353            }
354            QuantumPlatform::GoogleQuantumAI { .. } => {
355                ("Google".to_string(), QuantumTechnology::Superconducting)
356            }
357            QuantumPlatform::Custom { .. } => (
358                "Custom".to_string(),
359                QuantumTechnology::Other("Custom".to_string()),
360            ),
361        };
362
363        Ok(DeviceInfo {
364            device_id: format!("{platform:?}"),
365            provider,
366            technology,
367            specifications: DeviceSpecifications {
368                num_qubits: 20,
369                connectivity: ConnectivityInfo {
370                    topology_type: TopologyType::Heavy,
371                    coupling_map: vec![(0, 1), (1, 2), (2, 3)],
372                    connectivity_matrix: Array2::eye(20),
373                },
374                gate_set: vec![
375                    "X".to_string(),
376                    "Y".to_string(),
377                    "Z".to_string(),
378                    "H".to_string(),
379                    "CNOT".to_string(),
380                ],
381                coherence_times: CoherenceTimes {
382                    t1: (0..20).map(|i| (i, Duration::from_micros(100))).collect(),
383                    t2: (0..20).map(|i| (i, Duration::from_micros(50))).collect(),
384                    t2_echo: (0..20).map(|i| (i, Duration::from_micros(80))).collect(),
385                },
386                gate_times: [
387                    ("X".to_string(), Duration::from_nanos(20)),
388                    ("CNOT".to_string(), Duration::from_nanos(100)),
389                ]
390                .iter()
391                .cloned()
392                .collect(),
393                error_rates: [
394                    ("single_qubit".to_string(), 0.001),
395                    ("two_qubit".to_string(), 0.01),
396                ]
397                .iter()
398                .cloned()
399                .collect(),
400            },
401            current_status: DeviceStatus::Online,
402            calibration_date: Some(SystemTime::now()),
403        })
404    }
405
406    // Benchmark execution methods
407    async fn run_gate_level_benchmarks(
408        &self,
409        _platform: &QuantumPlatform,
410        _config: &GateBenchmarkConfig,
411    ) -> DeviceResult<GateLevelResults> {
412        // No hardware-level RB runner is wired in this crate yet; return a
413        // well-formed result with synthetic but representative values so the
414        // rest of the pipeline is fully exercisable. Deviation noted.
415        Ok(default_gate_level_results())
416    }
417
418    async fn run_circuit_level_benchmarks(
419        &self,
420        _platform: &QuantumPlatform,
421        _config: &CircuitBenchmarkConfig,
422    ) -> DeviceResult<CircuitLevelResults> {
423        // No XEB / QV runner is wired in this crate yet; return a representative
424        // synthetic result. Deviation noted.
425        Ok(default_circuit_level_results())
426    }
427
428    async fn run_algorithm_level_benchmarks(
429        &self,
430        _platform: &QuantumPlatform,
431        _config: &AlgorithmBenchmarkConfig,
432    ) -> DeviceResult<AlgorithmLevelResults> {
433        // No VQE / QAOA / Grover runner is wired in this crate yet; return a
434        // representative synthetic result. Deviation noted.
435        Ok(default_algorithm_level_results())
436    }
437
438    async fn run_system_level_benchmarks(
439        &self,
440        platform: &QuantumPlatform,
441        _config: &SystemBenchmarkConfig,
442    ) -> DeviceResult<SystemLevelResults> {
443        Ok(default_system_level_results(platform))
444    }
445
446    // Analysis methods
447    async fn perform_cross_platform_analysis(
448        &self,
449        platform_results: &HashMap<QuantumPlatform, PlatformBenchmarkResult>,
450    ) -> DeviceResult<CrossPlatformAnalysis> {
451        Ok(compute_cross_platform_analysis(platform_results))
452    }
453
454    async fn perform_scirs2_analysis(
455        &self,
456        _platform_results: &HashMap<QuantumPlatform, PlatformBenchmarkResult>,
457    ) -> DeviceResult<SciRS2AnalysisResult> {
458        // The scirs2 crate is not a direct dependency of quantrs2-device; all
459        // analysis is done through scirs2_core primitives only. Return a
460        // structurally valid result with zero/default values.
461        Ok(default_scirs2_analysis())
462    }
463
464    async fn perform_resource_analysis(
465        &self,
466        platform_results: &HashMap<QuantumPlatform, PlatformBenchmarkResult>,
467    ) -> DeviceResult<ResourceAnalysisResult> {
468        Ok(compute_resource_analysis(platform_results))
469    }
470
471    async fn perform_cost_analysis(
472        &self,
473        platform_results: &HashMap<QuantumPlatform, PlatformBenchmarkResult>,
474    ) -> DeviceResult<CostAnalysisResult> {
475        Ok(compute_cost_analysis(platform_results))
476    }
477
478    // Metrics calculation
479    async fn calculate_platform_performance_metrics(
480        &self,
481        gate_results: &GateLevelResults,
482        circuit_results: &CircuitLevelResults,
483        algorithm_results: &AlgorithmLevelResults,
484        system_results: &SystemLevelResults,
485    ) -> DeviceResult<PlatformPerformanceMetrics> {
486        Ok(compute_performance_metrics(
487            gate_results,
488            circuit_results,
489            algorithm_results,
490            system_results,
491        ))
492    }
493
494    async fn calculate_reliability_metrics(
495        &self,
496        gate_results: &GateLevelResults,
497        circuit_results: &CircuitLevelResults,
498        algorithm_results: &AlgorithmLevelResults,
499    ) -> DeviceResult<ReliabilityMetrics> {
500        Ok(compute_reliability_metrics(
501            gate_results,
502            circuit_results,
503            algorithm_results,
504        ))
505    }
506
507    async fn calculate_cost_metrics(
508        &self,
509        gate_results: &GateLevelResults,
510        circuit_results: &CircuitLevelResults,
511        algorithm_results: &AlgorithmLevelResults,
512    ) -> DeviceResult<CostMetrics> {
513        Ok(compute_cost_metrics(
514            gate_results,
515            circuit_results,
516            algorithm_results,
517        ))
518    }
519
520    // Utility methods
521    async fn generate_optimization_recommendations(
522        &self,
523        platform_results: &HashMap<QuantumPlatform, PlatformBenchmarkResult>,
524        cross_platform_analysis: &CrossPlatformAnalysis,
525        _scirs2_analysis: &SciRS2AnalysisResult,
526    ) -> DeviceResult<Vec<OptimizationRecommendation>> {
527        Ok(generate_recommendations_from_metrics(
528            platform_results,
529            cross_platform_analysis,
530        ))
531    }
532
533    async fn perform_historical_comparison(
534        &self,
535        platform_results: &HashMap<QuantumPlatform, PlatformBenchmarkResult>,
536    ) -> DeviceResult<Option<HistoricalComparisonResult>> {
537        // Compare current per-platform metrics with the most recent historical
538        // entry stored in `historical_data`. Without history, no comparison
539        // is possible — return `None`. Statistical significance here is a
540        // placeholder p-value derived from relative change; it is meant for
541        // dashboard ranking, not formal hypothesis testing (which would
542        // require keeping individual measurements per metric).
543        let historical_data = self
544            .historical_data
545            .read()
546            .unwrap_or_else(|e| e.into_inner());
547
548        // Skip the most recent entry if it's the run we just stored.
549        let baseline = historical_data.iter().next_back();
550        let baseline = match baseline {
551            Some(b) => b,
552            None => return Ok(None),
553        };
554
555        let mut baseline_comparison: Vec<super::results::MetricComparison> = Vec::new();
556        let mut trend_analysis: HashMap<String, super::results::TrendAnalysisResult> =
557            HashMap::new();
558
559        for (platform, current) in platform_results {
560            let prev = match baseline.platform_results.get(platform) {
561                Some(p) => p,
562                None => continue,
563            };
564
565            // Compare overall_fidelity, error_rate, throughput, availability.
566            let pairs: [(&str, f64, f64); 4] = [
567                (
568                    "overall_fidelity",
569                    current.performance_metrics.overall_fidelity,
570                    prev.performance_metrics.overall_fidelity,
571                ),
572                (
573                    "error_rate",
574                    current.performance_metrics.error_rate,
575                    prev.performance_metrics.error_rate,
576                ),
577                (
578                    "throughput",
579                    current.performance_metrics.throughput,
580                    prev.performance_metrics.throughput,
581                ),
582                (
583                    "availability",
584                    current.performance_metrics.availability,
585                    prev.performance_metrics.availability,
586                ),
587            ];
588
589            for (name, cur_val, base_val) in pairs {
590                let percentage_change = if base_val.abs() > f64::EPSILON {
591                    (cur_val - base_val) / base_val * 100.0
592                } else if cur_val.abs() < f64::EPSILON {
593                    0.0
594                } else {
595                    100.0
596                };
597                // Map magnitude of relative change to a pseudo p-value:
598                // larger changes → smaller p-value.
599                let mag = percentage_change.abs();
600                let significance = (-mag / 50.0).exp().clamp(0.0, 1.0);
601
602                let metric_label = format!("{platform:?}.{name}");
603                baseline_comparison.push(super::results::MetricComparison {
604                    metric_name: metric_label.clone(),
605                    current_value: cur_val,
606                    baseline_value: base_val,
607                    percentage_change,
608                    statistical_significance: significance,
609                });
610
611                let direction = if cur_val > base_val {
612                    "increasing"
613                } else if cur_val < base_val {
614                    "decreasing"
615                } else {
616                    "flat"
617                };
618                trend_analysis.insert(
619                    metric_label,
620                    super::results::TrendAnalysisResult {
621                        trend_detected: mag > 1.0,
622                        trend_direction: direction.to_string(),
623                        trend_strength: (mag / 100.0).clamp(0.0, 1.0),
624                        trend_coefficients: vec![cur_val - base_val],
625                        change_points: Vec::new(),
626                    },
627                );
628            }
629        }
630
631        // Build a single performance snapshot for the current run so the
632        // evolution series is monotonically growing on each invocation.
633        let mut metrics_map: HashMap<String, f64> = HashMap::new();
634        for (platform, result) in platform_results {
635            metrics_map.insert(
636                format!("{platform:?}.overall_fidelity"),
637                result.performance_metrics.overall_fidelity,
638            );
639            metrics_map.insert(
640                format!("{platform:?}.error_rate"),
641                result.performance_metrics.error_rate,
642            );
643            metrics_map.insert(
644                format!("{platform:?}.throughput"),
645                result.performance_metrics.throughput,
646            );
647            metrics_map.insert(
648                format!("{platform:?}.availability"),
649                result.performance_metrics.availability,
650            );
651        }
652
653        let snapshot = super::results::PerformanceSnapshot {
654            timestamp: SystemTime::now(),
655            metrics: metrics_map,
656            configuration: "current".to_string(),
657        };
658
659        if baseline_comparison.is_empty() {
660            return Ok(None);
661        }
662
663        Ok(Some(HistoricalComparisonResult {
664            baseline_comparison,
665            trend_analysis,
666            performance_evolution: vec![snapshot],
667        }))
668    }
669
670    async fn store_historical_result(&self, result: &UnifiedBenchmarkResult) {
671        let mut historical_data = self
672            .historical_data
673            .write()
674            .unwrap_or_else(|e| e.into_inner());
675        historical_data.push_back(result.clone());
676
677        // Keep only the last 10000 results
678        if historical_data.len() > 10000 {
679            historical_data.pop_front();
680        }
681    }
682
683    async fn update_baselines(&self, result: &UnifiedBenchmarkResult) {
684        // Write per-platform performance baselines as JSON to disk so they
685        // survive across process restarts. Uses $HOME (falling back to /tmp)
686        // to avoid a `dirs` crate dependency.
687        let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
688        let dir = format!("{home}/.cache/quantrs2-baselines");
689        if let Err(e) = std::fs::create_dir_all(&dir) {
690            eprintln!("update_baselines: could not create cache dir {dir}: {e}");
691            return;
692        }
693
694        let file_path = format!("{dir}/{}.json", result.execution_id);
695        // Collect a compact snapshot of per-platform metrics as the baseline
696        // record. We only persist the lightweight `PlatformPerformanceMetrics`
697        // to keep files small.
698        let mut snapshot: HashMap<String, serde_json::Value> = HashMap::new();
699        for (platform, pr) in &result.platform_results {
700            let m = &pr.performance_metrics;
701            let entry = serde_json::json!({
702                "overall_fidelity": m.overall_fidelity,
703                "error_rate":        m.error_rate,
704                "throughput":        m.throughput,
705                "availability":      m.availability,
706                "avg_exec_ms":       m.average_execution_time.as_millis(),
707            });
708            snapshot.insert(format!("{platform:?}"), entry);
709        }
710
711        match serde_json::to_vec_pretty(&snapshot) {
712            Ok(bytes) => {
713                if let Err(e) = std::fs::write(&file_path, bytes) {
714                    eprintln!("update_baselines: could not write {file_path}: {e}");
715                }
716            }
717            Err(e) => {
718                eprintln!("update_baselines: serialisation error: {e}");
719            }
720        }
721
722        // Also update the in-memory baseline map for the current process.
723        let mut baselines = self.baselines.write().unwrap_or_else(|e| e.into_inner());
724        for (platform, pr) in &result.platform_results {
725            let m = &pr.performance_metrics;
726            let ci_half = m.error_rate * 0.05; // rough 5% CI half-width
727            let baseline = PerformanceBaseline {
728                platform: platform.clone(),
729                metrics: vec![
730                    super::types::BaselineMetricValue {
731                        metric: super::types::BaselineMetric::Fidelity,
732                        value: m.overall_fidelity,
733                        confidence_interval: (
734                            (m.overall_fidelity - ci_half).max(0.0),
735                            (m.overall_fidelity + ci_half).min(1.0),
736                        ),
737                        measurement_count: 1,
738                    },
739                    super::types::BaselineMetricValue {
740                        metric: super::types::BaselineMetric::ErrorRate,
741                        value: m.error_rate,
742                        confidence_interval: (
743                            (m.error_rate - ci_half).max(0.0),
744                            m.error_rate + ci_half,
745                        ),
746                        measurement_count: 1,
747                    },
748                    super::types::BaselineMetricValue {
749                        metric: super::types::BaselineMetric::Throughput,
750                        value: m.throughput,
751                        confidence_interval: ((m.throughput * 0.9).max(0.0), m.throughput * 1.1),
752                        measurement_count: 1,
753                    },
754                ],
755                last_updated: result.timestamp,
756                version: result.execution_id.clone(),
757            };
758            baselines.insert(format!("{platform:?}"), baseline);
759        }
760    }
761
762    async fn trigger_optimization(&self, result: &UnifiedBenchmarkResult) -> DeviceResult<()> {
763        // Compute aggregated improvement signal from the recommendations and
764        // publish an `OptimizationCompleted` event. The `expected_improvement`
765        // values produced by `generate_optimization_recommendations` already
766        // express the headroom each recommendation can recover; we forward
767        // those to subscribers grouped by recommendation type.
768        let recommendations = &result.optimization_recommendations;
769        if recommendations.is_empty() {
770            return Ok(());
771        }
772
773        let mut improvements: HashMap<String, f64> = HashMap::new();
774        for r in recommendations {
775            *improvements
776                .entry(r.recommendation_type.clone())
777                .or_insert(0.0) += r.expected_improvement;
778        }
779
780        // Also raise per-platform alerts when fidelity / error rate breach
781        // configured thresholds. This makes the metrics visible to dashboards
782        // even before any reporting runs.
783        for (platform, platform_result) in &result.platform_results {
784            let metrics = &platform_result.performance_metrics;
785            if metrics.error_rate > 0.05 {
786                let _ = self.event_publisher.send(BenchmarkEvent::PerformanceAlert {
787                    metric: format!("{platform:?}.error_rate"),
788                    current_value: metrics.error_rate,
789                    threshold: 0.05,
790                    timestamp: SystemTime::now(),
791                });
792            }
793            if metrics.overall_fidelity < 0.90 {
794                let _ = self.event_publisher.send(BenchmarkEvent::PerformanceAlert {
795                    metric: format!("{platform:?}.overall_fidelity"),
796                    current_value: metrics.overall_fidelity,
797                    threshold: 0.90,
798                    timestamp: SystemTime::now(),
799                });
800            }
801        }
802
803        let _ = self
804            .event_publisher
805            .send(BenchmarkEvent::OptimizationCompleted {
806                execution_id: result.execution_id.clone(),
807                improvements,
808                timestamp: SystemTime::now(),
809            });
810
811        Ok(())
812    }
813
814    async fn generate_automated_reports(
815        &self,
816        result: &UnifiedBenchmarkResult,
817    ) -> DeviceResult<()> {
818        // Render the result to a JSON payload. We use `serde_json` (already a
819        // dependency of this crate) so the payload is portable and consumable
820        // by external tooling without any new dependency. Writing to a file
821        // is gated on the `recipients` list being a filesystem-style URI
822        // (`file://...`) — anything else is treated as out-of-scope here.
823        let payload = serde_json::to_vec_pretty(result).map_err(|err| {
824            DeviceError::APIError(format!("failed to serialize benchmark result: {err}"))
825        })?;
826
827        let config = self
828            .config
829            .read()
830            .unwrap_or_else(|e| e.into_inner())
831            .clone();
832
833        for recipient in &config.reporting_config.automated_reports.recipients {
834            if let Some(path) = recipient.strip_prefix("file://") {
835                let dir = std::path::Path::new(path);
836                if let Err(err) = std::fs::create_dir_all(dir) {
837                    return Err(DeviceError::APIError(format!(
838                        "failed to create report directory {dir:?}: {err}"
839                    )));
840                }
841                let file = dir.join(format!("benchmark_{}.json", result.execution_id));
842                if let Err(err) = std::fs::write(&file, &payload) {
843                    return Err(DeviceError::APIError(format!(
844                        "failed to write report {file:?}: {err}"
845                    )));
846                }
847            }
848        }
849
850        // Emit a per-recipient performance alert summarizing the size of the
851        // generated report — a concrete signal for downstream pipelines.
852        let _ = self.event_publisher.send(BenchmarkEvent::PerformanceAlert {
853            metric: "automated_report.bytes".to_string(),
854            current_value: payload.len() as f64,
855            threshold: 0.0,
856            timestamp: SystemTime::now(),
857        });
858
859        Ok(())
860    }
861
862    fn get_system_info(&self) -> super::results::SystemInfo {
863        super::results::SystemInfo {
864            hostname: "localhost".to_string(),
865            operating_system: std::env::consts::OS.to_string(),
866            cpu_info: "Unknown".to_string(),
867            memory_total: 0,
868            disk_space: 0,
869            network_info: "Unknown".to_string(),
870        }
871    }
872}
873
874/// Pure helper that maps per-platform performance metrics to a list of
875/// concrete optimization recommendations. Extracted so it can be unit-tested
876/// without instantiating `UnifiedQuantumBenchmarkSystem` (which requires a
877/// fully wired calibration manager and async runtime).
878pub(crate) fn generate_recommendations_from_metrics(
879    platform_results: &HashMap<QuantumPlatform, PlatformBenchmarkResult>,
880    cross_platform_analysis: &CrossPlatformAnalysis,
881) -> Vec<OptimizationRecommendation> {
882    let metrics_map: HashMap<QuantumPlatform, &PlatformPerformanceMetrics> = platform_results
883        .iter()
884        .map(|(p, r)| (p.clone(), &r.performance_metrics))
885        .collect();
886    generate_recommendations_from_perf_metrics(&metrics_map, cross_platform_analysis)
887}
888
889/// Inner helper that operates only on the lightweight
890/// [`PlatformPerformanceMetrics`] type so unit tests do not need to fabricate
891/// a full [`PlatformBenchmarkResult`].
892pub(crate) fn generate_recommendations_from_perf_metrics(
893    platform_metrics: &HashMap<QuantumPlatform, &PlatformPerformanceMetrics>,
894    cross_platform_analysis: &CrossPlatformAnalysis,
895) -> Vec<OptimizationRecommendation> {
896    let mut recommendations: Vec<OptimizationRecommendation> = Vec::new();
897
898    const ERROR_RATE_THRESHOLD: f64 = 0.05;
899    const FIDELITY_THRESHOLD: f64 = 0.90;
900    const AVAILABILITY_THRESHOLD: f64 = 0.95;
901    const EXEC_TIME_SECS_THRESHOLD: f64 = 1.0;
902    const THROUGHPUT_THRESHOLD: f64 = 1.0;
903
904    for (platform, metrics) in platform_metrics {
905        let platform_label = format!("{platform:?}");
906
907        if metrics.error_rate > ERROR_RATE_THRESHOLD {
908            recommendations.push(OptimizationRecommendation {
909                recommendation_type: "error_mitigation".to_string(),
910                description: format!(
911                    "Platform {platform_label} reports an error rate of {:.4} (threshold {:.2}). Apply zero-noise extrapolation, dynamical decoupling, or readout error mitigation to reduce systematic errors.",
912                    metrics.error_rate, ERROR_RATE_THRESHOLD
913                ),
914                expected_improvement: (metrics.error_rate - ERROR_RATE_THRESHOLD).max(0.0),
915                implementation_effort: "Medium".to_string(),
916                priority: 2,
917            });
918        }
919
920        if metrics.overall_fidelity < FIDELITY_THRESHOLD {
921            recommendations.push(OptimizationRecommendation {
922                recommendation_type: "calibration".to_string(),
923                description: format!(
924                    "Platform {platform_label} fidelity {:.4} is below threshold {:.2}. Schedule recalibration of single- and two-qubit gates and refresh readout calibration.",
925                    metrics.overall_fidelity, FIDELITY_THRESHOLD
926                ),
927                expected_improvement: (FIDELITY_THRESHOLD - metrics.overall_fidelity).max(0.0),
928                implementation_effort: "Low".to_string(),
929                priority: 1,
930            });
931        }
932
933        let avg_secs = metrics.average_execution_time.as_secs_f64();
934        if avg_secs > EXEC_TIME_SECS_THRESHOLD {
935            recommendations.push(OptimizationRecommendation {
936                recommendation_type: "circuit_reduction".to_string(),
937                description: format!(
938                    "Platform {platform_label} average execution time {avg_secs:.3}s exceeds threshold {EXEC_TIME_SECS_THRESHOLD:.2}s. Apply circuit transpilation passes (gate fusion, commutation analysis, and depth-aware routing)."
939                ),
940                expected_improvement: ((avg_secs - EXEC_TIME_SECS_THRESHOLD)
941                    / avg_secs.max(f64::EPSILON))
942                .clamp(0.0, 1.0),
943                implementation_effort: "Medium".to_string(),
944                priority: 3,
945            });
946        }
947
948        if metrics.availability < AVAILABILITY_THRESHOLD {
949            recommendations.push(OptimizationRecommendation {
950                recommendation_type: "platform_redundancy".to_string(),
951                description: format!(
952                    "Platform {platform_label} availability {:.3} is below threshold {:.2}. Configure failover to secondary platforms and enable retry policies.",
953                    metrics.availability, AVAILABILITY_THRESHOLD
954                ),
955                expected_improvement: (AVAILABILITY_THRESHOLD - metrics.availability).max(0.0),
956                implementation_effort: "Medium".to_string(),
957                priority: 2,
958            });
959        }
960
961        if metrics.throughput < THROUGHPUT_THRESHOLD && metrics.throughput > 0.0 {
962            recommendations.push(OptimizationRecommendation {
963                recommendation_type: "batching_optimization".to_string(),
964                description: format!(
965                    "Platform {platform_label} throughput {:.3} ops/s is below threshold {THROUGHPUT_THRESHOLD:.2}. Batch jobs and submit in larger groups to amortize queue overhead.",
966                    metrics.throughput
967                ),
968                expected_improvement: ((THROUGHPUT_THRESHOLD - metrics.throughput)
969                    / THROUGHPUT_THRESHOLD)
970                    .clamp(0.0, 1.0),
971                implementation_effort: "Low".to_string(),
972                priority: 4,
973            });
974        }
975    }
976
977    if platform_metrics.len() > 1 {
978        if let Some(best_for_fidelity) = cross_platform_analysis
979            .best_platform_per_metric
980            .get("fidelity")
981        {
982            recommendations.push(OptimizationRecommendation {
983                recommendation_type: "platform_routing".to_string(),
984                description: format!(
985                    "Cross-platform analysis identifies {best_for_fidelity:?} as the best platform for fidelity. Route fidelity-critical workloads there and use other platforms for high-throughput jobs."
986                ),
987                expected_improvement: 0.05,
988                implementation_effort: "Low".to_string(),
989                priority: 5,
990            });
991        }
992    }
993
994    if recommendations.is_empty() {
995        recommendations.push(OptimizationRecommendation {
996            recommendation_type: "monitoring".to_string(),
997            description: "All platform metrics are within configured thresholds. Continue periodic benchmarking to detect drift early.".to_string(),
998            expected_improvement: 0.0,
999            implementation_effort: "Low".to_string(),
1000            priority: 9,
1001        });
1002    }
1003
1004    recommendations.sort_by_key(|r| r.priority);
1005    recommendations
1006}
1007
1008#[cfg(test)]
1009mod tests {
1010    use super::*;
1011    use std::time::Duration;
1012
1013    fn perf(
1014        fidelity: f64,
1015        error_rate: f64,
1016        availability: f64,
1017        avg_exec_secs: u64,
1018        throughput: f64,
1019    ) -> PlatformPerformanceMetrics {
1020        PlatformPerformanceMetrics {
1021            overall_fidelity: fidelity,
1022            average_execution_time: Duration::from_secs(avg_exec_secs),
1023            throughput,
1024            error_rate,
1025            availability,
1026        }
1027    }
1028
1029    #[test]
1030    fn test_recommendations_within_thresholds() {
1031        // No metric breach → only the informational "monitoring" entry.
1032        let metrics = perf(0.99, 0.001, 0.99, 0, 100.0);
1033        let mut platforms: HashMap<QuantumPlatform, &PlatformPerformanceMetrics> = HashMap::new();
1034        platforms.insert(
1035            QuantumPlatform::IBMQuantum {
1036                device_name: "test".to_string(),
1037                hub: None,
1038            },
1039            &metrics,
1040        );
1041        let cpa = CrossPlatformAnalysis {
1042            platform_comparison: HashMap::new(),
1043            best_platform_per_metric: HashMap::new(),
1044            statistical_significance_tests: HashMap::new(),
1045        };
1046        let recs = generate_recommendations_from_perf_metrics(&platforms, &cpa);
1047        assert_eq!(recs.len(), 1, "expected single info entry, got {recs:?}");
1048        assert_eq!(recs[0].recommendation_type, "monitoring");
1049    }
1050
1051    #[test]
1052    fn test_recommendations_breach_thresholds() {
1053        // Breach error_rate, fidelity, availability, exec time, throughput.
1054        let metrics = perf(0.50, 0.20, 0.50, 5, 0.1);
1055        let mut platforms: HashMap<QuantumPlatform, &PlatformPerformanceMetrics> = HashMap::new();
1056        platforms.insert(
1057            QuantumPlatform::IBMQuantum {
1058                device_name: "test".to_string(),
1059                hub: None,
1060            },
1061            &metrics,
1062        );
1063        let cpa = CrossPlatformAnalysis {
1064            platform_comparison: HashMap::new(),
1065            best_platform_per_metric: HashMap::new(),
1066            statistical_significance_tests: HashMap::new(),
1067        };
1068        let recs = generate_recommendations_from_perf_metrics(&platforms, &cpa);
1069        assert!(
1070            recs.len() >= 5,
1071            "expected >=5 recommendations, got {}: {recs:?}",
1072            recs.len()
1073        );
1074        assert!(recs.iter().all(|r| r.recommendation_type != "monitoring"));
1075        for w in recs.windows(2) {
1076            assert!(w[0].priority <= w[1].priority, "not sorted: {recs:?}");
1077        }
1078    }
1079}