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 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| 0.1f64.mul_add(i as f64, 5.0)) .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| 0.1f64.mul_add((i % 7) as f64, 5.0)) .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)
187 .map(|i| 0.1f64.mul_add(i as f64, 5.0))
188 .collect(),
189 }
190 }
191
192 pub fn get_neighbors(&self, qubit: usize) -> Vec<usize> {
194 (0..self.num_qubits)
195 .filter(|&i| i != qubit && self.connectivity[[qubit, i]])
196 .collect()
197 }
198
199 pub fn distance(&self, qubit1: usize, qubit2: usize) -> f64 {
201 let pos1 = &self.positions[qubit1];
202 let pos2 = &self.positions[qubit2];
203 (pos1.2 - pos2.2)
204 .mul_add(
205 pos1.2 - pos2.2,
206 (pos1.1 - pos2.1).mul_add(pos1.1 - pos2.1, (pos1.0 - pos2.0).powi(2)),
207 )
208 .sqrt()
209 }
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct CalibrationData {
215 pub device_id: String,
217 pub timestamp: std::time::SystemTime,
219 pub single_qubit_fidelities: Vec<f64>,
221 pub two_qubit_fidelities: HashMap<(usize, usize), f64>,
223 pub t1_times: Vec<f64>,
225 pub t2_times: Vec<f64>,
227 pub readout_fidelities: Vec<f64>,
229 pub gate_times: GateTimes,
231 pub crosstalk_matrices: HashMap<String, Array2<f64>>,
233 pub frequency_drift: Vec<FrequencyDrift>,
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct GateTimes {
240 pub single_qubit: f64,
242 pub two_qubit: f64,
244 pub measurement: f64,
246 pub reset: f64,
248}
249
250impl Default for GateTimes {
251 fn default() -> Self {
252 Self {
253 single_qubit: 20.0, two_qubit: 100.0, measurement: 1000.0, reset: 500.0, }
258 }
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct FrequencyDrift {
264 pub qubit: usize,
266 pub drift_rate: f64,
268 pub random_walk_coeff: f64,
270}
271
272pub trait DeviceNoiseModel: Send + Sync {
274 fn device_type(&self) -> DeviceType;
276
277 fn apply_single_qubit_noise(
279 &self,
280 qubit: usize,
281 gate_time: f64,
282 state: &mut Array1<Complex64>,
283 ) -> Result<()>;
284
285 fn apply_two_qubit_noise(
287 &self,
288 qubit1: usize,
289 qubit2: usize,
290 gate_time: f64,
291 state: &mut Array1<Complex64>,
292 ) -> Result<()>;
293
294 fn apply_measurement_noise(&self, qubit: usize, measurement_result: bool) -> Result<bool>;
296
297 fn apply_idle_noise(
299 &self,
300 qubit: usize,
301 idle_time: f64,
302 state: &mut Array1<Complex64>,
303 ) -> Result<()>;
304
305 fn update_time_dependent_parameters(&mut self, current_time: f64) -> Result<()>;
307}
308
309#[derive(Debug, Clone)]
311pub struct SuperconductingNoiseModel {
312 config: DeviceNoiseConfig,
314 coherence_params: CoherenceParameters,
316 gate_errors: GateErrorRates,
318 readout_errors: Array2<f64>,
320 crosstalk: HashMap<String, Array2<f64>>,
322 current_time: f64,
324 rng_state: u64,
326}
327
328#[derive(Debug, Clone)]
330pub struct CoherenceParameters {
331 pub t1_times: Vec<f64>,
333 pub t2_times: Vec<f64>,
335 pub t2_star_times: Vec<f64>,
337 pub temperature_scaling: f64,
339}
340
341#[derive(Debug, Clone)]
343pub struct GateErrorRates {
344 pub single_qubit: Vec<f64>,
346 pub two_qubit: HashMap<(usize, usize), f64>,
348 pub depolarizing: Vec<f64>,
350 pub dephasing: Vec<f64>,
352 pub amplitude_damping: Vec<f64>,
354}
355
356impl SuperconductingNoiseModel {
357 pub fn new(config: DeviceNoiseConfig) -> Result<Self> {
359 let num_qubits = config.topology.num_qubits;
360
361 let coherence_params = CoherenceParameters {
363 t1_times: (0..num_qubits)
364 .map(|_| fastrand::f64().mul_add(50.0, 50.0))
365 .collect(), t2_times: (0..num_qubits)
367 .map(|_| fastrand::f64().mul_add(30.0, 20.0))
368 .collect(), t2_star_times: (0..num_qubits)
370 .map(|_| fastrand::f64().mul_add(10.0, 10.0))
371 .collect(), temperature_scaling: Self::calculate_temperature_scaling(config.temperature),
373 };
374
375 let gate_errors = GateErrorRates {
377 single_qubit: (0..num_qubits)
378 .map(|_| fastrand::f64().mul_add(1e-3, 1e-4))
379 .collect(), two_qubit: HashMap::new(), depolarizing: (0..num_qubits).map(|_| 1e-5).collect(),
382 dephasing: (0..num_qubits).map(|_| 1e-4).collect(),
383 amplitude_damping: (0..num_qubits).map(|_| 1e-5).collect(),
384 };
385
386 let readout_errors = Array2::from_elem((num_qubits, 2), 0.05); let rng_state = config.random_seed.unwrap_or_else(|| {
390 use std::time::{SystemTime, UNIX_EPOCH};
391 SystemTime::now()
392 .duration_since(UNIX_EPOCH)
393 .unwrap()
394 .as_nanos() as u64
395 });
396
397 Ok(Self {
398 config,
399 coherence_params,
400 gate_errors,
401 readout_errors,
402 crosstalk: HashMap::new(),
403 current_time: 0.0,
404 rng_state,
405 })
406 }
407
408 pub fn from_calibration_data(
410 config: DeviceNoiseConfig,
411 calibration: &CalibrationData,
412 ) -> Result<Self> {
413 let mut model = Self::new(config)?;
414
415 model.coherence_params.t1_times = calibration.t1_times.clone();
417 model.coherence_params.t2_times = calibration.t2_times.clone();
418 model.gate_errors.single_qubit = calibration
419 .single_qubit_fidelities
420 .iter()
421 .map(|&f| 1.0 - f)
422 .collect();
423
424 for (&(q1, q2), &fidelity) in &calibration.two_qubit_fidelities {
426 model.gate_errors.two_qubit.insert((q1, q2), 1.0 - fidelity);
427 }
428
429 for (i, &fidelity) in calibration.readout_fidelities.iter().enumerate() {
431 model.readout_errors[[i, 0]] = 1.0 - fidelity; model.readout_errors[[i, 1]] = 1.0 - fidelity; }
434
435 model.crosstalk = calibration.crosstalk_matrices.clone();
436
437 Ok(model)
438 }
439
440 fn calculate_temperature_scaling(temperature: f64) -> f64 {
442 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())
445 }
446
447 fn random(&mut self) -> f64 {
449 self.rng_state = self.rng_state.wrapping_mul(1103515245).wrapping_add(12345);
451 (self.rng_state as f64) / (u64::MAX as f64)
452 }
453
454 fn apply_decoherence(
456 &mut self,
457 qubit: usize,
458 time_ns: f64,
459 state: &mut Array1<Complex64>,
460 ) -> Result<()> {
461 let t1 = self.coherence_params.t1_times[qubit] * 1000.0; let t2 = self.coherence_params.t2_times[qubit] * 1000.0;
463
464 let gamma_1 = time_ns / t1;
466 if gamma_1 > 0.0 {
467 self.apply_amplitude_damping(qubit, gamma_1, state)?;
468 }
469
470 let gamma_2 = time_ns / t2;
472 if gamma_2 > 0.0 {
473 self.apply_dephasing(qubit, gamma_2, state)?;
474 }
475
476 Ok(())
477 }
478
479 fn apply_amplitude_damping(
481 &mut self,
482 qubit: usize,
483 gamma: f64,
484 state: &mut Array1<Complex64>,
485 ) -> Result<()> {
486 let state_size = state.len();
487 let qubit_mask = 1 << qubit;
488
489 let mut new_state = state.clone();
490
491 for i in 0..state_size {
492 if i & qubit_mask != 0 {
493 let j = i & !qubit_mask; if self.random() < gamma {
497 new_state[j] += state[i];
499 new_state[i] = Complex64::new(0.0, 0.0);
500 } else {
501 new_state[i] *= (1.0 - gamma).sqrt();
503 }
504 }
505 }
506
507 *state = new_state;
508 Ok(())
509 }
510
511 fn apply_dephasing(
513 &mut self,
514 qubit: usize,
515 gamma: f64,
516 state: &mut Array1<Complex64>,
517 ) -> Result<()> {
518 let state_size = state.len();
519 let qubit_mask = 1 << qubit;
520
521 for i in 0..state_size {
522 if i & qubit_mask != 0 {
523 if self.random() < gamma {
525 let phase = self.random() * 2.0 * std::f64::consts::PI;
526 state[i] *= Complex64::new(0.0, phase).exp();
527 }
528 }
529 }
530
531 Ok(())
532 }
533
534 fn apply_crosstalk(
536 &mut self,
537 active_qubits: &[usize],
538 state: &mut Array1<Complex64>,
539 ) -> Result<()> {
540 if !self.config.enable_correlated_noise {
541 return Ok(());
542 }
543
544 for &qubit in active_qubits {
546 let neighbors = self.config.topology.get_neighbors(qubit);
547
548 for neighbor in neighbors {
549 if !active_qubits.contains(&neighbor) {
550 let crosstalk_strength = 0.01; let angle = crosstalk_strength * self.random() * std::f64::consts::PI;
553
554 self.apply_small_rotation(neighbor, angle, state)?;
556 }
557 }
558 }
559
560 Ok(())
561 }
562
563 fn apply_small_rotation(
565 &mut self,
566 qubit: usize,
567 angle: f64,
568 state: &mut Array1<Complex64>,
569 ) -> Result<()> {
570 let qubit_mask = 1 << qubit;
571 let state_size = state.len();
572
573 let cos_half = (angle / 2.0).cos();
574 let sin_half = (angle / 2.0).sin();
575
576 for i in 0..state_size {
577 if i & qubit_mask == 0 {
578 let j = i | qubit_mask;
579 if j < state_size {
580 let amp_0 = state[i];
581 let amp_1 = state[j];
582
583 state[i] = cos_half * amp_0 - Complex64::new(0.0, sin_half) * amp_1;
584 state[j] = cos_half * amp_1 - Complex64::new(0.0, sin_half) * amp_0;
585 }
586 }
587 }
588
589 Ok(())
590 }
591}
592
593impl DeviceNoiseModel for SuperconductingNoiseModel {
594 fn device_type(&self) -> DeviceType {
595 DeviceType::Superconducting
596 }
597
598 fn apply_single_qubit_noise(
599 &self,
600 qubit: usize,
601 gate_time: f64,
602 state: &mut Array1<Complex64>,
603 ) -> Result<()> {
604 let mut model = self.clone();
605
606 model.apply_decoherence(qubit, gate_time, state)?;
608
609 let error_rate = model.gate_errors.single_qubit[qubit];
611 if model.random() < error_rate {
612 let pauli_error = (model.random() * 3.0) as usize;
614 model.apply_pauli_error(qubit, pauli_error, state)?;
615 }
616
617 model.apply_crosstalk(&[qubit], state)?;
619
620 Ok(())
621 }
622
623 fn apply_two_qubit_noise(
624 &self,
625 qubit1: usize,
626 qubit2: usize,
627 gate_time: f64,
628 state: &mut Array1<Complex64>,
629 ) -> Result<()> {
630 let mut model = self.clone();
631
632 model.apply_decoherence(qubit1, gate_time, state)?;
634 model.apply_decoherence(qubit2, gate_time, state)?;
635
636 let error_rate = model
638 .gate_errors
639 .two_qubit
640 .get(&(qubit1, qubit2))
641 .or_else(|| model.gate_errors.two_qubit.get(&(qubit2, qubit1)))
642 .copied()
643 .unwrap_or(0.01); if model.random() < error_rate {
646 model.apply_two_qubit_error(qubit1, qubit2, state)?;
648 }
649
650 model.apply_crosstalk(&[qubit1, qubit2], state)?;
652
653 Ok(())
654 }
655
656 fn apply_measurement_noise(&self, qubit: usize, measurement_result: bool) -> Result<bool> {
657 let error_prob = if measurement_result {
658 self.readout_errors[[qubit, 1]] } else {
660 self.readout_errors[[qubit, 0]] };
662
663 if fastrand::f64() < error_prob {
664 Ok(!measurement_result) } else {
666 Ok(measurement_result)
667 }
668 }
669
670 fn apply_idle_noise(
671 &self,
672 qubit: usize,
673 idle_time: f64,
674 state: &mut Array1<Complex64>,
675 ) -> Result<()> {
676 let mut model = self.clone();
677 model.apply_decoherence(qubit, idle_time, state)?;
678 Ok(())
679 }
680
681 fn update_time_dependent_parameters(&mut self, current_time: f64) -> Result<()> {
682 self.current_time = current_time;
683
684 if !self.config.enable_time_dependent_noise {
685 return Ok(());
686 }
687
688 if let Some(calibration) = &self.config.calibration_data {
690 for drift in &calibration.frequency_drift {
691 let frequency_shift = drift.drift_rate * current_time / 1e9; }
695 }
696
697 Ok(())
698 }
699}
700
701impl SuperconductingNoiseModel {
702 fn apply_pauli_error(
704 &mut self,
705 qubit: usize,
706 pauli_type: usize,
707 state: &mut Array1<Complex64>,
708 ) -> Result<()> {
709 let qubit_mask = 1 << qubit;
710 let state_size = state.len();
711
712 match pauli_type {
713 0 => {
714 for i in 0..state_size {
716 let j = i ^ qubit_mask;
717 if i < j {
718 let temp = state[i];
719 state[i] = state[j];
720 state[j] = temp;
721 }
722 }
723 }
724 1 => {
725 for i in 0..state_size {
727 let j = i ^ qubit_mask;
728 if i < j {
729 let temp = state[i];
730 state[i] = Complex64::new(0.0, 1.0) * state[j];
731 state[j] = Complex64::new(0.0, -1.0) * temp;
732 }
733 }
734 }
735 2 => {
736 for i in 0..state_size {
738 if i & qubit_mask != 0 {
739 state[i] = -state[i];
740 }
741 }
742 }
743 _ => {} }
745
746 Ok(())
747 }
748
749 fn apply_two_qubit_error(
751 &mut self,
752 qubit1: usize,
753 qubit2: usize,
754 state: &mut Array1<Complex64>,
755 ) -> Result<()> {
756 let error1 = (self.random() * 4.0) as usize;
758 let error2 = (self.random() * 4.0) as usize;
759
760 if error1 < 3 {
761 self.apply_pauli_error(qubit1, error1, state)?;
762 }
763 if error2 < 3 {
764 self.apply_pauli_error(qubit2, error2, state)?;
765 }
766
767 Ok(())
768 }
769}
770
771pub struct DeviceNoiseSimulator {
773 noise_model: Box<dyn DeviceNoiseModel>,
775 backend: Option<SciRS2Backend>,
777 stats: NoiseSimulationStats,
779 current_time: f64,
781}
782
783#[derive(Debug, Clone, Default, Serialize, Deserialize)]
785pub struct NoiseSimulationStats {
786 pub total_noise_ops: usize,
788 pub single_qubit_noise_events: usize,
790 pub two_qubit_noise_events: usize,
792 pub measurement_errors: usize,
794 pub coherence_events: usize,
796 pub crosstalk_events: usize,
798 pub total_simulation_time_ms: f64,
800}
801
802impl DeviceNoiseSimulator {
803 pub fn new(noise_model: Box<dyn DeviceNoiseModel>) -> Result<Self> {
805 Ok(Self {
806 noise_model,
807 backend: None,
808 stats: NoiseSimulationStats::default(),
809 current_time: 0.0,
810 })
811 }
812
813 pub fn with_backend(mut self) -> Result<Self> {
815 self.backend = Some(SciRS2Backend::new());
816 Ok(self)
817 }
818
819 pub fn apply_gate_noise(
821 &mut self,
822 gate_type: &str,
823 qubits: &[usize],
824 gate_time: f64,
825 state: &mut Array1<Complex64>,
826 ) -> Result<()> {
827 let start_time = std::time::Instant::now();
828
829 match qubits.len() {
830 1 => {
831 self.noise_model
832 .apply_single_qubit_noise(qubits[0], gate_time, state)?;
833 self.stats.single_qubit_noise_events += 1;
834 }
835 2 => {
836 self.noise_model
837 .apply_two_qubit_noise(qubits[0], qubits[1], gate_time, state)?;
838 self.stats.two_qubit_noise_events += 1;
839 }
840 _ => {
841 return Err(SimulatorError::UnsupportedOperation(format!(
842 "Noise model for {}-qubit gates not implemented",
843 qubits.len()
844 )));
845 }
846 }
847
848 self.current_time += gate_time;
849 self.noise_model
850 .update_time_dependent_parameters(self.current_time)?;
851
852 self.stats.total_noise_ops += 1;
853 self.stats.total_simulation_time_ms += start_time.elapsed().as_secs_f64() * 1000.0;
854
855 Ok(())
856 }
857
858 pub fn apply_measurement_noise(
860 &mut self,
861 qubits: &[usize],
862 results: &mut [bool],
863 ) -> Result<()> {
864 for (i, &qubit) in qubits.iter().enumerate() {
865 let noisy_result = self
866 .noise_model
867 .apply_measurement_noise(qubit, results[i])?;
868 if noisy_result != results[i] {
869 self.stats.measurement_errors += 1;
870 }
871 results[i] = noisy_result;
872 }
873
874 Ok(())
875 }
876
877 pub fn apply_idle_noise(
879 &mut self,
880 idle_time: f64,
881 state: &mut Array1<Complex64>,
882 num_qubits: usize,
883 ) -> Result<()> {
884 for qubit in 0..num_qubits {
885 self.noise_model.apply_idle_noise(qubit, idle_time, state)?;
886 }
887
888 self.current_time += idle_time;
889 self.noise_model
890 .update_time_dependent_parameters(self.current_time)?;
891
892 Ok(())
893 }
894
895 pub const fn get_stats(&self) -> &NoiseSimulationStats {
897 &self.stats
898 }
899
900 pub fn reset_stats(&mut self) {
902 self.stats = NoiseSimulationStats::default();
903 self.current_time = 0.0;
904 }
905}
906
907pub struct DeviceNoiseUtils;
909
910impl DeviceNoiseUtils {
911 pub fn create_ibm_device(
913 device_name: &str,
914 num_qubits: usize,
915 ) -> Result<SuperconductingNoiseModel> {
916 let topology = match device_name {
917 "ibm_washington" => DeviceTopology::heavy_hex(3),
918 "ibm_montreal" => DeviceTopology::linear_chain(num_qubits),
919 _ => DeviceTopology::square_lattice(4, 4),
920 };
921
922 let config = DeviceNoiseConfig {
923 device_type: DeviceType::Superconducting,
924 topology,
925 temperature: 0.015, enable_correlated_noise: true,
927 enable_time_dependent_noise: true,
928 calibration_data: None,
929 random_seed: None,
930 real_time_adaptation: false,
931 };
932
933 SuperconductingNoiseModel::new(config)
934 }
935
936 pub fn create_google_sycamore(num_qubits: usize) -> Result<SuperconductingNoiseModel> {
938 let width = (num_qubits as f64).sqrt().ceil() as usize;
939 let topology = DeviceTopology::square_lattice(width, width);
940
941 let config = DeviceNoiseConfig {
942 device_type: DeviceType::Superconducting,
943 topology,
944 temperature: 0.020, enable_correlated_noise: true,
946 enable_time_dependent_noise: true,
947 calibration_data: None,
948 random_seed: None,
949 real_time_adaptation: false,
950 };
951
952 SuperconductingNoiseModel::new(config)
953 }
954
955 pub fn create_trapped_ion_device(num_ions: usize) -> Result<Box<dyn DeviceNoiseModel>> {
957 let topology = DeviceTopology::linear_chain(num_ions);
959 let config = DeviceNoiseConfig {
960 device_type: DeviceType::TrappedIon,
961 topology,
962 temperature: 1e-6, enable_correlated_noise: true,
964 enable_time_dependent_noise: false,
965 calibration_data: None,
966 random_seed: None,
967 real_time_adaptation: false,
968 };
969
970 Ok(Box::new(SuperconductingNoiseModel::new(config)?))
971 }
972
973 pub fn benchmark_noise_models() -> Result<NoiseBenchmarkResults> {
975 let mut results = NoiseBenchmarkResults::default();
976
977 let device_types = vec![
978 ("ibm_montreal", 27),
979 ("google_sycamore", 53),
980 ("trapped_ion", 20),
981 ];
982
983 for (device_name, num_qubits) in device_types {
984 let model = if device_name.starts_with("ibm") {
985 Box::new(Self::create_ibm_device(device_name, num_qubits)?)
986 as Box<dyn DeviceNoiseModel>
987 } else if device_name.starts_with("google") {
988 Box::new(Self::create_google_sycamore(num_qubits)?) as Box<dyn DeviceNoiseModel>
989 } else {
990 Self::create_trapped_ion_device(num_qubits)?
991 };
992
993 let mut simulator = DeviceNoiseSimulator::new(model)?;
994 let mut state = Array1::from_elem(1 << num_qubits.min(10), Complex64::new(0.0, 0.0));
995 state[0] = Complex64::new(1.0, 0.0);
996
997 let start = std::time::Instant::now();
998
999 for i in 0..num_qubits.min(10) {
1001 simulator.apply_gate_noise("x", &[i], 20.0, &mut state)?;
1002 }
1003
1004 let execution_time = start.elapsed().as_secs_f64() * 1000.0;
1005 results
1006 .device_benchmarks
1007 .insert(device_name.to_string(), execution_time);
1008 }
1009
1010 Ok(results)
1011 }
1012}
1013
1014#[derive(Debug, Clone, Default)]
1016pub struct NoiseBenchmarkResults {
1017 pub device_benchmarks: HashMap<String, f64>,
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023 use super::*;
1024 use approx::assert_abs_diff_eq;
1025
1026 #[test]
1027 fn test_device_topology_linear_chain() {
1028 let topology = DeviceTopology::linear_chain(5);
1029 assert_eq!(topology.num_qubits, 5);
1030 assert!(topology.connectivity[[0, 1]]);
1031 assert!(!topology.connectivity[[0, 2]]);
1032 assert_eq!(topology.get_neighbors(0), vec![1]);
1033 assert_eq!(topology.get_neighbors(2), vec![1, 3]);
1034 }
1035
1036 #[test]
1037 fn test_device_topology_square_lattice() {
1038 let topology = DeviceTopology::square_lattice(3, 3);
1039 assert_eq!(topology.num_qubits, 9);
1040 assert!(topology.connectivity[[0, 1]]); assert!(topology.connectivity[[0, 3]]); assert!(!topology.connectivity[[0, 4]]); }
1044
1045 #[test]
1046 fn test_superconducting_noise_model() {
1047 let config = DeviceNoiseConfig {
1048 device_type: DeviceType::Superconducting,
1049 topology: DeviceTopology::linear_chain(3),
1050 ..Default::default()
1051 };
1052
1053 let model = SuperconductingNoiseModel::new(config).unwrap();
1054 assert_eq!(model.device_type(), DeviceType::Superconducting);
1055 assert_eq!(model.coherence_params.t1_times.len(), 3);
1056 }
1057
1058 #[test]
1059 fn test_device_noise_simulator() {
1060 let config = DeviceNoiseConfig {
1061 device_type: DeviceType::Superconducting,
1062 topology: DeviceTopology::linear_chain(2),
1063 random_seed: Some(12345),
1064 ..Default::default()
1065 };
1066
1067 let model = SuperconductingNoiseModel::new(config).unwrap();
1068 let mut simulator = DeviceNoiseSimulator::new(Box::new(model)).unwrap();
1069
1070 let mut state = Array1::from_elem(4, Complex64::new(0.0, 0.0));
1071 state[0] = Complex64::new(1.0, 0.0);
1072
1073 let result = simulator.apply_gate_noise("x", &[0], 20.0, &mut state);
1075 assert!(result.is_ok());
1076 assert_eq!(simulator.stats.single_qubit_noise_events, 1);
1077
1078 let result = simulator.apply_gate_noise("cnot", &[0, 1], 100.0, &mut state);
1080 assert!(result.is_ok());
1081 assert_eq!(simulator.stats.two_qubit_noise_events, 1);
1082 }
1083
1084 #[test]
1085 fn test_measurement_noise() {
1086 let config = DeviceNoiseConfig {
1087 device_type: DeviceType::Superconducting,
1088 topology: DeviceTopology::linear_chain(1),
1089 random_seed: Some(12345),
1090 ..Default::default()
1091 };
1092
1093 let model = SuperconductingNoiseModel::new(config).unwrap();
1094 let mut simulator = DeviceNoiseSimulator::new(Box::new(model)).unwrap();
1095
1096 let mut results = vec![false, true, false];
1097 simulator
1098 .apply_measurement_noise(&[0, 0, 0], &mut results)
1099 .unwrap();
1100
1101 }
1104
1105 #[test]
1106 fn test_gate_times_default() {
1107 let gate_times = GateTimes::default();
1108 assert_eq!(gate_times.single_qubit, 20.0);
1109 assert_eq!(gate_times.two_qubit, 100.0);
1110 assert_eq!(gate_times.measurement, 1000.0);
1111 }
1112
1113 #[test]
1114 fn test_device_noise_utils() {
1115 let result = DeviceNoiseUtils::create_ibm_device("ibm_montreal", 5);
1116 assert!(result.is_ok());
1117
1118 let result = DeviceNoiseUtils::create_google_sycamore(9);
1119 assert!(result.is_ok());
1120
1121 let result = DeviceNoiseUtils::create_trapped_ion_device(5);
1122 assert!(result.is_ok());
1123 }
1124
1125 #[test]
1126 fn test_calibration_data_integration() {
1127 let calibration = CalibrationData {
1128 device_id: "test_device".to_string(),
1129 timestamp: std::time::SystemTime::now(),
1130 single_qubit_fidelities: vec![0.999, 0.998, 0.999],
1131 two_qubit_fidelities: HashMap::new(),
1132 t1_times: vec![50.0, 45.0, 55.0],
1133 t2_times: vec![25.0, 20.0, 30.0],
1134 readout_fidelities: vec![0.95, 0.96, 0.94],
1135 gate_times: GateTimes::default(),
1136 crosstalk_matrices: HashMap::new(),
1137 frequency_drift: Vec::new(),
1138 };
1139
1140 let config = DeviceNoiseConfig {
1141 device_type: DeviceType::Superconducting,
1142 topology: DeviceTopology::linear_chain(3),
1143 calibration_data: Some(calibration.clone()),
1144 ..Default::default()
1145 };
1146
1147 let model = SuperconductingNoiseModel::from_calibration_data(config, &calibration).unwrap();
1148 assert_eq!(model.coherence_params.t1_times, calibration.t1_times);
1149 assert_eq!(model.coherence_params.t2_times, calibration.t2_times);
1150 }
1151
1152 #[test]
1153 fn test_temperature_scaling() {
1154 let scaling_15mk = SuperconductingNoiseModel::calculate_temperature_scaling(0.015);
1155 let scaling_50mk = SuperconductingNoiseModel::calculate_temperature_scaling(0.050);
1156
1157 assert!(scaling_50mk > scaling_15mk);
1159 assert!(scaling_15mk > 0.0);
1160 assert!(scaling_15mk < 1.0);
1161 }
1162}