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.gen::<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.gen::<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.gen::<f64>() * 0.015
371        });
372
373        // Generate T1 times (30-100 microseconds)
374        let t1_times = Array1::from_shape_fn(num_qubits, |_| 30.0 + self.rng.gen::<f64>() * 70.0);
375
376        // Generate T2 times (20-80 microseconds, always <= T1)
377        let t2_times = Array1::from_shape_fn(num_qubits, |i| {
378            let max_t2 = t1_times[i] * 0.8;
379            20.0 + self.rng.gen::<f64>() * (max_t2 - 20.0).max(0.0)
380        });
381
382        // Generate readout fidelities (95% - 99%)
383        let readout_fidelities =
384            Array1::from_shape_fn(num_qubits, |_| 0.95 + self.rng.gen::<f64>() * 0.04);
385
386        // Generate assignment error matrix (simplified 2x2 for binary readout)
387        let mut assignment_errors = Array2::zeros((2, 2));
388        assignment_errors[[0, 0]] = 0.98; // P(measure 0 | state 0)
389        assignment_errors[[0, 1]] = 0.02; // P(measure 1 | state 0)
390        assignment_errors[[1, 0]] = 0.03; // P(measure 0 | state 1)
391        assignment_errors[[1, 1]] = 0.97; // P(measure 1 | state 1)
392
393        BenchmarkMeasurementData {
394            gate_times,
395            fidelity_data: GateFidelityData {
396                single_qubit_fidelities,
397                two_qubit_fidelities,
398            },
399            coherence_data: CoherenceData { t1_times, t2_times },
400            readout_data: ReadoutData {
401                readout_fidelities,
402                assignment_errors,
403            },
404        }
405    }
406}
407
408/// Input data for hardware benchmarking
409#[derive(Debug, Clone)]
410pub struct BenchmarkMeasurementData {
411    /// Gate execution times (nanoseconds)
412    pub gate_times: Array1<f64>,
413    /// Gate fidelity measurements
414    pub fidelity_data: GateFidelityData,
415    /// Coherence time measurements
416    pub coherence_data: CoherenceData,
417    /// Readout fidelity measurements
418    pub readout_data: ReadoutData,
419}
420
421/// Gate fidelity measurement data
422#[derive(Debug, Clone)]
423pub struct GateFidelityData {
424    /// Single-qubit gate fidelities
425    pub single_qubit_fidelities: Array1<f64>,
426    /// Two-qubit gate fidelities
427    pub two_qubit_fidelities: Array1<f64>,
428}
429
430/// Coherence time measurement data
431#[derive(Debug, Clone)]
432pub struct CoherenceData {
433    /// T1 relaxation times (microseconds)
434    pub t1_times: Array1<f64>,
435    /// T2 dephasing times (microseconds)
436    pub t2_times: Array1<f64>,
437}
438
439/// Readout fidelity measurement data
440#[derive(Debug, Clone)]
441pub struct ReadoutData {
442    /// Readout fidelity per qubit
443    pub readout_fidelities: Array1<f64>,
444    /// Assignment error matrix
445    pub assignment_errors: Array2<f64>,
446}
447
448#[cfg(test)]
449mod tests {
450    use super::*;
451
452    #[test]
453    fn test_benchmarker_creation() {
454        let config = HardwareBenchmarkConfig::default();
455        let benchmarker = HardwareBenchmarker::new(config);
456        assert_eq!(benchmarker.config.num_iterations, 1000);
457    }
458
459    #[test]
460    fn test_synthetic_data_generation() {
461        let config = HardwareBenchmarkConfig::default();
462        let mut benchmarker = HardwareBenchmarker::new(config);
463
464        let data = benchmarker.generate_synthetic_data(3);
465
466        assert_eq!(data.gate_times.len(), 1000);
467        assert_eq!(data.fidelity_data.single_qubit_fidelities.len(), 3);
468        assert_eq!(data.coherence_data.t1_times.len(), 3);
469        assert_eq!(data.readout_data.readout_fidelities.len(), 3);
470    }
471
472    #[test]
473    fn test_full_benchmark_run() {
474        let config = HardwareBenchmarkConfig::default();
475        let mut benchmarker = HardwareBenchmarker::new(config);
476
477        let data = benchmarker.generate_synthetic_data(2);
478        let report = benchmarker.run_benchmarks("TestDevice", &data);
479
480        assert!(report.is_ok());
481        let report = report.expect("Benchmark failed");
482
483        assert_eq!(report.device_name, "TestDevice");
484        assert!(!report.benchmarks.is_empty());
485        assert!(report.overall_score > 0.0 && report.overall_score <= 100.0);
486    }
487
488    #[test]
489    fn test_gate_timing_benchmark() {
490        let config = HardwareBenchmarkConfig::default();
491        let benchmarker = HardwareBenchmarker::new(config);
492
493        let gate_times = Array1::from_vec(vec![100.0, 110.0, 105.0, 95.0, 100.0]);
494        let result = benchmarker.benchmark_gate_timing(&gate_times);
495
496        assert!(result.is_ok());
497        let result = result.expect("Benchmark failed");
498
499        assert_eq!(result.name, "Gate Execution Time");
500        assert!((result.mean_value - 102.0).abs() < 1.0);
501        assert!(result.std_dev > 0.0);
502        assert_eq!(result.num_samples, 5);
503    }
504
505    #[test]
506    fn test_gate_fidelity_analysis() {
507        let config = HardwareBenchmarkConfig::default();
508        let benchmarker = HardwareBenchmarker::new(config);
509
510        let fidelity_data = GateFidelityData {
511            single_qubit_fidelities: Array1::from_vec(vec![0.999, 0.998, 0.997]),
512            two_qubit_fidelities: Array1::from_vec(vec![0.99, 0.985]),
513        };
514
515        let analysis = benchmarker.analyze_gate_fidelity(&fidelity_data);
516
517        assert!(analysis.is_ok());
518        let analysis = analysis.expect("Analysis failed");
519
520        assert!((analysis.avg_single_qubit - 0.998).abs() < 0.001);
521        assert!((analysis.avg_two_qubit - 0.9875).abs() < 0.001);
522        assert_eq!(analysis.error_rates.len(), 3);
523    }
524
525    #[test]
526    fn test_coherence_analysis() {
527        let config = HardwareBenchmarkConfig::default();
528        let benchmarker = HardwareBenchmarker::new(config);
529
530        let coherence_data = CoherenceData {
531            t1_times: Array1::from_vec(vec![50.0, 60.0, 55.0]),
532            t2_times: Array1::from_vec(vec![40.0, 48.0, 44.0]),
533        };
534
535        let analysis = benchmarker.analyze_coherence(&coherence_data);
536
537        assert!(analysis.is_ok());
538        let analysis = analysis.expect("Analysis failed");
539
540        assert!((analysis.avg_t1 - 55.0).abs() < 0.1);
541        assert!((analysis.avg_t2 - 44.0).abs() < 0.1);
542        assert_eq!(analysis.t1_t2_ratio.len(), 3);
543    }
544
545    #[test]
546    fn test_readout_fidelity_analysis() {
547        let config = HardwareBenchmarkConfig::default();
548        let benchmarker = HardwareBenchmarker::new(config);
549
550        let mut assignment_errors = Array2::zeros((2, 2));
551        assignment_errors[[0, 0]] = 0.97;
552        assignment_errors[[0, 1]] = 0.03;
553        assignment_errors[[1, 0]] = 0.02;
554        assignment_errors[[1, 1]] = 0.98;
555
556        let readout_data = ReadoutData {
557            readout_fidelities: Array1::from_vec(vec![0.97, 0.98, 0.96]),
558            assignment_errors,
559        };
560
561        let analysis = benchmarker.analyze_readout_fidelity(&readout_data);
562
563        assert!(analysis.is_ok());
564        let analysis = analysis.expect("Analysis failed");
565
566        assert!((analysis.avg_fidelity - 0.97).abs() < 0.01);
567        assert!(analysis.spam_error > 0.0);
568    }
569
570    #[test]
571    fn test_overall_score_computation() {
572        let config = HardwareBenchmarkConfig::default();
573        let benchmarker = HardwareBenchmarker::new(config);
574
575        let gate_analysis = GateFidelityAnalysis {
576            single_qubit_fidelity: Array1::from_vec(vec![0.999]),
577            two_qubit_fidelity: Array1::from_vec(vec![0.99]),
578            avg_single_qubit: 0.999,
579            avg_two_qubit: 0.99,
580            error_rates: Array1::from_vec(vec![0.001]),
581        };
582
583        let coherence_analysis = CoherenceAnalysis {
584            t1_times: Array1::from_vec(vec![50.0]),
585            t2_times: Array1::from_vec(vec![40.0]),
586            avg_t1: 50.0,
587            avg_t2: 40.0,
588            t1_t2_ratio: Array1::from_vec(vec![1.25]),
589        };
590
591        let readout_analysis = ReadoutFidelityAnalysis {
592            readout_fidelity: Array1::from_vec(vec![0.97]),
593            avg_fidelity: 0.97,
594            assignment_errors: Array2::zeros((2, 2)),
595            spam_error: 0.025,
596        };
597
598        let score = benchmarker.compute_overall_score(
599            &Some(gate_analysis),
600            &Some(coherence_analysis),
601            &Some(readout_analysis),
602        );
603
604        assert!(score > 50.0 && score < 100.0);
605    }
606
607    #[test]
608    fn test_empty_gate_times_error() {
609        let config = HardwareBenchmarkConfig::default();
610        let benchmarker = HardwareBenchmarker::new(config);
611
612        let empty_times = Array1::from_vec(vec![]);
613        let result = benchmarker.benchmark_gate_timing(&empty_times);
614
615        assert!(result.is_err());
616    }
617}