1use crate::{DeviceError, DeviceResult};
7use scirs2_core::ndarray::{Array1, Array2};
8use scirs2_core::random::prelude::*;
9use scirs2_stats::{mean, median, std};
10
11#[derive(Debug, Clone)]
13pub struct HardwareBenchmarkConfig {
14 pub num_iterations: usize,
16 pub num_qubits: usize,
18 pub benchmark_gate_fidelity: bool,
20 pub benchmark_coherence: bool,
22 pub benchmark_readout: bool,
24 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#[derive(Debug, Clone)]
43pub struct BenchmarkResult {
44 pub name: String,
46 pub mean_value: f64,
48 pub std_dev: f64,
50 pub median_value: f64,
52 pub min_value: f64,
54 pub max_value: f64,
56 pub confidence_interval: (f64, f64),
58 pub num_samples: usize,
60 pub p_value: Option<f64>,
62}
63
64#[derive(Debug, Clone)]
66pub struct HardwareBenchmarkReport {
67 pub device_name: String,
69 pub benchmarks: Vec<BenchmarkResult>,
71 pub overall_score: f64,
73 pub gate_fidelity_analysis: Option<GateFidelityAnalysis>,
75 pub coherence_analysis: Option<CoherenceAnalysis>,
77 pub readout_analysis: Option<ReadoutFidelityAnalysis>,
79 pub timestamp: std::time::SystemTime,
81}
82
83#[derive(Debug, Clone)]
85pub struct GateFidelityAnalysis {
86 pub single_qubit_fidelity: Array1<f64>,
88 pub two_qubit_fidelity: Array1<f64>,
90 pub avg_single_qubit: f64,
92 pub avg_two_qubit: f64,
94 pub error_rates: Array1<f64>,
96}
97
98#[derive(Debug, Clone)]
100pub struct CoherenceAnalysis {
101 pub t1_times: Array1<f64>,
103 pub t2_times: Array1<f64>,
105 pub avg_t1: f64,
107 pub avg_t2: f64,
109 pub t1_t2_ratio: Array1<f64>,
111}
112
113#[derive(Debug, Clone)]
115pub struct ReadoutFidelityAnalysis {
116 pub readout_fidelity: Array1<f64>,
118 pub avg_fidelity: f64,
120 pub assignment_errors: Array2<f64>,
122 pub spam_error: f64,
124}
125
126pub struct HardwareBenchmarker {
128 config: HardwareBenchmarkConfig,
129 rng: StdRng,
130}
131
132impl HardwareBenchmarker {
133 pub fn new(config: HardwareBenchmarkConfig) -> Self {
135 Self {
136 config,
137 rng: StdRng::seed_from_u64(42),
138 }
139 }
140
141 pub fn default() -> Self {
143 Self::new(HardwareBenchmarkConfig::default())
144 }
145
146 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 let gate_timing = self.benchmark_gate_timing(&measurement_data.gate_times)?;
163 benchmarks.push(gate_timing);
164
165 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 let coherence_analysis = if self.config.benchmark_coherence {
174 Some(self.analyze_coherence(&measurement_data.coherence_data)?)
175 } else {
176 None
177 };
178
179 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 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 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 let n = gate_times.len() as f64;
221 let z_critical = 1.96; 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 fn analyze_gate_fidelity(
239 &self,
240 fidelity_data: &GateFidelityData,
241 ) -> DeviceResult<GateFidelityAnalysis> {
242 let avg_single = mean(&fidelity_data.single_qubit_fidelities.view())?;
244 let avg_two = mean(&fidelity_data.two_qubit_fidelities.view())?;
245
246 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 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 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 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 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 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 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 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 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 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 pub fn generate_synthetic_data(&mut self, num_qubits: usize) -> BenchmarkMeasurementData {
357 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 let single_qubit_fidelities =
366 Array1::from_shape_fn(num_qubits, |_| 0.995 + self.rng.gen::<f64>() * 0.0049);
367
368 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 let t1_times = Array1::from_shape_fn(num_qubits, |_| 30.0 + self.rng.gen::<f64>() * 70.0);
375
376 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 let readout_fidelities =
384 Array1::from_shape_fn(num_qubits, |_| 0.95 + self.rng.gen::<f64>() * 0.04);
385
386 let mut assignment_errors = Array2::zeros((2, 2));
388 assignment_errors[[0, 0]] = 0.98; assignment_errors[[0, 1]] = 0.02; assignment_errors[[1, 0]] = 0.03; assignment_errors[[1, 1]] = 0.97; 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#[derive(Debug, Clone)]
410pub struct BenchmarkMeasurementData {
411 pub gate_times: Array1<f64>,
413 pub fidelity_data: GateFidelityData,
415 pub coherence_data: CoherenceData,
417 pub readout_data: ReadoutData,
419}
420
421#[derive(Debug, Clone)]
423pub struct GateFidelityData {
424 pub single_qubit_fidelities: Array1<f64>,
426 pub two_qubit_fidelities: Array1<f64>,
428}
429
430#[derive(Debug, Clone)]
432pub struct CoherenceData {
433 pub t1_times: Array1<f64>,
435 pub t2_times: Array1<f64>,
437}
438
439#[derive(Debug, Clone)]
441pub struct ReadoutData {
442 pub readout_fidelities: Array1<f64>,
444 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}