quantrs2_sim/
device_noise_models.rs

1//! Realistic device noise models for quantum hardware simulation.
2//!
3//! This module provides comprehensive noise models that accurately represent
4//! the characteristics of real quantum devices, including superconducting
5//! transmon qubits, trapped ions, photonic systems, and other quantum
6//! computing platforms. It integrates with `SciRS2` for high-performance
7//! noise simulation and calibration data analysis.
8
9use 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/// Device types supported for noise modeling
18#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub enum DeviceType {
20    /// Superconducting transmon qubits
21    Superconducting,
22    /// Trapped ion systems
23    TrappedIon,
24    /// Photonic quantum systems
25    Photonic,
26    /// Silicon spin qubits
27    SiliconSpin,
28    /// Neutral atom arrays
29    NeutralAtom,
30    /// Nitrogen-vacancy centers
31    NVCenter,
32    /// Custom device type
33    Custom(String),
34}
35
36/// Device noise model configuration
37#[derive(Debug, Clone)]
38pub struct DeviceNoiseConfig {
39    /// Device type
40    pub device_type: DeviceType,
41    /// Device topology (connectivity graph)
42    pub topology: DeviceTopology,
43    /// Temperature in Kelvin
44    pub temperature: f64,
45    /// Enable correlated noise
46    pub enable_correlated_noise: bool,
47    /// Enable time-dependent noise
48    pub enable_time_dependent_noise: bool,
49    /// Noise calibration data
50    pub calibration_data: Option<CalibrationData>,
51    /// Random seed for noise sampling
52    pub random_seed: Option<u64>,
53    /// Real-time noise adaptation
54    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, // ~15 mK
63            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/// Device topology representation
73#[derive(Debug, Clone, PartialEq)]
74pub struct DeviceTopology {
75    /// Number of qubits
76    pub num_qubits: usize,
77    /// Connectivity matrix (symmetric)
78    pub connectivity: Array2<bool>,
79    /// Physical positions of qubits (x, y, z)
80    pub positions: Vec<(f64, f64, f64)>,
81    /// Coupling strengths between connected qubits
82    pub coupling_strengths: HashMap<(usize, usize), f64>,
83    /// Qubit frequencies
84    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    /// Create a linear chain topology
101    #[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)) // GHz, with detuning
117            .collect();
118
119        Self {
120            num_qubits,
121            connectivity,
122            positions,
123            coupling_strengths,
124            frequencies,
125        }
126    }
127
128    /// Create a square lattice topology
129    #[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                // Horizontal connections
140                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                // Vertical connections
149                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)) // Frequency comb to avoid crosstalk
165            .collect();
166
167        Self {
168            num_qubits,
169            connectivity,
170            positions,
171            coupling_strengths,
172            frequencies,
173        }
174    }
175
176    /// Create IBM heavy-hex topology
177    #[must_use]
178    pub fn heavy_hex(distance: usize) -> Self {
179        // Simplified heavy-hex implementation
180        let num_qubits = distance * distance * 2;
181        let connectivity = Array2::from_elem((num_qubits, num_qubits), false);
182
183        // This would be implemented with the actual heavy-hex connectivity pattern
184        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    /// Get nearest neighbors of a qubit
196    #[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    /// Calculate distance between two qubits
204    #[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/// Calibration data from real devices
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct CalibrationData {
220    /// Device identifier
221    pub device_id: String,
222    /// Calibration timestamp
223    pub timestamp: std::time::SystemTime,
224    /// Single-qubit gate fidelities
225    pub single_qubit_fidelities: Vec<f64>,
226    /// Two-qubit gate fidelities
227    pub two_qubit_fidelities: HashMap<(usize, usize), f64>,
228    /// Coherence times T1 (relaxation)
229    pub t1_times: Vec<f64>,
230    /// Coherence times T2 (dephasing)
231    pub t2_times: Vec<f64>,
232    /// Readout fidelities
233    pub readout_fidelities: Vec<f64>,
234    /// Gate times
235    pub gate_times: GateTimes,
236    /// Cross-talk matrices
237    pub crosstalk_matrices: HashMap<String, Array2<f64>>,
238    /// Frequency drift parameters
239    pub frequency_drift: Vec<FrequencyDrift>,
240}
241
242/// Gate timing information
243#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct GateTimes {
245    /// Single-qubit gate time (ns)
246    pub single_qubit: f64,
247    /// Two-qubit gate time (ns)
248    pub two_qubit: f64,
249    /// Measurement time (ns)
250    pub measurement: f64,
251    /// Reset time (ns)
252    pub reset: f64,
253}
254
255impl Default for GateTimes {
256    fn default() -> Self {
257        Self {
258            single_qubit: 20.0,  // 20 ns
259            two_qubit: 100.0,    // 100 ns
260            measurement: 1000.0, // 1 μs
261            reset: 500.0,        // 500 ns
262        }
263    }
264}
265
266/// Frequency drift model
267#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct FrequencyDrift {
269    /// Qubit index
270    pub qubit: usize,
271    /// Drift rate (Hz/s)
272    pub drift_rate: f64,
273    /// Random walk coefficient
274    pub random_walk_coeff: f64,
275}
276
277/// Device-specific noise models
278pub trait DeviceNoiseModel: Send + Sync {
279    /// Get the device type
280    fn device_type(&self) -> DeviceType;
281
282    /// Apply single-qubit gate noise
283    fn apply_single_qubit_noise(
284        &self,
285        qubit: usize,
286        gate_time: f64,
287        state: &mut Array1<Complex64>,
288    ) -> Result<()>;
289
290    /// Apply two-qubit gate noise
291    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    /// Apply measurement noise
300    fn apply_measurement_noise(&self, qubit: usize, measurement_result: bool) -> Result<bool>;
301
302    /// Apply idle noise (during gates on other qubits)
303    fn apply_idle_noise(
304        &self,
305        qubit: usize,
306        idle_time: f64,
307        state: &mut Array1<Complex64>,
308    ) -> Result<()>;
309
310    /// Update time-dependent parameters
311    fn update_time_dependent_parameters(&mut self, current_time: f64) -> Result<()>;
312}
313
314/// Superconducting transmon noise model
315#[derive(Debug, Clone)]
316pub struct SuperconductingNoiseModel {
317    /// Configuration
318    config: DeviceNoiseConfig,
319    /// Coherence parameters
320    coherence_params: CoherenceParameters,
321    /// Gate error rates
322    gate_errors: GateErrorRates,
323    /// Readout error matrix
324    readout_errors: Array2<f64>,
325    /// Cross-talk matrices
326    crosstalk: HashMap<String, Array2<f64>>,
327    /// Current time for time-dependent effects
328    current_time: f64,
329    /// Random number generator state
330    rng_state: u64,
331}
332
333/// Coherence parameters for superconducting qubits
334#[derive(Debug, Clone)]
335pub struct CoherenceParameters {
336    /// T1 times for each qubit (μs)
337    pub t1_times: Vec<f64>,
338    /// T2 times for each qubit (μs)
339    pub t2_times: Vec<f64>,
340    /// Pure dephasing times T2* (μs)
341    pub t2_star_times: Vec<f64>,
342    /// Temperature-dependent scaling
343    pub temperature_scaling: f64,
344}
345
346/// Gate error rates
347#[derive(Debug, Clone)]
348pub struct GateErrorRates {
349    /// Single-qubit gate error rates
350    pub single_qubit: Vec<f64>,
351    /// Two-qubit gate error rates
352    pub two_qubit: HashMap<(usize, usize), f64>,
353    /// Depolarizing error rates
354    pub depolarizing: Vec<f64>,
355    /// Dephasing error rates
356    pub dephasing: Vec<f64>,
357    /// Amplitude damping rates
358    pub amplitude_damping: Vec<f64>,
359}
360
361impl SuperconductingNoiseModel {
362    /// Create new superconducting noise model
363    pub fn new(config: DeviceNoiseConfig) -> Result<Self> {
364        let num_qubits = config.topology.num_qubits;
365
366        // Initialize coherence parameters based on typical superconducting values
367        let coherence_params = CoherenceParameters {
368            t1_times: (0..num_qubits)
369                .map(|_| fastrand::f64().mul_add(50.0, 50.0))
370                .collect(), // 50-100 μs
371            t2_times: (0..num_qubits)
372                .map(|_| fastrand::f64().mul_add(30.0, 20.0))
373                .collect(), // 20-50 μs
374            t2_star_times: (0..num_qubits)
375                .map(|_| fastrand::f64().mul_add(10.0, 10.0))
376                .collect(), // 10-20 μs
377            temperature_scaling: Self::calculate_temperature_scaling(config.temperature),
378        };
379
380        // Initialize gate error rates
381        let gate_errors = GateErrorRates {
382            single_qubit: (0..num_qubits)
383                .map(|_| fastrand::f64().mul_add(1e-3, 1e-4))
384                .collect(), // 0.01-0.1%
385            two_qubit: HashMap::new(), // Will be populated based on connectivity
386            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        // Initialize readout error matrix
392        let readout_errors = Array2::from_elem((num_qubits, 2), 0.05); // 5% readout error
393
394        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    /// Create from calibration data
414    pub fn from_calibration_data(
415        config: DeviceNoiseConfig,
416        calibration: &CalibrationData,
417    ) -> Result<Self> {
418        let mut model = Self::new(config)?;
419
420        // Update parameters from calibration data
421        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        // Update two-qubit gate errors
436        for (&(q1, q2), &fidelity) in &calibration.two_qubit_fidelities {
437            model.gate_errors.two_qubit.insert((q1, q2), 1.0 - fidelity);
438        }
439
440        // Update readout errors
441        for (i, &fidelity) in calibration.readout_fidelities.iter().enumerate() {
442            model.readout_errors[[i, 0]] = 1.0 - fidelity; // |0⟩ → |1⟩ error
443            model.readout_errors[[i, 1]] = 1.0 - fidelity; // |1⟩ → |0⟩ error
444        }
445
446        model.crosstalk.clone_from(&calibration.crosstalk_matrices);
447
448        Ok(model)
449    }
450
451    /// Calculate temperature scaling factor
452    fn calculate_temperature_scaling(temperature: f64) -> f64 {
453        // Thermal population of excited state
454        let kb_t_over_hf = temperature * 1.38e-23 / (6.626e-34 * 5e9); // Assuming 5 GHz qubit
455        (-1.0 / kb_t_over_hf).exp() / (1.0 + (-1.0 / kb_t_over_hf).exp())
456    }
457
458    /// Sample from random number generator
459    fn random(&mut self) -> f64 {
460        // Simple linear congruential generator
461        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    /// Apply decoherence channel
469    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; // Convert to ns
476        let t2 = self.coherence_params.t2_times[qubit] * 1000.0;
477
478        // Apply amplitude damping (T1 process)
479        let gamma_1 = time_ns / t1;
480        if gamma_1 > 0.0 {
481            self.apply_amplitude_damping(qubit, gamma_1, state)?;
482        }
483
484        // Apply dephasing (T2 process)
485        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    /// Apply amplitude damping channel
494    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                // |1⟩ state - apply damping
508                let j = i & !qubit_mask; // Corresponding |0⟩ state
509
510                if self.random() < gamma {
511                    // Decay |1⟩ → |0⟩
512                    new_state[j] += state[i];
513                    new_state[i] = Complex64::new(0.0, 0.0);
514                } else {
515                    // No decay, but renormalize
516                    new_state[i] *= (1.0 - gamma).sqrt();
517                }
518            }
519        }
520
521        *state = new_state;
522        Ok(())
523    }
524
525    /// Apply dephasing channel
526    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                // Apply random phase to |1⟩ states
538                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    /// Apply cross-talk effects
549    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        // Apply nearest-neighbor cross-talk
559        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                    // Apply small rotation due to cross-talk
565                    let crosstalk_strength = 0.01; // 1% cross-talk
566                    let angle = crosstalk_strength * self.random() * std::f64::consts::PI;
567
568                    // Apply random rotation
569                    self.apply_small_rotation(neighbor, angle, state)?;
570                }
571            }
572        }
573
574        Ok(())
575    }
576
577    /// Apply small rotation due to cross-talk
578    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        // Apply decoherence during gate
621        model.apply_decoherence(qubit, gate_time, state)?;
622
623        // Apply gate error
624        let error_rate = model.gate_errors.single_qubit[qubit];
625        if model.random() < error_rate {
626            // Apply random Pauli error
627            let pauli_error = (model.random() * 3.0) as usize;
628            model.apply_pauli_error(qubit, pauli_error, state)?;
629        }
630
631        // Apply cross-talk to neighboring qubits
632        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        // Apply decoherence to both qubits
647        model.apply_decoherence(qubit1, gate_time, state)?;
648        model.apply_decoherence(qubit2, gate_time, state)?;
649
650        // Apply two-qubit gate error
651        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); // Default 1% error
658
659        if model.random() < error_rate {
660            // Apply correlated two-qubit error
661            model.apply_two_qubit_error(qubit1, qubit2, state)?;
662        }
663
664        // Apply cross-talk
665        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]] // |1⟩ → |0⟩ error
673        } else {
674            self.readout_errors[[qubit, 0]] // |0⟩ → |1⟩ error
675        };
676
677        if fastrand::f64() < error_prob {
678            Ok(!measurement_result) // Flip the result
679        } 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        // Update frequency drift if calibration data is available
703        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; // Convert to Hz
706                                                                             // This would update the qubit frequency and affect gate fidelities
707                                                                             // For now, we just track the time
708            }
709        }
710
711        Ok(())
712    }
713}
714
715impl SuperconductingNoiseModel {
716    /// Apply Pauli error
717    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                // Pauli X
729                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                // Pauli Y
740                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                // Pauli Z
751                for i in 0..state_size {
752                    if i & qubit_mask != 0 {
753                        state[i] = -state[i];
754                    }
755                }
756            }
757            _ => {} // Identity (no error)
758        }
759
760        Ok(())
761    }
762
763    /// Apply two-qubit correlated error
764    fn apply_two_qubit_error(
765        &mut self,
766        qubit1: usize,
767        qubit2: usize,
768        state: &mut Array1<Complex64>,
769    ) -> Result<()> {
770        // Apply correlated Pauli errors
771        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
785/// Comprehensive device noise simulator
786pub struct DeviceNoiseSimulator {
787    /// Device noise model
788    noise_model: Box<dyn DeviceNoiseModel>,
789    /// `SciRS2` backend for optimization
790    backend: Option<SciRS2Backend>,
791    /// Simulation statistics
792    stats: NoiseSimulationStats,
793    /// Current simulation time
794    current_time: f64,
795}
796
797/// Noise simulation statistics
798#[derive(Debug, Clone, Default, Serialize, Deserialize)]
799pub struct NoiseSimulationStats {
800    /// Total noise operations applied
801    pub total_noise_ops: usize,
802    /// Single-qubit noise events
803    pub single_qubit_noise_events: usize,
804    /// Two-qubit noise events
805    pub two_qubit_noise_events: usize,
806    /// Measurement errors
807    pub measurement_errors: usize,
808    /// Coherence events (T1/T2)
809    pub coherence_events: usize,
810    /// Cross-talk events
811    pub crosstalk_events: usize,
812    /// Total simulation time
813    pub total_simulation_time_ms: f64,
814}
815
816impl DeviceNoiseSimulator {
817    /// Create new device noise simulator
818    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    /// Initialize with `SciRS2` backend
828    pub fn with_backend(mut self) -> Result<Self> {
829        self.backend = Some(SciRS2Backend::new());
830        Ok(self)
831    }
832
833    /// Apply gate noise
834    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    /// Apply measurement noise
873    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    /// Apply idle noise to all qubits
892    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    /// Get simulation statistics
910    #[must_use]
911    pub const fn get_stats(&self) -> &NoiseSimulationStats {
912        &self.stats
913    }
914
915    /// Reset simulation statistics
916    pub fn reset_stats(&mut self) {
917        self.stats = NoiseSimulationStats::default();
918        self.current_time = 0.0;
919    }
920}
921
922/// Device noise model utilities
923pub struct DeviceNoiseUtils;
924
925impl DeviceNoiseUtils {
926    /// Create IBM device model from real calibration data
927    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, // 15 mK
941            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    /// Create Google Sycamore-like device
952    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, // 20 mK
960            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    /// Create trapped ion device model
971    pub fn create_trapped_ion_device(num_ions: usize) -> Result<Box<dyn DeviceNoiseModel>> {
972        // Placeholder for trapped ion implementation
973        let topology = DeviceTopology::linear_chain(num_ions);
974        let config = DeviceNoiseConfig {
975            device_type: DeviceType::TrappedIon,
976            topology,
977            temperature: 1e-6, // μK range
978            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    /// Benchmark noise models
989    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            // Apply some gates with noise
1015            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/// Noise benchmark results
1030#[derive(Debug, Clone, Default)]
1031pub struct NoiseBenchmarkResults {
1032    /// Execution times by device type
1033    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]]); // Horizontal connection
1056        assert!(topology.connectivity[[0, 3]]); // Vertical connection
1057        assert!(!topology.connectivity[[0, 4]]); // Diagonal not connected
1058    }
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        // Apply single-qubit noise
1092        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        // Apply two-qubit noise
1097        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        // Some results might have flipped due to readout errors
1122        // We can't predict exactly which due to randomness, but the function should work
1123    }
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        // Higher temperature should give higher thermal population
1179        assert!(scaling_50mk > scaling_15mk);
1180        assert!(scaling_15mk > 0.0);
1181        assert!(scaling_15mk < 1.0);
1182    }
1183}