1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct DeviceCalibration {
21 pub device_id: String,
23 pub timestamp: SystemTime,
25 pub valid_duration: Duration,
27 pub qubit_calibrations: HashMap<QubitId, QubitCalibration>,
29 pub single_qubit_gates: HashMap<String, SingleQubitGateCalibration>,
31 pub two_qubit_gates: HashMap<(QubitId, QubitId), TwoQubitGateCalibration>,
33 pub multi_qubit_gates: HashMap<Vec<QubitId>, MultiQubitGateCalibration>,
35 pub readout_calibration: ReadoutCalibration,
37 pub crosstalk_matrix: CrosstalkMatrix,
39 pub topology: DeviceTopology,
41 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), 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 pub fn single_qubit_fidelity(&self, qubit: usize) -> Option<f64> {
66 let qubit_id = QubitId(qubit as u32);
67
68 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 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 None
86 }
87
88 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 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 None
104 }
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct QubitCalibration {
110 pub qubit_id: QubitId,
112 pub frequency: f64,
114 pub anharmonicity: f64,
116 pub t1: f64,
118 pub t2: f64,
120 pub t2_star: Option<f64>,
122 pub readout_error: f64,
124 pub thermal_population: f64,
126 pub temperature: Option<f64>,
128 pub parameters: HashMap<String, f64>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct SingleQubitGateCalibration {
135 pub gate_name: String,
137 pub qubit_data: HashMap<QubitId, SingleQubitGateData>,
139 pub default_parameters: GateParameters,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct SingleQubitGateData {
146 pub error_rate: f64,
148 pub fidelity: f64,
150 pub duration: f64,
152 pub amplitude: f64,
154 pub frequency: f64,
156 pub phase: f64,
158 pub pulse_shape: PulseShape,
160 pub calibrated_matrix: Option<Vec<Complex64>>,
162 pub parameter_calibrations: Option<ParameterCalibration>,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct TwoQubitGateCalibration {
169 pub gate_name: String,
171 pub control: QubitId,
173 pub target: QubitId,
175 pub error_rate: f64,
177 pub fidelity: f64,
179 pub duration: f64,
181 pub coupling_strength: f64,
183 pub cross_resonance: Option<CrossResonanceParameters>,
185 pub calibrated_matrix: Option<Vec<Complex64>>,
187 pub directional: bool,
189 pub reversed_calibration: Option<Box<Self>>,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct MultiQubitGateCalibration {
196 pub gate_name: String,
198 pub qubits: Vec<QubitId>,
200 pub error_rate: f64,
202 pub fidelity: f64,
204 pub duration: f64,
206 pub decomposition: GateDecomposition,
208 pub is_native: bool,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct ReadoutCalibration {
215 pub qubit_readout: HashMap<QubitId, QubitReadoutData>,
217 pub mitigation_matrix: Option<Vec<Vec<f64>>>,
219 pub duration: f64,
221 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, integration_time: 500.0, }
233 }
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct QubitReadoutData {
239 pub p0_given_0: f64,
241 pub p1_given_1: f64,
243 pub resonator_frequency: f64,
245 pub readout_amplitude: f64,
247 pub readout_phase: f64,
249 pub snr: f64,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct CrosstalkMatrix {
256 pub matrix: Vec<Vec<f64>>,
259 pub measurement_method: String,
261 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#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct DeviceTopology {
278 pub num_qubits: usize,
280 pub coupling_map: Vec<(QubitId, QubitId)>,
282 pub layout_type: String,
284 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#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct GateParameters {
302 pub amplitude_scale: f64,
304 pub phase_offset: f64,
306 pub duration_scale: f64,
308 pub drag_coefficient: Option<f64>,
310 pub custom_parameters: HashMap<String, f64>,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316pub enum PulseShape {
317 Gaussian { sigma: f64, cutoff: f64 },
319 GaussianDRAG { sigma: f64, beta: f64, cutoff: f64 },
321 Square { rise_time: f64 },
323 Cosine { rise_time: f64 },
325 Custom {
327 name: String,
328 parameters: HashMap<String, f64>,
329 },
330}
331
332#[derive(Debug, Clone, Serialize, Deserialize)]
334pub struct ParameterCalibration {
335 pub calibration_points: Vec<(f64, SingleQubitGateData)>,
337 pub interpolation: InterpolationMethod,
339 pub valid_range: (f64, f64),
341}
342
343#[derive(Debug, Clone, Serialize, Deserialize)]
345pub enum InterpolationMethod {
346 Linear,
348 CubicSpline,
350 Polynomial { degree: usize },
352 NearestNeighbor,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct CrossResonanceParameters {
359 pub drive_frequency: f64,
361 pub drive_amplitude: f64,
363 pub pulse_duration: f64,
365 pub echo_amplitude: f64,
367 pub echo_duration: f64,
368 pub zx_interaction_rate: f64,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct GateDecomposition {
375 pub gates: Vec<DecomposedGate>,
377 pub decomposition_error: f64,
379 pub is_optimal: bool,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct DecomposedGate {
386 pub gate_name: String,
388 pub qubits: Vec<QubitId>,
390 pub parameters: Vec<f64>,
392}
393
394#[derive(Debug, Clone)]
396pub struct CalibrationManager {
397 calibrations: HashMap<String, DeviceCalibration>,
399 history: Vec<(String, SystemTime, DeviceCalibration)>,
401 max_history: usize,
403}
404
405impl CalibrationManager {
406 pub fn new() -> Self {
408 Self {
409 calibrations: HashMap::new(),
410 history: Vec::new(),
411 max_history: 100,
412 }
413 }
414
415 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 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 pub fn update_calibration(&mut self, calibration: DeviceCalibration) {
447 let device_id = calibration.device_id.clone();
448 let timestamp = calibration.timestamp;
449
450 if let Some(old_cal) = self.calibrations.get(&device_id) {
452 self.history
453 .push((device_id.clone(), timestamp, old_cal.clone()));
454
455 if self.history.len() > self.max_history {
457 self.history.remove(0);
458 }
459 }
460
461 self.calibrations.insert(device_id, calibration);
463 }
464
465 pub fn get_calibration(&self, device_id: &str) -> Option<&DeviceCalibration> {
467 self.calibrations.get(device_id)
468 }
469
470 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 pub fn get_latest_calibration(&self) -> Option<&DeviceCalibration> {
483 self.calibrations.values().max_by_key(|cal| cal.timestamp)
484 }
485
486 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 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
541pub 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 pub fn new(device_id: String) -> Self {
559 Self {
560 device_id,
561 timestamp: SystemTime::now(),
562 valid_duration: Duration::from_secs(24 * 3600), 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 #[must_use]
576 pub const fn valid_duration(mut self, duration: Duration) -> Self {
577 self.valid_duration = duration;
578 self
579 }
580
581 #[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 #[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 #[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 #[must_use]
614 pub fn readout_calibration(mut self, calibration: ReadoutCalibration) -> Self {
615 self.readout_calibration = Some(calibration);
616 self
617 }
618
619 #[must_use]
621 pub fn crosstalk_matrix(mut self, matrix: CrosstalkMatrix) -> Self {
622 self.crosstalk_matrix = Some(matrix);
623 self
624 }
625
626 #[must_use]
628 pub fn topology(mut self, topology: DeviceTopology) -> Self {
629 self.topology = Some(topology);
630 self
631 }
632
633 #[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 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
670pub fn create_ideal_calibration(device_id: String, num_qubits: usize) -> DeviceCalibration {
672 let mut builder = CalibrationBuilder::new(device_id);
673
674 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, anharmonicity: -300e6, t1: 100_000.0, t2: 100_000.0, t2_star: Some(50_000.0), readout_error: 0.001, thermal_population: 0.01,
686 temperature: Some(20.0), parameters: HashMap::new(),
688 });
689 }
690
691 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, 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 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, coupling_strength: 30.0, 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 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, integration_time: 1500.0, });
785
786 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 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); }
886}