quantrs2_device/
calibration.rs

1//! Device-specific gate calibration data structures
2//!
3//! This module provides comprehensive gate calibration tracking for quantum devices,
4//! including error rates, gate fidelities, timing information, and hardware-specific
5//! parameters. This data is essential for circuit optimization and error mitigation.
6
7use scirs2_core::Complex64;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::time::{Duration, SystemTime};
11
12use quantrs2_core::{
13    error::{QuantRS2Error, QuantRS2Result},
14    gate::GateOp,
15    qubit::QubitId,
16};
17
18/// Complete calibration data for a quantum device
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct DeviceCalibration {
21    /// Device identifier
22    pub device_id: String,
23    /// Timestamp of calibration
24    pub timestamp: SystemTime,
25    /// Calibration validity duration
26    pub valid_duration: Duration,
27    /// Qubit-specific calibrations
28    pub qubit_calibrations: HashMap<QubitId, QubitCalibration>,
29    /// Single-qubit gate calibrations
30    pub single_qubit_gates: HashMap<String, SingleQubitGateCalibration>,
31    /// Two-qubit gate calibrations
32    pub two_qubit_gates: HashMap<(QubitId, QubitId), TwoQubitGateCalibration>,
33    /// Multi-qubit gate calibrations
34    pub multi_qubit_gates: HashMap<Vec<QubitId>, MultiQubitGateCalibration>,
35    /// Readout calibration data
36    pub readout_calibration: ReadoutCalibration,
37    /// Cross-talk matrix between qubits
38    pub crosstalk_matrix: CrosstalkMatrix,
39    /// Device topology and connectivity
40    pub topology: DeviceTopology,
41    /// Additional metadata
42    pub metadata: HashMap<String, String>,
43}
44
45impl Default for DeviceCalibration {
46    fn default() -> Self {
47        Self {
48            device_id: String::new(),
49            timestamp: SystemTime::UNIX_EPOCH,
50            valid_duration: Duration::from_secs(3600), // 1 hour default
51            qubit_calibrations: HashMap::new(),
52            single_qubit_gates: HashMap::new(),
53            two_qubit_gates: HashMap::new(),
54            multi_qubit_gates: HashMap::new(),
55            readout_calibration: ReadoutCalibration::default(),
56            crosstalk_matrix: CrosstalkMatrix::default(),
57            topology: DeviceTopology::default(),
58            metadata: HashMap::new(),
59        }
60    }
61}
62
63impl DeviceCalibration {
64    /// Get single-qubit gate fidelity for a specific qubit
65    pub fn single_qubit_fidelity(&self, qubit: usize) -> Option<f64> {
66        let qubit_id = QubitId(qubit as u32);
67
68        // Try to get fidelity from single-qubit gates (prefer X gate as representative)
69        if let Some(x_gate) = self.single_qubit_gates.get("X") {
70            if let Some(gate_data) = x_gate.qubit_data.get(&qubit_id) {
71                return Some(gate_data.fidelity);
72            }
73        }
74
75        // Fallback: try other common single-qubit gates
76        for gate_name in &["H", "Y", "Z", "RX", "RY", "RZ"] {
77            if let Some(gate) = self.single_qubit_gates.get(*gate_name) {
78                if let Some(gate_data) = gate.qubit_data.get(&qubit_id) {
79                    return Some(gate_data.fidelity);
80                }
81            }
82        }
83
84        // Default fallback
85        None
86    }
87
88    /// Get two-qubit gate fidelity between two qubits
89    pub fn gate_fidelity(&self, q1: usize, q2: usize) -> Option<f64> {
90        let qubit1 = QubitId(q1 as u32);
91        let qubit2 = QubitId(q2 as u32);
92
93        // Look for a two-qubit gate between these qubits (try both directions)
94        for gate in self.two_qubit_gates.values() {
95            if (gate.control == qubit1 && gate.target == qubit2)
96                || (gate.control == qubit2 && gate.target == qubit1)
97            {
98                return Some(gate.fidelity);
99            }
100        }
101
102        // Default fallback
103        None
104    }
105}
106
107/// Calibration data for individual qubits
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct QubitCalibration {
110    /// Qubit identifier
111    pub qubit_id: QubitId,
112    /// Qubit frequency (Hz)
113    pub frequency: f64,
114    /// Anharmonicity (Hz)
115    pub anharmonicity: f64,
116    /// T1 coherence time (microseconds)
117    pub t1: f64,
118    /// T2 coherence time (microseconds)
119    pub t2: f64,
120    /// T2* coherence time (microseconds)
121    pub t2_star: Option<f64>,
122    /// Readout assignment error
123    pub readout_error: f64,
124    /// Thermal population
125    pub thermal_population: f64,
126    /// Operating temperature (mK)
127    pub temperature: Option<f64>,
128    /// Additional qubit-specific parameters
129    pub parameters: HashMap<String, f64>,
130}
131
132/// Single-qubit gate calibration data
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct SingleQubitGateCalibration {
135    /// Gate name (e.g., "X", "Y", "Z", "H", "RX", "RY", "RZ")
136    pub gate_name: String,
137    /// Per-qubit calibration data
138    pub qubit_data: HashMap<QubitId, SingleQubitGateData>,
139    /// Default gate parameters
140    pub default_parameters: GateParameters,
141}
142
143/// Single-qubit gate data for a specific qubit
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct SingleQubitGateData {
146    /// Gate error rate
147    pub error_rate: f64,
148    /// Gate fidelity
149    pub fidelity: f64,
150    /// Gate duration (nanoseconds)
151    pub duration: f64,
152    /// Drive amplitude
153    pub amplitude: f64,
154    /// Drive frequency (Hz)
155    pub frequency: f64,
156    /// Phase correction
157    pub phase: f64,
158    /// Pulse shape parameters
159    pub pulse_shape: PulseShape,
160    /// Calibrated gate matrix (if different from ideal)
161    pub calibrated_matrix: Option<Vec<Complex64>>,
162    /// Parameter-dependent calibrations (e.g., for rotation angles)
163    pub parameter_calibrations: Option<ParameterCalibration>,
164}
165
166/// Two-qubit gate calibration data
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct TwoQubitGateCalibration {
169    /// Gate name (e.g., "CNOT", "CZ", "ISwap")
170    pub gate_name: String,
171    /// Control qubit
172    pub control: QubitId,
173    /// Target qubit
174    pub target: QubitId,
175    /// Gate error rate
176    pub error_rate: f64,
177    /// Gate fidelity
178    pub fidelity: f64,
179    /// Gate duration (nanoseconds)
180    pub duration: f64,
181    /// Coupling strength (MHz)
182    pub coupling_strength: f64,
183    /// Cross-resonance parameters (for CR gates)
184    pub cross_resonance: Option<CrossResonanceParameters>,
185    /// Calibrated gate matrix
186    pub calibrated_matrix: Option<Vec<Complex64>>,
187    /// Direction-specific calibration (some gates work better in one direction)
188    pub directional: bool,
189    /// Alternative calibration for reversed direction
190    pub reversed_calibration: Option<Box<Self>>,
191}
192
193/// Multi-qubit gate calibration data
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct MultiQubitGateCalibration {
196    /// Gate name (e.g., "Toffoli", "Fredkin")
197    pub gate_name: String,
198    /// Qubits involved
199    pub qubits: Vec<QubitId>,
200    /// Gate error rate
201    pub error_rate: f64,
202    /// Gate fidelity
203    pub fidelity: f64,
204    /// Gate duration (nanoseconds)
205    pub duration: f64,
206    /// Decomposition used on hardware
207    pub decomposition: GateDecomposition,
208    /// Native implementation available
209    pub is_native: bool,
210}
211
212/// Readout calibration data
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct ReadoutCalibration {
215    /// Per-qubit readout data
216    pub qubit_readout: HashMap<QubitId, QubitReadoutData>,
217    /// Readout mitigation matrix
218    pub mitigation_matrix: Option<Vec<Vec<f64>>>,
219    /// Readout duration (nanoseconds)
220    pub duration: f64,
221    /// Integration time (nanoseconds)
222    pub integration_time: f64,
223}
224
225impl Default for ReadoutCalibration {
226    fn default() -> Self {
227        Self {
228            qubit_readout: HashMap::new(),
229            mitigation_matrix: None,
230            duration: 1000.0,        // 1 microsecond default
231            integration_time: 500.0, // 500 ns default
232        }
233    }
234}
235
236/// Qubit-specific readout data
237#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct QubitReadoutData {
239    /// Probability of reading 0 when prepared in |0⟩
240    pub p0_given_0: f64,
241    /// Probability of reading 1 when prepared in |1⟩
242    pub p1_given_1: f64,
243    /// Readout resonator frequency (Hz)
244    pub resonator_frequency: f64,
245    /// Optimal readout amplitude
246    pub readout_amplitude: f64,
247    /// Optimal readout phase
248    pub readout_phase: f64,
249    /// Signal-to-noise ratio
250    pub snr: f64,
251}
252
253/// Cross-talk matrix between qubits
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct CrosstalkMatrix {
256    /// Matrix of crosstalk coefficients
257    /// Entry (i,j) represents crosstalk from qubit i to qubit j
258    pub matrix: Vec<Vec<f64>>,
259    /// Measurement method used
260    pub measurement_method: String,
261    /// Threshold for significant crosstalk
262    pub significance_threshold: f64,
263}
264
265impl Default for CrosstalkMatrix {
266    fn default() -> Self {
267        Self {
268            matrix: Vec::new(),
269            measurement_method: "default".to_string(),
270            significance_threshold: 0.01,
271        }
272    }
273}
274
275/// Device topology and connectivity
276#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct DeviceTopology {
278    /// Number of qubits
279    pub num_qubits: usize,
280    /// Coupling map: which qubits can interact
281    pub coupling_map: Vec<(QubitId, QubitId)>,
282    /// Physical layout (e.g., "linear", "grid", "heavy-hex")
283    pub layout_type: String,
284    /// Physical coordinates of qubits (if applicable)
285    pub qubit_coordinates: Option<HashMap<QubitId, (f64, f64)>>,
286}
287
288impl Default for DeviceTopology {
289    fn default() -> Self {
290        Self {
291            num_qubits: 0,
292            coupling_map: Vec::new(),
293            layout_type: "unknown".to_string(),
294            qubit_coordinates: None,
295        }
296    }
297}
298
299/// Gate parameters that can be calibrated
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct GateParameters {
302    /// Amplitude scaling factor
303    pub amplitude_scale: f64,
304    /// Phase offset
305    pub phase_offset: f64,
306    /// Duration scaling factor
307    pub duration_scale: f64,
308    /// DRAG coefficient (for single-qubit gates)
309    pub drag_coefficient: Option<f64>,
310    /// Additional hardware-specific parameters
311    pub custom_parameters: HashMap<String, f64>,
312}
313
314/// Pulse shape for gate implementation
315#[derive(Debug, Clone, Serialize, Deserialize)]
316pub enum PulseShape {
317    /// Gaussian pulse
318    Gaussian { sigma: f64, cutoff: f64 },
319    /// Gaussian with DRAG correction
320    GaussianDRAG { sigma: f64, beta: f64, cutoff: f64 },
321    /// Square pulse
322    Square { rise_time: f64 },
323    /// Cosine-shaped pulse
324    Cosine { rise_time: f64 },
325    /// Custom pulse shape
326    Custom {
327        name: String,
328        parameters: HashMap<String, f64>,
329    },
330}
331
332/// Parameter-dependent calibration (e.g., for rotation gates)
333#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct ParameterCalibration {
335    /// Calibration points (parameter value -> calibration data)
336    pub calibration_points: Vec<(f64, SingleQubitGateData)>,
337    /// Interpolation method
338    pub interpolation: InterpolationMethod,
339    /// Valid parameter range
340    pub valid_range: (f64, f64),
341}
342
343/// Interpolation method for parameter-dependent calibrations
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub enum InterpolationMethod {
346    /// Linear interpolation
347    Linear,
348    /// Cubic spline interpolation
349    CubicSpline,
350    /// Polynomial interpolation
351    Polynomial { degree: usize },
352    /// Nearest neighbor
353    NearestNeighbor,
354}
355
356/// Cross-resonance parameters for CNOT gates
357#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct CrossResonanceParameters {
359    /// Drive frequency (Hz)
360    pub drive_frequency: f64,
361    /// Drive amplitude
362    pub drive_amplitude: f64,
363    /// Pulse duration (ns)
364    pub pulse_duration: f64,
365    /// Echo pulse parameters
366    pub echo_amplitude: f64,
367    pub echo_duration: f64,
368    /// ZX interaction rate (MHz)
369    pub zx_interaction_rate: f64,
370}
371
372/// Gate decomposition for multi-qubit gates
373#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct GateDecomposition {
375    /// Sequence of gates in decomposition
376    pub gates: Vec<DecomposedGate>,
377    /// Total error from decomposition
378    pub decomposition_error: f64,
379    /// Optimal decomposition for this device
380    pub is_optimal: bool,
381}
382
383/// Individual gate in a decomposition
384#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct DecomposedGate {
386    /// Gate name
387    pub gate_name: String,
388    /// Qubits acted on
389    pub qubits: Vec<QubitId>,
390    /// Parameters (if any)
391    pub parameters: Vec<f64>,
392}
393
394/// Calibration manager for handling device calibrations
395#[derive(Debug, Clone)]
396pub struct CalibrationManager {
397    /// Current calibrations for each device
398    calibrations: HashMap<String, DeviceCalibration>,
399    /// Calibration history
400    history: Vec<(String, SystemTime, DeviceCalibration)>,
401    /// Maximum history size
402    max_history: usize,
403}
404
405impl CalibrationManager {
406    /// Create a new calibration manager
407    pub fn new() -> Self {
408        Self {
409            calibrations: HashMap::new(),
410            history: Vec::new(),
411            max_history: 100,
412        }
413    }
414
415    /// Load calibration from file
416    pub fn load_calibration(&mut self, path: &str) -> QuantRS2Result<()> {
417        let data = std::fs::read_to_string(path)
418            .map_err(|e| QuantRS2Error::InvalidInput(format!("Failed to read calibration: {e}")))?;
419
420        let calibration: DeviceCalibration = serde_json::from_str(&data).map_err(|e| {
421            QuantRS2Error::InvalidInput(format!("Failed to parse calibration: {e}"))
422        })?;
423
424        self.update_calibration(calibration);
425        Ok(())
426    }
427
428    /// Save calibration to file
429    pub fn save_calibration(&self, device_id: &str, path: &str) -> QuantRS2Result<()> {
430        let calibration = self.get_calibration(device_id).ok_or_else(|| {
431            QuantRS2Error::InvalidInput(format!("No calibration for device {device_id}"))
432        })?;
433
434        let data = serde_json::to_string_pretty(calibration).map_err(|e| {
435            QuantRS2Error::InvalidInput(format!("Failed to serialize calibration: {e}"))
436        })?;
437
438        std::fs::write(path, data).map_err(|e| {
439            QuantRS2Error::InvalidInput(format!("Failed to write calibration: {e}"))
440        })?;
441
442        Ok(())
443    }
444
445    /// Update calibration for a device
446    pub fn update_calibration(&mut self, calibration: DeviceCalibration) {
447        let device_id = calibration.device_id.clone();
448        let timestamp = calibration.timestamp;
449
450        // Store in history
451        if let Some(old_cal) = self.calibrations.get(&device_id) {
452            self.history
453                .push((device_id.clone(), timestamp, old_cal.clone()));
454
455            // Trim history if needed
456            if self.history.len() > self.max_history {
457                self.history.remove(0);
458            }
459        }
460
461        // Update current calibration
462        self.calibrations.insert(device_id, calibration);
463    }
464
465    /// Get current calibration for a device
466    pub fn get_calibration(&self, device_id: &str) -> Option<&DeviceCalibration> {
467        self.calibrations.get(device_id)
468    }
469
470    /// Check if calibration is still valid
471    pub fn is_calibration_valid(&self, device_id: &str) -> bool {
472        self.calibrations.get(device_id).map_or(false, |cal| {
473            let elapsed = SystemTime::now()
474                .duration_since(cal.timestamp)
475                .unwrap_or(Duration::from_secs(u64::MAX));
476
477            elapsed < cal.valid_duration
478        })
479    }
480
481    /// Get the latest calibration across all devices
482    pub fn get_latest_calibration(&self) -> Option<&DeviceCalibration> {
483        self.calibrations.values().max_by_key(|cal| cal.timestamp)
484    }
485
486    /// Get gate fidelity for a specific gate on specific qubits
487    pub fn get_gate_fidelity(
488        &self,
489        device_id: &str,
490        gate_name: &str,
491        qubits: &[QubitId],
492    ) -> Option<f64> {
493        let cal = self.calibrations.get(device_id)?;
494
495        match qubits.len() {
496            1 => {
497                let gate_cal = cal.single_qubit_gates.get(gate_name)?;
498                gate_cal.qubit_data.get(&qubits[0]).map(|d| d.fidelity)
499            }
500            2 => cal
501                .two_qubit_gates
502                .get(&(qubits[0], qubits[1]))
503                .filter(|g| g.gate_name == gate_name)
504                .map(|g| g.fidelity),
505            _ => cal
506                .multi_qubit_gates
507                .get(qubits)
508                .filter(|g| g.gate_name == gate_name)
509                .map(|g| g.fidelity),
510        }
511    }
512
513    /// Get gate duration
514    pub fn get_gate_duration(
515        &self,
516        device_id: &str,
517        gate_name: &str,
518        qubits: &[QubitId],
519    ) -> Option<f64> {
520        let cal = self.calibrations.get(device_id)?;
521
522        match qubits.len() {
523            1 => {
524                let gate_cal = cal.single_qubit_gates.get(gate_name)?;
525                gate_cal.qubit_data.get(&qubits[0]).map(|d| d.duration)
526            }
527            2 => cal
528                .two_qubit_gates
529                .get(&(qubits[0], qubits[1]))
530                .filter(|g| g.gate_name == gate_name)
531                .map(|g| g.duration),
532            _ => cal
533                .multi_qubit_gates
534                .get(qubits)
535                .filter(|g| g.gate_name == gate_name)
536                .map(|g| g.duration),
537        }
538    }
539}
540
541/// Builder for creating device calibrations
542pub struct CalibrationBuilder {
543    device_id: String,
544    timestamp: SystemTime,
545    valid_duration: Duration,
546    qubit_calibrations: HashMap<QubitId, QubitCalibration>,
547    single_qubit_gates: HashMap<String, SingleQubitGateCalibration>,
548    two_qubit_gates: HashMap<(QubitId, QubitId), TwoQubitGateCalibration>,
549    multi_qubit_gates: HashMap<Vec<QubitId>, MultiQubitGateCalibration>,
550    readout_calibration: Option<ReadoutCalibration>,
551    crosstalk_matrix: Option<CrosstalkMatrix>,
552    topology: Option<DeviceTopology>,
553    metadata: HashMap<String, String>,
554}
555
556impl CalibrationBuilder {
557    /// Create a new calibration builder
558    pub fn new(device_id: String) -> Self {
559        Self {
560            device_id,
561            timestamp: SystemTime::now(),
562            valid_duration: Duration::from_secs(24 * 3600), // 24 hours default
563            qubit_calibrations: HashMap::new(),
564            single_qubit_gates: HashMap::new(),
565            two_qubit_gates: HashMap::new(),
566            multi_qubit_gates: HashMap::new(),
567            readout_calibration: None,
568            crosstalk_matrix: None,
569            topology: None,
570            metadata: HashMap::new(),
571        }
572    }
573
574    /// Set validity duration
575    #[must_use]
576    pub const fn valid_duration(mut self, duration: Duration) -> Self {
577        self.valid_duration = duration;
578        self
579    }
580
581    /// Add qubit calibration
582    #[must_use]
583    pub fn add_qubit_calibration(mut self, calibration: QubitCalibration) -> Self {
584        self.qubit_calibrations
585            .insert(calibration.qubit_id, calibration);
586        self
587    }
588
589    /// Add single-qubit gate calibration
590    #[must_use]
591    pub fn add_single_qubit_gate(
592        mut self,
593        gate_name: String,
594        calibration: SingleQubitGateCalibration,
595    ) -> Self {
596        self.single_qubit_gates.insert(gate_name, calibration);
597        self
598    }
599
600    /// Add two-qubit gate calibration
601    #[must_use]
602    pub fn add_two_qubit_gate(
603        mut self,
604        control: QubitId,
605        target: QubitId,
606        calibration: TwoQubitGateCalibration,
607    ) -> Self {
608        self.two_qubit_gates.insert((control, target), calibration);
609        self
610    }
611
612    /// Set readout calibration
613    #[must_use]
614    pub fn readout_calibration(mut self, calibration: ReadoutCalibration) -> Self {
615        self.readout_calibration = Some(calibration);
616        self
617    }
618
619    /// Set crosstalk matrix
620    #[must_use]
621    pub fn crosstalk_matrix(mut self, matrix: CrosstalkMatrix) -> Self {
622        self.crosstalk_matrix = Some(matrix);
623        self
624    }
625
626    /// Set device topology
627    #[must_use]
628    pub fn topology(mut self, topology: DeviceTopology) -> Self {
629        self.topology = Some(topology);
630        self
631    }
632
633    /// Add metadata
634    #[must_use]
635    pub fn add_metadata(mut self, key: String, value: String) -> Self {
636        self.metadata.insert(key, value);
637        self
638    }
639
640    /// Build the calibration
641    pub fn build(self) -> QuantRS2Result<DeviceCalibration> {
642        let readout_calibration = self
643            .readout_calibration
644            .ok_or_else(|| QuantRS2Error::InvalidInput("Readout calibration required".into()))?;
645
646        let crosstalk_matrix = self
647            .crosstalk_matrix
648            .ok_or_else(|| QuantRS2Error::InvalidInput("Crosstalk matrix required".into()))?;
649
650        let topology = self
651            .topology
652            .ok_or_else(|| QuantRS2Error::InvalidInput("Device topology required".into()))?;
653
654        Ok(DeviceCalibration {
655            device_id: self.device_id,
656            timestamp: self.timestamp,
657            valid_duration: self.valid_duration,
658            qubit_calibrations: self.qubit_calibrations,
659            single_qubit_gates: self.single_qubit_gates,
660            two_qubit_gates: self.two_qubit_gates,
661            multi_qubit_gates: self.multi_qubit_gates,
662            readout_calibration,
663            crosstalk_matrix,
664            topology,
665            metadata: self.metadata,
666        })
667    }
668}
669
670/// Create a default calibration for ideal simulation
671pub fn create_ideal_calibration(device_id: String, num_qubits: usize) -> DeviceCalibration {
672    let mut builder = CalibrationBuilder::new(device_id);
673
674    // Add ideal qubit calibrations
675    for i in 0..num_qubits {
676        let qubit_id = QubitId(i as u32);
677        builder = builder.add_qubit_calibration(QubitCalibration {
678            qubit_id,
679            frequency: 5e9,          // 5 GHz
680            anharmonicity: -300e6,   // -300 MHz
681            t1: 100_000.0,           // 100 μs
682            t2: 100_000.0,           // 100 μs
683            t2_star: Some(50_000.0), // 50 μs
684            readout_error: 0.001,    // 0.1%
685            thermal_population: 0.01,
686            temperature: Some(20.0), // 20 mK
687            parameters: HashMap::new(),
688        });
689    }
690
691    // Add ideal single-qubit gates
692    for gate_name in ["X", "Y", "Z", "H", "S", "T", "RX", "RY", "RZ"] {
693        let mut qubit_data = HashMap::new();
694
695        for i in 0..num_qubits {
696            qubit_data.insert(
697                QubitId(i as u32),
698                SingleQubitGateData {
699                    error_rate: 0.001,
700                    fidelity: 0.999,
701                    duration: 20.0, // 20 ns
702                    amplitude: 1.0,
703                    frequency: 5e9,
704                    phase: 0.0,
705                    pulse_shape: PulseShape::GaussianDRAG {
706                        sigma: 5.0,
707                        beta: 0.5,
708                        cutoff: 2.0,
709                    },
710                    calibrated_matrix: None,
711                    parameter_calibrations: None,
712                },
713            );
714        }
715
716        builder = builder.add_single_qubit_gate(
717            gate_name.to_string(),
718            SingleQubitGateCalibration {
719                gate_name: gate_name.to_string(),
720                qubit_data,
721                default_parameters: GateParameters {
722                    amplitude_scale: 1.0,
723                    phase_offset: 0.0,
724                    duration_scale: 1.0,
725                    drag_coefficient: Some(0.5),
726                    custom_parameters: HashMap::new(),
727                },
728            },
729        );
730    }
731
732    // Add ideal two-qubit gates (nearest neighbor)
733    for i in 0..num_qubits - 1 {
734        let control = QubitId(i as u32);
735        let target = QubitId((i + 1) as u32);
736
737        builder = builder.add_two_qubit_gate(
738            control,
739            target,
740            TwoQubitGateCalibration {
741                gate_name: "CNOT".to_string(),
742                control,
743                target,
744                error_rate: 0.01,
745                fidelity: 0.99,
746                duration: 200.0,         // 200 ns
747                coupling_strength: 30.0, // 30 MHz
748                cross_resonance: Some(CrossResonanceParameters {
749                    drive_frequency: 4.8e9,
750                    drive_amplitude: 0.5,
751                    pulse_duration: 180.0,
752                    echo_amplitude: 0.25,
753                    echo_duration: 90.0,
754                    zx_interaction_rate: 3.0,
755                }),
756                calibrated_matrix: None,
757                directional: true,
758                reversed_calibration: None,
759            },
760        );
761    }
762
763    // Add ideal readout
764    let mut qubit_readout = HashMap::new();
765    for i in 0..num_qubits {
766        qubit_readout.insert(
767            QubitId(i as u32),
768            QubitReadoutData {
769                p0_given_0: 0.999,
770                p1_given_1: 0.999,
771                resonator_frequency: 6.5e9,
772                readout_amplitude: 0.1,
773                readout_phase: 0.0,
774                snr: 10.0,
775            },
776        );
777    }
778
779    builder = builder.readout_calibration(ReadoutCalibration {
780        qubit_readout,
781        mitigation_matrix: None,
782        duration: 2000.0,         // 2 μs
783        integration_time: 1500.0, // 1.5 μs
784    });
785
786    // Add ideal crosstalk (none)
787    let mut matrix = vec![vec![0.0; num_qubits]; num_qubits];
788    for i in 0..num_qubits {
789        matrix[i][i] = 1.0;
790    }
791
792    builder = builder.crosstalk_matrix(CrosstalkMatrix {
793        matrix,
794        measurement_method: "Ideal".to_string(),
795        significance_threshold: 0.01,
796    });
797
798    // Add linear topology
799    let mut coupling_map = Vec::new();
800    for i in 0..num_qubits - 1 {
801        coupling_map.push((QubitId(i as u32), QubitId((i + 1) as u32)));
802    }
803
804    builder = builder.topology(DeviceTopology {
805        num_qubits,
806        coupling_map,
807        layout_type: "linear".to_string(),
808        qubit_coordinates: None,
809    });
810
811    builder
812        .build()
813        .expect("Ideal calibration should always be valid with all required fields")
814}
815
816#[cfg(test)]
817mod tests {
818    use super::*;
819
820    #[test]
821    fn test_calibration_builder() {
822        let cal = CalibrationBuilder::new("test_device".to_string())
823            .add_qubit_calibration(QubitCalibration {
824                qubit_id: QubitId(0),
825                frequency: 5e9,
826                anharmonicity: -300e6,
827                t1: 50_000.0,
828                t2: 40_000.0,
829                t2_star: Some(30_000.0),
830                readout_error: 0.02,
831                thermal_population: 0.02,
832                temperature: Some(15.0),
833                parameters: HashMap::new(),
834            })
835            .readout_calibration(ReadoutCalibration {
836                qubit_readout: HashMap::new(),
837                mitigation_matrix: None,
838                duration: 2000.0,
839                integration_time: 1500.0,
840            })
841            .crosstalk_matrix(CrosstalkMatrix {
842                matrix: vec![vec![1.0]],
843                measurement_method: "Test".to_string(),
844                significance_threshold: 0.01,
845            })
846            .topology(DeviceTopology {
847                num_qubits: 1,
848                coupling_map: vec![],
849                layout_type: "single".to_string(),
850                qubit_coordinates: None,
851            })
852            .build()
853            .expect("Test calibration should build successfully");
854
855        assert_eq!(cal.device_id, "test_device");
856        assert_eq!(cal.qubit_calibrations.len(), 1);
857    }
858
859    #[test]
860    fn test_calibration_manager() {
861        let mut manager = CalibrationManager::new();
862        let cal = create_ideal_calibration("test_device".to_string(), 5);
863
864        manager.update_calibration(cal);
865
866        assert!(manager.is_calibration_valid("test_device"));
867        assert_eq!(
868            manager.get_gate_fidelity("test_device", "X", &[QubitId(0)]),
869            Some(0.999)
870        );
871        assert_eq!(
872            manager.get_gate_duration("test_device", "CNOT", &[QubitId(0), QubitId(1)]),
873            Some(200.0)
874        );
875    }
876
877    #[test]
878    fn test_ideal_calibration() {
879        let cal = create_ideal_calibration("ideal".to_string(), 10);
880
881        assert_eq!(cal.qubit_calibrations.len(), 10);
882        assert!(cal.single_qubit_gates.contains_key("X"));
883        assert!(cal.single_qubit_gates.contains_key("RZ"));
884        assert_eq!(cal.topology.coupling_map.len(), 9); // 9 connections for 10 qubits
885    }
886}