1use std::collections::HashMap;
16use std::ops::Range;
17
18use itertools::FoldWhile::{Continue, Done};
19use itertools::Itertools;
20
21use crate::instruction::{CalibrationIdentifier, MeasureCalibrationIdentifier};
22use crate::quil::Quil;
23use crate::{
24 expression::Expression,
25 instruction::{
26 Calibration, Capture, Delay, Fence, FrameIdentifier, Gate, Instruction,
27 MeasureCalibrationDefinition, Measurement, Pulse, Qubit, RawCapture, SetFrequency,
28 SetPhase, SetScale, ShiftFrequency, ShiftPhase,
29 },
30};
31
32use super::source_map::{SourceMap, SourceMapEntry, SourceMapIndexable};
33use super::{CalibrationSet, InstructionIndex, ProgramError};
34
35#[derive(Clone, Debug, Default, PartialEq)]
37pub struct Calibrations {
38 pub calibrations: CalibrationSet<Calibration>,
39 pub measure_calibrations: CalibrationSet<MeasureCalibrationDefinition>,
40}
41
42struct MatchedCalibration<'a> {
43 pub calibration: &'a Calibration,
44 pub fixed_qubit_count: usize,
45}
46
47impl<'a> MatchedCalibration<'a> {
48 pub fn new(calibration: &'a Calibration) -> Self {
49 Self {
50 calibration,
51 fixed_qubit_count: calibration
52 .identifier
53 .qubits
54 .iter()
55 .filter(|q| match q {
56 Qubit::Fixed(_) => true,
57 Qubit::Placeholder(_) | Qubit::Variable(_) => false,
58 })
59 .count(),
60 }
61 }
62}
63
64#[derive(Clone, Debug, PartialEq)]
66pub struct CalibrationExpansionOutput {
67 pub new_instructions: Vec<Instruction>,
69
70 pub detail: CalibrationExpansion,
72}
73
74#[derive(Clone, Debug, PartialEq)]
76pub struct CalibrationExpansion {
77 pub(crate) calibration_used: CalibrationSource,
79
80 pub(crate) range: Range<InstructionIndex>,
82
83 pub(crate) expansions: SourceMap<InstructionIndex, CalibrationExpansion>,
85}
86
87impl CalibrationExpansion {
88 pub(crate) fn remove_target_index(&mut self, target_index: InstructionIndex) {
93 if self.range.start >= target_index {
95 self.range.start = self.range.start.map(|v| v.saturating_sub(1));
96 }
97
98 if self.range.end > target_index {
100 self.range.end = self.range.end.map(|v| v.saturating_sub(1));
101 }
102
103 if let Some(target_within_expansion) = target_index.0.checked_sub(self.range.start.0) {
107 self.expansions.entries.retain_mut(
108 |entry: &mut SourceMapEntry<InstructionIndex, CalibrationExpansion>| {
109 entry
110 .target_location
111 .remove_target_index(InstructionIndex(target_within_expansion));
112
113 !entry.target_location.range.is_empty()
114 },
115 );
116 }
117 }
118
119 pub fn calibration_used(&self) -> &CalibrationSource {
120 &self.calibration_used
121 }
122
123 pub fn range(&self) -> &Range<InstructionIndex> {
124 &self.range
125 }
126
127 pub fn expansions(&self) -> &SourceMap<InstructionIndex, CalibrationExpansion> {
128 &self.expansions
129 }
130}
131
132impl SourceMapIndexable<InstructionIndex> for CalibrationExpansion {
133 fn intersects(&self, other: &InstructionIndex) -> bool {
134 self.range.contains(other)
135 }
136}
137
138impl SourceMapIndexable<CalibrationSource> for CalibrationExpansion {
139 fn intersects(&self, other: &CalibrationSource) -> bool {
140 self.calibration_used() == other
141 }
142}
143
144#[derive(Clone, Debug, PartialEq)]
146pub enum MaybeCalibrationExpansion {
147 Expanded(CalibrationExpansion),
149
150 Unexpanded(InstructionIndex),
152}
153
154impl SourceMapIndexable<InstructionIndex> for MaybeCalibrationExpansion {
155 fn intersects(&self, other: &InstructionIndex) -> bool {
156 match self {
157 MaybeCalibrationExpansion::Expanded(expansion) => expansion.intersects(other),
158 MaybeCalibrationExpansion::Unexpanded(index) => index == other,
159 }
160 }
161}
162
163impl SourceMapIndexable<CalibrationSource> for MaybeCalibrationExpansion {
164 fn intersects(&self, other: &CalibrationSource) -> bool {
165 match self {
166 MaybeCalibrationExpansion::Expanded(expansion) => expansion.intersects(other),
167 MaybeCalibrationExpansion::Unexpanded(_) => false,
168 }
169 }
170}
171
172#[derive(Clone, Debug, PartialEq)]
174pub enum CalibrationSource {
175 Calibration(CalibrationIdentifier),
177
178 MeasureCalibration(MeasureCalibrationIdentifier),
180}
181
182impl From<CalibrationIdentifier> for CalibrationSource {
183 fn from(value: CalibrationIdentifier) -> Self {
184 Self::Calibration(value)
185 }
186}
187
188impl From<MeasureCalibrationIdentifier> for CalibrationSource {
189 fn from(value: MeasureCalibrationIdentifier) -> Self {
190 Self::MeasureCalibration(value)
191 }
192}
193
194impl Calibrations {
195 pub fn calibrations(&self) -> Vec<&Calibration> {
197 self.iter_calibrations().collect()
198 }
199
200 pub fn measure_calibrations(&self) -> Vec<&MeasureCalibrationDefinition> {
203 self.iter_measure_calibrations().collect()
204 }
205
206 pub fn iter_calibrations(&self) -> impl Iterator<Item = &Calibration> {
208 self.calibrations.iter()
209 }
210
211 pub fn iter_measure_calibrations(&self) -> impl Iterator<Item = &MeasureCalibrationDefinition> {
213 self.measure_calibrations.iter()
214 }
215
216 pub fn expand(
223 &self,
224 instruction: &Instruction,
225 previous_calibrations: &[Instruction],
226 ) -> Result<Option<Vec<Instruction>>, ProgramError> {
227 self.expand_inner(instruction, previous_calibrations, false)
228 .map(|expansion| expansion.map(|expansion| expansion.new_instructions))
229 }
230
231 pub fn expand_with_detail(
237 &self,
238 instruction: &Instruction,
239 previous_calibrations: &[Instruction],
240 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
241 self.expand_inner(instruction, previous_calibrations, true)
242 }
243
244 fn expand_inner(
253 &self,
254 instruction: &Instruction,
255 previous_calibrations: &[Instruction],
256 build_source_map: bool,
257 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
258 if previous_calibrations.contains(instruction) {
259 return Err(ProgramError::RecursiveCalibration(instruction.clone()));
260 }
261 let expansion_result = match instruction {
262 Instruction::Gate(gate) => {
263 let matching_calibration = self.get_match_for_gate(gate);
264
265 match matching_calibration {
266 Some(calibration) => {
267 let mut qubit_expansions: HashMap<&String, Qubit> = HashMap::new();
268 for (index, calibration_qubit) in
269 calibration.identifier.qubits.iter().enumerate()
270 {
271 if let Qubit::Variable(identifier) = calibration_qubit {
272 qubit_expansions.insert(identifier, gate.qubits[index].clone());
273 }
274 }
275
276 let variable_expansions: HashMap<String, Expression> = calibration
279 .identifier
280 .parameters
281 .iter()
282 .zip(gate.parameters.iter())
283 .filter_map(|(calibration_expression, gate_expression)| {
284 if let Expression::Variable(variable_name) = calibration_expression
285 {
286 Some((variable_name.clone(), gate_expression.clone()))
287 } else {
288 None
289 }
290 })
291 .collect();
292
293 let mut instructions = calibration.instructions.clone();
294
295 for instruction in instructions.iter_mut() {
296 match instruction {
297 Instruction::Gate(Gate { qubits, .. })
298 | Instruction::Delay(Delay { qubits, .. })
299 | Instruction::Capture(Capture {
300 frame: FrameIdentifier { qubits, .. },
301 ..
302 })
303 | Instruction::RawCapture(RawCapture {
304 frame: FrameIdentifier { qubits, .. },
305 ..
306 })
307 | Instruction::SetFrequency(SetFrequency {
308 frame: FrameIdentifier { qubits, .. },
309 ..
310 })
311 | Instruction::SetPhase(SetPhase {
312 frame: FrameIdentifier { qubits, .. },
313 ..
314 })
315 | Instruction::SetScale(SetScale {
316 frame: FrameIdentifier { qubits, .. },
317 ..
318 })
319 | Instruction::ShiftFrequency(ShiftFrequency {
320 frame: FrameIdentifier { qubits, .. },
321 ..
322 })
323 | Instruction::ShiftPhase(ShiftPhase {
324 frame: FrameIdentifier { qubits, .. },
325 ..
326 })
327 | Instruction::Pulse(Pulse {
328 frame: FrameIdentifier { qubits, .. },
329 ..
330 })
331 | Instruction::Fence(Fence { qubits }) => {
332 for qubit in qubits {
334 match qubit {
335 Qubit::Variable(name) => {
336 if let Some(expansion) = qubit_expansions.get(name)
337 {
338 *qubit = expansion.clone();
339 }
340 }
341 Qubit::Fixed(_) | Qubit::Placeholder(_) => {}
342 }
343 }
344 }
345 _ => {}
346 }
347
348 instruction.apply_to_expressions(|expr| {
349 *expr = expr.substitute_variables(&variable_expansions);
350 })
351 }
352
353 Some((
354 instructions,
355 CalibrationSource::Calibration(calibration.identifier.clone()),
356 ))
357 }
358 None => None,
359 }
360 }
361 Instruction::Measurement(measurement) => {
362 let matching_calibration = self.get_match_for_measurement(measurement);
363
364 match matching_calibration {
365 Some(calibration) => {
366 let mut instructions = calibration.instructions.clone();
367 for instruction in instructions.iter_mut() {
368 match instruction {
369 Instruction::Pragma(pragma) => {
370 if pragma.name == "LOAD-MEMORY"
371 && pragma.data.as_ref()
372 == Some(&calibration.identifier.parameter)
373 {
374 if let Some(target) = &measurement.target {
375 pragma.data = Some(target.to_quil_or_debug())
376 }
377 }
378 }
379 Instruction::Capture(capture) => {
380 if let Some(target) = &measurement.target {
381 capture.memory_reference = target.clone()
382 }
383 }
384 _ => {}
385 }
386 }
387 Some((
388 instructions,
389 CalibrationSource::MeasureCalibration(calibration.identifier.clone()),
390 ))
391 }
392 None => None,
393 }
394 }
395 _ => None,
396 };
397
398 let mut calibration_path = Vec::with_capacity(previous_calibrations.len() + 1);
400 calibration_path.push(instruction.clone());
401 calibration_path.extend_from_slice(previous_calibrations);
402
403 self.recursively_expand_inner(expansion_result, &calibration_path, build_source_map)
404 }
405
406 fn recursively_expand_inner(
407 &self,
408 expansion_result: Option<(Vec<Instruction>, CalibrationSource)>,
409 calibration_path: &[Instruction],
410 build_source_map: bool,
411 ) -> Result<Option<CalibrationExpansionOutput>, ProgramError> {
412 Ok(match expansion_result {
413 Some((instructions, matched_calibration)) => {
414 let mut recursively_expanded_instructions = CalibrationExpansionOutput {
415 new_instructions: Vec::new(),
416 detail: CalibrationExpansion {
417 calibration_used: matched_calibration,
418 range: InstructionIndex(0)..InstructionIndex(0),
419 expansions: SourceMap::default(),
420 },
421 };
422
423 for (expanded_index, instruction) in instructions.into_iter().enumerate() {
424 let expanded_instructions =
425 self.expand_inner(&instruction, calibration_path, build_source_map)?;
426 match expanded_instructions {
427 Some(mut output) => {
428 if build_source_map {
429 let range_start = InstructionIndex(
430 recursively_expanded_instructions.new_instructions.len(),
431 );
432
433 recursively_expanded_instructions
434 .new_instructions
435 .extend(output.new_instructions);
436
437 let range_end = InstructionIndex(
438 recursively_expanded_instructions.new_instructions.len(),
439 );
440 output.detail.range = range_start..range_end;
441
442 recursively_expanded_instructions
443 .detail
444 .expansions
445 .entries
446 .push(SourceMapEntry {
447 source_location: InstructionIndex(expanded_index),
448 target_location: output.detail,
449 });
450 } else {
451 recursively_expanded_instructions
452 .new_instructions
453 .extend(output.new_instructions);
454 }
455 }
456 None => {
457 recursively_expanded_instructions
458 .new_instructions
459 .push(instruction);
460 }
461 };
462 }
463
464 if build_source_map {
465 recursively_expanded_instructions.detail.range = InstructionIndex(0)
468 ..InstructionIndex(
469 recursively_expanded_instructions.new_instructions.len(),
470 );
471 }
472
473 Some(recursively_expanded_instructions)
474 }
475 None => None,
476 })
477 }
478
479 pub fn get_match_for_measurement(
490 &self,
491 measurement: &Measurement,
492 ) -> Option<&MeasureCalibrationDefinition> {
493 measurement.target.as_ref()?;
494
495 self.measure_calibrations()
496 .into_iter()
497 .rev()
498 .fold_while(None, |best_match, calibration| {
499 if let Some(qubit) = &calibration.identifier.qubit {
500 match qubit {
501 Qubit::Fixed(_) if qubit == &measurement.qubit => Done(Some(calibration)),
502 Qubit::Variable(_)
503 if best_match.is_none()
504 || best_match.is_some_and(|c| c.identifier.qubit.is_none()) =>
505 {
506 Continue(Some(calibration))
507 }
508 _ => Continue(best_match),
509 }
510 } else if best_match.is_none() {
511 Continue(Some(calibration))
512 } else {
513 Continue(best_match)
514 }
515 })
516 .into_inner()
517 }
518
519 pub fn get_match_for_gate(&self, gate: &Gate) -> Option<&Calibration> {
529 let mut matched_calibration: Option<MatchedCalibration> = None;
530
531 for calibration in self
532 .iter_calibrations()
533 .filter(|calibration| calibration.identifier.matches(gate))
534 {
535 matched_calibration = match matched_calibration {
536 None => Some(MatchedCalibration::new(calibration)),
537 Some(previous_match) => {
538 let potential_match = MatchedCalibration::new(calibration);
539 if potential_match.fixed_qubit_count >= previous_match.fixed_qubit_count {
540 Some(potential_match)
541 } else {
542 Some(previous_match)
543 }
544 }
545 }
546 }
547
548 matched_calibration.map(|m| m.calibration)
549 }
550
551 pub fn len(&self) -> usize {
553 self.calibrations.len()
554 }
555
556 pub fn is_empty(&self) -> bool {
558 self.calibrations.is_empty()
559 }
560
561 pub fn insert_calibration(&mut self, calibration: Calibration) -> Option<Calibration> {
566 self.calibrations.replace(calibration)
567 }
568
569 pub fn insert_measurement_calibration(
574 &mut self,
575 calibration: MeasureCalibrationDefinition,
576 ) -> Option<MeasureCalibrationDefinition> {
577 self.measure_calibrations.replace(calibration)
578 }
579
580 pub fn extend(&mut self, other: Calibrations) {
585 self.calibrations.extend(other.calibrations);
586 self.measure_calibrations.extend(other.measure_calibrations);
587 }
588
589 pub fn into_instructions(self) -> Vec<Instruction> {
592 self.calibrations
593 .into_iter()
594 .map(Instruction::CalibrationDefinition)
595 .chain(
596 self.measure_calibrations
597 .into_iter()
598 .map(Instruction::MeasureCalibrationDefinition),
599 )
600 .collect()
601 }
602
603 pub fn to_instructions(&self) -> Vec<Instruction> {
605 self.iter_calibrations()
606 .cloned()
607 .map(Instruction::CalibrationDefinition)
608 .chain(
609 self.iter_measure_calibrations()
610 .cloned()
611 .map(Instruction::MeasureCalibrationDefinition),
612 )
613 .collect()
614 }
615}
616
617#[cfg(test)]
618mod tests {
619 use std::str::FromStr;
620
621 use crate::program::calibration::{CalibrationSource, MeasureCalibrationIdentifier};
622 use crate::program::source_map::{SourceMap, SourceMapEntry};
623 use crate::program::{InstructionIndex, Program};
624 use crate::quil::Quil;
625
626 use insta::assert_snapshot;
627 use rstest::rstest;
628
629 use super::{CalibrationExpansion, CalibrationExpansionOutput, CalibrationIdentifier};
630
631 #[rstest]
632 #[case(
633 "Calibration-Param-Precedence",
634 concat!(
635 "DEFCAL RX(%theta) %qubit:\n",
636 " PULSE 1 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
637 "DEFCAL RX(%theta) 0:\n",
638 " PULSE 2 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
639 "DEFCAL RX(pi/2) 0:\n",
640 " PULSE 3 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
641 "RX(pi/2) 1\n",
642 "RX(pi) 0\n",
643 "RX(pi/2) 0\n"
644 ),
645 )]
646 #[case(
647 "Calibration-Simple",
648 concat!(
649 "DEFCAL X 0:\n",
650 " PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
651 "X 0\n",
652 ),
653 )]
654 #[case(
655 "Calibration-Literal-Parameter",
656 concat!(
657 "DEFCAL RX(3.141592653589793) 0:\n",
658 " NOP\n",
659 "RX(3.141592653589793) 0\n",
660 ),
661 )]
662 #[case(
663 "Calibration-Instruction-Match",
664 concat!(
665 "DEFCAL X 0:\n",
666 " Y 0\n",
667 "DEFCAL Y 0:\n",
668 " PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)\n",
669 "X 0\n"
670 ),
671 )]
672 #[case(
673 "Measure-Calibration",
674 concat!(
675 "DEFCAL MEASURE 0 addr:\n",
676 " PRAGMA INCORRECT_ORDERING\n",
677 "DEFCAL MEASURE 0 addr:\n",
678 " PRAGMA CORRECT\n",
679 "DEFCAL MEASURE q addr:\n",
680 " PRAGMA INCORRECT_PRECEDENCE\n",
681 "DEFCAL MEASURE 1 addr:\n",
682 " PRAGMA INCORRECT_QUBIT\n",
683 "DEFCAL MEASURE addr:\n",
684 " PRAGMA INCORRECT_PRECEDENCE\n",
685 "MEASURE 0 ro\n",
686 ),
687 )]
688 #[case(
689 "Calibration-Variable-Qubit",
690 concat!("DEFCAL I %q:\n", " DELAY q 4e-8\n", "I 0\n",),
691 )]
692 #[case(
693 "Precedence-Fixed-Match",
694 concat!(
695 "DEFCAL MEASURE addr:\n",
696 " PRAGMA INCORRECT_PRECEDENCE\n",
697 "DEFCAL MEASURE q addr:\n",
698 " PRAGMA INCORRECT_PRECEDENCE\n",
699 "DEFCAL MEASURE 0 addr:\n",
700 " PRAGMA INCORRECT_ORDER\n",
701 "DEFCAL MEASURE 0 addr:\n",
702 " PRAGMA CORRECT\n",
703 "MEASURE 0 ro\n",
704 )
705 )]
706 #[case(
707 "Precedence-Variable-Match",
708 concat!(
709 "DEFCAL MEASURE addr:\n",
710 " PRAGMA INCORRECT_PRECEDENCE\n",
711 "DEFCAL MEASURE q addr:\n",
712 " PRAGMA INCORRECT_PRECEDENCE\n",
713 "DEFCAL MEASURE b addr:\n",
714 " PRAGMA CORRECT\n",
715 "MEASURE 0 ro\n",
716 )
717 )]
718 #[case(
719 "Precedence-No-Qubit-Match",
720 concat!(
721 "DEFCAL MEASURE addr:\n",
722 " PRAGMA INCORRECT_PRECEDENCE\n",
723 "DEFCAL MEASURE addr:\n",
724 " PRAGMA CORRECT\n",
725 "MEASURE 0 ro\n",
726 )
727 )]
728 #[case(
729 "ShiftPhase",
730 concat!(
731 "DEFCAL RZ(%theta) %q:\n",
732 " SHIFT-PHASE %q \"rf\" -%theta\n",
733 "RZ(pi) 0\n",
734 )
735 )]
736 #[case(
737 "FenceVariableQubit",
738 concat!(
739 "DEFCAL FENCES q0 q1:\n",
740 " FENCE q0\n",
741 " FENCE q1\n",
742 "FENCES 0 1\n",
743 )
744 )]
745 fn test_expansion(#[case] description: &str, #[case] input: &str) {
746 let program = Program::from_str(input).unwrap();
747 let calibrated_program = program.expand_calibrations().unwrap();
748 insta::with_settings!({
749 snapshot_suffix => description,
750 }, {
751 assert_snapshot!(calibrated_program.to_quil_or_debug())
752 })
753 }
754
755 #[test]
757 fn expand_with_detail_recursive() {
758 let input = r#"
759DEFCAL X 0:
760 Y 0
761 MEASURE 0 ro
762 Y 0
763
764DEFCAL Y 0:
765 NOP
766 Z 0
767
768DEFCAL Z 0:
769 WAIT
770
771DEFCAL MEASURE 0 addr:
772 HALT
773
774X 0
775"#;
776
777 let program = Program::from_str(input).unwrap();
778 let instruction = program.instructions.last().unwrap();
779 let expansion = program
780 .calibrations
781 .expand_with_detail(instruction, &[])
782 .unwrap();
783 let expected = CalibrationExpansionOutput {
784 new_instructions: vec![
785 crate::instruction::Instruction::Nop,
786 crate::instruction::Instruction::Wait,
787 crate::instruction::Instruction::Halt,
788 crate::instruction::Instruction::Nop,
789 crate::instruction::Instruction::Wait,
790 ],
791 detail: CalibrationExpansion {
792 calibration_used: CalibrationSource::Calibration(CalibrationIdentifier {
793 modifiers: vec![],
794 name: "X".to_string(),
795 parameters: vec![],
796 qubits: vec![crate::instruction::Qubit::Fixed(0)],
797 }),
798 range: InstructionIndex(0)..InstructionIndex(5),
799 expansions: SourceMap {
800 entries: vec![
801 SourceMapEntry {
802 source_location: InstructionIndex(0),
803 target_location: CalibrationExpansion {
804 calibration_used: CalibrationSource::Calibration(
805 CalibrationIdentifier {
806 modifiers: vec![],
807 name: "Y".to_string(),
808 parameters: vec![],
809 qubits: vec![crate::instruction::Qubit::Fixed(0)],
810 },
811 ),
812 range: InstructionIndex(0)..InstructionIndex(2),
813 expansions: SourceMap {
814 entries: vec![SourceMapEntry {
815 source_location: InstructionIndex(1),
816 target_location: CalibrationExpansion {
817 calibration_used: CalibrationSource::Calibration(
818 CalibrationIdentifier {
819 modifiers: vec![],
820 name: "Z".to_string(),
821 parameters: vec![],
822 qubits: vec![crate::instruction::Qubit::Fixed(
823 0,
824 )],
825 },
826 ),
827 range: InstructionIndex(1)..InstructionIndex(2),
828 expansions: SourceMap::default(),
829 },
830 }],
831 },
832 },
833 },
834 SourceMapEntry {
835 source_location: InstructionIndex(1),
836 target_location: CalibrationExpansion {
837 calibration_used: CalibrationSource::MeasureCalibration(
838 MeasureCalibrationIdentifier {
839 qubit: Some(crate::instruction::Qubit::Fixed(0)),
840 parameter: "addr".to_string(),
841 },
842 ),
843 range: InstructionIndex(2)..InstructionIndex(3),
844 expansions: SourceMap::default(),
845 },
846 },
847 SourceMapEntry {
848 source_location: InstructionIndex(2),
849 target_location: CalibrationExpansion {
850 calibration_used: CalibrationSource::Calibration(
851 CalibrationIdentifier {
852 modifiers: vec![],
853 name: "Y".to_string(),
854 parameters: vec![],
855 qubits: vec![crate::instruction::Qubit::Fixed(0)],
856 },
857 ),
858 range: InstructionIndex(3)..InstructionIndex(5),
859 expansions: SourceMap {
860 entries: vec![SourceMapEntry {
861 source_location: InstructionIndex(1),
862 target_location: CalibrationExpansion {
863 calibration_used: CalibrationSource::Calibration(
864 CalibrationIdentifier {
865 modifiers: vec![],
866 name: "Z".to_string(),
867 parameters: vec![],
868 qubits: vec![crate::instruction::Qubit::Fixed(
869 0,
870 )],
871 },
872 ),
873 range: InstructionIndex(1)..InstructionIndex(2),
874 expansions: SourceMap::default(),
875 },
876 }],
877 },
878 },
879 },
880 ],
881 },
882 },
883 };
884
885 pretty_assertions::assert_eq!(expansion, Some(expected));
886 }
887
888 #[test]
889 fn test_eq() {
890 let input = "DEFCAL X 0:
891 PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
892X 0";
893 let a = Program::from_str(input);
894 let b = Program::from_str(input);
895 assert_eq!(a, b);
896 }
897
898 #[test]
899 fn test_ne() {
900 let input_a = "DEFCAL X 0:
901 PULSE 0 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
902X 0";
903 let input_b = "DEFCAL X 1:
904 PULSE 1 \"xy\" gaussian(duration: 1, fwhm: 2, t0: 3)
905X 1";
906 let a = Program::from_str(input_a);
907 let b = Program::from_str(input_b);
908 assert_ne!(a, b);
909 }
910}