1use ndarray::{Array1, Array2};
10use num_complex::Complex64;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14use crate::error::{Result, SimulatorError};
15use crate::scirs2_integration::SciRS2Backend;
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum DeviceType {
20 Superconducting,
22 TrappedIon,
24 Photonic,
26 SiliconSpin,
28 NeutralAtom,
30 NVCenter,
32 Custom(String),
34}
35
36#[derive(Debug, Clone)]
38pub struct DeviceNoiseConfig {
39 pub device_type: DeviceType,
41 pub topology: DeviceTopology,
43 pub temperature: f64,
45 pub enable_correlated_noise: bool,
47 pub enable_time_dependent_noise: bool,
49 pub calibration_data: Option<CalibrationData>,
51 pub random_seed: Option<u64>,
53 pub real_time_adaptation: bool,
55}
56
57impl Default for DeviceNoiseConfig {
58 fn default() -> Self {
59 Self {
60 device_type: DeviceType::Superconducting,
61 topology: DeviceTopology::default(),
62 temperature: 0.015, enable_correlated_noise: true,
64 enable_time_dependent_noise: true,
65 calibration_data: None,
66 random_seed: None,
67 real_time_adaptation: false,
68 }
69 }
70}
71
72#[derive(Debug, Clone, PartialEq)]
74pub struct DeviceTopology {
75 pub num_qubits: usize,
77 pub connectivity: Array2<bool>,
79 pub positions: Vec<(f64, f64, f64)>,
81 pub coupling_strengths: HashMap<(usize, usize), f64>,
83 pub frequencies: Vec<f64>,
85}
86
87impl Default for DeviceTopology {
88 fn default() -> Self {
89 Self {
90 num_qubits: 0,
91 connectivity: Array2::default((0, 0)),
92 positions: Vec::new(),
93 coupling_strengths: HashMap::new(),
94 frequencies: Vec::new(),
95 }
96 }
97}
98
99impl DeviceTopology {
100 pub fn linear_chain(num_qubits: usize) -> Self {
102 let mut connectivity = Array2::from_elem((num_qubits, num_qubits), false);
103 let mut coupling_strengths = HashMap::new();
104
105 for i in 0..num_qubits - 1 {
106 connectivity[[i, i + 1]] = true;
107 connectivity[[i + 1, i]] = true;
108 coupling_strengths.insert((i, i + 1), 1.0);
109 coupling_strengths.insert((i + 1, i), 1.0);
110 }
111
112 let positions = (0..num_qubits).map(|i| (i as f64, 0.0, 0.0)).collect();
113
114 let frequencies = (0..num_qubits)
115 .map(|i| 5.0 + 0.1 * i as f64) .collect();
117
118 Self {
119 num_qubits,
120 connectivity,
121 positions,
122 coupling_strengths,
123 frequencies,
124 }
125 }
126
127 pub fn square_lattice(width: usize, height: usize) -> Self {
129 let num_qubits = width * height;
130 let mut connectivity = Array2::from_elem((num_qubits, num_qubits), false);
131 let mut coupling_strengths = HashMap::new();
132
133 for i in 0..height {
134 for j in 0..width {
135 let qubit = i * width + j;
136
137 if j < width - 1 {
139 let neighbor = i * width + (j + 1);
140 connectivity[[qubit, neighbor]] = true;
141 connectivity[[neighbor, qubit]] = true;
142 coupling_strengths.insert((qubit, neighbor), 1.0);
143 coupling_strengths.insert((neighbor, qubit), 1.0);
144 }
145
146 if i < height - 1 {
148 let neighbor = (i + 1) * width + j;
149 connectivity[[qubit, neighbor]] = true;
150 connectivity[[neighbor, qubit]] = true;
151 coupling_strengths.insert((qubit, neighbor), 1.0);
152 coupling_strengths.insert((neighbor, qubit), 1.0);
153 }
154 }
155 }
156
157 let positions = (0..height)
158 .flat_map(|i| (0..width).map(move |j| (j as f64, i as f64, 0.0)))
159 .collect();
160
161 let frequencies = (0..num_qubits)
162 .map(|i| 5.0 + 0.1 * (i % 7) as f64) .collect();
164
165 Self {
166 num_qubits,
167 connectivity,
168 positions,
169 coupling_strengths,
170 frequencies,
171 }
172 }
173
174 pub fn heavy_hex(distance: usize) -> Self {
176 let num_qubits = distance * distance * 2;
178 let connectivity = Array2::from_elem((num_qubits, num_qubits), false);
179
180 Self {
182 num_qubits,
183 connectivity,
184 positions: (0..num_qubits).map(|i| (i as f64, 0.0, 0.0)).collect(),
185 coupling_strengths: HashMap::new(),
186 frequencies: (0..num_qubits).map(|i| 5.0 + 0.1 * i as f64).collect(),
187 }
188 }
189
190 pub fn get_neighbors(&self, qubit: usize) -> Vec<usize> {
192 (0..self.num_qubits)
193 .filter(|&i| i != qubit && self.connectivity[[qubit, i]])
194 .collect()
195 }
196
197 pub fn distance(&self, qubit1: usize, qubit2: usize) -> f64 {
199 let pos1 = &self.positions[qubit1];
200 let pos2 = &self.positions[qubit2];
201 ((pos1.0 - pos2.0).powi(2) + (pos1.1 - pos2.1).powi(2) + (pos1.2 - pos2.2).powi(2)).sqrt()
202 }
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct CalibrationData {
208 pub device_id: String,
210 pub timestamp: std::time::SystemTime,
212 pub single_qubit_fidelities: Vec<f64>,
214 pub two_qubit_fidelities: HashMap<(usize, usize), f64>,
216 pub t1_times: Vec<f64>,
218 pub t2_times: Vec<f64>,
220 pub readout_fidelities: Vec<f64>,
222 pub gate_times: GateTimes,
224 pub crosstalk_matrices: HashMap<String, Array2<f64>>,
226 pub frequency_drift: Vec<FrequencyDrift>,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct GateTimes {
233 pub single_qubit: f64,
235 pub two_qubit: f64,
237 pub measurement: f64,
239 pub reset: f64,
241}
242
243impl Default for GateTimes {
244 fn default() -> Self {
245 Self {
246 single_qubit: 20.0, two_qubit: 100.0, measurement: 1000.0, reset: 500.0, }
251 }
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct FrequencyDrift {
257 pub qubit: usize,
259 pub drift_rate: f64,
261 pub random_walk_coeff: f64,
263}
264
265pub trait DeviceNoiseModel: Send + Sync {
267 fn device_type(&self) -> DeviceType;
269
270 fn apply_single_qubit_noise(
272 &self,
273 qubit: usize,
274 gate_time: f64,
275 state: &mut Array1<Complex64>,
276 ) -> Result<()>;
277
278 fn apply_two_qubit_noise(
280 &self,
281 qubit1: usize,
282 qubit2: usize,
283 gate_time: f64,
284 state: &mut Array1<Complex64>,
285 ) -> Result<()>;
286
287 fn apply_measurement_noise(&self, qubit: usize, measurement_result: bool) -> Result<bool>;
289
290 fn apply_idle_noise(
292 &self,
293 qubit: usize,
294 idle_time: f64,
295 state: &mut Array1<Complex64>,
296 ) -> Result<()>;
297
298 fn update_time_dependent_parameters(&mut self, current_time: f64) -> Result<()>;
300}
301
302#[derive(Debug, Clone)]
304pub struct SuperconductingNoiseModel {
305 config: DeviceNoiseConfig,
307 coherence_params: CoherenceParameters,
309 gate_errors: GateErrorRates,
311 readout_errors: Array2<f64>,
313 crosstalk: HashMap<String, Array2<f64>>,
315 current_time: f64,
317 rng_state: u64,
319}
320
321#[derive(Debug, Clone)]
323pub struct CoherenceParameters {
324 pub t1_times: Vec<f64>,
326 pub t2_times: Vec<f64>,
328 pub t2_star_times: Vec<f64>,
330 pub temperature_scaling: f64,
332}
333
334#[derive(Debug, Clone)]
336pub struct GateErrorRates {
337 pub single_qubit: Vec<f64>,
339 pub two_qubit: HashMap<(usize, usize), f64>,
341 pub depolarizing: Vec<f64>,
343 pub dephasing: Vec<f64>,
345 pub amplitude_damping: Vec<f64>,
347}
348
349impl SuperconductingNoiseModel {
350 pub fn new(config: DeviceNoiseConfig) -> Result<Self> {
352 let num_qubits = config.topology.num_qubits;
353
354 let coherence_params = CoherenceParameters {
356 t1_times: (0..num_qubits)
357 .map(|_| 50.0 + fastrand::f64() * 50.0)
358 .collect(), t2_times: (0..num_qubits)
360 .map(|_| 20.0 + fastrand::f64() * 30.0)
361 .collect(), t2_star_times: (0..num_qubits)
363 .map(|_| 10.0 + fastrand::f64() * 10.0)
364 .collect(), temperature_scaling: Self::calculate_temperature_scaling(config.temperature),
366 };
367
368 let gate_errors = GateErrorRates {
370 single_qubit: (0..num_qubits)
371 .map(|_| 1e-4 + fastrand::f64() * 1e-3)
372 .collect(), two_qubit: HashMap::new(), depolarizing: (0..num_qubits).map(|_| 1e-5).collect(),
375 dephasing: (0..num_qubits).map(|_| 1e-4).collect(),
376 amplitude_damping: (0..num_qubits).map(|_| 1e-5).collect(),
377 };
378
379 let readout_errors = Array2::from_elem((num_qubits, 2), 0.05); let rng_state = config.random_seed.unwrap_or_else(|| {
383 use std::time::{SystemTime, UNIX_EPOCH};
384 SystemTime::now()
385 .duration_since(UNIX_EPOCH)
386 .unwrap()
387 .as_nanos() as u64
388 });
389
390 Ok(Self {
391 config,
392 coherence_params,
393 gate_errors,
394 readout_errors,
395 crosstalk: HashMap::new(),
396 current_time: 0.0,
397 rng_state,
398 })
399 }
400
401 pub fn from_calibration_data(
403 config: DeviceNoiseConfig,
404 calibration: &CalibrationData,
405 ) -> Result<Self> {
406 let mut model = Self::new(config)?;
407
408 model.coherence_params.t1_times = calibration.t1_times.clone();
410 model.coherence_params.t2_times = calibration.t2_times.clone();
411 model.gate_errors.single_qubit = calibration
412 .single_qubit_fidelities
413 .iter()
414 .map(|&f| 1.0 - f)
415 .collect();
416
417 for (&(q1, q2), &fidelity) in &calibration.two_qubit_fidelities {
419 model.gate_errors.two_qubit.insert((q1, q2), 1.0 - fidelity);
420 }
421
422 for (i, &fidelity) in calibration.readout_fidelities.iter().enumerate() {
424 model.readout_errors[[i, 0]] = 1.0 - fidelity; model.readout_errors[[i, 1]] = 1.0 - fidelity; }
427
428 model.crosstalk = calibration.crosstalk_matrices.clone();
429
430 Ok(model)
431 }
432
433 fn calculate_temperature_scaling(temperature: f64) -> f64 {
435 let kb_t_over_hf = temperature * 1.38e-23 / (6.626e-34 * 5e9); (-1.0 / kb_t_over_hf).exp() / (1.0 + (-1.0 / kb_t_over_hf).exp())
438 }
439
440 fn random(&mut self) -> f64 {
442 self.rng_state = self.rng_state.wrapping_mul(1103515245).wrapping_add(12345);
444 (self.rng_state as f64) / (u64::MAX as f64)
445 }
446
447 fn apply_decoherence(
449 &mut self,
450 qubit: usize,
451 time_ns: f64,
452 state: &mut Array1<Complex64>,
453 ) -> Result<()> {
454 let t1 = self.coherence_params.t1_times[qubit] * 1000.0; let t2 = self.coherence_params.t2_times[qubit] * 1000.0;
456
457 let gamma_1 = time_ns / t1;
459 if gamma_1 > 0.0 {
460 self.apply_amplitude_damping(qubit, gamma_1, state)?;
461 }
462
463 let gamma_2 = time_ns / t2;
465 if gamma_2 > 0.0 {
466 self.apply_dephasing(qubit, gamma_2, state)?;
467 }
468
469 Ok(())
470 }
471
472 fn apply_amplitude_damping(
474 &mut self,
475 qubit: usize,
476 gamma: f64,
477 state: &mut Array1<Complex64>,
478 ) -> Result<()> {
479 let state_size = state.len();
480 let qubit_mask = 1 << qubit;
481
482 let mut new_state = state.clone();
483
484 for i in 0..state_size {
485 if i & qubit_mask != 0 {
486 let j = i & !qubit_mask; if self.random() < gamma {
490 new_state[j] += state[i];
492 new_state[i] = Complex64::new(0.0, 0.0);
493 } else {
494 new_state[i] *= (1.0 - gamma).sqrt();
496 }
497 }
498 }
499
500 *state = new_state;
501 Ok(())
502 }
503
504 fn apply_dephasing(
506 &mut self,
507 qubit: usize,
508 gamma: f64,
509 state: &mut Array1<Complex64>,
510 ) -> Result<()> {
511 let state_size = state.len();
512 let qubit_mask = 1 << qubit;
513
514 for i in 0..state_size {
515 if i & qubit_mask != 0 {
516 if self.random() < gamma {
518 let phase = self.random() * 2.0 * std::f64::consts::PI;
519 state[i] *= Complex64::new(0.0, phase).exp();
520 }
521 }
522 }
523
524 Ok(())
525 }
526
527 fn apply_crosstalk(
529 &mut self,
530 active_qubits: &[usize],
531 state: &mut Array1<Complex64>,
532 ) -> Result<()> {
533 if !self.config.enable_correlated_noise {
534 return Ok(());
535 }
536
537 for &qubit in active_qubits {
539 let neighbors = self.config.topology.get_neighbors(qubit);
540
541 for neighbor in neighbors {
542 if !active_qubits.contains(&neighbor) {
543 let crosstalk_strength = 0.01; let angle = crosstalk_strength * self.random() * std::f64::consts::PI;
546
547 self.apply_small_rotation(neighbor, angle, state)?;
549 }
550 }
551 }
552
553 Ok(())
554 }
555
556 fn apply_small_rotation(
558 &mut self,
559 qubit: usize,
560 angle: f64,
561 state: &mut Array1<Complex64>,
562 ) -> Result<()> {
563 let qubit_mask = 1 << qubit;
564 let state_size = state.len();
565
566 let cos_half = (angle / 2.0).cos();
567 let sin_half = (angle / 2.0).sin();
568
569 for i in 0..state_size {
570 if i & qubit_mask == 0 {
571 let j = i | qubit_mask;
572 if j < state_size {
573 let amp_0 = state[i];
574 let amp_1 = state[j];
575
576 state[i] = cos_half * amp_0 - Complex64::new(0.0, sin_half) * amp_1;
577 state[j] = cos_half * amp_1 - Complex64::new(0.0, sin_half) * amp_0;
578 }
579 }
580 }
581
582 Ok(())
583 }
584}
585
586impl DeviceNoiseModel for SuperconductingNoiseModel {
587 fn device_type(&self) -> DeviceType {
588 DeviceType::Superconducting
589 }
590
591 fn apply_single_qubit_noise(
592 &self,
593 qubit: usize,
594 gate_time: f64,
595 state: &mut Array1<Complex64>,
596 ) -> Result<()> {
597 let mut model = self.clone();
598
599 model.apply_decoherence(qubit, gate_time, state)?;
601
602 let error_rate = model.gate_errors.single_qubit[qubit];
604 if model.random() < error_rate {
605 let pauli_error = (model.random() * 3.0) as usize;
607 model.apply_pauli_error(qubit, pauli_error, state)?;
608 }
609
610 model.apply_crosstalk(&[qubit], state)?;
612
613 Ok(())
614 }
615
616 fn apply_two_qubit_noise(
617 &self,
618 qubit1: usize,
619 qubit2: usize,
620 gate_time: f64,
621 state: &mut Array1<Complex64>,
622 ) -> Result<()> {
623 let mut model = self.clone();
624
625 model.apply_decoherence(qubit1, gate_time, state)?;
627 model.apply_decoherence(qubit2, gate_time, state)?;
628
629 let error_rate = model
631 .gate_errors
632 .two_qubit
633 .get(&(qubit1, qubit2))
634 .or_else(|| model.gate_errors.two_qubit.get(&(qubit2, qubit1)))
635 .copied()
636 .unwrap_or(0.01); if model.random() < error_rate {
639 model.apply_two_qubit_error(qubit1, qubit2, state)?;
641 }
642
643 model.apply_crosstalk(&[qubit1, qubit2], state)?;
645
646 Ok(())
647 }
648
649 fn apply_measurement_noise(&self, qubit: usize, measurement_result: bool) -> Result<bool> {
650 let error_prob = if measurement_result {
651 self.readout_errors[[qubit, 1]] } else {
653 self.readout_errors[[qubit, 0]] };
655
656 if fastrand::f64() < error_prob {
657 Ok(!measurement_result) } else {
659 Ok(measurement_result)
660 }
661 }
662
663 fn apply_idle_noise(
664 &self,
665 qubit: usize,
666 idle_time: f64,
667 state: &mut Array1<Complex64>,
668 ) -> Result<()> {
669 let mut model = self.clone();
670 model.apply_decoherence(qubit, idle_time, state)?;
671 Ok(())
672 }
673
674 fn update_time_dependent_parameters(&mut self, current_time: f64) -> Result<()> {
675 self.current_time = current_time;
676
677 if !self.config.enable_time_dependent_noise {
678 return Ok(());
679 }
680
681 if let Some(calibration) = &self.config.calibration_data {
683 for drift in &calibration.frequency_drift {
684 let frequency_shift = drift.drift_rate * current_time / 1e9; }
688 }
689
690 Ok(())
691 }
692}
693
694impl SuperconductingNoiseModel {
695 fn apply_pauli_error(
697 &mut self,
698 qubit: usize,
699 pauli_type: usize,
700 state: &mut Array1<Complex64>,
701 ) -> Result<()> {
702 let qubit_mask = 1 << qubit;
703 let state_size = state.len();
704
705 match pauli_type {
706 0 => {
707 for i in 0..state_size {
709 let j = i ^ qubit_mask;
710 if i < j {
711 let temp = state[i];
712 state[i] = state[j];
713 state[j] = temp;
714 }
715 }
716 }
717 1 => {
718 for i in 0..state_size {
720 let j = i ^ qubit_mask;
721 if i < j {
722 let temp = state[i];
723 state[i] = Complex64::new(0.0, 1.0) * state[j];
724 state[j] = Complex64::new(0.0, -1.0) * temp;
725 }
726 }
727 }
728 2 => {
729 for i in 0..state_size {
731 if i & qubit_mask != 0 {
732 state[i] = -state[i];
733 }
734 }
735 }
736 _ => {} }
738
739 Ok(())
740 }
741
742 fn apply_two_qubit_error(
744 &mut self,
745 qubit1: usize,
746 qubit2: usize,
747 state: &mut Array1<Complex64>,
748 ) -> Result<()> {
749 let error1 = (self.random() * 4.0) as usize;
751 let error2 = (self.random() * 4.0) as usize;
752
753 if error1 < 3 {
754 self.apply_pauli_error(qubit1, error1, state)?;
755 }
756 if error2 < 3 {
757 self.apply_pauli_error(qubit2, error2, state)?;
758 }
759
760 Ok(())
761 }
762}
763
764pub struct DeviceNoiseSimulator {
766 noise_model: Box<dyn DeviceNoiseModel>,
768 backend: Option<SciRS2Backend>,
770 stats: NoiseSimulationStats,
772 current_time: f64,
774}
775
776#[derive(Debug, Clone, Default, Serialize, Deserialize)]
778pub struct NoiseSimulationStats {
779 pub total_noise_ops: usize,
781 pub single_qubit_noise_events: usize,
783 pub two_qubit_noise_events: usize,
785 pub measurement_errors: usize,
787 pub coherence_events: usize,
789 pub crosstalk_events: usize,
791 pub total_simulation_time_ms: f64,
793}
794
795impl DeviceNoiseSimulator {
796 pub fn new(noise_model: Box<dyn DeviceNoiseModel>) -> Result<Self> {
798 Ok(Self {
799 noise_model,
800 backend: None,
801 stats: NoiseSimulationStats::default(),
802 current_time: 0.0,
803 })
804 }
805
806 pub fn with_backend(mut self) -> Result<Self> {
808 self.backend = Some(SciRS2Backend::new());
809 Ok(self)
810 }
811
812 pub fn apply_gate_noise(
814 &mut self,
815 gate_type: &str,
816 qubits: &[usize],
817 gate_time: f64,
818 state: &mut Array1<Complex64>,
819 ) -> Result<()> {
820 let start_time = std::time::Instant::now();
821
822 match qubits.len() {
823 1 => {
824 self.noise_model
825 .apply_single_qubit_noise(qubits[0], gate_time, state)?;
826 self.stats.single_qubit_noise_events += 1;
827 }
828 2 => {
829 self.noise_model
830 .apply_two_qubit_noise(qubits[0], qubits[1], gate_time, state)?;
831 self.stats.two_qubit_noise_events += 1;
832 }
833 _ => {
834 return Err(SimulatorError::UnsupportedOperation(format!(
835 "Noise model for {}-qubit gates not implemented",
836 qubits.len()
837 )));
838 }
839 }
840
841 self.current_time += gate_time;
842 self.noise_model
843 .update_time_dependent_parameters(self.current_time)?;
844
845 self.stats.total_noise_ops += 1;
846 self.stats.total_simulation_time_ms += start_time.elapsed().as_secs_f64() * 1000.0;
847
848 Ok(())
849 }
850
851 pub fn apply_measurement_noise(
853 &mut self,
854 qubits: &[usize],
855 results: &mut [bool],
856 ) -> Result<()> {
857 for (i, &qubit) in qubits.iter().enumerate() {
858 let noisy_result = self
859 .noise_model
860 .apply_measurement_noise(qubit, results[i])?;
861 if noisy_result != results[i] {
862 self.stats.measurement_errors += 1;
863 }
864 results[i] = noisy_result;
865 }
866
867 Ok(())
868 }
869
870 pub fn apply_idle_noise(
872 &mut self,
873 idle_time: f64,
874 state: &mut Array1<Complex64>,
875 num_qubits: usize,
876 ) -> Result<()> {
877 for qubit in 0..num_qubits {
878 self.noise_model.apply_idle_noise(qubit, idle_time, state)?;
879 }
880
881 self.current_time += idle_time;
882 self.noise_model
883 .update_time_dependent_parameters(self.current_time)?;
884
885 Ok(())
886 }
887
888 pub fn get_stats(&self) -> &NoiseSimulationStats {
890 &self.stats
891 }
892
893 pub fn reset_stats(&mut self) {
895 self.stats = NoiseSimulationStats::default();
896 self.current_time = 0.0;
897 }
898}
899
900pub struct DeviceNoiseUtils;
902
903impl DeviceNoiseUtils {
904 pub fn create_ibm_device(
906 device_name: &str,
907 num_qubits: usize,
908 ) -> Result<SuperconductingNoiseModel> {
909 let topology = match device_name {
910 "ibm_washington" => DeviceTopology::heavy_hex(3),
911 "ibm_montreal" => DeviceTopology::linear_chain(num_qubits),
912 _ => DeviceTopology::square_lattice(4, 4),
913 };
914
915 let config = DeviceNoiseConfig {
916 device_type: DeviceType::Superconducting,
917 topology,
918 temperature: 0.015, enable_correlated_noise: true,
920 enable_time_dependent_noise: true,
921 calibration_data: None,
922 random_seed: None,
923 real_time_adaptation: false,
924 };
925
926 SuperconductingNoiseModel::new(config)
927 }
928
929 pub fn create_google_sycamore(num_qubits: usize) -> Result<SuperconductingNoiseModel> {
931 let width = (num_qubits as f64).sqrt().ceil() as usize;
932 let topology = DeviceTopology::square_lattice(width, width);
933
934 let config = DeviceNoiseConfig {
935 device_type: DeviceType::Superconducting,
936 topology,
937 temperature: 0.020, enable_correlated_noise: true,
939 enable_time_dependent_noise: true,
940 calibration_data: None,
941 random_seed: None,
942 real_time_adaptation: false,
943 };
944
945 SuperconductingNoiseModel::new(config)
946 }
947
948 pub fn create_trapped_ion_device(num_ions: usize) -> Result<Box<dyn DeviceNoiseModel>> {
950 let topology = DeviceTopology::linear_chain(num_ions);
952 let config = DeviceNoiseConfig {
953 device_type: DeviceType::TrappedIon,
954 topology,
955 temperature: 1e-6, enable_correlated_noise: true,
957 enable_time_dependent_noise: false,
958 calibration_data: None,
959 random_seed: None,
960 real_time_adaptation: false,
961 };
962
963 Ok(Box::new(SuperconductingNoiseModel::new(config)?))
964 }
965
966 pub fn benchmark_noise_models() -> Result<NoiseBenchmarkResults> {
968 let mut results = NoiseBenchmarkResults::default();
969
970 let device_types = vec![
971 ("ibm_montreal", 27),
972 ("google_sycamore", 53),
973 ("trapped_ion", 20),
974 ];
975
976 for (device_name, num_qubits) in device_types {
977 let model = if device_name.starts_with("ibm") {
978 Box::new(Self::create_ibm_device(device_name, num_qubits)?)
979 as Box<dyn DeviceNoiseModel>
980 } else if device_name.starts_with("google") {
981 Box::new(Self::create_google_sycamore(num_qubits)?) as Box<dyn DeviceNoiseModel>
982 } else {
983 Self::create_trapped_ion_device(num_qubits)?
984 };
985
986 let mut simulator = DeviceNoiseSimulator::new(model)?;
987 let mut state = Array1::from_elem(1 << num_qubits.min(10), Complex64::new(0.0, 0.0));
988 state[0] = Complex64::new(1.0, 0.0);
989
990 let start = std::time::Instant::now();
991
992 for i in 0..num_qubits.min(10) {
994 simulator.apply_gate_noise("x", &[i], 20.0, &mut state)?;
995 }
996
997 let execution_time = start.elapsed().as_secs_f64() * 1000.0;
998 results
999 .device_benchmarks
1000 .insert(device_name.to_string(), execution_time);
1001 }
1002
1003 Ok(results)
1004 }
1005}
1006
1007#[derive(Debug, Clone, Default)]
1009pub struct NoiseBenchmarkResults {
1010 pub device_benchmarks: HashMap<String, f64>,
1012}
1013
1014#[cfg(test)]
1015mod tests {
1016 use super::*;
1017 use approx::assert_abs_diff_eq;
1018
1019 #[test]
1020 fn test_device_topology_linear_chain() {
1021 let topology = DeviceTopology::linear_chain(5);
1022 assert_eq!(topology.num_qubits, 5);
1023 assert!(topology.connectivity[[0, 1]]);
1024 assert!(!topology.connectivity[[0, 2]]);
1025 assert_eq!(topology.get_neighbors(0), vec![1]);
1026 assert_eq!(topology.get_neighbors(2), vec![1, 3]);
1027 }
1028
1029 #[test]
1030 fn test_device_topology_square_lattice() {
1031 let topology = DeviceTopology::square_lattice(3, 3);
1032 assert_eq!(topology.num_qubits, 9);
1033 assert!(topology.connectivity[[0, 1]]); assert!(topology.connectivity[[0, 3]]); assert!(!topology.connectivity[[0, 4]]); }
1037
1038 #[test]
1039 fn test_superconducting_noise_model() {
1040 let config = DeviceNoiseConfig {
1041 device_type: DeviceType::Superconducting,
1042 topology: DeviceTopology::linear_chain(3),
1043 ..Default::default()
1044 };
1045
1046 let model = SuperconductingNoiseModel::new(config).unwrap();
1047 assert_eq!(model.device_type(), DeviceType::Superconducting);
1048 assert_eq!(model.coherence_params.t1_times.len(), 3);
1049 }
1050
1051 #[test]
1052 fn test_device_noise_simulator() {
1053 let config = DeviceNoiseConfig {
1054 device_type: DeviceType::Superconducting,
1055 topology: DeviceTopology::linear_chain(2),
1056 random_seed: Some(12345),
1057 ..Default::default()
1058 };
1059
1060 let model = SuperconductingNoiseModel::new(config).unwrap();
1061 let mut simulator = DeviceNoiseSimulator::new(Box::new(model)).unwrap();
1062
1063 let mut state = Array1::from_elem(4, Complex64::new(0.0, 0.0));
1064 state[0] = Complex64::new(1.0, 0.0);
1065
1066 let result = simulator.apply_gate_noise("x", &[0], 20.0, &mut state);
1068 assert!(result.is_ok());
1069 assert_eq!(simulator.stats.single_qubit_noise_events, 1);
1070
1071 let result = simulator.apply_gate_noise("cnot", &[0, 1], 100.0, &mut state);
1073 assert!(result.is_ok());
1074 assert_eq!(simulator.stats.two_qubit_noise_events, 1);
1075 }
1076
1077 #[test]
1078 fn test_measurement_noise() {
1079 let config = DeviceNoiseConfig {
1080 device_type: DeviceType::Superconducting,
1081 topology: DeviceTopology::linear_chain(1),
1082 random_seed: Some(12345),
1083 ..Default::default()
1084 };
1085
1086 let model = SuperconductingNoiseModel::new(config).unwrap();
1087 let mut simulator = DeviceNoiseSimulator::new(Box::new(model)).unwrap();
1088
1089 let mut results = vec![false, true, false];
1090 simulator
1091 .apply_measurement_noise(&[0, 0, 0], &mut results)
1092 .unwrap();
1093
1094 }
1097
1098 #[test]
1099 fn test_gate_times_default() {
1100 let gate_times = GateTimes::default();
1101 assert_eq!(gate_times.single_qubit, 20.0);
1102 assert_eq!(gate_times.two_qubit, 100.0);
1103 assert_eq!(gate_times.measurement, 1000.0);
1104 }
1105
1106 #[test]
1107 fn test_device_noise_utils() {
1108 let result = DeviceNoiseUtils::create_ibm_device("ibm_montreal", 5);
1109 assert!(result.is_ok());
1110
1111 let result = DeviceNoiseUtils::create_google_sycamore(9);
1112 assert!(result.is_ok());
1113
1114 let result = DeviceNoiseUtils::create_trapped_ion_device(5);
1115 assert!(result.is_ok());
1116 }
1117
1118 #[test]
1119 fn test_calibration_data_integration() {
1120 let calibration = CalibrationData {
1121 device_id: "test_device".to_string(),
1122 timestamp: std::time::SystemTime::now(),
1123 single_qubit_fidelities: vec![0.999, 0.998, 0.999],
1124 two_qubit_fidelities: HashMap::new(),
1125 t1_times: vec![50.0, 45.0, 55.0],
1126 t2_times: vec![25.0, 20.0, 30.0],
1127 readout_fidelities: vec![0.95, 0.96, 0.94],
1128 gate_times: GateTimes::default(),
1129 crosstalk_matrices: HashMap::new(),
1130 frequency_drift: Vec::new(),
1131 };
1132
1133 let config = DeviceNoiseConfig {
1134 device_type: DeviceType::Superconducting,
1135 topology: DeviceTopology::linear_chain(3),
1136 calibration_data: Some(calibration.clone()),
1137 ..Default::default()
1138 };
1139
1140 let model = SuperconductingNoiseModel::from_calibration_data(config, &calibration).unwrap();
1141 assert_eq!(model.coherence_params.t1_times, calibration.t1_times);
1142 assert_eq!(model.coherence_params.t2_times, calibration.t2_times);
1143 }
1144
1145 #[test]
1146 fn test_temperature_scaling() {
1147 let scaling_15mk = SuperconductingNoiseModel::calculate_temperature_scaling(0.015);
1148 let scaling_50mk = SuperconductingNoiseModel::calculate_temperature_scaling(0.050);
1149
1150 assert!(scaling_50mk > scaling_15mk);
1152 assert!(scaling_15mk > 0.0);
1153 assert!(scaling_15mk < 1.0);
1154 }
1155}