1use scirs2_core::ndarray::{Array1, Array2};
10use scirs2_core::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 #[must_use]
102 pub fn linear_chain(num_qubits: usize) -> Self {
103 let mut connectivity = Array2::from_elem((num_qubits, num_qubits), false);
104 let mut coupling_strengths = HashMap::new();
105
106 for i in 0..num_qubits - 1 {
107 connectivity[[i, i + 1]] = true;
108 connectivity[[i + 1, i]] = true;
109 coupling_strengths.insert((i, i + 1), 1.0);
110 coupling_strengths.insert((i + 1, i), 1.0);
111 }
112
113 let positions = (0..num_qubits).map(|i| (i as f64, 0.0, 0.0)).collect();
114
115 let frequencies = (0..num_qubits)
116 .map(|i| 0.1f64.mul_add(i as f64, 5.0)) .collect();
118
119 Self {
120 num_qubits,
121 connectivity,
122 positions,
123 coupling_strengths,
124 frequencies,
125 }
126 }
127
128 #[must_use]
130 pub fn square_lattice(width: usize, height: usize) -> Self {
131 let num_qubits = width * height;
132 let mut connectivity = Array2::from_elem((num_qubits, num_qubits), false);
133 let mut coupling_strengths = HashMap::new();
134
135 for i in 0..height {
136 for j in 0..width {
137 let qubit = i * width + j;
138
139 if j < width - 1 {
141 let neighbor = i * width + (j + 1);
142 connectivity[[qubit, neighbor]] = true;
143 connectivity[[neighbor, qubit]] = true;
144 coupling_strengths.insert((qubit, neighbor), 1.0);
145 coupling_strengths.insert((neighbor, qubit), 1.0);
146 }
147
148 if i < height - 1 {
150 let neighbor = (i + 1) * width + j;
151 connectivity[[qubit, neighbor]] = true;
152 connectivity[[neighbor, qubit]] = true;
153 coupling_strengths.insert((qubit, neighbor), 1.0);
154 coupling_strengths.insert((neighbor, qubit), 1.0);
155 }
156 }
157 }
158
159 let positions = (0..height)
160 .flat_map(|i| (0..width).map(move |j| (j as f64, i as f64, 0.0)))
161 .collect();
162
163 let frequencies = (0..num_qubits)
164 .map(|i| 0.1f64.mul_add((i % 7) as f64, 5.0)) .collect();
166
167 Self {
168 num_qubits,
169 connectivity,
170 positions,
171 coupling_strengths,
172 frequencies,
173 }
174 }
175
176 #[must_use]
178 pub fn heavy_hex(distance: usize) -> Self {
179 let num_qubits = distance * distance * 2;
181 let connectivity = Array2::from_elem((num_qubits, num_qubits), false);
182
183 Self {
185 num_qubits,
186 connectivity,
187 positions: (0..num_qubits).map(|i| (i as f64, 0.0, 0.0)).collect(),
188 coupling_strengths: HashMap::new(),
189 frequencies: (0..num_qubits)
190 .map(|i| 0.1f64.mul_add(i as f64, 5.0))
191 .collect(),
192 }
193 }
194
195 #[must_use]
197 pub fn get_neighbors(&self, qubit: usize) -> Vec<usize> {
198 (0..self.num_qubits)
199 .filter(|&i| i != qubit && self.connectivity[[qubit, i]])
200 .collect()
201 }
202
203 #[must_use]
205 pub fn distance(&self, qubit1: usize, qubit2: usize) -> f64 {
206 let pos1 = &self.positions[qubit1];
207 let pos2 = &self.positions[qubit2];
208 (pos1.2 - pos2.2)
209 .mul_add(
210 pos1.2 - pos2.2,
211 (pos1.1 - pos2.1).mul_add(pos1.1 - pos2.1, (pos1.0 - pos2.0).powi(2)),
212 )
213 .sqrt()
214 }
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct CalibrationData {
220 pub device_id: String,
222 pub timestamp: std::time::SystemTime,
224 pub single_qubit_fidelities: Vec<f64>,
226 pub two_qubit_fidelities: HashMap<(usize, usize), f64>,
228 pub t1_times: Vec<f64>,
230 pub t2_times: Vec<f64>,
232 pub readout_fidelities: Vec<f64>,
234 pub gate_times: GateTimes,
236 pub crosstalk_matrices: HashMap<String, Array2<f64>>,
238 pub frequency_drift: Vec<FrequencyDrift>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct GateTimes {
245 pub single_qubit: f64,
247 pub two_qubit: f64,
249 pub measurement: f64,
251 pub reset: f64,
253}
254
255impl Default for GateTimes {
256 fn default() -> Self {
257 Self {
258 single_qubit: 20.0, two_qubit: 100.0, measurement: 1000.0, reset: 500.0, }
263 }
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct FrequencyDrift {
269 pub qubit: usize,
271 pub drift_rate: f64,
273 pub random_walk_coeff: f64,
275}
276
277pub trait DeviceNoiseModel: Send + Sync {
279 fn device_type(&self) -> DeviceType;
281
282 fn apply_single_qubit_noise(
284 &self,
285 qubit: usize,
286 gate_time: f64,
287 state: &mut Array1<Complex64>,
288 ) -> Result<()>;
289
290 fn apply_two_qubit_noise(
292 &self,
293 qubit1: usize,
294 qubit2: usize,
295 gate_time: f64,
296 state: &mut Array1<Complex64>,
297 ) -> Result<()>;
298
299 fn apply_measurement_noise(&self, qubit: usize, measurement_result: bool) -> Result<bool>;
301
302 fn apply_idle_noise(
304 &self,
305 qubit: usize,
306 idle_time: f64,
307 state: &mut Array1<Complex64>,
308 ) -> Result<()>;
309
310 fn update_time_dependent_parameters(&mut self, current_time: f64) -> Result<()>;
312}
313
314#[derive(Debug, Clone)]
316pub struct SuperconductingNoiseModel {
317 config: DeviceNoiseConfig,
319 coherence_params: CoherenceParameters,
321 gate_errors: GateErrorRates,
323 readout_errors: Array2<f64>,
325 crosstalk: HashMap<String, Array2<f64>>,
327 current_time: f64,
329 rng_state: u64,
331}
332
333#[derive(Debug, Clone)]
335pub struct CoherenceParameters {
336 pub t1_times: Vec<f64>,
338 pub t2_times: Vec<f64>,
340 pub t2_star_times: Vec<f64>,
342 pub temperature_scaling: f64,
344}
345
346#[derive(Debug, Clone)]
348pub struct GateErrorRates {
349 pub single_qubit: Vec<f64>,
351 pub two_qubit: HashMap<(usize, usize), f64>,
353 pub depolarizing: Vec<f64>,
355 pub dephasing: Vec<f64>,
357 pub amplitude_damping: Vec<f64>,
359}
360
361impl SuperconductingNoiseModel {
362 pub fn new(config: DeviceNoiseConfig) -> Result<Self> {
364 let num_qubits = config.topology.num_qubits;
365
366 let coherence_params = CoherenceParameters {
368 t1_times: (0..num_qubits)
369 .map(|_| fastrand::f64().mul_add(50.0, 50.0))
370 .collect(), t2_times: (0..num_qubits)
372 .map(|_| fastrand::f64().mul_add(30.0, 20.0))
373 .collect(), t2_star_times: (0..num_qubits)
375 .map(|_| fastrand::f64().mul_add(10.0, 10.0))
376 .collect(), temperature_scaling: Self::calculate_temperature_scaling(config.temperature),
378 };
379
380 let gate_errors = GateErrorRates {
382 single_qubit: (0..num_qubits)
383 .map(|_| fastrand::f64().mul_add(1e-3, 1e-4))
384 .collect(), two_qubit: HashMap::new(), depolarizing: (0..num_qubits).map(|_| 1e-5).collect(),
387 dephasing: (0..num_qubits).map(|_| 1e-4).collect(),
388 amplitude_damping: (0..num_qubits).map(|_| 1e-5).collect(),
389 };
390
391 let readout_errors = Array2::from_elem((num_qubits, 2), 0.05); let rng_state = config.random_seed.unwrap_or_else(|| {
395 use std::time::{SystemTime, UNIX_EPOCH};
396 SystemTime::now()
397 .duration_since(UNIX_EPOCH)
398 .map(|d| d.as_nanos() as u64)
399 .unwrap_or_else(|_| fastrand::u64(..))
400 });
401
402 Ok(Self {
403 config,
404 coherence_params,
405 gate_errors,
406 readout_errors,
407 crosstalk: HashMap::new(),
408 current_time: 0.0,
409 rng_state,
410 })
411 }
412
413 pub fn from_calibration_data(
415 config: DeviceNoiseConfig,
416 calibration: &CalibrationData,
417 ) -> Result<Self> {
418 let mut model = Self::new(config)?;
419
420 model
422 .coherence_params
423 .t1_times
424 .clone_from(&calibration.t1_times);
425 model
426 .coherence_params
427 .t2_times
428 .clone_from(&calibration.t2_times);
429 model.gate_errors.single_qubit = calibration
430 .single_qubit_fidelities
431 .iter()
432 .map(|&f| 1.0 - f)
433 .collect();
434
435 for (&(q1, q2), &fidelity) in &calibration.two_qubit_fidelities {
437 model.gate_errors.two_qubit.insert((q1, q2), 1.0 - fidelity);
438 }
439
440 for (i, &fidelity) in calibration.readout_fidelities.iter().enumerate() {
442 model.readout_errors[[i, 0]] = 1.0 - fidelity; model.readout_errors[[i, 1]] = 1.0 - fidelity; }
445
446 model.crosstalk.clone_from(&calibration.crosstalk_matrices);
447
448 Ok(model)
449 }
450
451 fn calculate_temperature_scaling(temperature: f64) -> f64 {
453 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())
456 }
457
458 fn random(&mut self) -> f64 {
460 self.rng_state = self
462 .rng_state
463 .wrapping_mul(1_103_515_245)
464 .wrapping_add(12_345);
465 (self.rng_state as f64) / (u64::MAX as f64)
466 }
467
468 fn apply_decoherence(
470 &mut self,
471 qubit: usize,
472 time_ns: f64,
473 state: &mut Array1<Complex64>,
474 ) -> Result<()> {
475 let t1 = self.coherence_params.t1_times[qubit] * 1000.0; let t2 = self.coherence_params.t2_times[qubit] * 1000.0;
477
478 let gamma_1 = time_ns / t1;
480 if gamma_1 > 0.0 {
481 self.apply_amplitude_damping(qubit, gamma_1, state)?;
482 }
483
484 let gamma_2 = time_ns / t2;
486 if gamma_2 > 0.0 {
487 self.apply_dephasing(qubit, gamma_2, state)?;
488 }
489
490 Ok(())
491 }
492
493 fn apply_amplitude_damping(
495 &mut self,
496 qubit: usize,
497 gamma: f64,
498 state: &mut Array1<Complex64>,
499 ) -> Result<()> {
500 let state_size = state.len();
501 let qubit_mask = 1 << qubit;
502
503 let mut new_state = state.clone();
504
505 for i in 0..state_size {
506 if i & qubit_mask != 0 {
507 let j = i & !qubit_mask; if self.random() < gamma {
511 new_state[j] += state[i];
513 new_state[i] = Complex64::new(0.0, 0.0);
514 } else {
515 new_state[i] *= (1.0 - gamma).sqrt();
517 }
518 }
519 }
520
521 *state = new_state;
522 Ok(())
523 }
524
525 fn apply_dephasing(
527 &mut self,
528 qubit: usize,
529 gamma: f64,
530 state: &mut Array1<Complex64>,
531 ) -> Result<()> {
532 let state_size = state.len();
533 let qubit_mask = 1 << qubit;
534
535 for i in 0..state_size {
536 if i & qubit_mask != 0 {
537 if self.random() < gamma {
539 let phase = self.random() * 2.0 * std::f64::consts::PI;
540 state[i] *= Complex64::new(0.0, phase).exp();
541 }
542 }
543 }
544
545 Ok(())
546 }
547
548 fn apply_crosstalk(
550 &mut self,
551 active_qubits: &[usize],
552 state: &mut Array1<Complex64>,
553 ) -> Result<()> {
554 if !self.config.enable_correlated_noise {
555 return Ok(());
556 }
557
558 for &qubit in active_qubits {
560 let neighbors = self.config.topology.get_neighbors(qubit);
561
562 for neighbor in neighbors {
563 if !active_qubits.contains(&neighbor) {
564 let crosstalk_strength = 0.01; let angle = crosstalk_strength * self.random() * std::f64::consts::PI;
567
568 self.apply_small_rotation(neighbor, angle, state)?;
570 }
571 }
572 }
573
574 Ok(())
575 }
576
577 fn apply_small_rotation(
579 &self,
580 qubit: usize,
581 angle: f64,
582 state: &mut Array1<Complex64>,
583 ) -> Result<()> {
584 let qubit_mask = 1 << qubit;
585 let state_size = state.len();
586
587 let cos_half = (angle / 2.0).cos();
588 let sin_half = (angle / 2.0).sin();
589
590 for i in 0..state_size {
591 if i & qubit_mask == 0 {
592 let j = i | qubit_mask;
593 if j < state_size {
594 let amp_0 = state[i];
595 let amp_1 = state[j];
596
597 state[i] = cos_half * amp_0 - Complex64::new(0.0, sin_half) * amp_1;
598 state[j] = cos_half * amp_1 - Complex64::new(0.0, sin_half) * amp_0;
599 }
600 }
601 }
602
603 Ok(())
604 }
605}
606
607impl DeviceNoiseModel for SuperconductingNoiseModel {
608 fn device_type(&self) -> DeviceType {
609 DeviceType::Superconducting
610 }
611
612 fn apply_single_qubit_noise(
613 &self,
614 qubit: usize,
615 gate_time: f64,
616 state: &mut Array1<Complex64>,
617 ) -> Result<()> {
618 let mut model = self.clone();
619
620 model.apply_decoherence(qubit, gate_time, state)?;
622
623 let error_rate = model.gate_errors.single_qubit[qubit];
625 if model.random() < error_rate {
626 let pauli_error = (model.random() * 3.0) as usize;
628 model.apply_pauli_error(qubit, pauli_error, state)?;
629 }
630
631 model.apply_crosstalk(&[qubit], state)?;
633
634 Ok(())
635 }
636
637 fn apply_two_qubit_noise(
638 &self,
639 qubit1: usize,
640 qubit2: usize,
641 gate_time: f64,
642 state: &mut Array1<Complex64>,
643 ) -> Result<()> {
644 let mut model = self.clone();
645
646 model.apply_decoherence(qubit1, gate_time, state)?;
648 model.apply_decoherence(qubit2, gate_time, state)?;
649
650 let error_rate = model
652 .gate_errors
653 .two_qubit
654 .get(&(qubit1, qubit2))
655 .or_else(|| model.gate_errors.two_qubit.get(&(qubit2, qubit1)))
656 .copied()
657 .unwrap_or(0.01); if model.random() < error_rate {
660 model.apply_two_qubit_error(qubit1, qubit2, state)?;
662 }
663
664 model.apply_crosstalk(&[qubit1, qubit2], state)?;
666
667 Ok(())
668 }
669
670 fn apply_measurement_noise(&self, qubit: usize, measurement_result: bool) -> Result<bool> {
671 let error_prob = if measurement_result {
672 self.readout_errors[[qubit, 1]] } else {
674 self.readout_errors[[qubit, 0]] };
676
677 if fastrand::f64() < error_prob {
678 Ok(!measurement_result) } else {
680 Ok(measurement_result)
681 }
682 }
683
684 fn apply_idle_noise(
685 &self,
686 qubit: usize,
687 idle_time: f64,
688 state: &mut Array1<Complex64>,
689 ) -> Result<()> {
690 let mut model = self.clone();
691 model.apply_decoherence(qubit, idle_time, state)?;
692 Ok(())
693 }
694
695 fn update_time_dependent_parameters(&mut self, current_time: f64) -> Result<()> {
696 self.current_time = current_time;
697
698 if !self.config.enable_time_dependent_noise {
699 return Ok(());
700 }
701
702 if let Some(calibration) = &self.config.calibration_data {
704 for drift in &calibration.frequency_drift {
705 let frequency_shift = drift.drift_rate * current_time / 1e9; }
709 }
710
711 Ok(())
712 }
713}
714
715impl SuperconductingNoiseModel {
716 fn apply_pauli_error(
718 &self,
719 qubit: usize,
720 pauli_type: usize,
721 state: &mut Array1<Complex64>,
722 ) -> Result<()> {
723 let qubit_mask = 1 << qubit;
724 let state_size = state.len();
725
726 match pauli_type {
727 0 => {
728 for i in 0..state_size {
730 let j = i ^ qubit_mask;
731 if i < j {
732 let temp = state[i];
733 state[i] = state[j];
734 state[j] = temp;
735 }
736 }
737 }
738 1 => {
739 for i in 0..state_size {
741 let j = i ^ qubit_mask;
742 if i < j {
743 let temp = state[i];
744 state[i] = Complex64::new(0.0, 1.0) * state[j];
745 state[j] = Complex64::new(0.0, -1.0) * temp;
746 }
747 }
748 }
749 2 => {
750 for i in 0..state_size {
752 if i & qubit_mask != 0 {
753 state[i] = -state[i];
754 }
755 }
756 }
757 _ => {} }
759
760 Ok(())
761 }
762
763 fn apply_two_qubit_error(
765 &mut self,
766 qubit1: usize,
767 qubit2: usize,
768 state: &mut Array1<Complex64>,
769 ) -> Result<()> {
770 let error1 = (self.random() * 4.0) as usize;
772 let error2 = (self.random() * 4.0) as usize;
773
774 if error1 < 3 {
775 self.apply_pauli_error(qubit1, error1, state)?;
776 }
777 if error2 < 3 {
778 self.apply_pauli_error(qubit2, error2, state)?;
779 }
780
781 Ok(())
782 }
783}
784
785pub struct DeviceNoiseSimulator {
787 noise_model: Box<dyn DeviceNoiseModel>,
789 backend: Option<SciRS2Backend>,
791 stats: NoiseSimulationStats,
793 current_time: f64,
795}
796
797#[derive(Debug, Clone, Default, Serialize, Deserialize)]
799pub struct NoiseSimulationStats {
800 pub total_noise_ops: usize,
802 pub single_qubit_noise_events: usize,
804 pub two_qubit_noise_events: usize,
806 pub measurement_errors: usize,
808 pub coherence_events: usize,
810 pub crosstalk_events: usize,
812 pub total_simulation_time_ms: f64,
814}
815
816impl DeviceNoiseSimulator {
817 pub fn new(noise_model: Box<dyn DeviceNoiseModel>) -> Result<Self> {
819 Ok(Self {
820 noise_model,
821 backend: None,
822 stats: NoiseSimulationStats::default(),
823 current_time: 0.0,
824 })
825 }
826
827 pub fn with_backend(mut self) -> Result<Self> {
829 self.backend = Some(SciRS2Backend::new());
830 Ok(self)
831 }
832
833 pub fn apply_gate_noise(
835 &mut self,
836 gate_type: &str,
837 qubits: &[usize],
838 gate_time: f64,
839 state: &mut Array1<Complex64>,
840 ) -> Result<()> {
841 let start_time = std::time::Instant::now();
842
843 match qubits.len() {
844 1 => {
845 self.noise_model
846 .apply_single_qubit_noise(qubits[0], gate_time, state)?;
847 self.stats.single_qubit_noise_events += 1;
848 }
849 2 => {
850 self.noise_model
851 .apply_two_qubit_noise(qubits[0], qubits[1], gate_time, state)?;
852 self.stats.two_qubit_noise_events += 1;
853 }
854 _ => {
855 return Err(SimulatorError::UnsupportedOperation(format!(
856 "Noise model for {}-qubit gates not implemented",
857 qubits.len()
858 )));
859 }
860 }
861
862 self.current_time += gate_time;
863 self.noise_model
864 .update_time_dependent_parameters(self.current_time)?;
865
866 self.stats.total_noise_ops += 1;
867 self.stats.total_simulation_time_ms += start_time.elapsed().as_secs_f64() * 1000.0;
868
869 Ok(())
870 }
871
872 pub fn apply_measurement_noise(
874 &mut self,
875 qubits: &[usize],
876 results: &mut [bool],
877 ) -> Result<()> {
878 for (i, &qubit) in qubits.iter().enumerate() {
879 let noisy_result = self
880 .noise_model
881 .apply_measurement_noise(qubit, results[i])?;
882 if noisy_result != results[i] {
883 self.stats.measurement_errors += 1;
884 }
885 results[i] = noisy_result;
886 }
887
888 Ok(())
889 }
890
891 pub fn apply_idle_noise(
893 &mut self,
894 idle_time: f64,
895 state: &mut Array1<Complex64>,
896 num_qubits: usize,
897 ) -> Result<()> {
898 for qubit in 0..num_qubits {
899 self.noise_model.apply_idle_noise(qubit, idle_time, state)?;
900 }
901
902 self.current_time += idle_time;
903 self.noise_model
904 .update_time_dependent_parameters(self.current_time)?;
905
906 Ok(())
907 }
908
909 #[must_use]
911 pub const fn get_stats(&self) -> &NoiseSimulationStats {
912 &self.stats
913 }
914
915 pub fn reset_stats(&mut self) {
917 self.stats = NoiseSimulationStats::default();
918 self.current_time = 0.0;
919 }
920}
921
922pub struct DeviceNoiseUtils;
924
925impl DeviceNoiseUtils {
926 pub fn create_ibm_device(
928 device_name: &str,
929 num_qubits: usize,
930 ) -> Result<SuperconductingNoiseModel> {
931 let topology = match device_name {
932 "ibm_washington" => DeviceTopology::heavy_hex(3),
933 "ibm_montreal" => DeviceTopology::linear_chain(num_qubits),
934 _ => DeviceTopology::square_lattice(4, 4),
935 };
936
937 let config = DeviceNoiseConfig {
938 device_type: DeviceType::Superconducting,
939 topology,
940 temperature: 0.015, enable_correlated_noise: true,
942 enable_time_dependent_noise: true,
943 calibration_data: None,
944 random_seed: None,
945 real_time_adaptation: false,
946 };
947
948 SuperconductingNoiseModel::new(config)
949 }
950
951 pub fn create_google_sycamore(num_qubits: usize) -> Result<SuperconductingNoiseModel> {
953 let width = (num_qubits as f64).sqrt().ceil() as usize;
954 let topology = DeviceTopology::square_lattice(width, width);
955
956 let config = DeviceNoiseConfig {
957 device_type: DeviceType::Superconducting,
958 topology,
959 temperature: 0.020, enable_correlated_noise: true,
961 enable_time_dependent_noise: true,
962 calibration_data: None,
963 random_seed: None,
964 real_time_adaptation: false,
965 };
966
967 SuperconductingNoiseModel::new(config)
968 }
969
970 pub fn create_trapped_ion_device(num_ions: usize) -> Result<Box<dyn DeviceNoiseModel>> {
972 let topology = DeviceTopology::linear_chain(num_ions);
974 let config = DeviceNoiseConfig {
975 device_type: DeviceType::TrappedIon,
976 topology,
977 temperature: 1e-6, enable_correlated_noise: true,
979 enable_time_dependent_noise: false,
980 calibration_data: None,
981 random_seed: None,
982 real_time_adaptation: false,
983 };
984
985 Ok(Box::new(SuperconductingNoiseModel::new(config)?))
986 }
987
988 pub fn benchmark_noise_models() -> Result<NoiseBenchmarkResults> {
990 let mut results = NoiseBenchmarkResults::default();
991
992 let device_types = vec![
993 ("ibm_montreal", 27),
994 ("google_sycamore", 53),
995 ("trapped_ion", 20),
996 ];
997
998 for (device_name, num_qubits) in device_types {
999 let model = if device_name.starts_with("ibm") {
1000 Box::new(Self::create_ibm_device(device_name, num_qubits)?)
1001 as Box<dyn DeviceNoiseModel>
1002 } else if device_name.starts_with("google") {
1003 Box::new(Self::create_google_sycamore(num_qubits)?) as Box<dyn DeviceNoiseModel>
1004 } else {
1005 Self::create_trapped_ion_device(num_qubits)?
1006 };
1007
1008 let mut simulator = DeviceNoiseSimulator::new(model)?;
1009 let mut state = Array1::from_elem(1 << num_qubits.min(10), Complex64::new(0.0, 0.0));
1010 state[0] = Complex64::new(1.0, 0.0);
1011
1012 let start = std::time::Instant::now();
1013
1014 for i in 0..num_qubits.min(10) {
1016 simulator.apply_gate_noise("x", &[i], 20.0, &mut state)?;
1017 }
1018
1019 let execution_time = start.elapsed().as_secs_f64() * 1000.0;
1020 results
1021 .device_benchmarks
1022 .insert(device_name.to_string(), execution_time);
1023 }
1024
1025 Ok(results)
1026 }
1027}
1028
1029#[derive(Debug, Clone, Default)]
1031pub struct NoiseBenchmarkResults {
1032 pub device_benchmarks: HashMap<String, f64>,
1034}
1035
1036#[cfg(test)]
1037mod tests {
1038 use super::*;
1039 use approx::assert_abs_diff_eq;
1040
1041 #[test]
1042 fn test_device_topology_linear_chain() {
1043 let topology = DeviceTopology::linear_chain(5);
1044 assert_eq!(topology.num_qubits, 5);
1045 assert!(topology.connectivity[[0, 1]]);
1046 assert!(!topology.connectivity[[0, 2]]);
1047 assert_eq!(topology.get_neighbors(0), vec![1]);
1048 assert_eq!(topology.get_neighbors(2), vec![1, 3]);
1049 }
1050
1051 #[test]
1052 fn test_device_topology_square_lattice() {
1053 let topology = DeviceTopology::square_lattice(3, 3);
1054 assert_eq!(topology.num_qubits, 9);
1055 assert!(topology.connectivity[[0, 1]]); assert!(topology.connectivity[[0, 3]]); assert!(!topology.connectivity[[0, 4]]); }
1059
1060 #[test]
1061 fn test_superconducting_noise_model() {
1062 let config = DeviceNoiseConfig {
1063 device_type: DeviceType::Superconducting,
1064 topology: DeviceTopology::linear_chain(3),
1065 ..Default::default()
1066 };
1067
1068 let model = SuperconductingNoiseModel::new(config)
1069 .expect("SuperconductingNoiseModel creation should succeed");
1070 assert_eq!(model.device_type(), DeviceType::Superconducting);
1071 assert_eq!(model.coherence_params.t1_times.len(), 3);
1072 }
1073
1074 #[test]
1075 fn test_device_noise_simulator() {
1076 let config = DeviceNoiseConfig {
1077 device_type: DeviceType::Superconducting,
1078 topology: DeviceTopology::linear_chain(2),
1079 random_seed: Some(12_345),
1080 ..Default::default()
1081 };
1082
1083 let model = SuperconductingNoiseModel::new(config)
1084 .expect("SuperconductingNoiseModel creation should succeed");
1085 let mut simulator = DeviceNoiseSimulator::new(Box::new(model))
1086 .expect("DeviceNoiseSimulator creation should succeed");
1087
1088 let mut state = Array1::from_elem(4, Complex64::new(0.0, 0.0));
1089 state[0] = Complex64::new(1.0, 0.0);
1090
1091 let result = simulator.apply_gate_noise("x", &[0], 20.0, &mut state);
1093 assert!(result.is_ok());
1094 assert_eq!(simulator.stats.single_qubit_noise_events, 1);
1095
1096 let result = simulator.apply_gate_noise("cnot", &[0, 1], 100.0, &mut state);
1098 assert!(result.is_ok());
1099 assert_eq!(simulator.stats.two_qubit_noise_events, 1);
1100 }
1101
1102 #[test]
1103 fn test_measurement_noise() {
1104 let config = DeviceNoiseConfig {
1105 device_type: DeviceType::Superconducting,
1106 topology: DeviceTopology::linear_chain(1),
1107 random_seed: Some(12_345),
1108 ..Default::default()
1109 };
1110
1111 let model = SuperconductingNoiseModel::new(config)
1112 .expect("SuperconductingNoiseModel creation should succeed");
1113 let mut simulator = DeviceNoiseSimulator::new(Box::new(model))
1114 .expect("DeviceNoiseSimulator creation should succeed");
1115
1116 let mut results = vec![false, true, false];
1117 simulator
1118 .apply_measurement_noise(&[0, 0, 0], &mut results)
1119 .expect("apply_measurement_noise should succeed");
1120
1121 }
1124
1125 #[test]
1126 fn test_gate_times_default() {
1127 let gate_times = GateTimes::default();
1128 assert_eq!(gate_times.single_qubit, 20.0);
1129 assert_eq!(gate_times.two_qubit, 100.0);
1130 assert_eq!(gate_times.measurement, 1000.0);
1131 }
1132
1133 #[test]
1134 fn test_device_noise_utils() {
1135 let result = DeviceNoiseUtils::create_ibm_device("ibm_montreal", 5);
1136 assert!(result.is_ok());
1137
1138 let result = DeviceNoiseUtils::create_google_sycamore(9);
1139 assert!(result.is_ok());
1140
1141 let result = DeviceNoiseUtils::create_trapped_ion_device(5);
1142 assert!(result.is_ok());
1143 }
1144
1145 #[test]
1146 fn test_calibration_data_integration() {
1147 let calibration = CalibrationData {
1148 device_id: "test_device".to_string(),
1149 timestamp: std::time::SystemTime::now(),
1150 single_qubit_fidelities: vec![0.999, 0.998, 0.999],
1151 two_qubit_fidelities: HashMap::new(),
1152 t1_times: vec![50.0, 45.0, 55.0],
1153 t2_times: vec![25.0, 20.0, 30.0],
1154 readout_fidelities: vec![0.95, 0.96, 0.94],
1155 gate_times: GateTimes::default(),
1156 crosstalk_matrices: HashMap::new(),
1157 frequency_drift: Vec::new(),
1158 };
1159
1160 let config = DeviceNoiseConfig {
1161 device_type: DeviceType::Superconducting,
1162 topology: DeviceTopology::linear_chain(3),
1163 calibration_data: Some(calibration.clone()),
1164 ..Default::default()
1165 };
1166
1167 let model = SuperconductingNoiseModel::from_calibration_data(config, &calibration)
1168 .expect("from_calibration_data should succeed");
1169 assert_eq!(model.coherence_params.t1_times, calibration.t1_times);
1170 assert_eq!(model.coherence_params.t2_times, calibration.t2_times);
1171 }
1172
1173 #[test]
1174 fn test_temperature_scaling() {
1175 let scaling_15mk = SuperconductingNoiseModel::calculate_temperature_scaling(0.015);
1176 let scaling_50mk = SuperconductingNoiseModel::calculate_temperature_scaling(0.050);
1177
1178 assert!(scaling_50mk > scaling_15mk);
1180 assert!(scaling_15mk > 0.0);
1181 assert!(scaling_15mk < 1.0);
1182 }
1183}