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(
476 name: impl Into<String>,
477 physical_qubits: Vec<PhysicalQubit>,
478 ) -> Result<Self, DeviceError> {
479 let qubits = physical_qubits.iter().copied().collect::<HashSet<_>>();
480 let topology = Topology::line(physical_qubits).map_err(DeviceError::InvalidTopology)?;
481 Self::new(name, qubits, topology)
482 }
483
484 pub fn bidirectional_line(
489 name: impl Into<String>,
490 num_qubits: u32,
491 ) -> Result<Self, DeviceError> {
492 let edges = (0..num_qubits.saturating_sub(1))
493 .flat_map(|index| [(index, index + 1), (index + 1, index)])
494 .collect::<Vec<_>>();
495 Self::from_u32_edges(name, num_qubits, &edges)
496 }
497
498 pub fn ring(name: impl Into<String>, num_qubits: u32) -> Result<Self, DeviceError> {
504 let mut edges = BTreeSet::new();
505 if num_qubits >= 2 {
506 for index in 0..num_qubits {
507 let next = (index + 1) % num_qubits;
508 edges.insert((index, next));
509 edges.insert((next, index));
510 }
511 }
512 let edges = edges.into_iter().collect::<Vec<_>>();
513 Self::from_u32_edges(name, num_qubits, &edges)
514 }
515
516 pub fn star(
522 name: impl Into<String>,
523 num_qubits: u32,
524 center: u32,
525 ) -> Result<Self, DeviceError> {
526 let edges = (0..num_qubits)
527 .filter(|&index| index != center)
528 .flat_map(|index| [(center, index), (index, center)])
529 .collect::<Vec<_>>();
530 Self::from_u32_edges(name, num_qubits, &edges)
531 }
532
533 pub fn grid(name: impl Into<String>, rows: u32, cols: u32) -> Result<Self, DeviceError> {
538 let num_qubits = rows.saturating_mul(cols);
539 let mut edges = Vec::new();
540 for row in 0..rows {
541 for col in 0..cols {
542 let current = row * cols + col;
543 if col + 1 < cols {
544 edges.push((current, current + 1));
545 edges.push((current + 1, current));
546 }
547 if row + 1 < rows {
548 edges.push((current, current + cols));
549 edges.push((current + cols, current));
550 }
551 }
552 }
553 Self::from_u32_edges(name, num_qubits, &edges)
554 }
555
556 pub fn from_edges(
560 name: impl Into<String>,
561 num_qubits: u32,
562 edges: &[(u32, u32)],
563 ) -> Result<Self, DeviceError> {
564 Self::from_u32_edges(name, num_qubits, edges)
565 }
566
567 fn from_u32_edges(
568 name: impl Into<String>,
569 num_qubits: u32,
570 edges: &[(u32, u32)],
571 ) -> Result<Self, DeviceError> {
572 let physical_qubits = (0..num_qubits).map(PhysicalQubit::new).collect::<Vec<_>>();
573 let qubits = physical_qubits.iter().copied().collect::<HashSet<_>>();
574 let coupling_map = edges
575 .iter()
576 .enumerate()
577 .map(|(index, &(control, target))| {
578 (
579 PhysicalQubit::new(control),
580 PhysicalQubit::new(target),
581 format!("e{index}"),
582 )
583 })
584 .collect::<Vec<_>>();
585 let topology =
586 Topology::new(physical_qubits, coupling_map).map_err(DeviceError::InvalidTopology)?;
587 Self::new(name, qubits, topology)
588 }
589
590 pub fn with_invalid_qubits(
597 mut self,
598 invalid_qubits: HashSet<PhysicalQubit>,
599 ) -> Result<Self, DeviceError> {
600 self.set_invalid_qubits(invalid_qubits)?;
601 Ok(self)
602 }
603
604 pub fn set_invalid_qubits(
611 &mut self,
612 invalid_qubits: HashSet<PhysicalQubit>,
613 ) -> Result<(), DeviceError> {
614 for &qubit in &invalid_qubits {
615 if !self.qubits.contains(&qubit) {
616 return Err(DeviceError::QubitNotInDevice(qubit));
617 }
618 }
619 self.invalid_qubits = invalid_qubits.into_iter().collect();
620 Ok(())
621 }
622
623 pub fn with_native_gates(mut self, gates: Vec<Instruction>) -> Result<Self, DeviceError> {
633 self.set_native_gates(gates)?;
634 Ok(self)
635 }
636
637 pub fn set_native_gates(&mut self, gates: Vec<Instruction>) -> Result<(), DeviceError> {
646 for gate in &gates {
647 validate_native_gate_arity(gate, 0..=2)?;
648 }
649 self.native_gates = gates;
650 Ok(())
651 }
652
653 pub fn with_calibration_time(mut self, time: OffsetDateTime) -> Self {
655 self.calibration_time = Some(time);
656 self
657 }
658
659 pub fn set_calibration_time(&mut self, time: OffsetDateTime) {
660 self.calibration_time = Some(time);
661 }
662
663 pub fn with_default_t1(mut self, t1: f64) -> Self {
665 self.default_t1 = Some(t1);
666 self
667 }
668
669 pub fn set_default_t1(&mut self, t1: f64) {
670 self.default_t1 = Some(t1);
671 }
672
673 pub fn with_default_t2(mut self, t2: f64) -> Self {
675 self.default_t2 = Some(t2);
676 self
677 }
678
679 pub fn set_default_t2(&mut self, t2: f64) {
680 self.default_t2 = Some(t2);
681 }
682
683 pub fn with_default_readout_error(mut self, error: f64) -> Self {
685 self.default_readout_error = Some(error);
686 self
687 }
688
689 pub fn set_default_readout_error(&mut self, error: f64) {
690 self.default_readout_error = Some(error);
691 }
692
693 pub fn with_default_single_qubit_error(mut self, error: f64) -> Self {
695 self.default_single_qubit_error = Some(error);
696 self
697 }
698
699 pub fn set_default_single_qubit_error(&mut self, error: f64) {
700 self.default_single_qubit_error = Some(error);
701 }
702
703 pub fn with_default_two_qubit_error(mut self, error: f64) -> Self {
705 self.default_two_qubit_error = Some(error);
706 self
707 }
708
709 pub fn set_default_two_qubit_error(&mut self, error: f64) {
710 self.default_two_qubit_error = Some(error);
711 }
712
713 pub fn add_qubit_properties(
722 &mut self,
723 qubit: PhysicalQubit,
724 props: QubitProp,
725 ) -> Result<(), DeviceError> {
726 if !self.qubits.contains(&qubit) {
727 return Err(DeviceError::QubitNotInDevice(qubit));
728 }
729 if !self.topology.contains_qubit(&qubit) {
730 return Err(DeviceError::QubitNotInTopology(qubit));
731 }
732 self.qubit_properties.insert(qubit, props);
733 Ok(())
734 }
735
736 pub fn add_edge_properties(
742 &mut self,
743 control: PhysicalQubit,
744 target: PhysicalQubit,
745 props: EdgeProp,
746 ) -> Result<(), DeviceError> {
747 if !self.topology.supports_directed_coupling(control, target) {
748 return Err(DeviceError::EdgeNotInTopology(control, target));
749 }
750 self.edge_properties.insert((control, target), props);
751 Ok(())
752 }
753
754 fn instruction_matches(stored: &Instruction, requested: &Instruction) -> bool {
759 match (stored, requested) {
760 (Instruction::Standard(stored), Instruction::Standard(requested)) => {
761 stored == requested
762 }
763 _ => false,
764 }
765 }
766
767 fn resolve_native_instruction<'a>(
773 &'a self,
774 local: Option<&'a [InstructionProp]>,
775 instruction: &Instruction,
776 ) -> NativeInstructionSupport<'a> {
777 if let Some(local) = local.filter(|instructions| !instructions.is_empty()) {
778 return local
779 .iter()
780 .find(|prop| Self::instruction_matches(prop.instruction(), instruction))
781 .map_or(
782 NativeInstructionSupport::Unsupported,
783 NativeInstructionSupport::Explicit,
784 );
785 }
786
787 if self
788 .native_gates
789 .iter()
790 .any(|stored| Self::instruction_matches(stored, instruction))
791 {
792 NativeInstructionSupport::Inherited
793 } else {
794 NativeInstructionSupport::Unsupported
795 }
796 }
797
798 pub fn name(&self) -> &str {
800 &self.name
801 }
802
803 pub fn qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
805 self.qubits.iter().copied()
806 }
807
808 pub fn invalid_qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
810 self.invalid_qubits.iter().copied()
811 }
812
813 pub fn is_usable_qubit(&self, qubit: PhysicalQubit) -> bool {
815 self.qubits.contains(&qubit) && !self.invalid_qubits.contains(&qubit)
816 }
817
818 pub fn usable_qubits(&self) -> impl Iterator<Item = PhysicalQubit> + '_ {
820 self.qubits.difference(&self.invalid_qubits).copied()
821 }
822
823 pub fn num_usable_qubits(&self) -> usize {
825 self.qubits.len() - self.invalid_qubits.len()
826 }
827
828 pub fn topology(&self) -> &Topology {
830 &self.topology
831 }
832
833 pub fn native_gates(&self) -> &[Instruction] {
835 &self.native_gates
836 }
837
838 pub fn qubit_properties(&self, qubit: PhysicalQubit) -> Option<&QubitProp> {
840 self.qubit_properties.get(&qubit)
841 }
842
843 pub fn edge_properties(
845 &self,
846 control: PhysicalQubit,
847 target: PhysicalQubit,
848 ) -> Option<&EdgeProp> {
849 self.edge_properties.get(&(control, target))
850 }
851
852 pub fn supports_native_instruction(
862 &self,
863 instruction: &Instruction,
864 qargs: &[PhysicalQubit],
865 ) -> bool {
866 if qargs.iter().any(|qubit| !self.is_usable_qubit(*qubit)) {
867 return false;
868 }
869
870 match instruction {
871 Instruction::Standard(StandardGate::GPhase) if qargs.is_empty() => !matches!(
872 self.resolve_native_instruction(None, instruction),
873 NativeInstructionSupport::Unsupported
874 ),
875 Instruction::Standard(gate) if gate.num_qubits() == qargs.len() => match qargs {
876 [qubit] => !matches!(
877 self.resolve_native_instruction(
878 self.qubit_properties
879 .get(qubit)
880 .map(QubitProp::native_instructions),
881 instruction,
882 ),
883 NativeInstructionSupport::Unsupported
884 ),
885 [control, target]
886 if self.topology.supports_directed_coupling(*control, *target) =>
887 {
888 !matches!(
889 self.resolve_native_instruction(
890 self.edge_properties(*control, *target)
891 .map(EdgeProp::native_instructions),
892 instruction,
893 ),
894 NativeInstructionSupport::Unsupported
895 )
896 }
897 _ => false,
898 },
899 Instruction::Standard(_)
900 | Instruction::McGate(_)
901 | Instruction::UnitaryGate(_)
902 | Instruction::CircuitGate(_)
903 | Instruction::Directive(_)
904 | Instruction::ClassicalData(_)
905 | Instruction::ClassicalControl(_)
906 | Instruction::Delay => false,
907 }
908 }
909
910 pub fn validate_operation(&self, operation: &Operation) -> Result<(), DeviceValidationError> {
916 let qargs = self.validate_qargs(&operation.qubits)?;
917 match &operation.instruction {
918 Instruction::ClassicalControl(control) => match control {
919 ClassicalControlOp::If(op) => {
920 self.validate_control_body(op.then_body().operations())?;
921 if let Some(body) = op.else_body() {
922 self.validate_control_body(body.operations())?;
923 }
924 }
925 ClassicalControlOp::While(op) => {
926 self.validate_control_body(op.body().operations())?
927 }
928 ClassicalControlOp::For(op) => {
929 self.validate_control_body(op.body().operations())?
930 }
931 ClassicalControlOp::Switch(op) => {
932 for case in op.cases() {
933 self.validate_control_body(case.body().operations())?;
934 }
935 if let Some(body) = op.default() {
936 self.validate_control_body(body.operations())?;
937 }
938 }
939 ClassicalControlOp::Break | ClassicalControlOp::Continue => {}
940 },
941 instruction => self.validate_atomic_instruction(instruction, qargs)?,
942 }
943 Ok(())
944 }
945
946 pub fn validate_value_operation(
953 &self,
954 operation: &ValueOperation,
955 ) -> Result<(), DeviceValidationError> {
956 let qargs = self.validate_qargs(&operation.qubits)?;
957 match &operation.instruction {
958 ValueInstruction::Instruction(instruction) => {
959 self.validate_atomic_instruction(instruction, qargs)?
960 }
961 ValueInstruction::ClassicalControl(control) => match control {
962 ValueClassicalControlOp::If {
963 then_body,
964 else_body,
965 ..
966 } => {
967 self.validate_value_control_body(then_body.operations())?;
968 if let Some(body) = else_body {
969 self.validate_value_control_body(body.operations())?;
970 }
971 }
972 ValueClassicalControlOp::While { body, .. }
973 | ValueClassicalControlOp::For { body, .. } => {
974 self.validate_value_control_body(body.operations())?
975 }
976 ValueClassicalControlOp::Switch { cases, default, .. } => {
977 for case in cases {
978 self.validate_value_control_body(case.body.operations())?;
979 }
980 if let Some(body) = default {
981 self.validate_value_control_body(body.operations())?;
982 }
983 }
984 ValueClassicalControlOp::Break | ValueClassicalControlOp::Continue => {}
985 },
986 }
987 Ok(())
988 }
989
990 fn validate_qargs(
991 &self,
992 qubits: &[Qubit],
993 ) -> Result<Vec<PhysicalQubit>, DeviceValidationError> {
994 let qargs = qubits
995 .iter()
996 .copied()
997 .map(PhysicalQubit::from_qubit)
998 .collect::<Vec<_>>();
999 if let Some(qubit) = qargs
1002 .iter()
1003 .copied()
1004 .find(|qubit| !self.is_usable_qubit(*qubit))
1005 {
1006 return Err(DeviceValidationError::UnusablePhysicalQubit {
1007 device: self.name.clone(),
1008 qubit,
1009 });
1010 }
1011 Ok(qargs)
1012 }
1013
1014 fn validate_atomic_instruction(
1015 &self,
1016 instruction: &Instruction,
1017 qargs: Vec<PhysicalQubit>,
1018 ) -> Result<(), DeviceValidationError> {
1019 match instruction {
1020 Instruction::Directive(Directive::Measure | Directive::Reset) if qargs.len() == 1 => {}
1021 Instruction::Directive(Directive::Barrier) => {}
1022 Instruction::ClassicalData(op) => match op {
1023 crate::circuit::ClassicalDataOp::Store { .. } if qargs.is_empty() => {}
1024 crate::circuit::ClassicalDataOp::MeasureBit { .. } if qargs.len() == 1 => {}
1025 crate::circuit::ClassicalDataOp::MeasureBits { .. } if !qargs.is_empty() => {}
1026 _ => return Err(self.unsupported_instruction(instruction, qargs)),
1027 },
1028 Instruction::Delay if qargs.len() == 1 => {}
1029 Instruction::McGate(_) | Instruction::UnitaryGate(_) | Instruction::CircuitGate(_) => {
1030 return Err(DeviceValidationError::UndecomposedInstruction {
1031 device: self.name.clone(),
1032 instruction: instruction.to_string(),
1033 qargs,
1034 });
1035 }
1036 Instruction::Standard(gate) if gate.num_qubits() > 2 => {
1037 return Err(DeviceValidationError::UndecomposedInstruction {
1038 device: self.name.clone(),
1039 instruction: instruction.to_string(),
1040 qargs,
1041 });
1042 }
1043 Instruction::Standard(gate) if gate.num_qubits() == 2 => {
1044 let [control, target] = qargs.as_slice() else {
1045 return Err(self.unsupported_instruction(instruction, qargs));
1046 };
1047 if !self.topology.supports_directed_coupling(*control, *target) {
1048 return Err(DeviceValidationError::MissingDirectedCoupling {
1049 device: self.name.clone(),
1050 instruction: instruction.to_string(),
1051 control: *control,
1052 target: *target,
1053 });
1054 }
1055 if !self.supports_native_instruction(instruction, &qargs) {
1056 return Err(self.unsupported_instruction(instruction, qargs));
1057 }
1058 }
1059 Instruction::Standard(_) if self.supports_native_instruction(instruction, &qargs) => {}
1060 _ => return Err(self.unsupported_instruction(instruction, qargs)),
1061 }
1062 Ok(())
1063 }
1064
1065 pub fn validate_circuit(&self, circuit: &Circuit) -> Result<(), DeviceValidationError> {
1071 self.validate_operations(circuit.operations())
1072 }
1073
1074 fn validate_operations(&self, operations: &[Operation]) -> Result<(), DeviceValidationError> {
1075 for operation in operations {
1078 self.validate_operation(operation)?;
1079 }
1080 Ok(())
1081 }
1082
1083 fn validate_control_body(&self, operations: &[Operation]) -> Result<(), DeviceValidationError> {
1091 let operations = if operations.first().is_some_and(|operation| {
1092 matches!(
1093 operation.instruction,
1094 Instruction::Standard(StandardGate::GPhase)
1095 ) && operation.qubits.is_empty()
1096 }) {
1097 &operations[1..]
1098 } else {
1099 operations
1100 };
1101 self.validate_operations(operations)
1102 }
1103
1104 fn validate_value_control_body(
1105 &self,
1106 operations: &[ValueOperation],
1107 ) -> Result<(), DeviceValidationError> {
1108 let operations = if operations.first().is_some_and(|operation| {
1109 matches!(
1110 operation.instruction,
1111 ValueInstruction::Instruction(Instruction::Standard(StandardGate::GPhase))
1112 ) && operation.qubits.is_empty()
1113 }) {
1114 &operations[1..]
1115 } else {
1116 operations
1117 };
1118 for operation in operations {
1119 self.validate_value_operation(operation)?;
1120 }
1121 Ok(())
1122 }
1123
1124 fn unsupported_instruction(
1126 &self,
1127 instruction: &Instruction,
1128 qargs: Vec<PhysicalQubit>,
1129 ) -> DeviceValidationError {
1130 DeviceValidationError::UnsupportedInstruction {
1131 device: self.name.clone(),
1132 instruction: instruction.to_string(),
1133 qargs,
1134 }
1135 }
1136
1137 pub fn single_qubit_error(
1144 &self,
1145 qubit: PhysicalQubit,
1146 instruction: &Instruction,
1147 ) -> Option<f64> {
1148 if !self.is_usable_qubit(qubit) {
1149 return None;
1150 }
1151
1152 if !matches!(instruction, Instruction::Standard(gate) if gate.num_qubits() == 1) {
1153 return None;
1154 }
1155 match self.resolve_native_instruction(
1156 self.qubit_properties
1157 .get(&qubit)
1158 .map(QubitProp::native_instructions),
1159 instruction,
1160 ) {
1161 NativeInstructionSupport::Explicit(prop) => Some(prop.error_rate()),
1162 NativeInstructionSupport::Inherited => self.default_single_qubit_error,
1163 NativeInstructionSupport::Unsupported => None,
1164 }
1165 }
1166
1167 pub fn two_qubit_error(
1175 &self,
1176 control: PhysicalQubit,
1177 target: PhysicalQubit,
1178 instruction: &Instruction,
1179 ) -> Option<f64> {
1180 if !self.is_usable_qubit(control)
1181 || !self.is_usable_qubit(target)
1182 || !self.topology.supports_directed_coupling(control, target)
1183 {
1184 return None;
1185 }
1186
1187 if !matches!(instruction, Instruction::Standard(gate) if gate.num_qubits() == 2) {
1188 return None;
1189 }
1190 match self.resolve_native_instruction(
1191 self.edge_properties(control, target)
1192 .map(EdgeProp::native_instructions),
1193 instruction,
1194 ) {
1195 NativeInstructionSupport::Explicit(prop) => Some(prop.error_rate()),
1196 NativeInstructionSupport::Inherited => self.default_two_qubit_error,
1197 NativeInstructionSupport::Unsupported => None,
1198 }
1199 }
1200
1201 pub(crate) fn native_instruction_calibration(
1207 &self,
1208 instruction: &Instruction,
1209 qargs: &[PhysicalQubit],
1210 ) -> Option<NativeInstructionCalibration> {
1211 if !self.supports_native_instruction(instruction, qargs) {
1212 return None;
1213 }
1214
1215 let support = match qargs {
1216 [qubit] => self.resolve_native_instruction(
1217 self.qubit_properties
1218 .get(qubit)
1219 .map(QubitProp::native_instructions),
1220 instruction,
1221 ),
1222 [control, target] => self.resolve_native_instruction(
1223 self.edge_properties(*control, *target)
1224 .map(EdgeProp::native_instructions),
1225 instruction,
1226 ),
1227 _ => NativeInstructionSupport::Inherited,
1228 };
1229
1230 match support {
1231 NativeInstructionSupport::Explicit(prop) => Some(NativeInstructionCalibration {
1232 error_rate: Some(prop.error_rate()),
1233 duration: prop.length(),
1234 }),
1235 NativeInstructionSupport::Inherited => Some(NativeInstructionCalibration {
1236 error_rate: match qargs.len() {
1237 1 => self.default_single_qubit_error,
1238 2 => self.default_two_qubit_error,
1239 _ => None,
1240 },
1241 duration: None,
1242 }),
1243 NativeInstructionSupport::Unsupported => None,
1244 }
1245 }
1246
1247 pub fn edge_error(&self, control: PhysicalQubit, target: PhysicalQubit) -> Option<f64> {
1258 if !self.is_usable_qubit(control)
1259 || !self.is_usable_qubit(target)
1260 || !self.topology.supports_directed_coupling(control, target)
1261 {
1262 return None;
1263 }
1264
1265 self.edge_properties(control, target)
1266 .and_then(|props| {
1267 props
1268 .native_instructions()
1269 .iter()
1270 .map(InstructionProp::error_rate)
1271 .min_by(|a, b| a.total_cmp(b))
1272 })
1273 .or(self.default_two_qubit_error)
1274 }
1275
1276 pub fn get_t1(&self, qubit: PhysicalQubit) -> Option<f64> {
1280 self.qubit_properties
1281 .get(&qubit)
1282 .and_then(|p| p.t1)
1283 .or(self.default_t1)
1284 }
1285
1286 pub fn get_t2(&self, qubit: PhysicalQubit) -> Option<f64> {
1290 self.qubit_properties
1291 .get(&qubit)
1292 .and_then(|p| p.t2)
1293 .or(self.default_t2)
1294 }
1295
1296 pub fn get_readout_error(&self, qubit: PhysicalQubit) -> Option<f64> {
1300 self.qubit_properties
1301 .get(&qubit)
1302 .map(|p| p.readout_error)
1303 .or(self.default_readout_error)
1304 }
1305
1306 pub fn default_single_qubit_error(&self) -> Option<f64> {
1308 self.default_single_qubit_error
1309 }
1310
1311 pub fn default_two_qubit_error(&self) -> Option<f64> {
1313 self.default_two_qubit_error
1314 }
1315
1316 pub fn default_t1(&self) -> Option<f64> {
1318 self.default_t1
1319 }
1320
1321 pub fn default_t2(&self) -> Option<f64> {
1323 self.default_t2
1324 }
1325
1326 pub fn default_readout_error(&self) -> Option<f64> {
1328 self.default_readout_error
1329 }
1330
1331 pub fn calibration_time(&self) -> Option<OffsetDateTime> {
1333 self.calibration_time
1334 }
1335}
1336
1337#[cfg(test)]
1338#[path = "./device_test.rs"]
1339mod device_test;