1use crate::circuit::{
21 Circuit, ClassicalControlOp, Directive, Instruction, Operation, Qubit, StandardGate,
22 ValueClassicalControlOp, ValueInstruction, ValueOperation,
23};
24use crate::device::topology::Topology;
25use crate::device::{DeviceError, DeviceValidationError, PhysicalQubit};
26use std::collections::{BTreeSet, HashMap, HashSet};
27use std::ops::RangeInclusive;
28use time::OffsetDateTime;
29
30fn validate_native_gate_arity(
35 instruction: &Instruction,
36 expected: RangeInclusive<usize>,
37) -> Result<(), DeviceError> {
38 let Instruction::Standard(gate) = instruction else {
39 return Err(DeviceError::NonStandardNativeInstruction {
40 instruction: instruction.to_string(),
41 });
42 };
43 let actual = gate.num_qubits();
44 if expected.contains(&actual) {
45 Ok(())
46 } else {
47 Err(DeviceError::InvalidNativeInstructionArity {
48 instruction: instruction.to_string(),
49 expected,
50 actual,
51 })
52 }
53}
54
55#[derive(Debug, Clone)]
62pub struct InstructionProp {
63 instruction: Instruction,
65 error_rate: f64,
67 length: Option<f64>,
69}
70
71impl InstructionProp {
72 pub fn new(instruction: Instruction, error_rate: f64) -> Self {
74 Self {
75 instruction,
76 error_rate,
77 length: None,
78 }
79 }
80
81 pub fn with_length(mut self, length: f64) -> Self {
83 self.length = Some(length);
84 self
85 }
86
87 pub fn set_length(&mut self, length: f64) {
88 self.length = Some(length);
89 }
90 pub fn with_instruction(mut self, instruction: Instruction) -> Self {
91 self.instruction = instruction;
92 self
93 }
94
95 pub fn set_instruction(&mut self, instruction: Instruction) {
96 self.instruction = instruction;
97 }
98
99 pub fn with_error_rate(mut self, error_rate: f64) -> Self {
100 self.error_rate = error_rate;
101 self
102 }
103
104 pub fn set_error_rate(&mut self, error_rate: f64) {
105 self.error_rate = error_rate;
106 }
107
108 pub fn instruction(&self) -> &Instruction {
110 &self.instruction
111 }
112
113 pub fn error_rate(&self) -> f64 {
115 self.error_rate
116 }
117
118 pub fn length(&self) -> Option<f64> {
120 self.length
121 }
122
123 fn validate(&self) -> Result<(), DeviceError> {
124 if !(self.error_rate.is_finite() && (0.0..=1.0).contains(&self.error_rate)) {
125 return Err(DeviceError::InvalidNativeInstructionErrorRate {
126 instruction: self.instruction.to_string(),
127 value: format!("{:?}", self.error_rate),
128 });
129 }
130 if let Some(length) = self.length
131 && !(length.is_finite() && length >= 0.0)
132 {
133 return Err(DeviceError::InvalidNativeInstructionDuration {
134 instruction: self.instruction.to_string(),
135 value: format!("{length:?}"),
136 });
137 }
138 Ok(())
139 }
140}
141
142#[derive(Debug, Clone)]
149pub struct QubitProp {
150 readout_error: f64,
152 prob_meas0_prep1: Option<f64>,
154 prob_meas1_prep0: Option<f64>,
156
157 t1: Option<f64>,
159 t2: Option<f64>,
161 frequency: Option<f64>,
163 native_instructions: Vec<InstructionProp>,
169}
170
171impl QubitProp {
172 pub fn new(readout_error: f64) -> Self {
174 Self {
175 readout_error,
176 prob_meas0_prep1: None,
177 prob_meas1_prep0: None,
178 t1: None,
179 t2: None,
180 frequency: None,
181 native_instructions: Vec::new(),
182 }
183 }
184
185 pub fn with_prob_meas0_prep1(mut self, prob: f64) -> Self {
187 self.prob_meas0_prep1 = Some(prob);
188 self
189 }
190 pub fn set_prob_meas0_prep1(&mut self, prob: f64) {
191 self.prob_meas0_prep1 = Some(prob);
192 }
193
194 pub fn with_prob_meas1_prep0(mut self, prob: f64) -> Self {
196 self.prob_meas1_prep0 = Some(prob);
197 self
198 }
199 pub fn set_prob_meas1_prep0(&mut self, prob: f64) {
200 self.prob_meas1_prep0 = Some(prob);
201 }
202
203 pub fn with_t1(mut self, t1: f64) -> Self {
205 self.t1 = Some(t1);
206 self
207 }
208
209 pub fn set_t1(&mut self, t1: f64) {
210 self.t1 = Some(t1);
211 }
212
213 pub fn with_t2(mut self, t2: f64) -> Self {
215 self.t2 = Some(t2);
216 self
217 }
218 pub fn set_t2(&mut self, t2: f64) {
219 self.t2 = Some(t2);
220 }
221
222 pub fn with_frequency(mut self, frequency: f64) -> Self {
224 self.frequency = Some(frequency);
225 self
226 }
227
228 pub fn set_frequency(&mut self, frequency: f64) {
229 self.frequency = Some(frequency);
230 }
231
232 pub fn with_native_instruction(mut self, prop: InstructionProp) -> Result<Self, DeviceError> {
239 self.set_native_instruction(prop)?;
240 Ok(self)
241 }
242 pub fn set_native_instruction(&mut self, prop: InstructionProp) -> Result<(), DeviceError> {
244 validate_native_gate_arity(prop.instruction(), 1..=1)?;
245 prop.validate()?;
246 self.native_instructions.push(prop);
247 Ok(())
248 }
249
250 pub fn readout_error(&self) -> f64 {
252 self.readout_error
253 }
254
255 pub fn prob_meas0_prep1(&self) -> Option<f64> {
257 self.prob_meas0_prep1
258 }
259
260 pub fn prob_meas1_prep0(&self) -> Option<f64> {
262 self.prob_meas1_prep0
263 }
264
265 pub fn t1(&self) -> Option<f64> {
267 self.t1
268 }
269
270 pub fn t2(&self) -> Option<f64> {
272 self.t2
273 }
274
275 pub fn frequency(&self) -> Option<f64> {
277 self.frequency
278 }
279
280 pub fn native_instructions(&self) -> &[InstructionProp] {
282 &self.native_instructions
283 }
284}
285
286#[derive(Debug, Clone)]
292pub struct EdgeProp {
293 native_instructions: Vec<InstructionProp>,
299}
300
301enum NativeInstructionSupport<'a> {
303 Explicit(&'a InstructionProp),
305 Inherited,
307 Unsupported,
309}
310
311#[derive(Debug, Clone, Copy, Default, PartialEq)]
313pub(crate) struct NativeInstructionCalibration {
314 pub(crate) error_rate: Option<f64>,
315 pub(crate) duration: Option<f64>,
316}
317
318impl EdgeProp {
319 pub fn new() -> Self {
321 Self {
322 native_instructions: Vec::new(),
323 }
324 }
325
326 pub fn with_native_instruction(mut self, prop: InstructionProp) -> Result<Self, DeviceError> {
333 self.set_native_instruction(prop)?;
334 Ok(self)
335 }
336
337 pub fn set_native_instruction(&mut self, prop: InstructionProp) -> Result<(), DeviceError> {
339 validate_native_gate_arity(prop.instruction(), 2..=2)?;
340 prop.validate()?;
341 self.native_instructions.push(prop);
342 Ok(())
343 }
344
345 pub fn native_instructions(&self) -> &[InstructionProp] {
347 &self.native_instructions
348 }
349}
350
351impl Default for EdgeProp {
352 fn default() -> Self {
353 Self::new()
354 }
355}
356
357#[derive(Debug, Clone)]
397pub struct Device {
398 name: String,
399 qubits: BTreeSet<PhysicalQubit>,
401 invalid_qubits: BTreeSet<PhysicalQubit>,
403 topology: Topology,
405 native_gates: Vec<Instruction>,
411
412 calibration_time: Option<OffsetDateTime>,
414 default_t1: Option<f64>,
416 default_t2: Option<f64>,
418 default_readout_error: Option<f64>,
420 default_single_qubit_error: Option<f64>,
422 default_two_qubit_error: Option<f64>,
424
425 qubit_properties: HashMap<PhysicalQubit, QubitProp>,
427 edge_properties: HashMap<(PhysicalQubit, PhysicalQubit), EdgeProp>,
429}
430
431impl Device {
432 pub fn new(
434 name: impl Into<String>,
435 qubits: HashSet<PhysicalQubit>,
436 topology: Topology,
437 ) -> Result<Self, DeviceError> {
438 for q in topology.qubits() {
439 if !qubits.contains(&q) {
440 return Err(DeviceError::InvalidOnlineQubit(q));
441 }
442 }
443
444 Ok(Self {
445 name: name.into(),
446 qubits: qubits.into_iter().collect(),
447 invalid_qubits: BTreeSet::new(),
448 topology,
449 native_gates: Vec::new(),
450 calibration_time: None,
451 default_t1: None,
452 default_t2: None,
453 default_readout_error: None,
454 default_single_qubit_error: None,
455 default_two_qubit_error: None,
456 qubit_properties: HashMap::new(),
457 edge_properties: HashMap::new(),
458 })
459 }
460
461 pub fn line(name: impl Into<String>, num_qubits: u32) -> Result<Self, DeviceError> {
467 let physical_qubits = (0..num_qubits).map(PhysicalQubit::new).collect::<Vec<_>>();
468 Self::line_from_qubits(name, physical_qubits)
469 }
470
471 pub fn line_from_qubits(
480 name: impl Into<String>,
481 physical_qubits: Vec<PhysicalQubit>,
482 ) -> Result<Self, DeviceError> {
483 if physical_qubits.is_empty() {
484 return Err(DeviceError::EmptyDevice);
485 }
486 let qubits = physical_qubits.iter().copied().collect::<HashSet<_>>();
487 let topology = Topology::line(physical_qubits).map_err(DeviceError::InvalidTopology)?;
488 Self::new(name, qubits, topology)
489 }
490
491 pub fn bidirectional_line(
496 name: impl Into<String>,
497 num_qubits: u32,
498 ) -> Result<Self, DeviceError> {
499 let edges = (0..num_qubits.saturating_sub(1))
500 .flat_map(|index| [(index, index + 1), (index + 1, index)])
501 .collect::<Vec<_>>();
502 Self::from_u32_edges(name, num_qubits, &edges)
503 }
504
505 pub fn ring(name: impl Into<String>, num_qubits: u32) -> Result<Self, DeviceError> {
511 let mut edges = BTreeSet::new();
512 if num_qubits >= 2 {
513 for index in 0..num_qubits {
514 let next = (index + 1) % num_qubits;
515 edges.insert((index, next));
516 edges.insert((next, index));
517 }
518 }
519 let edges = edges.into_iter().collect::<Vec<_>>();
520 Self::from_u32_edges(name, num_qubits, &edges)
521 }
522
523 pub fn star(
529 name: impl Into<String>,
530 num_qubits: u32,
531 center: u32,
532 ) -> Result<Self, DeviceError> {
533 let edges = (0..num_qubits)
534 .filter(|&index| index != center)
535 .flat_map(|index| [(center, index), (index, center)])
536 .collect::<Vec<_>>();
537 Self::from_u32_edges(name, num_qubits, &edges)
538 }
539
540 pub fn grid(name: impl Into<String>, rows: u32, cols: u32) -> Result<Self, DeviceError> {
545 let num_qubits = rows.saturating_mul(cols);
546 let mut edges = Vec::new();
547 for row in 0..rows {
548 for col in 0..cols {
549 let current = row * cols + col;
550 if col + 1 < cols {
551 edges.push((current, current + 1));
552 edges.push((current + 1, current));
553 }
554 if row + 1 < rows {
555 edges.push((current, current + cols));
556 edges.push((current + cols, current));
557 }
558 }
559 }
560 Self::from_u32_edges(name, num_qubits, &edges)
561 }
562
563 pub fn from_edges(
567 name: impl Into<String>,
568 num_qubits: u32,
569 edges: &[(u32, u32)],
570 ) -> Result<Self, DeviceError> {
571 Self::from_u32_edges(name, num_qubits, edges)
572 }
573
574 fn from_u32_edges(
575 name: impl Into<String>,
576 num_qubits: u32,
577 edges: &[(u32, u32)],
578 ) -> Result<Self, DeviceError> {
579 if num_qubits == 0 {
580 return Err(DeviceError::EmptyDevice);
581 }
582 let physical_qubits = (0..num_qubits).map(PhysicalQubit::new).collect::<Vec<_>>();
583 let qubits = physical_qubits.iter().copied().collect::<HashSet<_>>();
584 let coupling_map = edges
585 .iter()
586 .enumerate()
587 .map(|(index, &(control, target))| {
588 (
589 PhysicalQubit::new(control),
590 PhysicalQubit::new(target),
591 format!("e{index}"),
592 )
593 })
594 .collect::<Vec<_>>();
595 let topology =
596 Topology::new(physical_qubits, coupling_map).map_err(DeviceError::InvalidTopology)?;
597 Self::new(name, qubits, topology)
598 }
599
600 pub fn with_invalid_qubits(
607 mut self,
608 invalid_qubits: HashSet<PhysicalQubit>,
609 ) -> Result<Self, DeviceError> {
610 self.set_invalid_qubits(invalid_qubits)?;
611 Ok(self)
612 }
613
614 pub fn set_invalid_qubits(
621 &mut self,
622 invalid_qubits: HashSet<PhysicalQubit>,
623 ) -> Result<(), DeviceError> {
624 for &qubit in &invalid_qubits {
625 if !self.qubits.contains(&qubit) {
626 return Err(DeviceError::QubitNotInDevice(qubit));
627 }
628 }
629 self.invalid_qubits = invalid_qubits.into_iter().collect();
630 Ok(())
631 }
632
633 pub fn with_native_gates(mut self, gates: Vec<Instruction>) -> Result<Self, DeviceError> {
643 self.set_native_gates(gates)?;
644 Ok(self)
645 }
646
647 pub fn set_native_gates(&mut self, gates: Vec<Instruction>) -> Result<(), DeviceError> {
656 for gate in &gates {
657 validate_native_gate_arity(gate, 0..=2)?;
658 }
659 self.native_gates = gates;
660 Ok(())
661 }
662
663 pub fn with_calibration_time(mut self, time: OffsetDateTime) -> Self {
665 self.calibration_time = Some(time);
666 self
667 }
668
669 pub fn set_calibration_time(&mut self, time: OffsetDateTime) {
670 self.calibration_time = Some(time);
671 }
672
673 pub fn with_default_t1(mut self, t1: f64) -> Self {
675 self.default_t1 = Some(t1);
676 self
677 }
678
679 pub fn set_default_t1(&mut self, t1: f64) {
680 self.default_t1 = Some(t1);
681 }
682
683 pub fn with_default_t2(mut self, t2: f64) -> Self {
685 self.default_t2 = Some(t2);
686 self
687 }
688
689 pub fn set_default_t2(&mut self, t2: f64) {
690 self.default_t2 = Some(t2);
691 }
692
693 pub fn with_default_readout_error(mut self, error: f64) -> Self {
695 self.default_readout_error = Some(error);
696 self
697 }
698
699 pub fn set_default_readout_error(&mut self, error: f64) {
700 self.default_readout_error = Some(error);
701 }
702
703 pub fn with_default_single_qubit_error(mut self, error: f64) -> Self {
705 self.default_single_qubit_error = Some(error);
706 self
707 }
708
709 pub fn set_default_single_qubit_error(&mut self, error: f64) {
710 self.default_single_qubit_error = Some(error);
711 }
712
713 pub fn with_default_two_qubit_error(mut self, error: f64) -> Self {
715 self.default_two_qubit_error = Some(error);
716 self
717 }
718
719 pub fn set_default_two_qubit_error(&mut self, error: f64) {
720 self.default_two_qubit_error = Some(error);
721 }
722
723 pub fn add_qubit_properties(
732 &mut self,
733 qubit: PhysicalQubit,
734 props: QubitProp,
735 ) -> Result<(), DeviceError> {
736 if !self.qubits.contains(&qubit) {
737 return Err(DeviceError::QubitNotInDevice(qubit));
738 }
739 if !self.topology.contains_qubit(&qubit) {
740 return Err(DeviceError::QubitNotInTopology(qubit));
741 }
742 self.qubit_properties.insert(qubit, props);
743 Ok(())
744 }
745
746 pub fn add_edge_properties(
752 &mut self,
753 control: PhysicalQubit,
754 target: PhysicalQubit,
755 props: EdgeProp,
756 ) -> Result<(), DeviceError> {
757 if !self.topology.supports_directed_coupling(control, target) {
758 return Err(DeviceError::EdgeNotInTopology(control, target));
759 }
760 self.edge_properties.insert((control, target), props);
761 Ok(())
762 }
763
764 fn instruction_matches(stored: &Instruction, requested: &Instruction) -> bool {
769 match (stored, requested) {
770 (Instruction::Standard(stored), Instruction::Standard(requested)) => {
771 stored == requested
772 }
773 _ => false,
774 }
775 }
776
777 fn resolve_native_instruction<'a>(
783 &'a self,
784 local: Option<&'a [InstructionProp]>,
785 instruction: &Instruction,
786 ) -> NativeInstructionSupport<'a> {
787 if let Some(local) = local.filter(|instructions| !instructions.is_empty()) {
788 return local
789 .iter()
790 .find(|prop| Self::instruction_matches(prop.instruction(), instruction))
791 .map_or(
792 NativeInstructionSupport::Unsupported,
793 NativeInstructionSupport::Explicit,
794 );
795 }
796
797 if self
798 .native_gates
799 .iter()
800 .any(|stored| Self::instruction_matches(stored, instruction))
801 {
802 NativeInstructionSupport::Inherited
803 } else {
804 NativeInstructionSupport::Unsupported
805 }
806 }
807
808 pub fn name(&self) -> &str {
810 &self.name
811 }
812
813 pub fn qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
815 self.qubits.iter().copied()
816 }
817
818 pub fn invalid_qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
820 self.invalid_qubits.iter().copied()
821 }
822
823 pub fn is_usable_qubit(&self, qubit: PhysicalQubit) -> bool {
825 self.qubits.contains(&qubit) && !self.invalid_qubits.contains(&qubit)
826 }
827
828 pub fn usable_qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
830 self.qubits.difference(&self.invalid_qubits).copied()
831 }
832
833 pub fn num_usable_qubits(&self) -> usize {
835 self.qubits.len() - self.invalid_qubits.len()
836 }
837
838 pub fn topology(&self) -> &Topology {
840 &self.topology
841 }
842
843 pub fn native_gates(&self) -> &[Instruction] {
845 &self.native_gates
846 }
847
848 pub fn qubit_properties(&self, qubit: PhysicalQubit) -> Option<&QubitProp> {
850 self.qubit_properties.get(&qubit)
851 }
852
853 pub fn edge_properties(
855 &self,
856 control: PhysicalQubit,
857 target: PhysicalQubit,
858 ) -> Option<&EdgeProp> {
859 self.edge_properties.get(&(control, target))
860 }
861
862 pub fn supports_native_instruction(
872 &self,
873 instruction: &Instruction,
874 qargs: &[PhysicalQubit],
875 ) -> bool {
876 if qargs.iter().any(|qubit| !self.is_usable_qubit(*qubit)) {
877 return false;
878 }
879
880 match instruction {
881 Instruction::Standard(StandardGate::GPhase) if qargs.is_empty() => !matches!(
882 self.resolve_native_instruction(None, instruction),
883 NativeInstructionSupport::Unsupported
884 ),
885 Instruction::Standard(gate) if gate.num_qubits() == qargs.len() => match qargs {
886 [qubit] => !matches!(
887 self.resolve_native_instruction(
888 self.qubit_properties
889 .get(qubit)
890 .map(QubitProp::native_instructions),
891 instruction,
892 ),
893 NativeInstructionSupport::Unsupported
894 ),
895 [control, target]
896 if self.topology.supports_directed_coupling(*control, *target) =>
897 {
898 !matches!(
899 self.resolve_native_instruction(
900 self.edge_properties(*control, *target)
901 .map(EdgeProp::native_instructions),
902 instruction,
903 ),
904 NativeInstructionSupport::Unsupported
905 )
906 }
907 _ => false,
908 },
909 Instruction::Standard(_)
910 | Instruction::McGate(_)
911 | Instruction::UnitaryGate(_)
912 | Instruction::CircuitGate(_)
913 | Instruction::Directive(_)
914 | Instruction::ClassicalData(_)
915 | Instruction::ClassicalControl(_)
916 | Instruction::Delay => false,
917 }
918 }
919
920 pub fn validate_operation(&self, operation: &Operation) -> Result<(), DeviceValidationError> {
926 let qargs = self.validate_qargs(&operation.qubits)?;
927 match &operation.instruction {
928 Instruction::ClassicalControl(control) => match control {
929 ClassicalControlOp::If(op) => {
930 self.validate_control_body(op.then_body().operations())?;
931 if let Some(body) = op.else_body() {
932 self.validate_control_body(body.operations())?;
933 }
934 }
935 ClassicalControlOp::While(op) => {
936 self.validate_control_body(op.body().operations())?
937 }
938 ClassicalControlOp::For(op) => {
939 self.validate_control_body(op.body().operations())?
940 }
941 ClassicalControlOp::Switch(op) => {
942 for case in op.cases() {
943 self.validate_control_body(case.body().operations())?;
944 }
945 if let Some(body) = op.default() {
946 self.validate_control_body(body.operations())?;
947 }
948 }
949 ClassicalControlOp::Break | ClassicalControlOp::Continue => {}
950 },
951 instruction => self.validate_atomic_instruction(instruction, qargs)?,
952 }
953 Ok(())
954 }
955
956 pub fn validate_value_operation(
963 &self,
964 operation: &ValueOperation,
965 ) -> Result<(), DeviceValidationError> {
966 let qargs = self.validate_qargs(&operation.qubits)?;
967 match &operation.instruction {
968 ValueInstruction::Instruction(instruction) => {
969 self.validate_atomic_instruction(instruction, qargs)?
970 }
971 ValueInstruction::ClassicalControl(control) => match control {
972 ValueClassicalControlOp::If {
973 then_body,
974 else_body,
975 ..
976 } => {
977 self.validate_value_control_body(then_body.operations())?;
978 if let Some(body) = else_body {
979 self.validate_value_control_body(body.operations())?;
980 }
981 }
982 ValueClassicalControlOp::While { body, .. }
983 | ValueClassicalControlOp::For { body, .. } => {
984 self.validate_value_control_body(body.operations())?
985 }
986 ValueClassicalControlOp::Switch { cases, default, .. } => {
987 for case in cases {
988 self.validate_value_control_body(case.body.operations())?;
989 }
990 if let Some(body) = default {
991 self.validate_value_control_body(body.operations())?;
992 }
993 }
994 ValueClassicalControlOp::Break | ValueClassicalControlOp::Continue => {}
995 },
996 }
997 Ok(())
998 }
999
1000 fn validate_qargs(
1001 &self,
1002 qubits: &[Qubit],
1003 ) -> Result<Vec<PhysicalQubit>, DeviceValidationError> {
1004 let qargs = qubits
1005 .iter()
1006 .copied()
1007 .map(PhysicalQubit::from_qubit)
1008 .collect::<Vec<_>>();
1009 if let Some(qubit) = qargs
1012 .iter()
1013 .copied()
1014 .find(|qubit| !self.is_usable_qubit(*qubit))
1015 {
1016 return Err(DeviceValidationError::UnusablePhysicalQubit {
1017 device: self.name.clone(),
1018 qubit,
1019 });
1020 }
1021 Ok(qargs)
1022 }
1023
1024 fn validate_atomic_instruction(
1025 &self,
1026 instruction: &Instruction,
1027 qargs: Vec<PhysicalQubit>,
1028 ) -> Result<(), DeviceValidationError> {
1029 match instruction {
1030 Instruction::Directive(Directive::Measure | Directive::Reset) if qargs.len() == 1 => {}
1031 Instruction::Directive(Directive::Barrier) => {}
1032 Instruction::ClassicalData(op) => match op {
1033 crate::circuit::ClassicalDataOp::Store { .. } if qargs.is_empty() => {}
1034 crate::circuit::ClassicalDataOp::MeasureBit { .. } if qargs.len() == 1 => {}
1035 crate::circuit::ClassicalDataOp::MeasureBits { .. } if !qargs.is_empty() => {}
1036 _ => return Err(self.unsupported_instruction(instruction, qargs)),
1037 },
1038 Instruction::Delay if qargs.len() == 1 => {}
1039 Instruction::McGate(_) | Instruction::UnitaryGate(_) | Instruction::CircuitGate(_) => {
1040 return Err(DeviceValidationError::UndecomposedInstruction {
1041 device: self.name.clone(),
1042 instruction: instruction.to_string(),
1043 qargs,
1044 });
1045 }
1046 Instruction::Standard(gate) if gate.num_qubits() > 2 => {
1047 return Err(DeviceValidationError::UndecomposedInstruction {
1048 device: self.name.clone(),
1049 instruction: instruction.to_string(),
1050 qargs,
1051 });
1052 }
1053 Instruction::Standard(gate) if gate.num_qubits() == 2 => {
1054 let [control, target] = qargs.as_slice() else {
1055 return Err(self.unsupported_instruction(instruction, qargs));
1056 };
1057 if !self.topology.supports_directed_coupling(*control, *target) {
1058 return Err(DeviceValidationError::MissingDirectedCoupling {
1059 device: self.name.clone(),
1060 instruction: instruction.to_string(),
1061 control: *control,
1062 target: *target,
1063 });
1064 }
1065 if !self.supports_native_instruction(instruction, &qargs) {
1066 return Err(self.unsupported_instruction(instruction, qargs));
1067 }
1068 }
1069 Instruction::Standard(_) if self.supports_native_instruction(instruction, &qargs) => {}
1070 _ => return Err(self.unsupported_instruction(instruction, qargs)),
1071 }
1072 Ok(())
1073 }
1074
1075 pub fn validate_circuit(&self, circuit: &Circuit) -> Result<(), DeviceValidationError> {
1081 self.validate_operations(circuit.operations())
1082 }
1083
1084 fn validate_operations(&self, operations: &[Operation]) -> Result<(), DeviceValidationError> {
1085 for operation in operations {
1088 self.validate_operation(operation)?;
1089 }
1090 Ok(())
1091 }
1092
1093 fn validate_control_body(&self, operations: &[Operation]) -> Result<(), DeviceValidationError> {
1101 let operations = if operations.first().is_some_and(|operation| {
1102 matches!(
1103 operation.instruction,
1104 Instruction::Standard(StandardGate::GPhase)
1105 ) && operation.qubits.is_empty()
1106 }) {
1107 &operations[1..]
1108 } else {
1109 operations
1110 };
1111 self.validate_operations(operations)
1112 }
1113
1114 fn validate_value_control_body(
1115 &self,
1116 operations: &[ValueOperation],
1117 ) -> Result<(), DeviceValidationError> {
1118 let operations = if operations.first().is_some_and(|operation| {
1119 matches!(
1120 operation.instruction,
1121 ValueInstruction::Instruction(Instruction::Standard(StandardGate::GPhase))
1122 ) && operation.qubits.is_empty()
1123 }) {
1124 &operations[1..]
1125 } else {
1126 operations
1127 };
1128 for operation in operations {
1129 self.validate_value_operation(operation)?;
1130 }
1131 Ok(())
1132 }
1133
1134 fn unsupported_instruction(
1136 &self,
1137 instruction: &Instruction,
1138 qargs: Vec<PhysicalQubit>,
1139 ) -> DeviceValidationError {
1140 DeviceValidationError::UnsupportedInstruction {
1141 device: self.name.clone(),
1142 instruction: instruction.to_string(),
1143 qargs,
1144 }
1145 }
1146
1147 pub fn single_qubit_error(
1154 &self,
1155 qubit: PhysicalQubit,
1156 instruction: &Instruction,
1157 ) -> Option<f64> {
1158 if !self.is_usable_qubit(qubit) {
1159 return None;
1160 }
1161
1162 if !matches!(instruction, Instruction::Standard(gate) if gate.num_qubits() == 1) {
1163 return None;
1164 }
1165 match self.resolve_native_instruction(
1166 self.qubit_properties
1167 .get(&qubit)
1168 .map(QubitProp::native_instructions),
1169 instruction,
1170 ) {
1171 NativeInstructionSupport::Explicit(prop) => Some(prop.error_rate()),
1172 NativeInstructionSupport::Inherited => self.default_single_qubit_error,
1173 NativeInstructionSupport::Unsupported => None,
1174 }
1175 }
1176
1177 pub fn two_qubit_error(
1185 &self,
1186 control: PhysicalQubit,
1187 target: PhysicalQubit,
1188 instruction: &Instruction,
1189 ) -> Option<f64> {
1190 if !self.is_usable_qubit(control)
1191 || !self.is_usable_qubit(target)
1192 || !self.topology.supports_directed_coupling(control, target)
1193 {
1194 return None;
1195 }
1196
1197 if !matches!(instruction, Instruction::Standard(gate) if gate.num_qubits() == 2) {
1198 return None;
1199 }
1200 match self.resolve_native_instruction(
1201 self.edge_properties(control, target)
1202 .map(EdgeProp::native_instructions),
1203 instruction,
1204 ) {
1205 NativeInstructionSupport::Explicit(prop) => Some(prop.error_rate()),
1206 NativeInstructionSupport::Inherited => self.default_two_qubit_error,
1207 NativeInstructionSupport::Unsupported => None,
1208 }
1209 }
1210
1211 pub(crate) fn native_instruction_calibration(
1217 &self,
1218 instruction: &Instruction,
1219 qargs: &[PhysicalQubit],
1220 ) -> Option<NativeInstructionCalibration> {
1221 if !self.supports_native_instruction(instruction, qargs) {
1222 return None;
1223 }
1224
1225 let support = match qargs {
1226 [qubit] => self.resolve_native_instruction(
1227 self.qubit_properties
1228 .get(qubit)
1229 .map(QubitProp::native_instructions),
1230 instruction,
1231 ),
1232 [control, target] => self.resolve_native_instruction(
1233 self.edge_properties(*control, *target)
1234 .map(EdgeProp::native_instructions),
1235 instruction,
1236 ),
1237 _ => NativeInstructionSupport::Inherited,
1238 };
1239
1240 match support {
1241 NativeInstructionSupport::Explicit(prop) => Some(NativeInstructionCalibration {
1242 error_rate: Some(prop.error_rate()),
1243 duration: prop.length(),
1244 }),
1245 NativeInstructionSupport::Inherited => Some(NativeInstructionCalibration {
1246 error_rate: match qargs.len() {
1247 1 => self.default_single_qubit_error,
1248 2 => self.default_two_qubit_error,
1249 _ => None,
1250 },
1251 duration: None,
1252 }),
1253 NativeInstructionSupport::Unsupported => None,
1254 }
1255 }
1256
1257 pub fn edge_error(&self, control: PhysicalQubit, target: PhysicalQubit) -> Option<f64> {
1268 if !self.is_usable_qubit(control)
1269 || !self.is_usable_qubit(target)
1270 || !self.topology.supports_directed_coupling(control, target)
1271 {
1272 return None;
1273 }
1274
1275 self.edge_properties(control, target)
1276 .and_then(|props| {
1277 props
1278 .native_instructions()
1279 .iter()
1280 .map(InstructionProp::error_rate)
1281 .min_by(|a, b| a.total_cmp(b))
1282 })
1283 .or(self.default_two_qubit_error)
1284 }
1285
1286 pub fn get_t1(&self, qubit: PhysicalQubit) -> Option<f64> {
1290 self.qubit_properties
1291 .get(&qubit)
1292 .and_then(|p| p.t1)
1293 .or(self.default_t1)
1294 }
1295
1296 pub fn get_t2(&self, qubit: PhysicalQubit) -> Option<f64> {
1300 self.qubit_properties
1301 .get(&qubit)
1302 .and_then(|p| p.t2)
1303 .or(self.default_t2)
1304 }
1305
1306 pub fn get_readout_error(&self, qubit: PhysicalQubit) -> Option<f64> {
1310 self.qubit_properties
1311 .get(&qubit)
1312 .map(|p| p.readout_error)
1313 .or(self.default_readout_error)
1314 }
1315
1316 pub fn default_single_qubit_error(&self) -> Option<f64> {
1318 self.default_single_qubit_error
1319 }
1320
1321 pub fn default_two_qubit_error(&self) -> Option<f64> {
1323 self.default_two_qubit_error
1324 }
1325
1326 pub fn default_t1(&self) -> Option<f64> {
1328 self.default_t1
1329 }
1330
1331 pub fn default_t2(&self) -> Option<f64> {
1333 self.default_t2
1334 }
1335
1336 pub fn default_readout_error(&self) -> Option<f64> {
1338 self.default_readout_error
1339 }
1340
1341 pub fn calibration_time(&self) -> Option<OffsetDateTime> {
1343 self.calibration_time
1344 }
1345}
1346
1347#[cfg(test)]
1348#[path = "./device_test.rs"]
1349mod device_test;