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<TwoQubitGateCalibration>>,
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).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 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 pub fn update_calibration(&mut self, calibration: DeviceCalibration) {
448 let device_id = calibration.device_id.clone();
449 let timestamp = calibration.timestamp;
450
451 if let Some(old_cal) = self.calibrations.get(&device_id) {
453 self.history
454 .push((device_id.clone(), timestamp, old_cal.clone()));
455
456 if self.history.len() > self.max_history {
458 self.history.remove(0);
459 }
460 }
461
462 self.calibrations.insert(device_id, calibration);
464 }
465
466 pub fn get_calibration(&self, device_id: &str) -> Option<&DeviceCalibration> {
468 self.calibrations.get(device_id)
469 }
470
471 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 pub fn get_latest_calibration(&self) -> Option<&DeviceCalibration> {
486 self.calibrations.values().max_by_key(|cal| cal.timestamp)
487 }
488
489 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 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
544pub 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 pub fn new(device_id: String) -> Self {
562 Self {
563 device_id,
564 timestamp: SystemTime::now(),
565 valid_duration: Duration::from_secs(24 * 3600), 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 pub fn valid_duration(mut self, duration: Duration) -> Self {
579 self.valid_duration = duration;
580 self
581 }
582
583 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 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 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 pub fn readout_calibration(mut self, calibration: ReadoutCalibration) -> Self {
613 self.readout_calibration = Some(calibration);
614 self
615 }
616
617 pub fn crosstalk_matrix(mut self, matrix: CrosstalkMatrix) -> Self {
619 self.crosstalk_matrix = Some(matrix);
620 self
621 }
622
623 pub fn topology(mut self, topology: DeviceTopology) -> Self {
625 self.topology = Some(topology);
626 self
627 }
628
629 pub fn add_metadata(mut self, key: String, value: String) -> Self {
631 self.metadata.insert(key, value);
632 self
633 }
634
635 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
665pub fn create_ideal_calibration(device_id: String, num_qubits: usize) -> DeviceCalibration {
667 let mut builder = CalibrationBuilder::new(device_id);
668
669 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, anharmonicity: -300e6, t1: 100_000.0, t2: 100_000.0, t2_star: Some(50_000.0), readout_error: 0.001, thermal_population: 0.01,
681 temperature: Some(20.0), parameters: HashMap::new(),
683 });
684 }
685
686 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, 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 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, coupling_strength: 30.0, 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 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, integration_time: 1500.0, });
780
781 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 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); }
879}