1use std::collections::HashMap;
16use std::iter::FusedIterator;
17use std::ops::Range;
18
19use itertools::{Either, Itertools as _};
20#[cfg(feature = "stubs")]
21use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyclass_complex_enum, gen_stub_pymethods};
22
23use crate::instruction::{CalibrationIdentifier, MeasureCalibrationIdentifier};
24use crate::quil::Quil;
25use crate::{
26 expression::Expression,
27 instruction::{
28 CalibrationDefinition, Capture, Delay, Fence, FrameIdentifier, Gate, Instruction,
29 MeasureCalibrationDefinition, Measurement, Pulse, Qubit, RawCapture, SetFrequency,
30 SetPhase, SetScale, ShiftFrequency, ShiftPhase,
31 },
32};
33
34use super::source_map::{SourceMap, SourceMapEntry, SourceMapIndexable};
35use super::{CalibrationSet, InstructionIndex, ProgramError};
36
37#[cfg(not(feature = "python"))]
38use optipy::strip_pyo3;
39
40#[derive(Clone, Debug, Default, PartialEq)]
45#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
46#[cfg_attr(
47 feature = "python",
48 pyo3::pyclass(name = "CalibrationSet", module = "quil.program", eq, subclass)
49)]
50pub struct Calibrations {
51 pub calibrations: CalibrationSet<CalibrationDefinition>,
52 pub measure_calibrations: CalibrationSet<MeasureCalibrationDefinition>,
53}
54
55#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
56#[cfg_attr(feature = "python", pyo3::pymethods)]
57#[cfg_attr(not(feature = "python"), strip_pyo3)]
58impl Calibrations {
59 #[pyo3(name = "__len__")]
61 pub fn len(&self) -> usize {
62 self.calibrations.len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.calibrations.is_empty()
68 }
69
70 pub fn insert_calibration(
75 &mut self,
76 calibration: CalibrationDefinition,
77 ) -> Option<CalibrationDefinition> {
78 self.calibrations.replace(calibration)
79 }
80
81 pub fn insert_measurement_calibration(
86 &mut self,
87 calibration: MeasureCalibrationDefinition,
88 ) -> Option<MeasureCalibrationDefinition> {
89 self.measure_calibrations.replace(calibration)
90 }
91
92 pub fn extend(&mut self, other: Calibrations) {
97 self.calibrations.extend(other.calibrations);
98 self.measure_calibrations.extend(other.measure_calibrations);
99 }
100
101 pub fn to_instructions(&self) -> Vec<Instruction> {
103 self.iter_calibrations()
104 .cloned()
105 .map(Instruction::CalibrationDefinition)
106 .chain(
107 self.iter_measure_calibrations()
108 .cloned()
109 .map(Instruction::MeasureCalibrationDefinition),
110 )
111 .collect()
112 }
113}
114
115struct MatchedCalibration<'a> {
116 pub calibration: &'a CalibrationDefinition,
117 pub fixed_qubit_count: usize,
118}
119
120impl<'a> MatchedCalibration<'a> {
121 pub fn new(calibration: &'a CalibrationDefinition) -> Self {
122 Self {
123 calibration,
124 fixed_qubit_count: calibration
125 .identifier
126 .qubits
127 .iter()
128 .filter(|q| match q {
129 Qubit::Fixed(_) => true,
130 Qubit::Placeholder(_) | Qubit::Variable(_) => false,
131 })
132 .count(),
133 }
134 }
135}
136
137#[derive(Clone, Debug, PartialEq)]
139pub struct CalibrationExpansionOutput {
140 pub new_instructions: Vec<Instruction>,
142
143 pub detail: CalibrationExpansion,
145}
146
147#[derive(Clone, Debug, PartialEq)]
149#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
150#[cfg_attr(feature = "python", pyo3::pyclass(module = "quil.program", eq, frozen))]
151#[cfg_attr(not(feature = "python"), strip_pyo3)]
152pub struct CalibrationExpansion {
153 #[pyo3(get)]
155 pub(crate) calibration_used: CalibrationSource,
156
157 pub(crate) range: Range<InstructionIndex>,
159
160 pub(crate) expansions: SourceMap<InstructionIndex, CalibrationExpansion>,
162}
163
164impl CalibrationExpansion {
165 pub(crate) fn remove_target_index(&mut self, target_index: InstructionIndex) {
170 if self.range.start >= target_index {
172 self.range.start = self.range.start.map(|v| v.saturating_sub(1));
173 }
174
175 if self.range.end > target_index {
177 self.range.end = self.range.end.map(|v| v.saturating_sub(1));
178 }
179
180 if let Some(target_within_expansion) = target_index.0.checked_sub(self.range.start.0) {
184 self.expansions.entries.retain_mut(
185 |entry: &mut SourceMapEntry<InstructionIndex, CalibrationExpansion>| {
186 entry
187 .target_location
188 .remove_target_index(InstructionIndex(target_within_expansion));
189
190 !entry.target_location.range.is_empty()
191 },
192 );
193 }
194 }
195
196 pub fn calibration_used(&self) -> &CalibrationSource {
197 &self.calibration_used
198 }
199
200 pub fn range(&self) -> &Range<InstructionIndex> {
201 &self.range
202 }
203
204 pub fn expansions(&self) -> &SourceMap<InstructionIndex, CalibrationExpansion> {
205 &self.expansions
206 }
207}
208
209impl SourceMapIndexable<InstructionIndex> for CalibrationExpansion {
210 fn intersects(&self, other: &InstructionIndex) -> bool {
211 self.range.contains(other)
212 }
213}
214
215impl SourceMapIndexable<CalibrationSource> for CalibrationExpansion {
216 fn intersects(&self, other: &CalibrationSource) -> bool {
217 self.calibration_used() == other
218 }
219}
220
221#[derive(Clone, Debug, PartialEq)]
223#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
224#[cfg_attr(feature = "python", pyo3::pyclass(module = "quil.program", eq, frozen))]
225pub enum MaybeCalibrationExpansion {
226 Expanded(CalibrationExpansion),
228
229 Unexpanded(InstructionIndex),
231}
232
233impl SourceMapIndexable<InstructionIndex> for MaybeCalibrationExpansion {
234 fn intersects(&self, other: &InstructionIndex) -> bool {
235 match self {
236 MaybeCalibrationExpansion::Expanded(expansion) => expansion.intersects(other),
237 MaybeCalibrationExpansion::Unexpanded(index) => index == other,
238 }
239 }
240}
241
242impl SourceMapIndexable<CalibrationSource> for MaybeCalibrationExpansion {
243 fn intersects(&self, other: &CalibrationSource) -> bool {
244 match self {
245 MaybeCalibrationExpansion::Expanded(expansion) => expansion.intersects(other),
246 MaybeCalibrationExpansion::Unexpanded(_) => false,
247 }
248 }
249}
250
251#[derive(Clone, Debug, PartialEq)]
254#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
255#[cfg_attr(feature = "python", pyo3::pyclass(module = "quil.program", eq, frozen))]
256pub enum CalibrationSource {
257 Calibration(CalibrationIdentifier),
259
260 MeasureCalibration(MeasureCalibrationIdentifier),
262}
263
264impl From<CalibrationIdentifier> for CalibrationSource {
265 fn from(value: CalibrationIdentifier) -> Self {
266 Self::Calibration(value)
267 }
268}
269
270impl From<MeasureCalibrationIdentifier> for CalibrationSource {
271 fn from(value: MeasureCalibrationIdentifier) -> Self {
272 Self::MeasureCalibration(value)
273 }
274}
275
276impl Calibrations {
277 pub fn iter_calibrations(
279 &self,
280 ) -> impl DoubleEndedIterator<Item = &CalibrationDefinition> + FusedIterator {
281 self.calibrations.iter()
282 }
283
284 pub fn iter_measure_calibrations(
286 &self,
287 ) -> impl DoubleEndedIterator<Item = &MeasureCalibrationDefinition> + FusedIterator {
288 self.measure_calibrations.iter()
289 }
290
291 pub fn expand(
298 &self,
299 instruction: &Instruction,
300 previous_calibrations: &[Instruction],
301 ) -> Result<Option<Vec<Instruction>>, ProgramError> {
302 self.expand_inner(instruction, previous_calibrations, false)
303 .map(|expansion| expansion.map(|expansion| expansion.new_instructions))
304 }
305
306 pub fn expand_with_detail(
312 &self,
313 instruction: &Instruction,
314 previous_calibrations: &[Instruction],
315 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
316 self.expand_inner(instruction, previous_calibrations, true)
317 }
318
319 fn expand_inner(
328 &self,
329 instruction: &Instruction,
330 previous_calibrations: &[Instruction],
331 build_source_map: bool,
332 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
333 if previous_calibrations.contains(instruction) {
334 return Err(ProgramError::RecursiveCalibration(instruction.clone()));
335 }
336 let expansion_result = match instruction {
337 Instruction::Gate(gate) => {
338 let matching_calibration = self.get_match_for_gate(gate);
339
340 match matching_calibration {
341 Some(calibration) => {
342 let mut qubit_expansions: HashMap<&String, Qubit> = HashMap::new();
343 for (index, calibration_qubit) in
344 calibration.identifier.qubits.iter().enumerate()
345 {
346 if let Qubit::Variable(identifier) = calibration_qubit {
347 qubit_expansions.insert(identifier, gate.qubits[index].clone());
348 }
349 }
350
351 let variable_expansions: HashMap<String, Expression> = calibration
354 .identifier
355 .parameters
356 .iter()
357 .zip(gate.parameters.iter())
358 .filter_map(|(calibration_expression, gate_expression)| {
359 if let Expression::Variable(variable_name) = calibration_expression
360 {
361 Some((variable_name.clone(), gate_expression.clone()))
362 } else {
363 None
364 }
365 })
366 .collect();
367
368 let mut instructions = calibration.instructions.clone();
369
370 for instruction in instructions.iter_mut() {
371 match instruction {
372 Instruction::Gate(Gate { qubits, .. })
373 | Instruction::Delay(Delay { qubits, .. })
374 | Instruction::Capture(Capture {
375 frame: FrameIdentifier { qubits, .. },
376 ..
377 })
378 | Instruction::RawCapture(RawCapture {
379 frame: FrameIdentifier { qubits, .. },
380 ..
381 })
382 | Instruction::SetFrequency(SetFrequency {
383 frame: FrameIdentifier { qubits, .. },
384 ..
385 })
386 | Instruction::SetPhase(SetPhase {
387 frame: FrameIdentifier { qubits, .. },
388 ..
389 })
390 | Instruction::SetScale(SetScale {
391 frame: FrameIdentifier { qubits, .. },
392 ..
393 })
394 | Instruction::ShiftFrequency(ShiftFrequency {
395 frame: FrameIdentifier { qubits, .. },
396 ..
397 })
398 | Instruction::ShiftPhase(ShiftPhase {
399 frame: FrameIdentifier { qubits, .. },
400 ..
401 })
402 | Instruction::Pulse(Pulse {
403 frame: FrameIdentifier { qubits, .. },
404 ..
405 })
406 | Instruction::Fence(Fence { qubits }) => {
407 for qubit in qubits {
409 match qubit {
410 Qubit::Variable(name) => {
411 if let Some(expansion) = qubit_expansions.get(name)
412 {
413 *qubit = expansion.clone();
414 }
415 }
416 Qubit::Fixed(_) | Qubit::Placeholder(_) => {}
417 }
418 }
419 }
420 _ => {}
421 }
422
423 instruction.apply_to_expressions(|expr| {
424 *expr = expr.substitute_variables(&variable_expansions);
425 })
426 }
427
428 Some((
429 instructions,
430 CalibrationSource::Calibration(calibration.identifier.clone()),
431 ))
432 }
433 None => None,
434 }
435 }
436 Instruction::Measurement(measurement) => {
437 let matching_calibration = self.get_match_for_measurement(measurement);
438
439 match matching_calibration {
440 Some(calibration) => {
441 let mut instructions = calibration.instructions.clone();
442 for instruction in instructions.iter_mut() {
443 match instruction {
444 Instruction::Pragma(pragma) => {
445 if pragma.name == "LOAD-MEMORY"
446 && pragma.data == calibration.identifier.target
447 {
448 if let Some(target) = &measurement.target {
449 pragma.data = Some(target.to_quil_or_debug())
450 }
451 }
452 }
453 Instruction::Capture(capture) => {
454 if let Some(target) = &measurement.target {
455 capture.memory_reference = target.clone()
456 }
457 }
458 _ => {}
459 }
460 }
461 Some((
462 instructions,
463 CalibrationSource::MeasureCalibration(calibration.identifier.clone()),
464 ))
465 }
466 None => None,
467 }
468 }
469 _ => None,
470 };
471
472 let mut calibration_path = Vec::with_capacity(previous_calibrations.len() + 1);
474 calibration_path.push(instruction.clone());
475 calibration_path.extend_from_slice(previous_calibrations);
476
477 self.recursively_expand_inner(expansion_result, &calibration_path, build_source_map)
478 }
479
480 fn recursively_expand_inner(
481 &self,
482 expansion_result: Option<(Vec<Instruction>, CalibrationSource)>,
483 calibration_path: &[Instruction],
484 build_source_map: bool,
485 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
486 Ok(match expansion_result {
487 Some((instructions, matched_calibration)) => {
488 let mut recursively_expanded_instructions = CalibrationExpansionOutput {
489 new_instructions: Vec::new(),
490 detail: CalibrationExpansion {
491 calibration_used: matched_calibration,
492 range: InstructionIndex(0)..InstructionIndex(0),
493 expansions: SourceMap::default(),
494 },
495 };
496
497 for (expanded_index, instruction) in instructions.into_iter().enumerate() {
498 let expanded_instructions =
499 self.expand_inner(&instruction, calibration_path, build_source_map)?;
500 match expanded_instructions {
501 Some(mut output) => {
502 if build_source_map {
503 let range_start = InstructionIndex(
504 recursively_expanded_instructions.new_instructions.len(),
505 );
506
507 recursively_expanded_instructions
508 .new_instructions
509 .extend(output.new_instructions);
510
511 let range_end = InstructionIndex(
512 recursively_expanded_instructions.new_instructions.len(),
513 );
514 output.detail.range = range_start..range_end;
515
516 recursively_expanded_instructions
517 .detail
518 .expansions
519 .entries
520 .push(SourceMapEntry {
521 source_location: InstructionIndex(expanded_index),
522 target_location: output.detail,
523 });
524 } else {
525 recursively_expanded_instructions
526 .new_instructions
527 .extend(output.new_instructions);
528 }
529 }
530 None => {
531 recursively_expanded_instructions
532 .new_instructions
533 .push(instruction);
534 }
535 };
536 }
537
538 if build_source_map {
539 recursively_expanded_instructions.detail.range = InstructionIndex(0)
542 ..InstructionIndex(
543 recursively_expanded_instructions.new_instructions.len(),
544 );
545 }
546
547 Some(recursively_expanded_instructions)
548 }
549 None => None,
550 })
551 }
552
553 pub fn get_match_for_measurement(
564 &self,
565 measurement: &Measurement,
566 ) -> Option<&MeasureCalibrationDefinition> {
567 struct First<T>(Option<T>);
569
570 impl<T> Default for First<T> {
571 fn default() -> Self {
572 Self(None)
573 }
574 }
575
576 impl<A> Extend<A> for First<A> {
577 fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
578 if self.0.is_none() {
579 self.0 = iter.into_iter().next()
580 }
581 }
582 }
583
584 let Measurement {
585 name,
586 qubit,
587 target,
588 } = measurement;
589
590 let (First(exact), First(wildcard)) = self
593 .iter_measure_calibrations()
594 .rev()
595 .filter_map(|calibration| {
596 let identifier = &calibration.identifier;
597
598 if !(name == &identifier.name && target.is_some() == identifier.target.is_some()) {
599 return None;
600 }
601
602 match &identifier.qubit {
603 fixed @ Qubit::Fixed(_) if qubit == fixed => Some((calibration, true)),
604 Qubit::Variable(_) => Some((calibration, false)),
605 Qubit::Fixed(_) | Qubit::Placeholder(_) => None,
606 }
607 })
608 .partition_map(|(calibration, exact)| {
609 if exact {
610 Either::Left(calibration)
611 } else {
612 Either::Right(calibration)
613 }
614 });
615
616 exact.or(wildcard)
617 }
618
619 pub fn get_match_for_gate(&self, gate: &Gate) -> Option<&CalibrationDefinition> {
629 let mut matched_calibration: Option<MatchedCalibration> = None;
630
631 for calibration in self
632 .iter_calibrations()
633 .filter(|calibration| calibration.identifier.matches(gate))
634 {
635 matched_calibration = match matched_calibration {
636 None => Some(MatchedCalibration::new(calibration)),
637 Some(previous_match) => {
638 let potential_match = MatchedCalibration::new(calibration);
639 if potential_match.fixed_qubit_count >= previous_match.fixed_qubit_count {
640 Some(potential_match)
641 } else {
642 Some(previous_match)
643 }
644 }
645 }
646 }
647
648 matched_calibration.map(|m| m.calibration)
649 }
650
651 pub fn into_instructions(self) -> Vec<Instruction> {
653 self.calibrations
654 .into_iter()
655 .map(Instruction::CalibrationDefinition)
656 .chain(
657 self.measure_calibrations
658 .into_iter()
659 .map(Instruction::MeasureCalibrationDefinition),
660 )
661 .collect()
662 }
663}
664
665#[cfg(test)]
666mod tests {
667 use std::str::FromStr;
668
669 use crate::program::calibration::{CalibrationSource, MeasureCalibrationIdentifier};
670 use crate::program::source_map::{SourceMap, SourceMapEntry};
671 use crate::program::{InstructionIndex, Program};
672 use crate::quil::Quil;
673
674 use insta::assert_snapshot;
675 use rstest::rstest;
676
677 use super::{CalibrationExpansion, CalibrationExpansionOutput, CalibrationIdentifier};
678
679 #[rstest]
680 #[case(
681 "Calibration-Param-Precedence",
682 concat!(
683 "DEFCAL RX(%theta) %qubit:\n",
684 " PULSE 1 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
685 "DEFCAL RX(%theta) 0:\n",
686 " PULSE 2 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
687 "DEFCAL RX(pi/2) 0:\n",
688 " PULSE 3 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
689 "RX(pi/2) 1\n",
690 "RX(pi) 0\n",
691 "RX(pi/2) 0\n"
692 ),
693 )]
694 #[case(
695 "Calibration-Simple",
696 concat!(
697 "DEFCAL X 0:\n",
698 " PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
699 "X 0\n",
700 ),
701 )]
702 #[case(
703 "Calibration-Literal-Parameter",
704 concat!(
705 "DEFCAL RX(3.141592653589793) 0:\n",
706 " NOP\n",
707 "RX(3.141592653589793) 0\n",
708 ),
709 )]
710 #[case(
711 "Calibration-Instruction-Match",
712 concat!(
713 "DEFCAL X 0:\n",
714 " Y 0\n",
715 "DEFCAL Y 0:\n",
716 " PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
717 "X 0\n"
718 ),
719 )]
720 #[case(
721 "Measure-Calibration",
722 concat!(
723 "DEFCAL MEASURE 0 addr:\n",
724 " PRAGMA INCORRECT_ORDERING\n",
725 "DEFCAL MEASURE 0 addr:\n",
726 " PRAGMA CORRECT\n",
727 "DEFCAL MEASURE q addr:\n",
728 " PRAGMA INCORRECT_PRECEDENCE\n",
729 "DEFCAL MEASURE 1 addr:\n",
730 " PRAGMA INCORRECT_QUBIT\n",
731 "DEFCAL MEASURE q:\n",
732 " PRAGMA INCORRECT_RECORD_VS_EFFECT\n",
733 "MEASURE 0 ro\n",
734 ),
735 )]
736 #[case(
737 "Calibration-Variable-Qubit",
738 concat!("DEFCAL I %q:\n", " DELAY q 4e-8\n", "I 0\n",),
739 )]
740 #[case(
741 "Precedence-Fixed-Match",
742 concat!(
743 "DEFCAL MEASURE q:\n",
744 " PRAGMA INCORRECT_RECORD_VS_EFFECT\n",
745 "DEFCAL MEASURE b addr:\n",
746 " PRAGMA INCORRECT_PRECEDENCE\n",
747 "DEFCAL MEASURE 0 addr:\n",
748 " PRAGMA INCORRECT_ORDER\n",
749 "DEFCAL MEASURE 0 addr:\n",
750 " PRAGMA CORRECT\n",
751 "DEFCAL MEASURE q addr:\n",
752 " PRAGMA INCORRECT_PRECEDENCE\n",
753 "DEFCAL MEASURE 1 addr:\n",
754 " PRAGMA INCORRECT_QUBIT\n",
755 "MEASURE 0 ro\n",
756 )
757 )]
758 #[case(
759 "Precedence-Variable-Match",
760 concat!(
761 "DEFCAL MEASURE q:\n",
762 " PRAGMA INCORRECT_RECORD_VS_EFFECT\n",
763 "DEFCAL MEASURE b addr:\n",
764 " PRAGMA INCORRECT_ORDER\n",
765 "DEFCAL MEASURE q addr:\n",
766 " PRAGMA CORRECT\n",
767 "MEASURE 0 ro\n",
768 )
769 )]
770 #[case(
771 "Precedence-No-Target-Match",
772 concat!(
773 "DEFCAL MEASURE b:\n",
774 " PRAGMA INCORRECT_SAME_NAME_SHOULD_NOT_APPEAR_IN_OUTPUT\n",
775 "DEFCAL MEASURE b:\n",
776 " PRAGMA INCORRECT_ORDER\n",
777 "DEFCAL MEASURE q:\n",
778 " PRAGMA CORRECT\n",
779 "MEASURE 0\n",
780 )
781 )]
782 #[case(
783 "Precedence-Prefer-Exact-Qubit-Match",
784 concat!(
785 "DEFCAL MEASURE 0 addr:\n",
786 " PRAGMA CORRECT\n",
787 "DEFCAL MEASURE q addr:\n",
788 " PRAGMA INCORRECT_PRECEDENCE\n",
789 "MEASURE 0 ro\n",
790 )
791 )]
792 #[case(
793 "Precedence-Prefer-Exact-Qubit-Match-No-Target",
794 concat!(
795 "DEFCAL MEASURE 0:\n",
796 " PRAGMA CORRECT\n",
797 "DEFCAL MEASURE q:\n",
798 " PRAGMA INCORRECT_PRECEDENCE\n",
799 "MEASURE 0\n",
800 )
801 )]
802 #[case(
803 "Precedence-Require-No-Name-Match",
804 concat!(
805 "DEFCAL MEASURE q addr:\n",
806 " PRAGMA CORRECT\n",
807 "DEFCAL MEASURE!wrong-name 0 addr:\n",
808 " PRAGMA INCORRECT_NAME\n",
809 "DEFCAL MEASURE!midcircuit 0 addr:\n",
810 " PRAGMA INCORRECT_NAME\n",
811 "MEASURE 0 ro\n",
812 )
813 )]
814 #[case(
815 "Precedence-Require-Name-Match",
816 concat!(
817 "DEFCAL MEASURE!midcircuit q addr:\n",
818 " PRAGMA CORRECT\n",
819 "DEFCAL MEASURE!wrong-name 0 addr:\n",
820 " PRAGMA INCORRECT_NAME\n",
821 "DEFCAL MEASURE 0 addr:\n",
822 " PRAGMA INCORRECT_NAME\n",
823 "MEASURE!midcircuit 0 ro\n",
824 )
825 )]
826 #[case(
827 "ShiftPhase",
828 concat!(
829 "DEFCAL RZ(%theta) %q:\n",
830 " SHIFT-PHASE %q \"rf\" -%theta\n",
831 "RZ(pi) 0\n",
832 )
833 )]
834 #[case(
835 "FenceVariableQubit",
836 concat!(
837 "DEFCAL FENCES q0 q1:\n",
838 " FENCE q0\n",
839 " FENCE q1\n",
840 "FENCES 0 1\n",
841 )
842 )]
843 fn test_expansion(#[case] description: &str, #[case] input: &str) {
844 let program = Program::from_str(input).unwrap();
845 let calibrated_program = program.expand_calibrations().unwrap();
846 insta::with_settings!({
847 snapshot_suffix => description,
848 }, {
849 assert_snapshot!(calibrated_program.to_quil_or_debug())
850 })
851 }
852
853 #[test]
855 fn expand_with_detail_recursive() {
856 let input = r#"
857DEFCAL X 0:
858 Y 0
859 MEASURE 0 ro
860 Y 0
861
862DEFCAL Y 0:
863 NOP
864 Z 0
865
866DEFCAL Z 0:
867 WAIT
868
869DEFCAL MEASURE 0 addr:
870 HALT
871
872X 0
873"#;
874
875 let program = Program::from_str(input).unwrap();
876 let instruction = program.instructions.last().unwrap();
877 let expansion = program
878 .calibrations
879 .expand_with_detail(instruction, &[])
880 .unwrap();
881 let expected = CalibrationExpansionOutput {
882 new_instructions: vec![
883 crate::instruction::Instruction::Nop(),
884 crate::instruction::Instruction::Wait(),
885 crate::instruction::Instruction::Halt(),
886 crate::instruction::Instruction::Nop(),
887 crate::instruction::Instruction::Wait(),
888 ],
889 detail: CalibrationExpansion {
890 calibration_used: CalibrationSource::Calibration(CalibrationIdentifier {
891 modifiers: vec![],
892 name: "X".to_string(),
893 parameters: vec![],
894 qubits: vec![crate::instruction::Qubit::Fixed(0)],
895 }),
896 range: InstructionIndex(0)..InstructionIndex(5),
897 expansions: SourceMap {
898 entries: vec![
899 SourceMapEntry {
900 source_location: InstructionIndex(0),
901 target_location: CalibrationExpansion {
902 calibration_used: CalibrationSource::Calibration(
903 CalibrationIdentifier {
904 modifiers: vec![],
905 name: "Y".to_string(),
906 parameters: vec![],
907 qubits: vec![crate::instruction::Qubit::Fixed(0)],
908 },
909 ),
910 range: InstructionIndex(0)..InstructionIndex(2),
911 expansions: SourceMap {
912 entries: vec![SourceMapEntry {
913 source_location: InstructionIndex(1),
914 target_location: CalibrationExpansion {
915 calibration_used: CalibrationSource::Calibration(
916 CalibrationIdentifier {
917 modifiers: vec![],
918 name: "Z".to_string(),
919 parameters: vec![],
920 qubits: vec![crate::instruction::Qubit::Fixed(
921 0,
922 )],
923 },
924 ),
925 range: InstructionIndex(1)..InstructionIndex(2),
926 expansions: SourceMap::default(),
927 },
928 }],
929 },
930 },
931 },
932 SourceMapEntry {
933 source_location: InstructionIndex(1),
934 target_location: CalibrationExpansion {
935 calibration_used: CalibrationSource::MeasureCalibration(
936 MeasureCalibrationIdentifier {
937 name: None,
938 qubit: crate::instruction::Qubit::Fixed(0),
939 target: Some("addr".to_string()),
940 },
941 ),
942 range: InstructionIndex(2)..InstructionIndex(3),
943 expansions: SourceMap::default(),
944 },
945 },
946 SourceMapEntry {
947 source_location: InstructionIndex(2),
948 target_location: CalibrationExpansion {
949 calibration_used: CalibrationSource::Calibration(
950 CalibrationIdentifier {
951 modifiers: vec![],
952 name: "Y".to_string(),
953 parameters: vec![],
954 qubits: vec![crate::instruction::Qubit::Fixed(0)],
955 },
956 ),
957 range: InstructionIndex(3)..InstructionIndex(5),
958 expansions: SourceMap {
959 entries: vec![SourceMapEntry {
960 source_location: InstructionIndex(1),
961 target_location: CalibrationExpansion {
962 calibration_used: CalibrationSource::Calibration(
963 CalibrationIdentifier {
964 modifiers: vec![],
965 name: "Z".to_string(),
966 parameters: vec![],
967 qubits: vec![crate::instruction::Qubit::Fixed(
968 0,
969 )],
970 },
971 ),
972 range: InstructionIndex(1)..InstructionIndex(2),
973 expansions: SourceMap::default(),
974 },
975 }],
976 },
977 },
978 },
979 ],
980 },
981 },
982 };
983
984 pretty_assertions::assert_eq!(expansion, Some(expected));
985 }
986
987 #[test]
988 fn test_eq() {
989 let input = "DEFCAL X 0:
990 PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
991X 0";
992 let a = Program::from_str(input);
993 let b = Program::from_str(input);
994 assert_eq!(a, b);
995 }
996
997 #[test]
998 fn test_ne() {
999 let input_a = "DEFCAL X 0:
1000 PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
1001X 0";
1002 let input_b = "DEFCAL X 1:
1003 PULSE 1 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
1004X 1";
1005 let a = Program::from_str(input_a);
1006 let b = Program::from_str(input_b);
1007 assert_ne!(a, b);
1008 }
1009}