Skip to main content

quantrs2_device/
hardware_benchmarks_scirs2.rs

1//! Hardware Benchmarking with SciRS2 Analysis
2//!
3//! This module provides comprehensive hardware performance benchmarking using SciRS2's
4//! advanced statistical analysis and metrics capabilities for quantum device characterization.
5
6use crate::{DeviceError, DeviceResult};
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::random::prelude::*;
9use scirs2_stats::{mean, median, std};
10
11/// Hardware benchmark configuration
12#[derive(Debug, Clone)]
13pub struct HardwareBenchmarkConfig {
14    /// Number of benchmark iterations
15    pub num_iterations: usize,
16    /// Number of qubits to benchmark
17    pub num_qubits: usize,
18    /// Include gate fidelity benchmarks
19    pub benchmark_gate_fidelity: bool,
20    /// Include coherence time benchmarks
21    pub benchmark_coherence: bool,
22    /// Include readout fidelity benchmarks
23    pub benchmark_readout: bool,
24    /// Confidence level for statistical analysis
25    pub confidence_level: f64,
26}
27
28impl Default for HardwareBenchmarkConfig {
29    fn default() -> Self {
30        Self {
31            num_iterations: 1000,
32            num_qubits: 2,
33            benchmark_gate_fidelity: true,
34            benchmark_coherence: true,
35            benchmark_readout: true,
36            confidence_level: 0.95,
37        }
38    }
39}
40
41/// Benchmark result with SciRS2 statistical analysis
42#[derive(Debug, Clone)]
43pub struct BenchmarkResult {
44    /// Benchmark name
45    pub name: String,
46    /// Mean performance metric
47    pub mean_value: f64,
48    /// Standard deviation
49    pub std_dev: f64,
50    /// Median value
51    pub median_value: f64,
52    /// Minimum value observed
53    pub min_value: f64,
54    /// Maximum value observed
55    pub max_value: f64,
56    /// 95% confidence interval
57    pub confidence_interval: (f64, f64),
58    /// Number of samples
59    pub num_samples: usize,
60    /// Statistical significance (p-value if comparing to baseline)
61    pub p_value: Option<f64>,
62}
63
64/// Comprehensive hardware benchmark report
65#[derive(Debug, Clone)]
66pub struct HardwareBenchmarkReport {
67    /// Device name or identifier
68    pub device_name: String,
69    /// Individual benchmark results
70    pub benchmarks: Vec<BenchmarkResult>,
71    /// Overall device score (0-100)
72    pub overall_score: f64,
73    /// Gate fidelity analysis
74    pub gate_fidelity_analysis: Option<GateFidelityAnalysis>,
75    /// Coherence time analysis
76    pub coherence_analysis: Option<CoherenceAnalysis>,
77    /// Readout fidelity analysis
78    pub readout_analysis: Option<ReadoutFidelityAnalysis>,
79    /// Timestamp of benchmark run
80    pub timestamp: std::time::SystemTime,
81}
82
83/// Gate fidelity analysis using SciRS2 metrics
84#[derive(Debug, Clone)]
85pub struct GateFidelityAnalysis {
86    /// Single-qubit gate fidelities
87    pub single_qubit_fidelity: Array1<f64>,
88    /// Two-qubit gate fidelities
89    pub two_qubit_fidelity: Array1<f64>,
90    /// Average single-qubit fidelity
91    pub avg_single_qubit: f64,
92    /// Average two-qubit fidelity
93    pub avg_two_qubit: f64,
94    /// Gate error rates
95    pub error_rates: Array1<f64>,
96}
97
98/// Coherence time analysis
99#[derive(Debug, Clone)]
100pub struct CoherenceAnalysis {
101    /// T1 relaxation times per qubit (microseconds)
102    pub t1_times: Array1<f64>,
103    /// T2 dephasing times per qubit (microseconds)
104    pub t2_times: Array1<f64>,
105    /// Average T1 time
106    pub avg_t1: f64,
107    /// Average T2 time
108    pub avg_t2: f64,
109    /// T1/T2 ratio analysis
110    pub t1_t2_ratio: Array1<f64>,
111}
112
113/// Readout fidelity analysis
114#[derive(Debug, Clone)]
115pub struct ReadoutFidelityAnalysis {
116    /// Readout fidelity per qubit
117    pub readout_fidelity: Array1<f64>,
118    /// Average readout fidelity
119    pub avg_fidelity: f64,
120    /// Assignment error matrix (confusion matrix)
121    pub assignment_errors: Array2<f64>,
122    /// SPAM (State Preparation And Measurement) error
123    pub spam_error: f64,
124}
125
126/// Hardware benchmarking engine using SciRS2
127pub struct HardwareBenchmarker {
128    config: HardwareBenchmarkConfig,
129    rng: StdRng,
130}
131
132impl HardwareBenchmarker {
133    /// Create a new hardware benchmarker
134    pub fn new(config: HardwareBenchmarkConfig) -> Self {
135        Self {
136            config,
137            rng: StdRng::seed_from_u64(42),
138        }
139    }
140
141    /// Create benchmarker with default configuration
142    pub fn default() -> Self {
143        Self::new(HardwareBenchmarkConfig::default())
144    }
145
146    /// Run comprehensive hardware benchmarks
147    ///
148    /// # Arguments
149    /// * `device_name` - Name or identifier of the device being benchmarked
150    /// * `measurement_data` - Raw measurement data from hardware
151    ///
152    /// # Returns
153    /// Comprehensive benchmark report with SciRS2 statistical analysis
154    pub fn run_benchmarks(
155        &mut self,
156        device_name: &str,
157        measurement_data: &BenchmarkMeasurementData,
158    ) -> DeviceResult<HardwareBenchmarkReport> {
159        let mut benchmarks = Vec::new();
160
161        // Benchmark gate execution times
162        let gate_timing = self.benchmark_gate_timing(&measurement_data.gate_times)?;
163        benchmarks.push(gate_timing);
164
165        // Benchmark gate fidelities if enabled
166        let gate_fidelity_analysis = if self.config.benchmark_gate_fidelity {
167            Some(self.analyze_gate_fidelity(&measurement_data.fidelity_data)?)
168        } else {
169            None
170        };
171
172        // Benchmark coherence times if enabled
173        let coherence_analysis = if self.config.benchmark_coherence {
174            Some(self.analyze_coherence(&measurement_data.coherence_data)?)
175        } else {
176            None
177        };
178
179        // Benchmark readout fidelity if enabled
180        let readout_analysis = if self.config.benchmark_readout {
181            Some(self.analyze_readout_fidelity(&measurement_data.readout_data)?)
182        } else {
183            None
184        };
185
186        // Compute overall device score
187        let overall_score = self.compute_overall_score(
188            &gate_fidelity_analysis,
189            &coherence_analysis,
190            &readout_analysis,
191        );
192
193        Ok(HardwareBenchmarkReport {
194            device_name: device_name.to_string(),
195            benchmarks,
196            overall_score,
197            gate_fidelity_analysis,
198            coherence_analysis,
199            readout_analysis,
200            timestamp: std::time::SystemTime::now(),
201        })
202    }
203
204    /// Benchmark gate execution timing
205    fn benchmark_gate_timing(&self, gate_times: &Array1<f64>) -> DeviceResult<BenchmarkResult> {
206        if gate_times.is_empty() {
207            return Err(DeviceError::InvalidInput(
208                "Empty gate timing data".to_string(),
209            ));
210        }
211
212        let mean_time = mean(&gate_times.view())?;
213        let std_time = std(&gate_times.view(), 1, None)?;
214        let median_time = median(&gate_times.view())?;
215
216        let min_time = gate_times.iter().cloned().fold(f64::INFINITY, f64::min);
217        let max_time = gate_times.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
218
219        // Compute confidence interval (assuming normal distribution)
220        let n = gate_times.len() as f64;
221        let z_critical = 1.96; // 95% confidence
222        let margin = z_critical * std_time / n.sqrt();
223
224        Ok(BenchmarkResult {
225            name: "Gate Execution Time".to_string(),
226            mean_value: mean_time,
227            std_dev: std_time,
228            median_value: median_time,
229            min_value: min_time,
230            max_value: max_time,
231            confidence_interval: (mean_time - margin, mean_time + margin),
232            num_samples: gate_times.len(),
233            p_value: None,
234        })
235    }
236
237    /// Analyze gate fidelity using SciRS2 metrics
238    fn analyze_gate_fidelity(
239        &self,
240        fidelity_data: &GateFidelityData,
241    ) -> DeviceResult<GateFidelityAnalysis> {
242        // Compute average fidelities
243        let avg_single = mean(&fidelity_data.single_qubit_fidelities.view())?;
244        let avg_two = mean(&fidelity_data.two_qubit_fidelities.view())?;
245
246        // Compute error rates (1 - fidelity)
247        let error_rates = fidelity_data.single_qubit_fidelities.mapv(|f| 1.0 - f);
248
249        Ok(GateFidelityAnalysis {
250            single_qubit_fidelity: fidelity_data.single_qubit_fidelities.clone(),
251            two_qubit_fidelity: fidelity_data.two_qubit_fidelities.clone(),
252            avg_single_qubit: avg_single,
253            avg_two_qubit: avg_two,
254            error_rates,
255        })
256    }
257
258    /// Analyze coherence times
259    fn analyze_coherence(&self, coherence_data: &CoherenceData) -> DeviceResult<CoherenceAnalysis> {
260        let avg_t1 = mean(&coherence_data.t1_times.view())?;
261        let avg_t2 = mean(&coherence_data.t2_times.view())?;
262
263        // Compute T1/T2 ratio for each qubit
264        let t1_t2_ratio = Array1::from_shape_fn(coherence_data.t1_times.len(), |i| {
265            coherence_data.t1_times[i] / coherence_data.t2_times[i].max(0.001)
266        });
267
268        Ok(CoherenceAnalysis {
269            t1_times: coherence_data.t1_times.clone(),
270            t2_times: coherence_data.t2_times.clone(),
271            avg_t1,
272            avg_t2,
273            t1_t2_ratio,
274        })
275    }
276
277    /// Analyze readout fidelity
278    fn analyze_readout_fidelity(
279        &self,
280        readout_data: &ReadoutData,
281    ) -> DeviceResult<ReadoutFidelityAnalysis> {
282        let avg_fidelity = mean(&readout_data.readout_fidelities.view())?;
283
284        // Compute SPAM error (average of assignment errors)
285        let spam_error = if readout_data.assignment_errors.nrows() > 0
286            && readout_data.assignment_errors.ncols() > 0
287        {
288            let mut sum = 0.0;
289            let mut count = 0;
290            for i in 0..readout_data.assignment_errors.nrows() {
291                for j in 0..readout_data.assignment_errors.ncols() {
292                    if i != j {
293                        // Off-diagonal elements are errors
294                        sum += readout_data.assignment_errors[[i, j]];
295                        count += 1;
296                    }
297                }
298            }
299            if count > 0 {
300                sum / count as f64
301            } else {
302                0.0
303            }
304        } else {
305            0.0
306        };
307
308        Ok(ReadoutFidelityAnalysis {
309            readout_fidelity: readout_data.readout_fidelities.clone(),
310            avg_fidelity,
311            assignment_errors: readout_data.assignment_errors.clone(),
312            spam_error,
313        })
314    }
315
316    /// Compute overall device score (0-100)
317    fn compute_overall_score(
318        &self,
319        gate_fidelity: &Option<GateFidelityAnalysis>,
320        coherence: &Option<CoherenceAnalysis>,
321        readout: &Option<ReadoutFidelityAnalysis>,
322    ) -> f64 {
323        let mut score = 0.0;
324        let mut weight_sum = 0.0;
325
326        // Gate fidelity contributes 40% to score
327        if let Some(gf) = gate_fidelity {
328            let gate_score = (gf.avg_single_qubit + gf.avg_two_qubit) / 2.0 * 100.0;
329            score += gate_score * 0.4;
330            weight_sum += 0.4;
331        }
332
333        // Coherence times contribute 30% to score (normalized to 100 μs)
334        if let Some(coh) = coherence {
335            let coherence_score =
336                ((coh.avg_t1 / 100.0).min(1.0) + (coh.avg_t2 / 100.0).min(1.0)) / 2.0 * 100.0;
337            score += coherence_score * 0.3;
338            weight_sum += 0.3;
339        }
340
341        // Readout fidelity contributes 30% to score
342        if let Some(ro) = readout {
343            let readout_score = ro.avg_fidelity * 100.0;
344            score += readout_score * 0.3;
345            weight_sum += 0.3;
346        }
347
348        if weight_sum > 0.0 {
349            score / weight_sum
350        } else {
351            0.0
352        }
353    }
354
355    /// Generate synthetic benchmark data for testing
356    pub fn generate_synthetic_data(&mut self, num_qubits: usize) -> BenchmarkMeasurementData {
357        // Generate gate execution times (50-200 nanoseconds with noise)
358        let gate_times = Array1::from_shape_fn(self.config.num_iterations, |_| {
359            let base_time = 100.0;
360            let noise = self.rng.random::<f64>() * 50.0;
361            base_time + noise
362        });
363
364        // Generate single-qubit gate fidelities (99.5% - 99.99%)
365        let single_qubit_fidelities =
366            Array1::from_shape_fn(num_qubits, |_| 0.995 + self.rng.random::<f64>() * 0.0049);
367
368        // Generate two-qubit gate fidelities (98% - 99.5%)
369        let two_qubit_fidelities = Array1::from_shape_fn(num_qubits.saturating_sub(1), |_| {
370            0.980 + self.rng.random::<f64>() * 0.015
371        });
372
373        // Generate T1 times (30-100 microseconds)
374        let t1_times =
375            Array1::from_shape_fn(num_qubits, |_| 30.0 + self.rng.random::<f64>() * 70.0);
376
377        // Generate T2 times (20-80 microseconds, always <= T1)
378        let t2_times = Array1::from_shape_fn(num_qubits, |i| {
379            let max_t2 = t1_times[i] * 0.8;
380            20.0 + self.rng.random::<f64>() * (max_t2 - 20.0).max(0.0)
381        });
382
383        // Generate readout fidelities (95% - 99%)
384        let readout_fidelities =
385            Array1::from_shape_fn(num_qubits, |_| 0.95 + self.rng.random::<f64>() * 0.04);
386
387        // Generate assignment error matrix (simplified 2x2 for binary readout)
388        let mut assignment_errors = Array2::zeros((2, 2));
389        assignment_errors[[0, 0]] = 0.98; // P(measure 0 | state 0)
390        assignment_errors[[0, 1]] = 0.02; // P(measure 1 | state 0)
391        assignment_errors[[1, 0]] = 0.03; // P(measure 0 | state 1)
392        assignment_errors[[1, 1]] = 0.97; // P(measure 1 | state 1)
393
394        BenchmarkMeasurementData {
395            gate_times,
396            fidelity_data: GateFidelityData {
397                single_qubit_fidelities,
398                two_qubit_fidelities,
399            },
400            coherence_data: CoherenceData { t1_times, t2_times },
401            readout_data: ReadoutData {
402                readout_fidelities,
403                assignment_errors,
404            },
405        }
406    }
407}
408
409/// Input data for hardware benchmarking
410#[derive(Debug, Clone)]
411pub struct BenchmarkMeasurementData {
412    /// Gate execution times (nanoseconds)
413    pub gate_times: Array1<f64>,
414    /// Gate fidelity measurements
415    pub fidelity_data: GateFidelityData,
416    /// Coherence time measurements
417    pub coherence_data: CoherenceData,
418    /// Readout fidelity measurements
419    pub readout_data: ReadoutData,
420}
421
422/// Gate fidelity measurement data
423#[derive(Debug, Clone)]
424pub struct GateFidelityData {
425    /// Single-qubit gate fidelities
426    pub single_qubit_fidelities: Array1<f64>,
427    /// Two-qubit gate fidelities
428    pub two_qubit_fidelities: Array1<f64>,
429}
430
431/// Coherence time measurement data
432#[derive(Debug, Clone)]
433pub struct CoherenceData {
434    /// T1 relaxation times (microseconds)
435    pub t1_times: Array1<f64>,
436    /// T2 dephasing times (microseconds)
437    pub t2_times: Array1<f64>,
438}
439
440/// Readout fidelity measurement data
441#[derive(Debug, Clone)]
442pub struct ReadoutData {
443    /// Readout fidelity per qubit
444    pub readout_fidelities: Array1<f64>,
445    /// Assignment error matrix
446    pub assignment_errors: Array2<f64>,
447}
448
449#[cfg(test)]
450mod tests {
451    use super::*;
452
453    #[test]
454    fn test_benchmarker_creation() {
455        let config = HardwareBenchmarkConfig::default();
456        let benchmarker = HardwareBenchmarker::new(config);
457        assert_eq!(benchmarker.config.num_iterations, 1000);
458    }
459
460    #[test]
461    fn test_synthetic_data_generation() {
462        let config = HardwareBenchmarkConfig::default();
463        let mut benchmarker = HardwareBenchmarker::new(config);
464
465        let data = benchmarker.generate_synthetic_data(3);
466
467        assert_eq!(data.gate_times.len(), 1000);
468        assert_eq!(data.fidelity_data.single_qubit_fidelities.len(), 3);
469        assert_eq!(data.coherence_data.t1_times.len(), 3);
470        assert_eq!(data.readout_data.readout_fidelities.len(), 3);
471    }
472
473    #[test]
474    fn test_full_benchmark_run() {
475        let config = HardwareBenchmarkConfig::default();
476        let mut benchmarker = HardwareBenchmarker::new(config);
477
478        let data = benchmarker.generate_synthetic_data(2);
479        let report = benchmarker.run_benchmarks("TestDevice", &data);
480
481        assert!(report.is_ok());
482        let report = report.expect("Benchmark failed");
483
484        assert_eq!(report.device_name, "TestDevice");
485        assert!(!report.benchmarks.is_empty());
486        assert!(report.overall_score > 0.0 && report.overall_score <= 100.0);
487    }
488
489    #[test]
490    fn test_gate_timing_benchmark() {
491        let config = HardwareBenchmarkConfig::default();
492        let benchmarker = HardwareBenchmarker::new(config);
493
494        let gate_times = Array1::from_vec(vec![100.0, 110.0, 105.0, 95.0, 100.0]);
495        let result = benchmarker.benchmark_gate_timing(&gate_times);
496
497        assert!(result.is_ok());
498        let result = result.expect("Benchmark failed");
499
500        assert_eq!(result.name, "Gate Execution Time");
501        assert!((result.mean_value - 102.0).abs() < 1.0);
502        assert!(result.std_dev > 0.0);
503        assert_eq!(result.num_samples, 5);
504    }
505
506    #[test]
507    fn test_gate_fidelity_analysis() {
508        let config = HardwareBenchmarkConfig::default();
509        let benchmarker = HardwareBenchmarker::new(config);
510
511        let fidelity_data = GateFidelityData {
512            single_qubit_fidelities: Array1::from_vec(vec![0.999, 0.998, 0.997]),
513            two_qubit_fidelities: Array1::from_vec(vec![0.99, 0.985]),
514        };
515
516        let analysis = benchmarker.analyze_gate_fidelity(&fidelity_data);
517
518        assert!(analysis.is_ok());
519        let analysis = analysis.expect("Analysis failed");
520
521        assert!((analysis.avg_single_qubit - 0.998).abs() < 0.001);
522        assert!((analysis.avg_two_qubit - 0.9875).abs() < 0.001);
523        assert_eq!(analysis.error_rates.len(), 3);
524    }
525
526    #[test]
527    fn test_coherence_analysis() {
528        let config = HardwareBenchmarkConfig::default();
529        let benchmarker = HardwareBenchmarker::new(config);
530
531        let coherence_data = CoherenceData {
532            t1_times: Array1::from_vec(vec![50.0, 60.0, 55.0]),
533            t2_times: Array1::from_vec(vec![40.0, 48.0, 44.0]),
534        };
535
536        let analysis = benchmarker.analyze_coherence(&coherence_data);
537
538        assert!(analysis.is_ok());
539        let analysis = analysis.expect("Analysis failed");
540
541        assert!((analysis.avg_t1 - 55.0).abs() < 0.1);
542        assert!((analysis.avg_t2 - 44.0).abs() < 0.1);
543        assert_eq!(analysis.t1_t2_ratio.len(), 3);
544    }
545
546    #[test]
547    fn test_readout_fidelity_analysis() {
548        let config = HardwareBenchmarkConfig::default();
549        let benchmarker = HardwareBenchmarker::new(config);
550
551        let mut assignment_errors = Array2::zeros((2, 2));
552        assignment_errors[[0, 0]] = 0.97;
553        assignment_errors[[0, 1]] = 0.03;
554        assignment_errors[[1, 0]] = 0.02;
555        assignment_errors[[1, 1]] = 0.98;
556
557        let readout_data = ReadoutData {
558            readout_fidelities: Array1::from_vec(vec![0.97, 0.98, 0.96]),
559            assignment_errors,
560        };
561
562        let analysis = benchmarker.analyze_readout_fidelity(&readout_data);
563
564        assert!(analysis.is_ok());
565        let analysis = analysis.expect("Analysis failed");
566
567        assert!((analysis.avg_fidelity - 0.97).abs() < 0.01);
568        assert!(analysis.spam_error > 0.0);
569    }
570
571    #[test]
572    fn test_overall_score_computation() {
573        let config = HardwareBenchmarkConfig::default();
574        let benchmarker = HardwareBenchmarker::new(config);
575
576        let gate_analysis = GateFidelityAnalysis {
577            single_qubit_fidelity: Array1::from_vec(vec![0.999]),
578            two_qubit_fidelity: Array1::from_vec(vec![0.99]),
579            avg_single_qubit: 0.999,
580            avg_two_qubit: 0.99,
581            error_rates: Array1::from_vec(vec![0.001]),
582        };
583
584        let coherence_analysis = CoherenceAnalysis {
585            t1_times: Array1::from_vec(vec![50.0]),
586            t2_times: Array1::from_vec(vec![40.0]),
587            avg_t1: 50.0,
588            avg_t2: 40.0,
589            t1_t2_ratio: Array1::from_vec(vec![1.25]),
590        };
591
592        let readout_analysis = ReadoutFidelityAnalysis {
593            readout_fidelity: Array1::from_vec(vec![0.97]),
594            avg_fidelity: 0.97,
595            assignment_errors: Array2::zeros((2, 2)),
596            spam_error: 0.025,
597        };
598
599        let score = benchmarker.compute_overall_score(
600            &Some(gate_analysis),
601            &Some(coherence_analysis),
602            &Some(readout_analysis),
603        );
604
605        assert!(score > 50.0 && score < 100.0);
606    }
607
608    #[test]
609    fn test_empty_gate_times_error() {
610        let config = HardwareBenchmarkConfig::default();
611        let benchmarker = HardwareBenchmarker::new(config);
612
613        let empty_times = Array1::from_vec(vec![]);
614        let result = benchmarker.benchmark_gate_timing(&empty_times);
615
616        assert!(result.is_err());
617    }
618}