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<TwoQubitGateCalibration>>,
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).map_err(|e| {
418            QuantRS2Error::InvalidInput(format!("Failed to read calibration: {}", e))
419        })?;
420
421        let calibration: DeviceCalibration = serde_json::from_str(&data).map_err(|e| {
422            QuantRS2Error::InvalidInput(format!("Failed to parse calibration: {}", e))
423        })?;
424
425        self.update_calibration(calibration);
426        Ok(())
427    }
428
429    /// Save calibration to file
430    pub fn save_calibration(&self, device_id: &str, path: &str) -> QuantRS2Result<()> {
431        let calibration = self.get_calibration(device_id).ok_or_else(|| {
432            QuantRS2Error::InvalidInput(format!("No calibration for device {}", device_id))
433        })?;
434
435        let data = serde_json::to_string_pretty(calibration).map_err(|e| {
436            QuantRS2Error::InvalidInput(format!("Failed to serialize calibration: {}", e))
437        })?;
438
439        std::fs::write(path, data).map_err(|e| {
440            QuantRS2Error::InvalidInput(format!("Failed to write calibration: {}", e))
441        })?;
442
443        Ok(())
444    }
445
446    /// Update calibration for a device
447    pub fn update_calibration(&mut self, calibration: DeviceCalibration) {
448        let device_id = calibration.device_id.clone();
449        let timestamp = calibration.timestamp;
450
451        // Store in history
452        if let Some(old_cal) = self.calibrations.get(&device_id) {
453            self.history
454                .push((device_id.clone(), timestamp, old_cal.clone()));
455
456            // Trim history if needed
457            if self.history.len() > self.max_history {
458                self.history.remove(0);
459            }
460        }
461
462        // Update current calibration
463        self.calibrations.insert(device_id, calibration);
464    }
465
466    /// Get current calibration for a device
467    pub fn get_calibration(&self, device_id: &str) -> Option<&DeviceCalibration> {
468        self.calibrations.get(device_id)
469    }
470
471    /// Check if calibration is still valid
472    pub fn is_calibration_valid(&self, device_id: &str) -> bool {
473        if let Some(cal) = self.calibrations.get(device_id) {
474            let elapsed = SystemTime::now()
475                .duration_since(cal.timestamp)
476                .unwrap_or(Duration::from_secs(u64::MAX));
477
478            elapsed < cal.valid_duration
479        } else {
480            false
481        }
482    }
483
484    /// Get the latest calibration across all devices
485    pub fn get_latest_calibration(&self) -> Option<&DeviceCalibration> {
486        self.calibrations.values().max_by_key(|cal| cal.timestamp)
487    }
488
489    /// Get gate fidelity for a specific gate on specific qubits
490    pub fn get_gate_fidelity(
491        &self,
492        device_id: &str,
493        gate_name: &str,
494        qubits: &[QubitId],
495    ) -> Option<f64> {
496        let cal = self.calibrations.get(device_id)?;
497
498        match qubits.len() {
499            1 => {
500                let gate_cal = cal.single_qubit_gates.get(gate_name)?;
501                gate_cal.qubit_data.get(&qubits[0]).map(|d| d.fidelity)
502            }
503            2 => cal
504                .two_qubit_gates
505                .get(&(qubits[0], qubits[1]))
506                .filter(|g| g.gate_name == gate_name)
507                .map(|g| g.fidelity),
508            _ => cal
509                .multi_qubit_gates
510                .get(qubits)
511                .filter(|g| g.gate_name == gate_name)
512                .map(|g| g.fidelity),
513        }
514    }
515
516    /// Get gate duration
517    pub fn get_gate_duration(
518        &self,
519        device_id: &str,
520        gate_name: &str,
521        qubits: &[QubitId],
522    ) -> Option<f64> {
523        let cal = self.calibrations.get(device_id)?;
524
525        match qubits.len() {
526            1 => {
527                let gate_cal = cal.single_qubit_gates.get(gate_name)?;
528                gate_cal.qubit_data.get(&qubits[0]).map(|d| d.duration)
529            }
530            2 => cal
531                .two_qubit_gates
532                .get(&(qubits[0], qubits[1]))
533                .filter(|g| g.gate_name == gate_name)
534                .map(|g| g.duration),
535            _ => cal
536                .multi_qubit_gates
537                .get(qubits)
538                .filter(|g| g.gate_name == gate_name)
539                .map(|g| g.duration),
540        }
541    }
542}
543
544/// Builder for creating device calibrations
545pub struct CalibrationBuilder {
546    device_id: String,
547    timestamp: SystemTime,
548    valid_duration: Duration,
549    qubit_calibrations: HashMap<QubitId, QubitCalibration>,
550    single_qubit_gates: HashMap<String, SingleQubitGateCalibration>,
551    two_qubit_gates: HashMap<(QubitId, QubitId), TwoQubitGateCalibration>,
552    multi_qubit_gates: HashMap<Vec<QubitId>, MultiQubitGateCalibration>,
553    readout_calibration: Option<ReadoutCalibration>,
554    crosstalk_matrix: Option<CrosstalkMatrix>,
555    topology: Option<DeviceTopology>,
556    metadata: HashMap<String, String>,
557}
558
559impl CalibrationBuilder {
560    /// Create a new calibration builder
561    pub fn new(device_id: String) -> Self {
562        Self {
563            device_id,
564            timestamp: SystemTime::now(),
565            valid_duration: Duration::from_secs(24 * 3600), // 24 hours default
566            qubit_calibrations: HashMap::new(),
567            single_qubit_gates: HashMap::new(),
568            two_qubit_gates: HashMap::new(),
569            multi_qubit_gates: HashMap::new(),
570            readout_calibration: None,
571            crosstalk_matrix: None,
572            topology: None,
573            metadata: HashMap::new(),
574        }
575    }
576
577    /// Set validity duration
578    pub fn valid_duration(mut self, duration: Duration) -> Self {
579        self.valid_duration = duration;
580        self
581    }
582
583    /// Add qubit calibration
584    pub fn add_qubit_calibration(mut self, calibration: QubitCalibration) -> Self {
585        self.qubit_calibrations
586            .insert(calibration.qubit_id, calibration);
587        self
588    }
589
590    /// Add single-qubit gate calibration
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    pub fn add_two_qubit_gate(
602        mut self,
603        control: QubitId,
604        target: QubitId,
605        calibration: TwoQubitGateCalibration,
606    ) -> Self {
607        self.two_qubit_gates.insert((control, target), calibration);
608        self
609    }
610
611    /// Set readout calibration
612    pub fn readout_calibration(mut self, calibration: ReadoutCalibration) -> Self {
613        self.readout_calibration = Some(calibration);
614        self
615    }
616
617    /// Set crosstalk matrix
618    pub fn crosstalk_matrix(mut self, matrix: CrosstalkMatrix) -> Self {
619        self.crosstalk_matrix = Some(matrix);
620        self
621    }
622
623    /// Set device topology
624    pub fn topology(mut self, topology: DeviceTopology) -> Self {
625        self.topology = Some(topology);
626        self
627    }
628
629    /// Add metadata
630    pub fn add_metadata(mut self, key: String, value: String) -> Self {
631        self.metadata.insert(key, value);
632        self
633    }
634
635    /// Build the calibration
636    pub fn build(self) -> QuantRS2Result<DeviceCalibration> {
637        let readout_calibration = self
638            .readout_calibration
639            .ok_or_else(|| QuantRS2Error::InvalidInput("Readout calibration required".into()))?;
640
641        let crosstalk_matrix = self
642            .crosstalk_matrix
643            .ok_or_else(|| QuantRS2Error::InvalidInput("Crosstalk matrix required".into()))?;
644
645        let topology = self
646            .topology
647            .ok_or_else(|| QuantRS2Error::InvalidInput("Device topology required".into()))?;
648
649        Ok(DeviceCalibration {
650            device_id: self.device_id,
651            timestamp: self.timestamp,
652            valid_duration: self.valid_duration,
653            qubit_calibrations: self.qubit_calibrations,
654            single_qubit_gates: self.single_qubit_gates,
655            two_qubit_gates: self.two_qubit_gates,
656            multi_qubit_gates: self.multi_qubit_gates,
657            readout_calibration,
658            crosstalk_matrix,
659            topology,
660            metadata: self.metadata,
661        })
662    }
663}
664
665/// Create a default calibration for ideal simulation
666pub fn create_ideal_calibration(device_id: String, num_qubits: usize) -> DeviceCalibration {
667    let mut builder = CalibrationBuilder::new(device_id);
668
669    // Add ideal qubit calibrations
670    for i in 0..num_qubits {
671        let qubit_id = QubitId(i as u32);
672        builder = builder.add_qubit_calibration(QubitCalibration {
673            qubit_id,
674            frequency: 5e9,          // 5 GHz
675            anharmonicity: -300e6,   // -300 MHz
676            t1: 100_000.0,           // 100 μs
677            t2: 100_000.0,           // 100 μs
678            t2_star: Some(50_000.0), // 50 μs
679            readout_error: 0.001,    // 0.1%
680            thermal_population: 0.01,
681            temperature: Some(20.0), // 20 mK
682            parameters: HashMap::new(),
683        });
684    }
685
686    // Add ideal single-qubit gates
687    for gate_name in ["X", "Y", "Z", "H", "S", "T", "RX", "RY", "RZ"] {
688        let mut qubit_data = HashMap::new();
689
690        for i in 0..num_qubits {
691            qubit_data.insert(
692                QubitId(i as u32),
693                SingleQubitGateData {
694                    error_rate: 0.001,
695                    fidelity: 0.999,
696                    duration: 20.0, // 20 ns
697                    amplitude: 1.0,
698                    frequency: 5e9,
699                    phase: 0.0,
700                    pulse_shape: PulseShape::GaussianDRAG {
701                        sigma: 5.0,
702                        beta: 0.5,
703                        cutoff: 2.0,
704                    },
705                    calibrated_matrix: None,
706                    parameter_calibrations: None,
707                },
708            );
709        }
710
711        builder = builder.add_single_qubit_gate(
712            gate_name.to_string(),
713            SingleQubitGateCalibration {
714                gate_name: gate_name.to_string(),
715                qubit_data,
716                default_parameters: GateParameters {
717                    amplitude_scale: 1.0,
718                    phase_offset: 0.0,
719                    duration_scale: 1.0,
720                    drag_coefficient: Some(0.5),
721                    custom_parameters: HashMap::new(),
722                },
723            },
724        );
725    }
726
727    // Add ideal two-qubit gates (nearest neighbor)
728    for i in 0..num_qubits - 1 {
729        let control = QubitId(i as u32);
730        let target = QubitId((i + 1) as u32);
731
732        builder = builder.add_two_qubit_gate(
733            control,
734            target,
735            TwoQubitGateCalibration {
736                gate_name: "CNOT".to_string(),
737                control,
738                target,
739                error_rate: 0.01,
740                fidelity: 0.99,
741                duration: 200.0,         // 200 ns
742                coupling_strength: 30.0, // 30 MHz
743                cross_resonance: Some(CrossResonanceParameters {
744                    drive_frequency: 4.8e9,
745                    drive_amplitude: 0.5,
746                    pulse_duration: 180.0,
747                    echo_amplitude: 0.25,
748                    echo_duration: 90.0,
749                    zx_interaction_rate: 3.0,
750                }),
751                calibrated_matrix: None,
752                directional: true,
753                reversed_calibration: None,
754            },
755        );
756    }
757
758    // Add ideal readout
759    let mut qubit_readout = HashMap::new();
760    for i in 0..num_qubits {
761        qubit_readout.insert(
762            QubitId(i as u32),
763            QubitReadoutData {
764                p0_given_0: 0.999,
765                p1_given_1: 0.999,
766                resonator_frequency: 6.5e9,
767                readout_amplitude: 0.1,
768                readout_phase: 0.0,
769                snr: 10.0,
770            },
771        );
772    }
773
774    builder = builder.readout_calibration(ReadoutCalibration {
775        qubit_readout,
776        mitigation_matrix: None,
777        duration: 2000.0,         // 2 μs
778        integration_time: 1500.0, // 1.5 μs
779    });
780
781    // Add ideal crosstalk (none)
782    let mut matrix = vec![vec![0.0; num_qubits]; num_qubits];
783    for i in 0..num_qubits {
784        matrix[i][i] = 1.0;
785    }
786
787    builder = builder.crosstalk_matrix(CrosstalkMatrix {
788        matrix,
789        measurement_method: "Ideal".to_string(),
790        significance_threshold: 0.01,
791    });
792
793    // Add linear topology
794    let mut coupling_map = Vec::new();
795    for i in 0..num_qubits - 1 {
796        coupling_map.push((QubitId(i as u32), QubitId((i + 1) as u32)));
797    }
798
799    builder = builder.topology(DeviceTopology {
800        num_qubits,
801        coupling_map,
802        layout_type: "linear".to_string(),
803        qubit_coordinates: None,
804    });
805
806    builder.build().unwrap()
807}
808
809#[cfg(test)]
810mod tests {
811    use super::*;
812
813    #[test]
814    fn test_calibration_builder() {
815        let cal = CalibrationBuilder::new("test_device".to_string())
816            .add_qubit_calibration(QubitCalibration {
817                qubit_id: QubitId(0),
818                frequency: 5e9,
819                anharmonicity: -300e6,
820                t1: 50_000.0,
821                t2: 40_000.0,
822                t2_star: Some(30_000.0),
823                readout_error: 0.02,
824                thermal_population: 0.02,
825                temperature: Some(15.0),
826                parameters: HashMap::new(),
827            })
828            .readout_calibration(ReadoutCalibration {
829                qubit_readout: HashMap::new(),
830                mitigation_matrix: None,
831                duration: 2000.0,
832                integration_time: 1500.0,
833            })
834            .crosstalk_matrix(CrosstalkMatrix {
835                matrix: vec![vec![1.0]],
836                measurement_method: "Test".to_string(),
837                significance_threshold: 0.01,
838            })
839            .topology(DeviceTopology {
840                num_qubits: 1,
841                coupling_map: vec![],
842                layout_type: "single".to_string(),
843                qubit_coordinates: None,
844            })
845            .build()
846            .unwrap();
847
848        assert_eq!(cal.device_id, "test_device");
849        assert_eq!(cal.qubit_calibrations.len(), 1);
850    }
851
852    #[test]
853    fn test_calibration_manager() {
854        let mut manager = CalibrationManager::new();
855        let cal = create_ideal_calibration("test_device".to_string(), 5);
856
857        manager.update_calibration(cal);
858
859        assert!(manager.is_calibration_valid("test_device"));
860        assert_eq!(
861            manager.get_gate_fidelity("test_device", "X", &[QubitId(0)]),
862            Some(0.999)
863        );
864        assert_eq!(
865            manager.get_gate_duration("test_device", "CNOT", &[QubitId(0), QubitId(1)]),
866            Some(200.0)
867        );
868    }
869
870    #[test]
871    fn test_ideal_calibration() {
872        let cal = create_ideal_calibration("ideal".to_string(), 10);
873
874        assert_eq!(cal.qubit_calibrations.len(), 10);
875        assert!(cal.single_qubit_gates.contains_key("X"));
876        assert!(cal.single_qubit_gates.contains_key("RZ"));
877        assert_eq!(cal.topology.coupling_map.len(), 9); // 9 connections for 10 qubits
878    }
879}