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::{ExpansionResult, 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, ExpansionResult<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<
186 InstructionIndex,
187 ExpansionResult<CalibrationExpansion>,
188 >| {
189 if let ExpansionResult::Rewritten(ref mut expansion) = entry.target_location {
190 expansion.remove_target_index(InstructionIndex(target_within_expansion));
191 !expansion.range.is_empty()
192 } else {
193 true
194 }
195 },
196 );
197 }
198 }
199
200 pub fn calibration_used(&self) -> &CalibrationSource {
201 &self.calibration_used
202 }
203
204 pub fn range(&self) -> &Range<InstructionIndex> {
205 &self.range
206 }
207
208 pub fn expansions(
209 &self,
210 ) -> &SourceMap<InstructionIndex, ExpansionResult<CalibrationExpansion>> {
211 &self.expansions
212 }
213}
214
215impl SourceMapIndexable<InstructionIndex> for CalibrationExpansion {
216 fn contains(&self, other: &InstructionIndex) -> bool {
217 self.range.contains(other)
218 }
219}
220
221impl SourceMapIndexable<CalibrationSource> for CalibrationExpansion {
222 fn contains(&self, other: &CalibrationSource) -> bool {
223 self.calibration_used() == other
224 }
225}
226
227#[derive(Clone, Debug, PartialEq)]
230#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
231#[cfg_attr(feature = "python", pyo3::pyclass(module = "quil.program", eq, frozen))]
232pub enum CalibrationSource {
233 Calibration(CalibrationIdentifier),
235
236 MeasureCalibration(MeasureCalibrationIdentifier),
238}
239
240impl From<CalibrationIdentifier> for CalibrationSource {
241 fn from(value: CalibrationIdentifier) -> Self {
242 Self::Calibration(value)
243 }
244}
245
246impl From<MeasureCalibrationIdentifier> for CalibrationSource {
247 fn from(value: MeasureCalibrationIdentifier) -> Self {
248 Self::MeasureCalibration(value)
249 }
250}
251
252impl Calibrations {
253 pub fn iter_calibrations(
255 &self,
256 ) -> impl DoubleEndedIterator<Item = &CalibrationDefinition> + FusedIterator {
257 self.calibrations.iter()
258 }
259
260 pub fn iter_measure_calibrations(
262 &self,
263 ) -> impl DoubleEndedIterator<Item = &MeasureCalibrationDefinition> + FusedIterator {
264 self.measure_calibrations.iter()
265 }
266
267 pub fn expand(
274 &self,
275 instruction: &Instruction,
276 previous_calibrations: &[Instruction],
277 ) -> Result<Option<Vec<Instruction>>, ProgramError> {
278 self.expand_inner(instruction, previous_calibrations, false)
279 .map(|expansion| expansion.map(|expansion| expansion.new_instructions))
280 }
281
282 pub fn expand_with_detail(
288 &self,
289 instruction: &Instruction,
290 previous_calibrations: &[Instruction],
291 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
292 self.expand_inner(instruction, previous_calibrations, true)
293 }
294
295 fn expand_inner(
304 &self,
305 instruction: &Instruction,
306 previous_calibrations: &[Instruction],
307 build_source_map: bool,
308 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
309 if previous_calibrations.contains(instruction) {
310 return Err(ProgramError::RecursiveCalibration(instruction.clone()));
311 }
312 let expansion_result = match instruction {
313 Instruction::Gate(gate) => {
314 let matching_calibration = self.get_match_for_gate(gate);
315
316 match matching_calibration {
317 Some(calibration) => {
318 let mut qubit_expansions: HashMap<&String, Qubit> = HashMap::new();
319 for (index, calibration_qubit) in
320 calibration.identifier.qubits.iter().enumerate()
321 {
322 if let Qubit::Variable(identifier) = calibration_qubit {
323 qubit_expansions.insert(identifier, gate.qubits[index].clone());
324 }
325 }
326
327 let variable_expansions: HashMap<String, Expression> = calibration
330 .identifier
331 .parameters
332 .iter()
333 .zip(gate.parameters.iter())
334 .filter_map(|(calibration_expression, gate_expression)| {
335 if let Expression::Variable(variable_name) = calibration_expression
336 {
337 Some((variable_name.clone(), gate_expression.clone()))
338 } else {
339 None
340 }
341 })
342 .collect();
343
344 let mut instructions = calibration.instructions.clone();
345
346 for instruction in instructions.iter_mut() {
347 match instruction {
348 Instruction::Gate(Gate { qubits, .. })
349 | Instruction::Delay(Delay { qubits, .. })
350 | Instruction::Capture(Capture {
351 frame: FrameIdentifier { qubits, .. },
352 ..
353 })
354 | Instruction::RawCapture(RawCapture {
355 frame: FrameIdentifier { qubits, .. },
356 ..
357 })
358 | Instruction::SetFrequency(SetFrequency {
359 frame: FrameIdentifier { qubits, .. },
360 ..
361 })
362 | Instruction::SetPhase(SetPhase {
363 frame: FrameIdentifier { qubits, .. },
364 ..
365 })
366 | Instruction::SetScale(SetScale {
367 frame: FrameIdentifier { qubits, .. },
368 ..
369 })
370 | Instruction::ShiftFrequency(ShiftFrequency {
371 frame: FrameIdentifier { qubits, .. },
372 ..
373 })
374 | Instruction::ShiftPhase(ShiftPhase {
375 frame: FrameIdentifier { qubits, .. },
376 ..
377 })
378 | Instruction::Pulse(Pulse {
379 frame: FrameIdentifier { qubits, .. },
380 ..
381 })
382 | Instruction::Fence(Fence { qubits }) => {
383 for qubit in qubits {
385 match qubit {
386 Qubit::Variable(name) => {
387 if let Some(expansion) = qubit_expansions.get(name)
388 {
389 *qubit = expansion.clone();
390 }
391 }
392 Qubit::Fixed(_) | Qubit::Placeholder(_) => {}
393 }
394 }
395 }
396 _ => {}
397 }
398
399 instruction.apply_to_expressions(|expr| {
400 *expr = expr.substitute_variables(&variable_expansions);
401 })
402 }
403
404 Some((
405 instructions,
406 CalibrationSource::Calibration(calibration.identifier.clone()),
407 ))
408 }
409 None => None,
410 }
411 }
412 Instruction::Measurement(measurement) => {
413 let matching_calibration = self.get_match_for_measurement(measurement);
414
415 match matching_calibration {
416 Some(calibration) => {
417 let mut instructions = calibration.instructions.clone();
418 for instruction in instructions.iter_mut() {
419 match instruction {
420 Instruction::Pragma(pragma) => {
421 if pragma.name == "LOAD-MEMORY"
422 && pragma.data == calibration.identifier.target
423 {
424 if let Some(target) = &measurement.target {
425 pragma.data = Some(target.to_quil_or_debug())
426 }
427 }
428 }
429 Instruction::Capture(capture) => {
430 if let Some(target) = &measurement.target {
431 capture.memory_reference = target.clone()
432 }
433 }
434 _ => {}
435 }
436 }
437 Some((
438 instructions,
439 CalibrationSource::MeasureCalibration(calibration.identifier.clone()),
440 ))
441 }
442 None => None,
443 }
444 }
445 _ => None,
446 };
447
448 let mut calibration_path = Vec::with_capacity(previous_calibrations.len() + 1);
450 calibration_path.push(instruction.clone());
451 calibration_path.extend_from_slice(previous_calibrations);
452
453 self.recursively_expand_inner(expansion_result, &calibration_path, build_source_map)
454 }
455
456 fn recursively_expand_inner(
457 &self,
458 expansion_result: Option<(Vec<Instruction>, CalibrationSource)>,
459 calibration_path: &[Instruction],
460 build_source_map: bool,
461 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
462 Ok(match expansion_result {
463 Some((instructions, matched_calibration)) => {
464 let mut recursively_expanded_instructions = CalibrationExpansionOutput {
465 new_instructions: Vec::new(),
466 detail: CalibrationExpansion {
467 calibration_used: matched_calibration,
468 range: InstructionIndex(0)..InstructionIndex(0),
469 expansions: SourceMap::default(),
470 },
471 };
472
473 for (expanded_index, instruction) in instructions.into_iter().enumerate() {
474 let expanded_instructions =
475 self.expand_inner(&instruction, calibration_path, build_source_map)?;
476 match expanded_instructions {
477 Some(mut output) => {
478 if build_source_map {
479 let range_start = InstructionIndex(
480 recursively_expanded_instructions.new_instructions.len(),
481 );
482
483 recursively_expanded_instructions
484 .new_instructions
485 .extend(output.new_instructions);
486
487 let range_end = InstructionIndex(
488 recursively_expanded_instructions.new_instructions.len(),
489 );
490 output.detail.range = range_start..range_end;
491
492 recursively_expanded_instructions
493 .detail
494 .expansions
495 .entries
496 .push(SourceMapEntry {
497 source_location: InstructionIndex(expanded_index),
498 target_location: ExpansionResult::Rewritten(output.detail),
499 });
500 } else {
501 recursively_expanded_instructions
502 .new_instructions
503 .extend(output.new_instructions);
504 }
505 }
506 None => {
507 if build_source_map {
508 let target_index = InstructionIndex(
509 recursively_expanded_instructions.new_instructions.len(),
510 );
511 recursively_expanded_instructions
512 .detail
513 .expansions
514 .entries
515 .push(SourceMapEntry {
516 source_location: InstructionIndex(expanded_index),
517 target_location: ExpansionResult::Unmodified(target_index),
518 });
519 }
520 recursively_expanded_instructions
521 .new_instructions
522 .push(instruction);
523 }
524 };
525 }
526
527 if build_source_map {
528 recursively_expanded_instructions.detail.range = InstructionIndex(0)
531 ..InstructionIndex(
532 recursively_expanded_instructions.new_instructions.len(),
533 );
534 }
535
536 Some(recursively_expanded_instructions)
537 }
538 None => None,
539 })
540 }
541
542 pub fn get_match_for_measurement(
553 &self,
554 measurement: &Measurement,
555 ) -> Option<&MeasureCalibrationDefinition> {
556 struct First<T>(Option<T>);
558
559 impl<T> Default for First<T> {
560 fn default() -> Self {
561 Self(None)
562 }
563 }
564
565 impl<A> Extend<A> for First<A> {
566 fn extend<T: IntoIterator<Item = A>>(&mut self, iter: T) {
567 if self.0.is_none() {
568 self.0 = iter.into_iter().next()
569 }
570 }
571 }
572
573 let Measurement {
574 name,
575 qubit,
576 target,
577 } = measurement;
578
579 let (First(exact), First(wildcard)) = self
582 .iter_measure_calibrations()
583 .rev()
584 .filter_map(|calibration| {
585 let identifier = &calibration.identifier;
586
587 if !(name == &identifier.name && target.is_some() == identifier.target.is_some()) {
588 return None;
589 }
590
591 match &identifier.qubit {
592 fixed @ Qubit::Fixed(_) if qubit == fixed => Some((calibration, true)),
593 Qubit::Variable(_) => Some((calibration, false)),
594 Qubit::Fixed(_) | Qubit::Placeholder(_) => None,
595 }
596 })
597 .partition_map(|(calibration, exact)| {
598 if exact {
599 Either::Left(calibration)
600 } else {
601 Either::Right(calibration)
602 }
603 });
604
605 exact.or(wildcard)
606 }
607
608 pub fn get_match_for_gate(&self, gate: &Gate) -> Option<&CalibrationDefinition> {
618 let mut matched_calibration: Option<MatchedCalibration> = None;
619
620 for calibration in self
621 .iter_calibrations()
622 .filter(|calibration| calibration.identifier.matches(gate))
623 {
624 matched_calibration = match matched_calibration {
625 None => Some(MatchedCalibration::new(calibration)),
626 Some(previous_match) => {
627 let potential_match = MatchedCalibration::new(calibration);
628 if potential_match.fixed_qubit_count >= previous_match.fixed_qubit_count {
629 Some(potential_match)
630 } else {
631 Some(previous_match)
632 }
633 }
634 }
635 }
636
637 matched_calibration.map(|m| m.calibration)
638 }
639
640 pub fn into_instructions(self) -> Vec<Instruction> {
642 self.calibrations
643 .into_iter()
644 .map(Instruction::CalibrationDefinition)
645 .chain(
646 self.measure_calibrations
647 .into_iter()
648 .map(Instruction::MeasureCalibrationDefinition),
649 )
650 .collect()
651 }
652}
653
654#[cfg(test)]
655mod tests {
656 use std::str::FromStr;
657
658 use crate::program::calibration::{CalibrationSource, MeasureCalibrationIdentifier};
659 use crate::program::source_map::{ExpansionResult, SourceMap, SourceMapEntry};
660 use crate::program::{InstructionIndex, Program};
661 use crate::quil::Quil;
662
663 use insta::assert_snapshot;
664 use rstest::rstest;
665
666 use super::{CalibrationExpansion, CalibrationExpansionOutput, CalibrationIdentifier};
667
668 #[rstest]
669 #[case(
670 "Calibration-Param-Precedence",
671 concat!(
672 "DEFCAL RX(%theta) %qubit:\n",
673 " PULSE 1 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
674 "DEFCAL RX(%theta) 0:\n",
675 " PULSE 2 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
676 "DEFCAL RX(pi/2) 0:\n",
677 " PULSE 3 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
678 "RX(pi/2) 1\n",
679 "RX(pi) 0\n",
680 "RX(pi/2) 0\n"
681 ),
682 )]
683 #[case(
684 "Calibration-Simple",
685 concat!(
686 "DEFCAL X 0:\n",
687 " PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
688 "X 0\n",
689 ),
690 )]
691 #[case(
692 "Calibration-Literal-Parameter",
693 concat!(
694 "DEFCAL RX(3.141592653589793) 0:\n",
695 " NOP\n",
696 "RX(3.141592653589793) 0\n",
697 ),
698 )]
699 #[case(
700 "Calibration-Instruction-Match",
701 concat!(
702 "DEFCAL X 0:\n",
703 " Y 0\n",
704 "DEFCAL Y 0:\n",
705 " PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
706 "X 0\n"
707 ),
708 )]
709 #[case(
710 "Measure-Calibration",
711 concat!(
712 "DEFCAL MEASURE 0 addr:\n",
713 " PRAGMA INCORRECT_ORDERING\n",
714 "DEFCAL MEASURE 0 addr:\n",
715 " PRAGMA CORRECT\n",
716 "DEFCAL MEASURE q addr:\n",
717 " PRAGMA INCORRECT_PRECEDENCE\n",
718 "DEFCAL MEASURE 1 addr:\n",
719 " PRAGMA INCORRECT_QUBIT\n",
720 "DEFCAL MEASURE q:\n",
721 " PRAGMA INCORRECT_RECORD_VS_EFFECT\n",
722 "MEASURE 0 ro\n",
723 ),
724 )]
725 #[case(
726 "Calibration-Variable-Qubit",
727 concat!("DEFCAL I %q:\n", " DELAY q 4e-8\n", "I 0\n",),
728 )]
729 #[case(
730 "Precedence-Fixed-Match",
731 concat!(
732 "DEFCAL MEASURE q:\n",
733 " PRAGMA INCORRECT_RECORD_VS_EFFECT\n",
734 "DEFCAL MEASURE b addr:\n",
735 " PRAGMA INCORRECT_PRECEDENCE\n",
736 "DEFCAL MEASURE 0 addr:\n",
737 " PRAGMA INCORRECT_ORDER\n",
738 "DEFCAL MEASURE 0 addr:\n",
739 " PRAGMA CORRECT\n",
740 "DEFCAL MEASURE q addr:\n",
741 " PRAGMA INCORRECT_PRECEDENCE\n",
742 "DEFCAL MEASURE 1 addr:\n",
743 " PRAGMA INCORRECT_QUBIT\n",
744 "MEASURE 0 ro\n",
745 )
746 )]
747 #[case(
748 "Precedence-Variable-Match",
749 concat!(
750 "DEFCAL MEASURE q:\n",
751 " PRAGMA INCORRECT_RECORD_VS_EFFECT\n",
752 "DEFCAL MEASURE b addr:\n",
753 " PRAGMA INCORRECT_ORDER\n",
754 "DEFCAL MEASURE q addr:\n",
755 " PRAGMA CORRECT\n",
756 "MEASURE 0 ro\n",
757 )
758 )]
759 #[case(
760 "Precedence-No-Target-Match",
761 concat!(
762 "DEFCAL MEASURE b:\n",
763 " PRAGMA INCORRECT_SAME_NAME_SHOULD_NOT_APPEAR_IN_OUTPUT\n",
764 "DEFCAL MEASURE b:\n",
765 " PRAGMA INCORRECT_ORDER\n",
766 "DEFCAL MEASURE q:\n",
767 " PRAGMA CORRECT\n",
768 "MEASURE 0\n",
769 )
770 )]
771 #[case(
772 "Precedence-Prefer-Exact-Qubit-Match",
773 concat!(
774 "DEFCAL MEASURE 0 addr:\n",
775 " PRAGMA CORRECT\n",
776 "DEFCAL MEASURE q addr:\n",
777 " PRAGMA INCORRECT_PRECEDENCE\n",
778 "MEASURE 0 ro\n",
779 )
780 )]
781 #[case(
782 "Precedence-Prefer-Exact-Qubit-Match-No-Target",
783 concat!(
784 "DEFCAL MEASURE 0:\n",
785 " PRAGMA CORRECT\n",
786 "DEFCAL MEASURE q:\n",
787 " PRAGMA INCORRECT_PRECEDENCE\n",
788 "MEASURE 0\n",
789 )
790 )]
791 #[case(
792 "Precedence-Require-No-Name-Match",
793 concat!(
794 "DEFCAL MEASURE q addr:\n",
795 " PRAGMA CORRECT\n",
796 "DEFCAL MEASURE!wrong-name 0 addr:\n",
797 " PRAGMA INCORRECT_NAME\n",
798 "DEFCAL MEASURE!midcircuit 0 addr:\n",
799 " PRAGMA INCORRECT_NAME\n",
800 "MEASURE 0 ro\n",
801 )
802 )]
803 #[case(
804 "Precedence-Require-Name-Match",
805 concat!(
806 "DEFCAL MEASURE!midcircuit q addr:\n",
807 " PRAGMA CORRECT\n",
808 "DEFCAL MEASURE!wrong-name 0 addr:\n",
809 " PRAGMA INCORRECT_NAME\n",
810 "DEFCAL MEASURE 0 addr:\n",
811 " PRAGMA INCORRECT_NAME\n",
812 "MEASURE!midcircuit 0 ro\n",
813 )
814 )]
815 #[case(
816 "ShiftPhase",
817 concat!(
818 "DEFCAL RZ(%theta) %q:\n",
819 " SHIFT-PHASE %q \"rf\" -%theta\n",
820 "RZ(pi) 0\n",
821 )
822 )]
823 #[case(
824 "FenceVariableQubit",
825 concat!(
826 "DEFCAL FENCES q0 q1:\n",
827 " FENCE q0\n",
828 " FENCE q1\n",
829 "FENCES 0 1\n",
830 )
831 )]
832 fn test_expansion(#[case] description: &str, #[case] input: &str) {
833 let program = Program::from_str(input).unwrap();
834 let calibrated_program = program.expand_calibrations().unwrap();
835 insta::with_settings!({
836 snapshot_suffix => description,
837 }, {
838 assert_snapshot!(calibrated_program.to_quil_or_debug())
839 })
840 }
841
842 #[test]
844 fn expand_with_detail_recursive() {
845 let input = r#"
846DEFCAL X 0:
847 Y 0
848 MEASURE 0 ro
849 Y 0
850
851DEFCAL Y 0:
852 NOP
853 Z 0
854
855DEFCAL Z 0:
856 WAIT
857
858DEFCAL MEASURE 0 addr:
859 HALT
860
861X 0
862"#;
863
864 let program = Program::from_str(input).unwrap();
865 let instruction = program.instructions.last().unwrap();
866 let expansion = program
867 .calibrations
868 .expand_with_detail(instruction, &[])
869 .unwrap();
870 let expected = CalibrationExpansionOutput {
871 new_instructions: vec![
872 crate::instruction::Instruction::Nop(),
873 crate::instruction::Instruction::Wait(),
874 crate::instruction::Instruction::Halt(),
875 crate::instruction::Instruction::Nop(),
876 crate::instruction::Instruction::Wait(),
877 ],
878 detail: CalibrationExpansion {
879 calibration_used: CalibrationSource::Calibration(CalibrationIdentifier {
880 modifiers: vec![],
881 name: "X".to_string(),
882 parameters: vec![],
883 qubits: vec![crate::instruction::Qubit::Fixed(0)],
884 }),
885 range: InstructionIndex(0)..InstructionIndex(5),
886 expansions: SourceMap {
887 entries: vec![
888 SourceMapEntry {
889 source_location: InstructionIndex(0),
890 target_location: ExpansionResult::Rewritten(CalibrationExpansion {
891 calibration_used: CalibrationSource::Calibration(
892 CalibrationIdentifier {
893 modifiers: vec![],
894 name: "Y".to_string(),
895 parameters: vec![],
896 qubits: vec![crate::instruction::Qubit::Fixed(0)],
897 },
898 ),
899 range: InstructionIndex(0)..InstructionIndex(2),
900 expansions: SourceMap {
901 entries: vec![
902 SourceMapEntry {
903 source_location: InstructionIndex(0),
904 target_location: ExpansionResult::Unmodified(
905 InstructionIndex(0),
906 ),
907 },
908 SourceMapEntry {
909 source_location: InstructionIndex(1),
910 target_location: ExpansionResult::Rewritten(
911 CalibrationExpansion {
912 calibration_used:
913 CalibrationSource::Calibration(
914 CalibrationIdentifier {
915 modifiers: vec![],
916 name: "Z".to_string(),
917 parameters: vec![],
918 qubits: vec![
919 crate::instruction::Qubit::Fixed(0),
920 ],
921 },
922 ),
923 range: InstructionIndex(1)..InstructionIndex(2),
924 expansions: SourceMap {
925 entries: vec![SourceMapEntry {
926 source_location: InstructionIndex(0),
927 target_location:
928 ExpansionResult::Unmodified(
929 InstructionIndex(0),
930 ),
931 }],
932 },
933 },
934 ),
935 },
936 ],
937 },
938 }),
939 },
940 SourceMapEntry {
941 source_location: InstructionIndex(1),
942 target_location: ExpansionResult::Rewritten(CalibrationExpansion {
943 calibration_used: CalibrationSource::MeasureCalibration(
944 MeasureCalibrationIdentifier {
945 name: None,
946 qubit: crate::instruction::Qubit::Fixed(0),
947 target: Some("addr".to_string()),
948 },
949 ),
950 range: InstructionIndex(2)..InstructionIndex(3),
951 expansions: SourceMap {
952 entries: vec![SourceMapEntry {
953 source_location: InstructionIndex(0),
954 target_location: ExpansionResult::Unmodified(
955 InstructionIndex(0),
956 ),
957 }],
958 },
959 }),
960 },
961 SourceMapEntry {
962 source_location: InstructionIndex(2),
963 target_location: ExpansionResult::Rewritten(CalibrationExpansion {
964 calibration_used: CalibrationSource::Calibration(
965 CalibrationIdentifier {
966 modifiers: vec![],
967 name: "Y".to_string(),
968 parameters: vec![],
969 qubits: vec![crate::instruction::Qubit::Fixed(0)],
970 },
971 ),
972 range: InstructionIndex(3)..InstructionIndex(5),
973 expansions: SourceMap {
974 entries: vec![
975 SourceMapEntry {
976 source_location: InstructionIndex(0),
977 target_location: ExpansionResult::Unmodified(
978 InstructionIndex(0),
979 ),
980 },
981 SourceMapEntry {
982 source_location: InstructionIndex(1),
983 target_location: ExpansionResult::Rewritten(
984 CalibrationExpansion {
985 calibration_used:
986 CalibrationSource::Calibration(
987 CalibrationIdentifier {
988 modifiers: vec![],
989 name: "Z".to_string(),
990 parameters: vec![],
991 qubits: vec![
992 crate::instruction::Qubit::Fixed(0),
993 ],
994 },
995 ),
996 range: InstructionIndex(1)..InstructionIndex(2),
997 expansions: SourceMap {
998 entries: vec![SourceMapEntry {
999 source_location: InstructionIndex(0),
1000 target_location:
1001 ExpansionResult::Unmodified(
1002 InstructionIndex(0),
1003 ),
1004 }],
1005 },
1006 },
1007 ),
1008 },
1009 ],
1010 },
1011 }),
1012 },
1013 ],
1014 },
1015 },
1016 };
1017
1018 pretty_assertions::assert_eq!(expansion, Some(expected));
1019 }
1020
1021 #[test]
1022 fn test_eq() {
1023 let input = "DEFCAL X 0:
1024 PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
1025X 0";
1026 let a = Program::from_str(input);
1027 let b = Program::from_str(input);
1028 assert_eq!(a, b);
1029 }
1030
1031 #[test]
1032 fn test_ne() {
1033 let input_a = "DEFCAL X 0:
1034 PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
1035X 0";
1036 let input_b = "DEFCAL X 1:
1037 PULSE 1 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
1038X 1";
1039 let a = Program::from_str(input_a);
1040 let b = Program::from_str(input_b);
1041 assert_ne!(a, b);
1042 }
1043}