quantrs2_device/
benchmarking.rs

1//! Hardware benchmarking suite with SciRS2 analysis
2//!
3//! This module provides comprehensive benchmarking capabilities for quantum hardware,
4//! using SciRS2's statistical and linear algebra capabilities for advanced analysis.
5
6use std::collections::HashMap;
7use std::time::{Duration, Instant};
8
9use scirs2_core::random::prelude::*;
10use scirs2_core::random::thread_rng;
11use serde::{Deserialize, Serialize};
12
13use quantrs2_circuit::prelude::*;
14use quantrs2_core::{
15    error::{QuantRS2Error, QuantRS2Result},
16    gate::GateOp,
17    qubit::QubitId,
18};
19
20use scirs2_graph::{
21    betweenness_centrality, closeness_centrality, clustering_coefficient, dijkstra_path,
22    graph_density, spectral_radius, Graph,
23};
24use scirs2_linalg::{
25    correlationmatrix, det, eigvals, inv, matrix_norm, svd, LinalgError, LinalgResult,
26};
27use scirs2_stats::{
28    distributions, mean, median, pearsonr, spearmanr, std, ttest::Alternative, ttest_1samp, var,
29    TTestResult,
30};
31
32use scirs2_core::ndarray::{s, Array1, Array2, ArrayView1, ArrayView2};
33
34use crate::{
35    backend_traits::{query_backend_capabilities, BackendCapabilities},
36    calibration::{CalibrationManager, DeviceCalibration},
37    noise_model::CalibrationNoiseModel,
38    topology::HardwareTopology,
39    CircuitResult, DeviceError, DeviceResult,
40};
41
42/// Comprehensive benchmarking suite configuration
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct BenchmarkConfig {
45    /// Number of benchmark iterations
46    pub iterations: usize,
47    /// Statistical confidence level (0.95 = 95%)
48    pub confidence_level: f64,
49    /// Maximum runtime per benchmark (seconds)
50    pub max_runtime: Duration,
51    /// Circuit depths to benchmark
52    pub circuit_depths: Vec<usize>,
53    /// Qubit counts to benchmark
54    pub qubit_counts: Vec<usize>,
55    /// Gate types to benchmark
56    pub gate_types: Vec<String>,
57    /// Enable advanced statistical analysis
58    pub enable_advanced_stats: bool,
59    /// Enable graph-theoretic analysis
60    pub enable_graph_analysis: bool,
61    /// Enable noise correlation analysis
62    pub enable_noise_analysis: bool,
63}
64
65impl Default for BenchmarkConfig {
66    fn default() -> Self {
67        Self {
68            iterations: 100,
69            confidence_level: 0.95,
70            max_runtime: Duration::from_secs(300),
71            circuit_depths: vec![5, 10, 20, 50, 100],
72            qubit_counts: vec![2, 4, 8, 16],
73            gate_types: vec!["H".to_string(), "CNOT".to_string(), "RZ".to_string()],
74            enable_advanced_stats: true,
75            enable_graph_analysis: true,
76            enable_noise_analysis: true,
77        }
78    }
79}
80
81/// Comprehensive benchmark results with SciRS2 analysis
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct BenchmarkSuite {
84    /// Device identifier
85    pub device_id: String,
86    /// Backend capabilities
87    pub backend_capabilities: BackendCapabilities,
88    /// Benchmark configuration used
89    pub config: BenchmarkConfig,
90    /// Individual benchmark results
91    pub benchmark_results: Vec<BenchmarkResult>,
92    /// Statistical analysis
93    pub statistical_analysis: StatisticalAnalysis,
94    /// Graph-theoretic analysis
95    pub graph_analysis: Option<GraphAnalysis>,
96    /// Noise correlation analysis
97    pub noise_analysis: Option<NoiseAnalysis>,
98    /// Performance metrics
99    pub performance_metrics: PerformanceMetrics,
100    /// Execution time
101    pub execution_time: Duration,
102}
103
104/// Individual benchmark result
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct BenchmarkResult {
107    /// Benchmark name
108    pub name: String,
109    /// Circuit used
110    pub circuit_description: String,
111    /// Number of qubits
112    pub num_qubits: usize,
113    /// Circuit depth
114    pub circuit_depth: usize,
115    /// Gate count
116    pub gate_count: usize,
117    /// Execution times (seconds)
118    pub execution_times: Vec<f64>,
119    /// Fidelity measurements
120    pub fidelities: Vec<f64>,
121    /// Error rates
122    pub error_rates: Vec<f64>,
123    /// Success probabilities
124    pub success_probabilities: Vec<f64>,
125    /// Queue times (seconds)
126    pub queue_times: Vec<f64>,
127}
128
129/// Statistical analysis using SciRS2
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct StatisticalAnalysis {
132    /// Execution time statistics
133    pub execution_time_stats: DescriptiveStats,
134    /// Fidelity statistics
135    pub fidelity_stats: DescriptiveStats,
136    /// Error rate statistics
137    pub error_rate_stats: DescriptiveStats,
138    /// Correlation matrix between metrics
139    pub correlationmatrix: Array2<f64>,
140    /// Statistical tests results
141    pub statistical_tests: HashMap<String, TestResult>,
142    /// Distribution fitting results
143    pub distribution_fits: HashMap<String, DistributionFit>,
144}
145
146/// Graph-theoretic analysis of connectivity and performance
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct GraphAnalysis {
149    /// Connectivity graph metrics
150    pub connectivity_metrics: ConnectivityMetrics,
151    /// Performance-topology correlations
152    pub topology_correlations: HashMap<String, f64>,
153    /// Critical path analysis
154    pub critical_paths: Vec<CriticalPath>,
155    /// Spectral properties
156    pub spectral_properties: SpectralProperties,
157}
158
159/// Noise correlation analysis
160#[derive(Debug, Clone, Serialize, Deserialize)]
161pub struct NoiseAnalysis {
162    /// Cross-talk correlations
163    pub crosstalk_correlations: Array2<f64>,
164    /// Temporal noise correlations
165    pub temporal_correlations: Array1<f64>,
166    /// Spatial noise patterns
167    pub spatial_patterns: HashMap<String, Array2<f64>>,
168    /// Noise model validation
169    pub model_validation: NoiseModelValidation,
170}
171
172/// Performance metrics summary
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct PerformanceMetrics {
175    /// Overall device score (0-100)
176    pub overall_score: f64,
177    /// Reliability score
178    pub reliability_score: f64,
179    /// Speed score
180    pub speed_score: f64,
181    /// Accuracy score
182    pub accuracy_score: f64,
183    /// Efficiency score
184    pub efficiency_score: f64,
185    /// Scalability metrics
186    pub scalability_metrics: ScalabilityMetrics,
187}
188
189/// Descriptive statistics
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct DescriptiveStats {
192    pub mean: f64,
193    pub median: f64,
194    pub std_dev: f64,
195    pub variance: f64,
196    pub min: f64,
197    pub max: f64,
198    pub q25: f64,
199    pub q75: f64,
200    pub confidence_interval: (f64, f64),
201}
202
203/// Statistical test result
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct TestResult {
206    pub test_name: String,
207    pub statistic: f64,
208    pub p_value: f64,
209    pub significant: bool,
210    pub effect_size: Option<f64>,
211}
212
213/// Distribution fitting result
214#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct DistributionFit {
216    pub distribution_name: String,
217    pub parameters: Vec<f64>,
218    pub goodness_of_fit: f64,
219    pub p_value: f64,
220}
221
222/// Connectivity metrics
223#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct ConnectivityMetrics {
225    pub average_path_length: f64,
226    pub clustering_coefficient: f64,
227    pub graph_density: f64,
228    pub diameter: usize,
229    pub spectral_gap: f64,
230    pub centrality_measures: HashMap<usize, CentralityMeasures>,
231}
232
233/// Centrality measures for qubits
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct CentralityMeasures {
236    pub betweenness: f64,
237    pub closeness: f64,
238    pub eigenvector: f64,
239    pub degree: f64,
240}
241
242/// Critical path information
243#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct CriticalPath {
245    pub path: Vec<usize>,
246    pub length: usize,
247    pub bottleneck_qubits: Vec<usize>,
248    pub expected_performance_impact: f64,
249}
250
251/// Spectral properties of the connectivity graph
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub struct SpectralProperties {
254    pub eigenvalues: Array1<f64>,
255    pub spectral_radius: f64,
256    pub algebraic_connectivity: f64,
257    pub spectral_gap: f64,
258}
259
260/// Noise model validation results
261#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct NoiseModelValidation {
263    pub model_accuracy: f64,
264    pub prediction_errors: Array1<f64>,
265    pub residual_analysis: ResidualAnalysis,
266}
267
268/// Residual analysis for noise model
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct ResidualAnalysis {
271    pub normality_test: TestResult,
272    pub autocorrelation: Array1<f64>,
273    pub heteroscedasticity_test: TestResult,
274}
275
276/// Scalability metrics
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct ScalabilityMetrics {
279    pub depth_scaling_coefficient: f64,
280    pub width_scaling_coefficient: f64,
281    pub resource_efficiency: f64,
282    pub parallelization_factor: f64,
283}
284
285/// Main benchmarking suite
286pub struct HardwareBenchmarkSuite {
287    calibration_manager: CalibrationManager,
288    config: BenchmarkConfig,
289}
290
291impl HardwareBenchmarkSuite {
292    /// Create a new benchmark suite
293    pub const fn new(calibration_manager: CalibrationManager, config: BenchmarkConfig) -> Self {
294        Self {
295            calibration_manager,
296            config,
297        }
298    }
299
300    /// Run comprehensive benchmarking suite
301    pub async fn run_benchmark_suite<E: DeviceExecutor>(
302        &self,
303        device_id: &str,
304        executor: &E,
305    ) -> DeviceResult<BenchmarkSuite> {
306        let start_time = Instant::now();
307
308        // Get device information
309        let backend_capabilities = query_backend_capabilities(Self::get_backend_type(device_id)?);
310
311        let calibration = self
312            .calibration_manager
313            .get_calibration(device_id)
314            .ok_or_else(|| DeviceError::APIError("No calibration data".into()))?;
315
316        // Run individual benchmarks
317        let mut benchmark_results = Vec::new();
318
319        // Basic gate benchmarks
320        for gate_type in &self.config.gate_types {
321            for &num_qubits in &self.config.qubit_counts {
322                if num_qubits <= backend_capabilities.features.max_qubits {
323                    let result = self
324                        .benchmark_gate_type(gate_type, num_qubits, executor, calibration)
325                        .await?;
326                    benchmark_results.push(result);
327                }
328            }
329        }
330
331        // Circuit depth benchmarks
332        for &depth in &self.config.circuit_depths {
333            for &num_qubits in &self.config.qubit_counts {
334                if num_qubits <= backend_capabilities.features.max_qubits {
335                    let result = self
336                        .benchmark_circuit_depth(depth, num_qubits, executor, calibration)
337                        .await?;
338                    benchmark_results.push(result);
339                }
340            }
341        }
342
343        // Randomized circuit benchmarks
344        let random_results = self
345            .benchmark_random_circuits(executor, calibration)
346            .await?;
347        benchmark_results.extend(random_results);
348
349        // Perform statistical analysis
350        let statistical_analysis = Self::perform_statistical_analysis(&benchmark_results)?;
351
352        // Perform graph analysis if enabled
353        let graph_analysis = if self.config.enable_graph_analysis {
354            Some(Self::perform_graph_analysis(
355                calibration,
356                &benchmark_results,
357            )?)
358        } else {
359            None
360        };
361
362        // Perform noise analysis if enabled
363        let noise_analysis = if self.config.enable_noise_analysis {
364            Some(Self::perform_noise_analysis(
365                calibration,
366                &benchmark_results,
367            )?)
368        } else {
369            None
370        };
371
372        // Calculate performance metrics
373        let performance_metrics = Self::calculate_performance_metrics(
374            &benchmark_results,
375            &statistical_analysis,
376            graph_analysis.as_ref(),
377        )?;
378
379        let execution_time = start_time.elapsed();
380
381        Ok(BenchmarkSuite {
382            device_id: device_id.to_string(),
383            backend_capabilities,
384            config: self.config.clone(),
385            benchmark_results,
386            statistical_analysis,
387            graph_analysis,
388            noise_analysis,
389            performance_metrics,
390            execution_time,
391        })
392    }
393
394    /// Benchmark specific gate type
395    async fn benchmark_gate_type<E: DeviceExecutor>(
396        &self,
397        gate_type: &str,
398        num_qubits: usize,
399        executor: &E,
400        calibration: &DeviceCalibration,
401    ) -> DeviceResult<BenchmarkResult> {
402        let mut execution_times = Vec::new();
403        let mut fidelities = Vec::new();
404        let mut error_rates = Vec::new();
405        let mut success_probabilities = Vec::new();
406        let mut queue_times = Vec::new();
407
408        for _ in 0..self.config.iterations {
409            let circuit: Circuit<8> = Self::create_gate_benchmark_circuit(gate_type, num_qubits)?;
410
411            let queue_start = Instant::now();
412            let exec_start = Instant::now();
413
414            // Execute circuit
415            let result = executor.execute_circuit(&circuit, 1000).await?;
416
417            let exec_time = exec_start.elapsed().as_secs_f64();
418            let queue_time = queue_start.elapsed().as_secs_f64();
419
420            // Calculate metrics
421            let fidelity = Self::calculate_fidelity(&result, &circuit, calibration)?;
422            let error_rate = 1.0 - fidelity;
423            let success_prob = Self::calculate_success_probability(&result)?;
424
425            execution_times.push(exec_time);
426            fidelities.push(fidelity);
427            error_rates.push(error_rate);
428            success_probabilities.push(success_prob);
429            queue_times.push(queue_time);
430        }
431
432        Ok(BenchmarkResult {
433            name: format!("{gate_type}_gate_benchmark"),
434            circuit_description: format!("{gate_type} gate on {num_qubits} qubits"),
435            num_qubits,
436            circuit_depth: 1,
437            gate_count: 1,
438            execution_times,
439            fidelities,
440            error_rates,
441            success_probabilities,
442            queue_times,
443        })
444    }
445
446    /// Benchmark circuit depth scaling
447    async fn benchmark_circuit_depth<E: DeviceExecutor>(
448        &self,
449        depth: usize,
450        num_qubits: usize,
451        executor: &E,
452        calibration: &DeviceCalibration,
453    ) -> DeviceResult<BenchmarkResult> {
454        let mut execution_times = Vec::new();
455        let mut fidelities = Vec::new();
456        let mut error_rates = Vec::new();
457        let mut success_probabilities = Vec::new();
458        let mut queue_times = Vec::new();
459
460        for _ in 0..self.config.iterations {
461            let circuit: Circuit<8> = Self::create_depth_benchmark_circuit(depth, num_qubits)?;
462
463            let queue_start = Instant::now();
464            let exec_start = Instant::now();
465
466            let result = executor.execute_circuit(&circuit, 1000).await?;
467
468            let exec_time = exec_start.elapsed().as_secs_f64();
469            let queue_time = queue_start.elapsed().as_secs_f64();
470
471            let fidelity = Self::calculate_fidelity(&result, &circuit, calibration)?;
472            let error_rate = 1.0 - fidelity;
473            let success_prob = Self::calculate_success_probability(&result)?;
474
475            execution_times.push(exec_time);
476            fidelities.push(fidelity);
477            error_rates.push(error_rate);
478            success_probabilities.push(success_prob);
479            queue_times.push(queue_time);
480        }
481
482        Ok(BenchmarkResult {
483            name: format!("depth_{depth}_benchmark"),
484            circuit_description: format!("Depth {depth} circuit on {num_qubits} qubits"),
485            num_qubits,
486            circuit_depth: depth,
487            gate_count: depth * num_qubits / 2, // Rough estimate
488            execution_times,
489            fidelities,
490            error_rates,
491            success_probabilities,
492            queue_times,
493        })
494    }
495
496    /// Benchmark random circuits
497    async fn benchmark_random_circuits<E: DeviceExecutor>(
498        &self,
499        executor: &E,
500        calibration: &DeviceCalibration,
501    ) -> DeviceResult<Vec<BenchmarkResult>> {
502        let mut results = Vec::new();
503
504        // Random circuit volume benchmarks
505        for &num_qubits in &self.config.qubit_counts {
506            for &depth in &self.config.circuit_depths {
507                if num_qubits <= 8 && depth <= 50 {
508                    // Reasonable limits
509                    let result = self
510                        .benchmark_random_circuit_volume(num_qubits, depth, executor, calibration)
511                        .await?;
512                    results.push(result);
513                }
514            }
515        }
516
517        Ok(results)
518    }
519
520    /// Benchmark random circuit volume
521    async fn benchmark_random_circuit_volume<E: DeviceExecutor>(
522        &self,
523        num_qubits: usize,
524        depth: usize,
525        executor: &E,
526        calibration: &DeviceCalibration,
527    ) -> DeviceResult<BenchmarkResult> {
528        let mut execution_times = Vec::new();
529        let mut fidelities = Vec::new();
530        let mut error_rates = Vec::new();
531        let mut success_probabilities = Vec::new();
532        let mut queue_times = Vec::new();
533
534        for _ in 0..self.config.iterations.min(20) {
535            // Limit for random circuits
536            let circuit = Self::create_random_circuit(num_qubits, depth)?;
537
538            let queue_start = Instant::now();
539            let exec_start = Instant::now();
540
541            let result = executor.execute_circuit(&circuit, 1000).await?;
542
543            let exec_time = exec_start.elapsed().as_secs_f64();
544            let queue_time = queue_start.elapsed().as_secs_f64();
545
546            let fidelity = Self::calculate_fidelity(&result, &circuit, calibration)?;
547            let error_rate = 1.0 - fidelity;
548            let success_prob = Self::calculate_success_probability(&result)?;
549
550            execution_times.push(exec_time);
551            fidelities.push(fidelity);
552            error_rates.push(error_rate);
553            success_probabilities.push(success_prob);
554            queue_times.push(queue_time);
555        }
556
557        Ok(BenchmarkResult {
558            name: format!("random_circuit_{num_qubits}q_{depth}d"),
559            circuit_description: format!("Random {num_qubits} qubit, depth {depth} circuit"),
560            num_qubits,
561            circuit_depth: depth,
562            gate_count: depth * num_qubits,
563            execution_times,
564            fidelities,
565            error_rates,
566            success_probabilities,
567            queue_times,
568        })
569    }
570
571    /// Perform comprehensive statistical analysis using SciRS2
572    fn perform_statistical_analysis(
573        results: &[BenchmarkResult],
574    ) -> DeviceResult<StatisticalAnalysis> {
575        // Collect all execution times and fidelities
576        let all_exec_times: Vec<f64> = results
577            .iter()
578            .flat_map(|r| r.execution_times.iter())
579            .copied()
580            .collect();
581
582        let all_fidelities: Vec<f64> = results
583            .iter()
584            .flat_map(|r| r.fidelities.iter())
585            .copied()
586            .collect();
587
588        let all_error_rates: Vec<f64> = results
589            .iter()
590            .flat_map(|r| r.error_rates.iter())
591            .copied()
592            .collect();
593
594        // Convert to ndarray for SciRS2
595        let exec_times_array = Array1::from_vec(all_exec_times);
596        let fidelities_array = Array1::from_vec(all_fidelities);
597        let error_rates_array = Array1::from_vec(all_error_rates);
598
599        // Compute descriptive statistics
600        let execution_time_stats = Self::compute_descriptive_stats(&exec_times_array)?;
601        let fidelity_stats = Self::compute_descriptive_stats(&fidelities_array)?;
602        let error_rate_stats = Self::compute_descriptive_stats(&error_rates_array)?;
603
604        // Compute correlation matrix
605        let exec_slice = exec_times_array
606            .as_slice()
607            .ok_or_else(|| DeviceError::APIError("Failed to get exec_times slice".to_string()))?;
608        let fid_slice = fidelities_array
609            .as_slice()
610            .ok_or_else(|| DeviceError::APIError("Failed to get fidelities slice".to_string()))?;
611        let err_slice = error_rates_array
612            .as_slice()
613            .ok_or_else(|| DeviceError::APIError("Failed to get error_rates slice".to_string()))?;
614
615        let data_matrix = Array2::from_shape_vec(
616            (exec_times_array.len(), 3),
617            [exec_slice, fid_slice, err_slice].concat(),
618        )
619        .map_err(|e| DeviceError::APIError(format!("Array creation error: {e}")))?;
620
621        let correlationmatrix = correlationmatrix(&data_matrix.view(), None)
622            .map_err(|e| DeviceError::APIError(format!("Correlation computation error: {e:?}")))?;
623
624        // Statistical tests
625        let mut statistical_tests = HashMap::new();
626
627        // Test if execution times follow normal distribution
628        if exec_times_array.len() >= 8 {
629            // One-sample t-test against expected baseline
630            let baseline_time = 1.0; // 1 second baseline
631            let t_test = ttest_1samp(
632                &exec_times_array.view(),
633                baseline_time,
634                Alternative::TwoSided,
635                "propagate",
636            )
637            .map_err(|e| DeviceError::APIError(format!("T-test error: {e:?}")))?;
638
639            statistical_tests.insert(
640                "execution_time_t_test".to_string(),
641                TestResult {
642                    test_name: "One-sample t-test (execution time)".to_string(),
643                    statistic: t_test.statistic,
644                    p_value: t_test.pvalue,
645                    significant: t_test.pvalue < 0.05,
646                    effect_size: Some(
647                        (execution_time_stats.mean - baseline_time) / execution_time_stats.std_dev,
648                    ),
649                },
650            );
651        }
652
653        // Distribution fitting
654        let mut distribution_fits = HashMap::new();
655
656        // Fit normal distribution to execution times
657        if let Ok(normal_dist) =
658            distributions::norm(execution_time_stats.mean, execution_time_stats.std_dev)
659        {
660            // Calculate goodness of fit (simplified)
661            let goodness_of_fit = Self::calculate_goodness_of_fit(&exec_times_array, &normal_dist)?;
662
663            distribution_fits.insert(
664                "execution_time_normal".to_string(),
665                DistributionFit {
666                    distribution_name: "Normal".to_string(),
667                    parameters: vec![execution_time_stats.mean, execution_time_stats.std_dev],
668                    goodness_of_fit,
669                    p_value: 0.5, // Placeholder
670                },
671            );
672        }
673
674        Ok(StatisticalAnalysis {
675            execution_time_stats,
676            fidelity_stats,
677            error_rate_stats,
678            correlationmatrix,
679            statistical_tests,
680            distribution_fits,
681        })
682    }
683
684    /// Perform graph-theoretic analysis
685    fn perform_graph_analysis(
686        calibration: &DeviceCalibration,
687        results: &[BenchmarkResult],
688    ) -> DeviceResult<GraphAnalysis> {
689        // Build connectivity graph from topology
690        let mut graph = Graph::new();
691        let mut node_map = HashMap::new();
692
693        // Add nodes for each qubit
694        for i in 0..calibration.topology.num_qubits {
695            let node = graph.add_node(i);
696            node_map.insert(i, node);
697        }
698
699        // Add edges based on coupling map
700        for &(q1, q2) in &calibration.topology.coupling_map {
701            let q1_idx = q1.0 as usize;
702            let q2_idx = q2.0 as usize;
703            if let (Some(&n1), Some(&n2)) = (node_map.get(&q1_idx), node_map.get(&q2_idx)) {
704                let _ = graph.add_edge(n1.index(), n2.index(), 1.0);
705            }
706        }
707
708        // Calculate graph metrics
709        let graph_density_val = graph_density(&graph)
710            .map_err(|e| DeviceError::APIError(format!("Graph density error: {e:?}")))?;
711
712        let clustering_coeff = clustering_coefficient(&graph)
713            .map_err(|e| DeviceError::APIError(format!("Clustering coefficient error: {e:?}")))?;
714
715        // Calculate centrality measures for each qubit
716        let betweenness_values = betweenness_centrality(&graph, false);
717
718        let closeness_values = closeness_centrality(&graph, true);
719
720        let mut centrality_measures = HashMap::new();
721        for i in 0..calibration.topology.num_qubits {
722            if let Some(&node) = node_map.get(&i) {
723                let node_idx = node.index();
724                centrality_measures.insert(
725                    i,
726                    CentralityMeasures {
727                        betweenness: betweenness_values.get(&node_idx).copied().unwrap_or(0.0),
728                        closeness: closeness_values.get(&node_idx).copied().unwrap_or(0.0),
729                        eigenvector: 0.0, // Placeholder
730                        degree: graph
731                            .neighbors(&node_idx)
732                            .map(|neighbors| neighbors.len() as f64)
733                            .unwrap_or(0.0),
734                    },
735                );
736            }
737        }
738
739        // Calculate spectral properties
740        let spectral_properties = Self::calculate_spectral_properties(calibration)?;
741
742        // Find critical paths
743        let compatible_node_map: HashMap<usize, usize> =
744            node_map.iter().map(|(&k, &v)| (k, v.index())).collect();
745        let critical_paths = Self::find_critical_paths(&graph, &compatible_node_map, results)?;
746
747        // Calculate topology-performance correlations
748        let mut topology_correlations = HashMap::new();
749
750        // Correlate connectivity with performance
751        let connectivity_scores: Vec<f64> = centrality_measures
752            .values()
753            .map(|c| c.betweenness + c.closeness)
754            .collect();
755
756        let avg_fidelities: Vec<f64> = results
757            .iter()
758            .take(connectivity_scores.len())
759            .map(|r| r.fidelities.iter().sum::<f64>() / r.fidelities.len() as f64)
760            .collect();
761
762        if connectivity_scores.len() == avg_fidelities.len() && connectivity_scores.len() >= 3 {
763            let conn_array = Array1::from_vec(connectivity_scores);
764            let fid_array = Array1::from_vec(avg_fidelities);
765
766            if let Ok((correlation, _)) =
767                pearsonr(&conn_array.view(), &fid_array.view(), "two-sided")
768            {
769                topology_correlations.insert("connectivity_fidelity".to_string(), correlation);
770            }
771        }
772
773        let connectivity_metrics = ConnectivityMetrics {
774            average_path_length: 0.0, // Placeholder - would calculate actual shortest paths
775            clustering_coefficient: clustering_coeff.values().sum::<f64>()
776                / clustering_coeff.len() as f64,
777            graph_density: graph_density_val,
778            diameter: 0, // Placeholder
779            spectral_gap: spectral_properties.spectral_gap,
780            centrality_measures,
781        };
782
783        Ok(GraphAnalysis {
784            connectivity_metrics,
785            topology_correlations,
786            critical_paths,
787            spectral_properties,
788        })
789    }
790
791    /// Perform noise correlation analysis
792    fn perform_noise_analysis(
793        calibration: &DeviceCalibration,
794        results: &[BenchmarkResult],
795    ) -> DeviceResult<NoiseAnalysis> {
796        let num_qubits = calibration.topology.num_qubits;
797
798        // Build crosstalk correlation matrix
799        let crosstalk_correlations = Array2::from_shape_fn((num_qubits, num_qubits), |(i, j)| {
800            if i == j {
801                1.0
802            } else {
803                // Use crosstalk matrix data if available
804                calibration
805                    .crosstalk_matrix
806                    .matrix
807                    .get(i)
808                    .and_then(|row| row.get(j))
809                    .copied()
810                    .unwrap_or(0.0)
811            }
812        });
813
814        // Temporal correlations (placeholder)
815        let temporal_correlations = Array1::zeros(results.len());
816
817        // Spatial noise patterns
818        let mut spatial_patterns = HashMap::new();
819
820        // Error rate spatial pattern
821        let error_pattern = Array2::from_shape_fn((num_qubits, num_qubits), |(i, j)| {
822            // Simplified spatial error pattern based on distance
823            let distance = ((i as f64) - (j as f64)).abs();
824            (-distance * 0.1).exp() * 0.01 // Exponential decay with distance
825        });
826        spatial_patterns.insert("error_rates".to_string(), error_pattern);
827
828        // Noise model validation
829        let noise_model = CalibrationNoiseModel::from_calibration(calibration);
830        let model_validation = Self::validate_noise_model(&noise_model, results)?;
831
832        Ok(NoiseAnalysis {
833            crosstalk_correlations,
834            temporal_correlations,
835            spatial_patterns,
836            model_validation,
837        })
838    }
839
840    /// Calculate performance metrics
841    fn calculate_performance_metrics(
842        results: &[BenchmarkResult],
843        stats: &StatisticalAnalysis,
844        graph_analysis: Option<&GraphAnalysis>,
845    ) -> DeviceResult<PerformanceMetrics> {
846        // Calculate component scores (0-100)
847        let reliability_score = (stats.fidelity_stats.mean * 100.0).min(100.0);
848        let speed_score = (1.0 / stats.execution_time_stats.mean * 10.0).min(100.0);
849        let accuracy_score = ((1.0 - stats.error_rate_stats.mean) * 100.0).min(100.0);
850
851        // Efficiency based on correlation between resources and performance
852        let efficiency_score = graph_analysis.map_or(50.0, |graph| {
853            // Use connectivity metrics to evaluate efficiency
854            (graph.connectivity_metrics.clustering_coefficient * 100.0).min(100.0)
855        });
856
857        // Scalability metrics
858        let scalability_metrics = Self::calculate_scalability_metrics(results)?;
859
860        // Overall score (weighted average)
861        let overall_score = (reliability_score * 0.3
862            + speed_score * 0.2
863            + accuracy_score * 0.3
864            + efficiency_score * 0.2)
865            .min(100.0);
866
867        Ok(PerformanceMetrics {
868            overall_score,
869            reliability_score,
870            speed_score,
871            accuracy_score,
872            efficiency_score,
873            scalability_metrics,
874        })
875    }
876
877    // Helper methods
878
879    fn get_backend_type(device_id: &str) -> DeviceResult<crate::translation::HardwareBackend> {
880        // Infer backend type from device ID
881        if device_id.contains("ibm") {
882            Ok(crate::translation::HardwareBackend::IBMQuantum)
883        } else if device_id.contains("ionq") {
884            Ok(crate::translation::HardwareBackend::IonQ)
885        } else {
886            Ok(crate::translation::HardwareBackend::IBMQuantum) // Default
887        }
888    }
889
890    fn create_gate_benchmark_circuit(
891        gate_type: &str,
892        num_qubits: usize,
893    ) -> DeviceResult<Circuit<8>> {
894        let mut circuit = Circuit::<8>::new();
895
896        match gate_type {
897            "H" => {
898                let _ = circuit.h(QubitId(0));
899            }
900            "CNOT" => {
901                let _ = circuit.cnot(QubitId(0), QubitId(1));
902            }
903            "RZ" => {
904                let _ = circuit.rz(QubitId(0), std::f64::consts::PI / 4.0);
905            }
906            _ => {
907                return Err(DeviceError::UnsupportedOperation(format!(
908                    "Gate type: {gate_type}"
909                )))
910            }
911        }
912
913        Ok(circuit)
914    }
915
916    fn create_depth_benchmark_circuit(depth: usize, num_qubits: usize) -> DeviceResult<Circuit<8>> {
917        let mut circuit = Circuit::<8>::new();
918
919        for layer in 0..depth {
920            for qubit in 0..num_qubits {
921                if layer % 2 == 0 {
922                    let _ = circuit.h(QubitId(qubit as u32));
923                } else if qubit + 1 < num_qubits {
924                    let _ = circuit.cnot(QubitId(qubit as u32), QubitId((qubit + 1) as u32));
925                }
926            }
927        }
928
929        Ok(circuit)
930    }
931
932    fn create_random_circuit<const N: usize>(
933        num_qubits: usize,
934        depth: usize,
935    ) -> DeviceResult<Circuit<N>> {
936        use scirs2_core::random::prelude::*;
937        let mut rng = thread_rng();
938        let mut circuit = Circuit::<N>::new();
939
940        let gates = ["H", "X", "Y", "Z", "CNOT", "RZ"];
941
942        for _ in 0..depth {
943            let gate_idx = rng.gen_range(0..gates.len());
944            let gate = gates[gate_idx];
945            let qubit = rng.gen_range(0..num_qubits) as u32;
946
947            match gate {
948                "H" => {
949                    let _ = circuit.h(QubitId(qubit));
950                }
951                "X" => {
952                    let _ = circuit.x(QubitId(qubit));
953                }
954                "Y" => {
955                    let _ = circuit.y(QubitId(qubit));
956                }
957                "Z" => {
958                    let _ = circuit.z(QubitId(qubit));
959                }
960                "CNOT" => {
961                    let target = rng.gen_range(0..num_qubits) as u32;
962                    if target != qubit {
963                        let _ = circuit.cnot(QubitId(qubit), QubitId(target));
964                    }
965                }
966                "RZ" => {
967                    let angle = rng.gen_range(-std::f64::consts::PI..std::f64::consts::PI);
968                    let _ = circuit.rz(QubitId(qubit), angle);
969                }
970                _ => {}
971            }
972        }
973
974        Ok(circuit)
975    }
976
977    fn calculate_fidelity(
978        result: &CircuitResult,
979        circuit: &Circuit<8>,
980        calibration: &DeviceCalibration,
981    ) -> DeviceResult<f64> {
982        // Simplified fidelity calculation based on expected vs actual outcomes
983        // In practice, this would use state tomography or process tomography
984
985        // For now, estimate based on gate error rates
986        let mut total_fidelity = 1.0;
987
988        for gate in circuit.gates() {
989            let qubits = gate.qubits();
990
991            if qubits.len() == 1 {
992                if let Some(gate_cal) = calibration.single_qubit_gates.get(gate.name()) {
993                    if let Some(qubit_data) = gate_cal.qubit_data.get(&qubits[0]) {
994                        total_fidelity *= qubit_data.fidelity;
995                    }
996                }
997            } else if qubits.len() == 2 {
998                let qubit_pair = (qubits[0], qubits[1]);
999                if let Some(gate_cal) = calibration.two_qubit_gates.get(&qubit_pair) {
1000                    total_fidelity *= gate_cal.fidelity;
1001                }
1002            }
1003        }
1004
1005        Ok(total_fidelity)
1006    }
1007
1008    fn calculate_success_probability(result: &CircuitResult) -> DeviceResult<f64> {
1009        // Calculate success probability based on measurement outcomes
1010        let total_shots = result.shots as f64;
1011        let successful_outcomes = result
1012            .counts
1013            .values()
1014            .map(|&count| count as f64)
1015            .sum::<f64>();
1016
1017        Ok(successful_outcomes / total_shots)
1018    }
1019
1020    fn compute_descriptive_stats(data: &Array1<f64>) -> DeviceResult<DescriptiveStats> {
1021        let mean_val = mean(&data.view())
1022            .map_err(|e| DeviceError::APIError(format!("Mean calculation error: {e:?}")))?;
1023
1024        let median_val = median(&data.view())
1025            .map_err(|e| DeviceError::APIError(format!("Median calculation error: {e:?}")))?;
1026
1027        let std_val = std(&data.view(), 1, None)
1028            .map_err(|e| DeviceError::APIError(format!("Std calculation error: {e:?}")))?;
1029
1030        let var_val = var(&data.view(), 1, None)
1031            .map_err(|e| DeviceError::APIError(format!("Variance calculation error: {e:?}")))?;
1032
1033        let min_val = data.iter().fold(f64::INFINITY, |a, &b| a.min(b));
1034        let max_val = data.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
1035
1036        // Calculate quartiles (simplified)
1037        let mut sorted_data = data.to_vec();
1038        sorted_data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1039
1040        let n = sorted_data.len();
1041        let q25 = sorted_data[n / 4];
1042        let q75 = sorted_data[3 * n / 4];
1043
1044        // Confidence interval (95%)
1045        let t_critical = 1.96; // Approximate for large samples
1046        let sem = std_val / (n as f64).sqrt();
1047        let margin = t_critical * sem;
1048        let confidence_interval = (mean_val - margin, mean_val + margin);
1049
1050        Ok(DescriptiveStats {
1051            mean: mean_val,
1052            median: median_val,
1053            std_dev: std_val,
1054            variance: var_val,
1055            min: min_val,
1056            max: max_val,
1057            q25,
1058            q75,
1059            confidence_interval,
1060        })
1061    }
1062
1063    fn calculate_goodness_of_fit(
1064        data: &Array1<f64>,
1065        distribution: &dyn scirs2_stats::traits::Distribution<f64>,
1066    ) -> DeviceResult<f64> {
1067        // Simplified goodness of fit using Kolmogorov-Smirnov test
1068        // In practice, would use proper KS test from SciRS2
1069
1070        let mut sorted_data = data.to_vec();
1071        sorted_data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
1072
1073        let n = sorted_data.len() as f64;
1074        let mut max_diff: f64 = 0.0;
1075
1076        for (i, &value) in sorted_data.iter().enumerate() {
1077            let empirical_cdf = (i + 1) as f64 / n;
1078            let theoretical_cdf = 0.5; // Fallback for missing CDF method
1079            let diff = (empirical_cdf - theoretical_cdf).abs();
1080            max_diff = max_diff.max(diff);
1081        }
1082
1083        Ok(1.0 - max_diff) // Convert to goodness (higher is better)
1084    }
1085
1086    fn calculate_spectral_properties(
1087        calibration: &DeviceCalibration,
1088    ) -> DeviceResult<SpectralProperties> {
1089        let n = calibration.topology.num_qubits;
1090
1091        // Build adjacency matrix
1092        let mut adj_matrix = Array2::zeros((n, n));
1093        for &(q1, q2) in &calibration.topology.coupling_map {
1094            adj_matrix[[q1.0 as usize, q2.0 as usize]] = 1.0;
1095            adj_matrix[[q2.0 as usize, q1.0 as usize]] = 1.0;
1096        }
1097
1098        // Calculate eigenvalues
1099        let eigenvalues = match eigvals(&adj_matrix.view(), None) {
1100            Ok(vals) => vals.mapv(|c| c.re), // Take real parts
1101            Err(_) => Array1::zeros(n),      // Fallback
1102        };
1103
1104        let spectral_radius = eigenvalues
1105            .iter()
1106            .fold(0.0_f64, |max, &val: &f64| max.max(val.abs()));
1107
1108        // Sort eigenvalues
1109        let mut sorted_eigenvals = eigenvalues.to_vec();
1110        sorted_eigenvals.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
1111
1112        let algebraic_connectivity = if sorted_eigenvals.len() > 1 {
1113            sorted_eigenvals[sorted_eigenvals.len() - 2]
1114        } else {
1115            0.0
1116        };
1117
1118        let spectral_gap = if sorted_eigenvals.len() > 1 {
1119            sorted_eigenvals[0] - sorted_eigenvals[1]
1120        } else {
1121            0.0
1122        };
1123
1124        Ok(SpectralProperties {
1125            eigenvalues: Array1::from_vec(sorted_eigenvals),
1126            spectral_radius,
1127            algebraic_connectivity,
1128            spectral_gap,
1129        })
1130    }
1131
1132    fn find_critical_paths(
1133        graph: &Graph<usize, f64>,
1134        node_map: &HashMap<usize, usize>,
1135        results: &[BenchmarkResult],
1136    ) -> DeviceResult<Vec<CriticalPath>> {
1137        let mut critical_paths = Vec::new();
1138
1139        // Find paths between high-error qubits
1140        // This is a simplified version - would use more sophisticated analysis
1141
1142        for i in 0..3 {
1143            // Find up to 3 critical paths
1144            let path = vec![i, i + 1, i + 2]; // Simplified linear path
1145
1146            critical_paths.push(CriticalPath {
1147                path: path.clone(),
1148                length: path.len(),
1149                bottleneck_qubits: vec![i + 1], // Middle qubit as bottleneck
1150                expected_performance_impact: 0.1 * (i + 1) as f64,
1151            });
1152        }
1153
1154        Ok(critical_paths)
1155    }
1156
1157    fn validate_noise_model(
1158        noise_model: &CalibrationNoiseModel,
1159        results: &[BenchmarkResult],
1160    ) -> DeviceResult<NoiseModelValidation> {
1161        // Compare predicted vs actual error rates
1162        let predicted_errors: Vec<f64> = results
1163            .iter()
1164            .map(|r| {
1165                // Simplified prediction based on gate count
1166                let base_error = 0.001;
1167                base_error * r.gate_count as f64
1168            })
1169            .collect();
1170
1171        let actual_errors: Vec<f64> = results
1172            .iter()
1173            .map(|r| r.error_rates.iter().sum::<f64>() / r.error_rates.len() as f64)
1174            .collect();
1175
1176        let prediction_errors = Array1::from_vec(
1177            predicted_errors
1178                .iter()
1179                .zip(actual_errors.iter())
1180                .map(|(pred, actual)| (pred - actual).abs())
1181                .collect(),
1182        );
1183
1184        let model_accuracy = 1.0 - (prediction_errors.mean().unwrap_or(0.5));
1185
1186        // Residual analysis (simplified)
1187        let residuals = Array1::from_vec(
1188            predicted_errors
1189                .iter()
1190                .zip(actual_errors.iter())
1191                .map(|(pred, actual)| pred - actual)
1192                .collect(),
1193        );
1194
1195        let normality_test = TestResult {
1196            test_name: "Residual normality test".to_string(),
1197            statistic: 0.0,
1198            p_value: 0.5,
1199            significant: false,
1200            effect_size: None,
1201        };
1202
1203        let residual_analysis = ResidualAnalysis {
1204            normality_test,
1205            autocorrelation: Array1::zeros(5), // Placeholder
1206            heteroscedasticity_test: TestResult {
1207                test_name: "Heteroscedasticity test".to_string(),
1208                statistic: 0.0,
1209                p_value: 0.5,
1210                significant: false,
1211                effect_size: None,
1212            },
1213        };
1214
1215        Ok(NoiseModelValidation {
1216            model_accuracy,
1217            prediction_errors,
1218            residual_analysis,
1219        })
1220    }
1221
1222    fn calculate_scalability_metrics(
1223        results: &[BenchmarkResult],
1224    ) -> DeviceResult<ScalabilityMetrics> {
1225        // Analyze how performance scales with circuit size
1226
1227        // Group results by depth and width
1228        let mut depth_times = HashMap::new();
1229        let mut width_times = HashMap::new();
1230
1231        for result in results {
1232            depth_times
1233                .entry(result.circuit_depth)
1234                .or_insert_with(Vec::new)
1235                .extend(&result.execution_times);
1236
1237            width_times
1238                .entry(result.num_qubits)
1239                .or_insert_with(Vec::new)
1240                .extend(&result.execution_times);
1241        }
1242
1243        // Calculate scaling coefficients (simplified linear regression)
1244        let depth_scaling_coefficient = Self::calculate_scaling_coefficient(&depth_times)?;
1245        let width_scaling_coefficient = Self::calculate_scaling_coefficient(&width_times)?;
1246
1247        let resource_efficiency =
1248            1.0 / (depth_scaling_coefficient + width_scaling_coefficient).max(0.1);
1249        let parallelization_factor = 0.8; // Placeholder - would measure actual parallelization
1250
1251        Ok(ScalabilityMetrics {
1252            depth_scaling_coefficient,
1253            width_scaling_coefficient,
1254            resource_efficiency,
1255            parallelization_factor,
1256        })
1257    }
1258
1259    fn calculate_scaling_coefficient(data: &HashMap<usize, Vec<f64>>) -> DeviceResult<f64> {
1260        if data.len() < 2 {
1261            return Ok(1.0); // Default linear scaling
1262        }
1263
1264        // Simple linear regression: time = a * size + b
1265        // Return coefficient 'a'
1266
1267        let mut x_values = Vec::new();
1268        let mut y_values = Vec::new();
1269
1270        for (&size, times) in data {
1271            let avg_time = times.iter().sum::<f64>() / times.len() as f64;
1272            x_values.push(size as f64);
1273            y_values.push(avg_time);
1274        }
1275
1276        // Calculate slope (scaling coefficient)
1277        let n = x_values.len() as f64;
1278        let sum_x: f64 = x_values.iter().sum();
1279        let sum_y: f64 = y_values.iter().sum();
1280        let sum_xy: f64 = x_values
1281            .iter()
1282            .zip(y_values.iter())
1283            .map(|(x, y)| x * y)
1284            .sum();
1285        let sum_x_sq: f64 = x_values.iter().map(|x| x * x).sum();
1286
1287        let slope = n.mul_add(sum_xy, -(sum_x * sum_y)) / n.mul_add(sum_x_sq, -(sum_x * sum_x));
1288
1289        Ok(slope.max(0.1)) // Ensure positive scaling
1290    }
1291}
1292
1293/// Trait for devices that can execute circuits (for benchmarking)
1294#[async_trait::async_trait]
1295pub trait DeviceExecutor {
1296    async fn execute_circuit<const N: usize>(
1297        &self,
1298        circuit: &Circuit<N>,
1299        shots: usize,
1300    ) -> DeviceResult<CircuitResult>;
1301}
1302
1303#[cfg(test)]
1304mod tests {
1305    use super::*;
1306    use crate::calibration::create_ideal_calibration;
1307
1308    #[test]
1309    fn test_benchmark_config_default() {
1310        let config = BenchmarkConfig::default();
1311        assert_eq!(config.iterations, 100);
1312        assert!(config.enable_advanced_stats);
1313    }
1314
1315    #[test]
1316    fn test_descriptive_stats_calculation() {
1317        let data = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
1318        let suite =
1319            HardwareBenchmarkSuite::new(CalibrationManager::new(), BenchmarkConfig::default());
1320
1321        let stats = HardwareBenchmarkSuite::compute_descriptive_stats(&data)
1322            .expect("Descriptive stats computation should succeed");
1323        assert!((stats.mean - 3.0).abs() < 1e-10);
1324        assert!((stats.median - 3.0).abs() < 1e-10);
1325    }
1326
1327    #[test]
1328    fn test_spectral_properties() {
1329        let calibration = create_ideal_calibration("test".to_string(), 4);
1330        let suite =
1331            HardwareBenchmarkSuite::new(CalibrationManager::new(), BenchmarkConfig::default());
1332
1333        let spectral_props = HardwareBenchmarkSuite::calculate_spectral_properties(&calibration)
1334            .expect("Spectral properties calculation should succeed");
1335        assert!(spectral_props.eigenvalues.len() == 4);
1336        assert!(spectral_props.spectral_radius >= 0.0);
1337    }
1338}