1#![allow(clippy::result_large_err)]
17
18use std::collections::{HashMap, HashSet};
19use std::ops::{self};
20use std::str::FromStr;
21
22use indexmap::{IndexMap, IndexSet};
23use ndarray::Array2;
24use nom_locate::LocatedSpan;
25use petgraph::algo::DfsSpace;
26use petgraph::Graph;
27
28#[cfg(feature = "stubs")]
29use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};
30
31use crate::instruction::{
32 Arithmetic, ArithmeticOperand, ArithmeticOperator, Call, Declaration,
33 DefGateSequenceExpansionError, ExternError, ExternPragmaMap, ExternSignatureMap,
34 FrameDefinition, FrameIdentifier, GateDefinition, GateError, GateSpecification, Instruction,
35 InstructionHandler, Jump, JumpUnless, Label, Matrix, MemoryReference, Move, Pragma, Qubit,
36 QubitPlaceholder, ScalarType, Target, TargetPlaceholder, Vector, Waveform, WaveformDefinition,
37 RESERVED_PRAGMA_EXTERN,
38};
39use crate::parser::{lex, parse_instructions, ParseError};
40use crate::program::defgate_sequence_expansion::{
41 ExpandedInstructionsWithSourceMap, ProgramDefGateSequenceExpander,
42};
43use crate::quil::Quil;
44
45pub use self::calibration::{
46 CalibrationExpansion, CalibrationExpansionOutput, CalibrationSource, Calibrations,
47};
48pub use self::calibration_set::CalibrationSet;
49pub use self::defgate_sequence_expansion::DefGateSequenceExpansion;
50pub use self::error::{
51 disallow_leftover, map_parsed, recover, LeftoverError, ParseProgramError, SyntaxError,
52};
53pub use self::frame::FrameSet;
54pub use self::frame::MatchedFrames;
55pub use self::memory::{MemoryAccesses, MemoryAccessesError, MemoryRegion};
56pub use self::source_map::{ExpansionResult, SourceMap, SourceMapEntry, SourceMapIndexable};
57
58pub mod analysis;
59mod calibration;
60mod calibration_set;
61mod defgate_sequence_expansion;
62mod error;
63pub(crate) mod frame;
64mod memory;
65pub mod scheduling;
66mod source_map;
67pub mod type_check;
68
69#[cfg(not(feature = "python"))]
70use optipy::strip_pyo3;
71#[cfg(feature = "python")]
72pub(crate) mod quilpy;
73
74#[allow(clippy::large_enum_variant)]
76#[derive(Clone, Debug, PartialEq, thiserror::Error)]
77pub enum ProgramError {
78 #[error("{0}")]
79 ParsingError(#[from] ParseProgramError<Program>),
80
81 #[error("this operation isn't supported on instruction: {}", .0.to_quil_or_debug())]
82 UnsupportedOperation(Instruction),
83
84 #[error("instruction {} expands into itself", .0.to_quil_or_debug())]
85 RecursiveCalibration(Instruction),
86
87 #[error("{0}")]
88 GateError(#[from] GateError),
89
90 #[error(transparent)]
91 DefGateSequenceExpansionError(#[from] DefGateSequenceExpansionError),
92
93 #[error("can only compute program unitary for programs composed of `Gate`s; found unsupported instruction: {}", .0.to_quil_or_debug())]
94 UnsupportedForUnitary(Instruction),
95}
96
97type Result<T> = std::result::Result<T, ProgramError>;
98
99#[derive(Clone, Debug, Default, PartialEq)]
105#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
106#[cfg_attr(feature = "python", pyo3::pyclass(module = "quil.program", eq))]
107#[cfg_attr(not(feature = "python"), strip_pyo3)]
108pub struct Program {
109 #[pyo3(get, set)]
110 pub calibrations: Calibrations,
111 #[pyo3(get, name = "pragma_extern_map")]
112 pub extern_pragma_map: ExternPragmaMap,
113 #[pyo3(get, set)]
114 pub frames: FrameSet,
115 #[pyo3(get, set)]
116 pub memory_regions: IndexMap<String, MemoryRegion>,
117 #[pyo3(get, set)]
118 pub waveforms: IndexMap<String, Waveform>,
119 #[pyo3(get, set)]
120 pub gate_definitions: IndexMap<String, GateDefinition>,
121 #[pyo3(get, set)]
122 instructions: Vec<Instruction>,
123 #[pyo3(get)]
125 used_qubits: HashSet<Qubit>,
126}
127
128#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
129#[cfg_attr(feature = "python", pyo3::pymethods)]
130#[cfg_attr(not(feature = "python"), strip_pyo3)]
131impl Program {
132 #[new]
133 pub fn new() -> Self {
134 Program::default()
135 }
136
137 pub fn clone_without_body_instructions(&self) -> Self {
139 Self {
140 calibrations: self.calibrations.clone(),
141 extern_pragma_map: self.extern_pragma_map.clone(),
142 frames: self.frames.clone(),
143 memory_regions: self.memory_regions.clone(),
144 waveforms: self.waveforms.clone(),
145 gate_definitions: self.gate_definitions.clone(),
146 instructions: Vec::new(),
147 used_qubits: HashSet::new(),
148 }
149 }
150
151 pub fn add_instruction(&mut self, instruction: Instruction) {
158 self.used_qubits
159 .extend(instruction.get_qubits().into_iter().cloned());
160
161 match instruction {
162 Instruction::CalibrationDefinition(calibration) => {
163 self.calibrations.insert_calibration(calibration);
164 }
165 Instruction::FrameDefinition(FrameDefinition {
166 identifier,
167 attributes,
168 }) => {
169 self.frames.insert(identifier, attributes);
170 }
171 Instruction::Declaration(Declaration {
172 name,
173 size,
174 sharing,
175 }) => {
176 self.memory_regions
177 .insert(name, MemoryRegion { size, sharing });
178 }
179 Instruction::GateDefinition(gate_definition) => {
180 self.gate_definitions
181 .insert(gate_definition.name.clone(), gate_definition);
182 }
183 Instruction::MeasureCalibrationDefinition(calibration) => {
184 self.calibrations
185 .insert_measurement_calibration(calibration);
186 }
187 Instruction::WaveformDefinition(WaveformDefinition { name, definition }) => {
188 self.waveforms.insert(name, definition);
189 }
190 Instruction::Gate(gate) => {
191 self.instructions.push(Instruction::Gate(gate));
192 }
193 Instruction::Measurement(measurement) => {
194 self.instructions
195 .push(Instruction::Measurement(measurement));
196 }
197 Instruction::Reset(reset) => {
198 self.instructions.push(Instruction::Reset(reset));
199 }
200 Instruction::Delay(delay) => {
201 self.instructions.push(Instruction::Delay(delay));
202 }
203 Instruction::Fence(fence) => {
204 self.instructions.push(Instruction::Fence(fence));
205 }
206 Instruction::Capture(capture) => {
207 self.instructions.push(Instruction::Capture(capture));
208 }
209 Instruction::Pulse(pulse) => {
210 self.instructions.push(Instruction::Pulse(pulse));
211 }
212 Instruction::Pragma(pragma) if pragma.name == RESERVED_PRAGMA_EXTERN => {
213 self.extern_pragma_map.insert(pragma);
214 }
215 Instruction::RawCapture(raw_capture) => {
216 self.instructions.push(Instruction::RawCapture(raw_capture));
217 }
218 other => self.instructions.push(other),
219 }
220 }
221
222 pub fn dagger(&self) -> Result<Self> {
229 self.to_instructions().into_iter().try_rfold(
230 Program::new(),
231 |mut new_program, instruction| match instruction {
232 Instruction::Gate(gate) => {
233 new_program.add_instruction(Instruction::Gate(gate.dagger()));
234 Ok(new_program)
235 }
236 _ => Err(ProgramError::UnsupportedOperation(instruction)),
237 },
238 )
239 }
240
241 pub fn expand_calibrations(&self) -> Result<Self> {
249 self.expand_calibrations_inner(None)
250 }
251
252 pub fn wrap_in_loop(
267 &self,
268 loop_count_reference: MemoryReference,
269 start_target: Target,
270 end_target: Target,
271 iterations: u32,
272 ) -> Self {
273 if iterations == 0 {
274 return self.clone();
275 }
276
277 let mut looped_program = self.clone_without_body_instructions();
278
279 looped_program.add_instructions(
280 vec![
281 Instruction::Declaration(Declaration {
282 name: loop_count_reference.name.clone(),
283 size: Vector {
284 data_type: ScalarType::Integer,
285 length: 1,
286 },
287 sharing: None,
288 }),
289 Instruction::Move(Move {
290 destination: loop_count_reference.clone(),
291 source: ArithmeticOperand::LiteralInteger(iterations.into()),
292 }),
293 Instruction::Label(Label {
294 target: start_target.clone(),
295 }),
296 ]
297 .into_iter()
298 .chain(self.body_instructions().cloned())
299 .chain(vec![
300 Instruction::Arithmetic(Arithmetic {
301 operator: ArithmeticOperator::Subtract,
302 destination: MemoryReference {
303 name: loop_count_reference.name.clone(),
304 index: 0,
305 },
306 source: ArithmeticOperand::LiteralInteger(1),
307 }),
308 Instruction::JumpUnless(JumpUnless {
309 target: end_target.clone(),
310 condition: loop_count_reference,
311 }),
312 Instruction::Jump(Jump {
313 target: start_target,
314 }),
315 Instruction::Label(Label { target: end_target }),
316 ])
317 .collect::<Vec<Instruction>>(),
318 );
319
320 looped_program
321 }
322
323 pub fn resolve_placeholders(&mut self) {
329 self.resolve_placeholders_with_custom_resolvers(
330 self.default_target_resolver(),
331 self.default_qubit_resolver(),
332 )
333 }
334
335 pub fn to_instructions(&self) -> Vec<Instruction> {
337 let mut instructions: Vec<Instruction> = Vec::with_capacity(self.len());
338
339 instructions.extend(self.extern_pragma_map.to_instructions());
340 instructions.extend(self.memory_regions.iter().map(|(name, descriptor)| {
341 Instruction::Declaration(Declaration {
342 name: name.clone(),
343 size: descriptor.size.clone(),
344 sharing: descriptor.sharing.clone(),
345 })
346 }));
347 instructions.extend(self.frames.to_instructions());
348 instructions.extend(self.waveforms.iter().map(|(name, definition)| {
349 Instruction::WaveformDefinition(WaveformDefinition {
350 name: name.clone(),
351 definition: definition.clone(),
352 })
353 }));
354 instructions.extend(self.calibrations.to_instructions());
355 instructions.extend(
356 self.gate_definitions
357 .values()
358 .cloned()
359 .map(Instruction::GateDefinition),
360 );
361 instructions.extend(self.instructions.clone());
362 instructions
363 }
364}
365
366impl Program {
367 pub fn body_instructions(&self) -> impl Iterator<Item = &Instruction> {
369 self.instructions.iter()
370 }
371
372 pub fn into_body_instructions(self) -> impl Iterator<Item = Instruction> {
373 self.instructions.into_iter()
374 }
375
376 #[cfg(test)]
378 pub(crate) fn for_each_body_instruction<F>(&mut self, closure: F)
379 where
380 F: Fn(&mut Instruction),
381 {
382 let mut instructions = std::mem::take(&mut self.instructions);
383 self.used_qubits.clear();
384
385 instructions.iter_mut().for_each(closure);
386
387 self.add_instructions(instructions);
388 }
389
390 pub fn add_instructions<I>(&mut self, instructions: I)
391 where
392 I: IntoIterator<Item = Instruction>,
393 {
394 instructions
395 .into_iter()
396 .for_each(|i| self.add_instruction(i));
397 }
398
399 pub fn filter_instructions(&self, predicate: impl FnMut(&Instruction) -> bool) -> Program {
402 Program::from_instructions(
403 self.to_instructions()
404 .into_iter()
405 .filter(predicate)
406 .collect(),
407 )
408 }
409
410 pub fn expand_calibrations_with_source_map(
413 &self,
414 ) -> Result<(
415 Program,
416 SourceMap<InstructionIndex, ExpansionResult<CalibrationExpansion>>,
417 )> {
418 let mut source_mapping = ProgramCalibrationExpansionSourceMap::default();
419 let new_program = self.expand_calibrations_inner(Some(&mut source_mapping))?;
420
421 Ok((new_program, source_mapping))
422 }
423
424 fn expand_calibrations_inner(
430 &self,
431 mut source_mapping: Option<&mut ProgramCalibrationExpansionSourceMap>,
432 ) -> Result<Self> {
433 let mut new_program = Self {
434 calibrations: self.calibrations.clone(),
435 extern_pragma_map: self.extern_pragma_map.clone(),
436 frames: self.frames.clone(),
437 memory_regions: self.memory_regions.clone(),
438 waveforms: self.waveforms.clone(),
439 gate_definitions: self.gate_definitions.clone(),
440 instructions: Vec::new(),
441 used_qubits: HashSet::new(),
442 };
443
444 for (index, instruction) in self.instructions.iter().enumerate() {
445 let index = InstructionIndex(index);
446
447 match self.calibrations.expand_with_detail(instruction, &[])? {
448 Some(expanded) => {
449 new_program.append_calibration_expansion_output_inner(
450 expanded,
451 index,
452 &mut source_mapping,
453 );
454 }
455 None => {
456 new_program.add_instruction(instruction.clone());
457 if let Some(source_mapping) = source_mapping.as_mut() {
458 source_mapping.entries.push(SourceMapEntry {
459 source_location: index,
460 target_location: ExpansionResult::Unmodified(InstructionIndex(
461 new_program.instructions.len() - 1,
462 )),
463 });
464 }
465 }
466 }
467 }
468
469 Ok(new_program)
470 }
471
472 pub fn expand_defgate_sequences<F>(self, filter: F) -> Result<Self>
562 where
563 F: Fn(&str) -> bool,
564 {
565 let (expansion, gate_definitions) = self.initialize_defgate_sequence_expander(filter);
566 let new_instructions = expansion.expand(&self.instructions)?;
567
568 let mut new_program = Self {
569 calibrations: self.calibrations,
570 extern_pragma_map: self.extern_pragma_map,
571 frames: self.frames,
572 memory_regions: self.memory_regions,
573 waveforms: self.waveforms,
574 gate_definitions,
575 instructions: Vec::new(),
576 used_qubits: HashSet::new(),
577 };
578 new_program.add_instructions(new_instructions);
579 Ok(new_program)
580 }
581
582 pub fn expand_defgate_sequences_with_source_map<F>(
595 &self,
596 filter: F,
597 ) -> Result<(
598 Self,
599 SourceMap<InstructionIndex, ExpansionResult<DefGateSequenceExpansion<'_>>>,
600 )>
601 where
602 F: Fn(&str) -> bool,
603 {
604 let (expander, gate_definitions) = self.initialize_defgate_sequence_expander(filter);
605 let ExpandedInstructionsWithSourceMap {
606 instructions: new_instructions,
607 source_map,
608 } = expander.expand_with_source_map(&self.instructions)?;
609
610 let mut new_program = Self {
611 calibrations: self.calibrations.clone(),
612 extern_pragma_map: self.extern_pragma_map.clone(),
613 frames: self.frames.clone(),
614 memory_regions: self.memory_regions.clone(),
615 waveforms: self.waveforms.clone(),
616 gate_definitions,
617 instructions: Vec::new(),
618 used_qubits: HashSet::new(),
619 };
620 new_program.add_instructions(new_instructions);
621 Ok((new_program, source_map))
622 }
623
624 fn initialize_defgate_sequence_expander<F>(
625 &self,
626 filter: F,
627 ) -> (
628 ProgramDefGateSequenceExpander<'_, F>,
629 IndexMap<String, GateDefinition>,
630 )
631 where
632 F: Fn(&str) -> bool,
633 {
634 let gate_definitions_to_keep =
635 filter_sequence_gate_definitions_to_keep(&self.gate_definitions, &filter);
636 let expansion = ProgramDefGateSequenceExpander::new(&self.gate_definitions, filter);
637 (expansion, gate_definitions_to_keep)
638 }
639
640 fn append_calibration_expansion_output_inner(
646 &mut self,
647 mut expansion_output: CalibrationExpansionOutput,
648 source_index: InstructionIndex,
649 source_mapping: &mut Option<&mut ProgramCalibrationExpansionSourceMap>,
650 ) {
651 if let Some(source_mapping) = source_mapping.as_mut() {
652 let previous_program_instruction_body_length = self.instructions.len();
653
654 for instruction in expansion_output.new_instructions {
655 let start_length = self.instructions.len();
656 self.add_instruction(instruction.clone());
657 let end_length = self.instructions.len();
658
659 if start_length == end_length {
662 let relative_target_index =
663 InstructionIndex(start_length - previous_program_instruction_body_length);
664 expansion_output
665 .detail
666 .remove_target_index(relative_target_index);
667 }
668 }
669
670 expansion_output.detail.range =
671 InstructionIndex(previous_program_instruction_body_length)
672 ..InstructionIndex(self.instructions.len());
673
674 if !expansion_output.detail.range.is_empty() {
675 source_mapping.entries.push(SourceMapEntry {
676 source_location: source_index,
677 target_location: ExpansionResult::Rewritten(expansion_output.detail),
678 });
679 }
680 } else {
681 self.add_instructions(expansion_output.new_instructions);
682 }
683 }
684
685 pub fn from_instructions(instructions: Vec<Instruction>) -> Self {
687 let mut program = Self::default();
688 for instruction in instructions {
689 program.add_instruction(instruction);
690 }
691 program
692 }
693
694 fn get_targets(&self) -> Vec<&Target> {
696 self.instructions
697 .iter()
698 .filter_map(|i| match i {
699 Instruction::Label(label) => Some(&label.target),
700 Instruction::Jump(jump) => Some(&jump.target),
701 Instruction::JumpWhen(jump_when) => Some(&jump_when.target),
702 Instruction::JumpUnless(jump_unless) => Some(&jump_unless.target),
703 _ => None,
704 })
705 .collect()
706 }
707
708 pub fn get_used_qubits(&self) -> &HashSet<Qubit> {
710 &self.used_qubits
711 }
712
713 fn rebuild_used_qubits(&mut self) {
715 self.used_qubits = self
716 .to_instructions()
717 .iter()
718 .flat_map(|instruction| instruction.get_qubits().into_iter().cloned())
719 .collect()
720 }
721
722 pub fn into_instructions(self) -> Vec<Instruction> {
724 let mut instructions: Vec<Instruction> = Vec::with_capacity(self.len());
725
726 instructions.extend(self.memory_regions.into_iter().map(|(name, descriptor)| {
727 Instruction::Declaration(Declaration {
728 name,
729 size: descriptor.size,
730 sharing: descriptor.sharing,
731 })
732 }));
733 instructions.extend(self.frames.into_instructions());
734 instructions.extend(self.waveforms.into_iter().map(|(name, definition)| {
735 Instruction::WaveformDefinition(WaveformDefinition { name, definition })
736 }));
737 instructions.extend(self.calibrations.to_instructions());
738 instructions.extend(
739 self.gate_definitions
740 .into_values()
741 .map(Instruction::GateDefinition),
742 );
743 instructions.extend(self.extern_pragma_map.into_instructions());
744 instructions.extend(self.instructions);
745 instructions
746 }
747
748 pub fn simplify<H: InstructionHandler>(&self, handler: &H) -> Result<Self> {
760 let mut expanded_program = self.expand_calibrations()?;
761 expanded_program.calibrations = Calibrations::default();
765
766 let mut frames_used: HashSet<&FrameIdentifier> = HashSet::new();
767 let mut waveforms_used: HashSet<&String> = HashSet::new();
768 let mut extern_signatures_used: HashSet<&String> = HashSet::new();
769
770 for instruction in &expanded_program.instructions {
771 if let Some(matched_frames) = handler.matching_frames(&expanded_program, instruction) {
772 frames_used.extend(matched_frames.used)
773 }
774
775 if let Some(waveform) = instruction.get_waveform_invocation() {
776 waveforms_used.insert(&waveform.name);
777 }
778
779 if let Instruction::Call(Call { name, .. }) = instruction {
780 extern_signatures_used.insert(name);
781 }
782 }
783
784 expanded_program.frames = self.frames.intersection(&frames_used);
785 expanded_program
786 .waveforms
787 .retain(|name, _definition| waveforms_used.contains(name));
788 expanded_program
789 .extern_pragma_map
790 .retain(|name, _signature| {
791 name.as_ref()
792 .map(|name| extern_signatures_used.contains(name))
793 .unwrap_or(false)
794 });
795
796 Ok(expanded_program)
797 }
798
799 #[allow(clippy::type_complexity)]
809 pub fn resolve_placeholders_with_custom_resolvers(
810 &mut self,
811 target_resolver: Box<dyn Fn(&TargetPlaceholder) -> Option<String>>,
812 qubit_resolver: Box<dyn Fn(&QubitPlaceholder) -> Option<u64>>,
813 ) {
814 for instruction in &mut self.instructions {
815 instruction.resolve_placeholders(&target_resolver, &qubit_resolver);
816 }
817 self.rebuild_used_qubits()
818 }
819
820 #[allow(clippy::type_complexity)]
823 pub fn default_target_resolver(&self) -> Box<dyn Fn(&TargetPlaceholder) -> Option<String>> {
824 let mut fixed_labels = HashSet::new();
825 let mut label_placeholders = IndexSet::new();
826 for target in self.get_targets() {
827 match target {
828 Target::Fixed(fixed) => {
829 fixed_labels.insert(fixed.clone());
830 }
831 Target::Placeholder(placeholder) => {
832 label_placeholders.insert(placeholder.clone());
833 }
834 }
835 }
836
837 let target_resolutions: HashMap<TargetPlaceholder, String> = label_placeholders
838 .into_iter()
839 .map(|p| {
840 let base_label = p.as_inner();
841 let mut next_label = format!("{base_label}_0");
842 let mut next_suffix = 1;
843
844 while fixed_labels.contains(&next_label) {
845 next_label = format!("{base_label}_{next_suffix}");
846 next_suffix += 1;
847 }
848 fixed_labels.insert(next_label.clone());
849
850 (p, next_label)
851 })
852 .collect();
853
854 Box::new(move |key| target_resolutions.get(key).cloned())
855 }
856
857 #[allow(clippy::type_complexity)]
860 pub fn default_qubit_resolver(&self) -> Box<dyn Fn(&QubitPlaceholder) -> Option<u64>> {
861 let mut qubits_used: HashSet<u64> = HashSet::new();
862 let mut qubit_placeholders: IndexSet<QubitPlaceholder> = IndexSet::new();
863
864 for instruction in &self.instructions {
866 let qubits = instruction.get_qubits();
867
868 for qubit in qubits {
869 match qubit {
870 Qubit::Fixed(index) => {
871 qubits_used.insert(*index);
872 }
873 Qubit::Placeholder(placeholder) => {
874 qubit_placeholders.insert(placeholder.clone());
875 }
876 Qubit::Variable(_) => {}
877 }
878 }
879 }
880
881 let qubit_iterator = (0u64..).filter(|index| !qubits_used.contains(index));
882 let qubit_resolutions: HashMap<QubitPlaceholder, u64> =
883 qubit_placeholders.into_iter().zip(qubit_iterator).collect();
884
885 Box::new(move |key| qubit_resolutions.get(key).copied())
886 }
887
888 pub fn is_empty(&self) -> bool {
889 self.len() == 0
890 }
891
892 pub fn len(&self) -> usize {
893 self.memory_regions.len()
894 + self.frames.len()
895 + self.waveforms.len()
896 + self.gate_definitions.len()
897 + self.instructions.len()
898 + self.extern_pragma_map.len()
899 }
900
901 pub fn to_unitary(&self, n_qubits: u64) -> Result<Matrix> {
907 let mut umat = Array2::eye(2usize.pow(n_qubits as u32));
908 for instruction in self.instructions.clone() {
909 match instruction {
910 Instruction::Halt() => {}
911 Instruction::Gate(mut gate) => {
912 umat = gate.to_unitary(n_qubits)?.dot(&umat);
913 }
914 _ => return Err(ProgramError::UnsupportedForUnitary(instruction)),
915 }
916 }
917 Ok(umat)
918 }
919
920 pub fn get_instruction(&self, index: usize) -> Option<&Instruction> {
922 self.instructions.get(index)
923 }
924
925 pub fn try_extern_signature_map_from_pragma_map(
931 &self,
932 ) -> std::result::Result<ExternSignatureMap, (Pragma, ExternError)> {
933 ExternSignatureMap::try_from(self.extern_pragma_map.clone())
934 }
935}
936
937fn filter_sequence_gate_definitions_to_keep<F>(
943 gate_definitions: &IndexMap<String, GateDefinition>,
944 filter: &F,
945) -> IndexMap<String, GateDefinition>
946where
947 F: Fn(&str) -> bool,
948{
949 let mut graph: Graph<usize, u8> = Graph::new();
950 let gate_sequence_definitions = gate_definitions
951 .iter()
952 .filter_map(|(gate_name, definition)| {
953 if let GateSpecification::Sequence(sequence) = &definition.specification {
954 Some((gate_name.clone(), sequence.clone()))
955 } else {
956 None
957 }
958 })
959 .map(|(gate_name, sequence)| (gate_name, (graph.add_node(1), sequence)))
960 .collect::<HashMap<_, _>>();
961
962 gate_sequence_definitions
963 .values()
964 .flat_map(|(i, sequence)| {
965 sequence.gates.iter().filter_map(|gate| {
966 if let Some((j, _)) = gate_sequence_definitions.get(&gate.name) {
967 Some((*i, *j))
968 } else {
969 None
970 }
971 })
972 })
973 .for_each(|edge| {
974 graph.add_edge(edge.0, edge.1, 1);
975 });
976
977 let mut space = DfsSpace::new(&graph);
978 let mut seq_defgates_referenced_by_unfiltered_seq_defgates = HashSet::new();
979
980 for (_, (i, _)) in gate_sequence_definitions
981 .iter()
982 .filter(|(name, _)| !filter(name))
983 {
984 for (gate_name, (j, _)) in &gate_sequence_definitions {
985 if petgraph::algo::has_path_connecting(&graph, *i, *j, Some(&mut space)) {
986 seq_defgates_referenced_by_unfiltered_seq_defgates.insert(gate_name.clone());
987 }
988 }
989 }
990
991 gate_definitions
992 .iter()
993 .filter(|(gate_name, definition)| {
994 if let GateSpecification::Sequence(_) = definition.specification {
995 !filter(gate_name)
996 || seq_defgates_referenced_by_unfiltered_seq_defgates.contains(*gate_name)
997 } else {
998 true
999 }
1000 })
1001 .map(|(gate_name, definition)| (gate_name.clone(), definition.clone()))
1002 .collect()
1003}
1004
1005impl Quil for Program {
1006 fn write(
1007 &self,
1008 writer: &mut impl std::fmt::Write,
1009 fall_back_to_debug: bool,
1010 ) -> std::result::Result<(), crate::quil::ToQuilError> {
1011 for instruction in self.to_instructions() {
1012 instruction.write(writer, fall_back_to_debug)?;
1013 writeln!(writer)?;
1014 }
1015 Ok(())
1016 }
1017}
1018
1019impl FromStr for Program {
1020 type Err = ProgramError;
1021 fn from_str(s: &str) -> Result<Self> {
1022 let input = LocatedSpan::new(s);
1023 let lexed = lex(input).map_err(ParseProgramError::<Self>::from)?;
1024 map_parsed(
1025 disallow_leftover(
1026 parse_instructions(&lexed).map_err(ParseError::from_nom_internal_err),
1027 ),
1028 |instructions| {
1029 let mut program = Self::new();
1030 program.add_instructions(instructions);
1031 program
1032 },
1033 )
1034 .map_err(ProgramError::from)
1035 }
1036}
1037
1038impl From<Vec<Instruction>> for Program {
1039 fn from(instructions: Vec<Instruction>) -> Self {
1040 let mut p = Program::new();
1041 p.add_instructions(instructions);
1042 p
1043 }
1044}
1045
1046impl ops::Add<Program> for Program {
1047 type Output = Program;
1048
1049 fn add(mut self, rhs: Program) -> Program {
1050 self += rhs;
1051 self
1052 }
1053}
1054
1055impl ops::AddAssign<Program> for Program {
1056 fn add_assign(&mut self, rhs: Program) {
1057 self.calibrations.extend(rhs.calibrations);
1058 self.memory_regions.extend(rhs.memory_regions);
1059 self.frames.merge(rhs.frames);
1060 self.waveforms.extend(rhs.waveforms);
1061 self.gate_definitions.extend(rhs.gate_definitions);
1062 self.extern_pragma_map.extend(rhs.extern_pragma_map);
1063 self.instructions.extend(rhs.instructions);
1064 self.used_qubits.extend(rhs.used_qubits);
1065 }
1066}
1067
1068#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
1069#[cfg_attr(
1070 feature = "python",
1071 derive(pyo3::FromPyObject, pyo3::IntoPyObject, pyo3::IntoPyObjectRef)
1072)]
1073pub struct InstructionIndex(pub usize);
1074
1075impl InstructionIndex {
1076 fn map(self, f: impl FnOnce(usize) -> usize) -> Self {
1077 Self(f(self.0))
1078 }
1079}
1080
1081type ProgramCalibrationExpansionSourceMap =
1082 SourceMap<InstructionIndex, ExpansionResult<CalibrationExpansion>>;
1083
1084#[cfg(test)]
1085mod tests {
1086 use super::Program;
1087 use crate::{
1088 expression::{
1089 Expression, InfixExpression, InfixOperator, PrefixExpression, PrefixOperator,
1090 },
1091 imag,
1092 instruction::{
1093 CalibrationIdentifier, Call, Declaration, DefGateSequence, DefaultHandler,
1094 ExternSignatureMap, Gate, GateDefinition, GateSpecification, Instruction,
1095 InstructionHandler, Jump, JumpUnless, JumpWhen, Label, Matrix, MemoryReference, Qubit,
1096 QubitPlaceholder, ScalarType, Target, TargetPlaceholder, UnresolvedCallArgument,
1097 Vector, RESERVED_PRAGMA_EXTERN,
1098 },
1099 program::{
1100 calibration::{CalibrationExpansion, CalibrationSource},
1101 source_map::{ExpansionResult, SourceMap, SourceMapEntry},
1102 InstructionIndex, MemoryAccesses,
1103 },
1104 quil::{Quil, INDENT},
1105 real,
1106 };
1107 use approx::assert_abs_diff_eq;
1108 use insta::{assert_debug_snapshot, assert_snapshot};
1109 use internment::ArcIntern;
1110 use ndarray::{array, linalg::kron, Array2};
1111 use num_complex::Complex64;
1112 use once_cell::sync::Lazy;
1113
1114 use rstest::rstest;
1115 use std::{
1116 collections::{HashMap, HashSet},
1117 str::FromStr,
1118 };
1119
1120 #[test]
1121 fn program_eq() {
1122 let input = "
1123DECLARE ro BIT
1124MEASURE q ro
1125JUMP-UNLESS @end-reset ro
1126X q
1127LABEL @end-reset
1128
1129DEFCAL I 0:
1130 DELAY 0 1.0
1131DEFFRAME 0 \"rx\":
1132 HARDWARE-OBJECT: \"hardware\"
1133DEFWAVEFORM custom:
1134 1,2
1135I 0
1136";
1137 let a = Program::from_str(input).unwrap();
1138 let b = Program::from_str(input).unwrap();
1139 assert_eq!(a, b);
1140 }
1141
1142 #[test]
1143 fn program_neq() {
1144 let input_a = "
1145DECLARE ro BIT
1146MEASURE q ro
1147JUMP-UNLESS @end-reset ro
1148X q
1149LABEL @end-reset
1150
1151DEFCAL I 0:
1152 DELAY 0 1.0
1153DEFFRAME 0 \"rx\":
1154 HARDWARE-OBJECT: \"hardware\"
1155DEFWAVEFORM custom:
1156 1,2
1157I 0
1158";
1159 let input_b = "
1160DECLARE readout BIT
1161MEASURE q readout
1162JUMP-UNLESS @end-reset readout
1163X q
1164LABEL @end-reset
1165
1166DEFCAL I 1:
1167 DELAY 1 1.0
1168DEFFRAME 1 \"rx\":
1169 HARDWARE-OBJECT: \"hardware\"
1170DEFWAVEFORM custom:
1171 1,2
1172I 0
1173";
1174 let a = Program::from_str(input_a).unwrap();
1175 let b = Program::from_str(input_b).unwrap();
1176 assert_ne!(a, b);
1177 }
1178
1179 #[test]
1182 fn program_headers() {
1183 let input = "
1184DECLARE ro BIT[5]
1185DEFCAL I 0:
1186 DELAY 0 1.0
1187DEFFRAME 0 \"rx\":
1188 HARDWARE-OBJECT: \"hardware\"
1189DEFWAVEFORM custom:
1190 1, 2
1191I 0
1192";
1193 let program = Program::from_str(input).unwrap();
1194 assert_eq!(program.calibrations.len(), 1);
1195 assert_eq!(program.memory_regions.len(), 1);
1196 assert_eq!(program.frames.len(), 1);
1197 assert_eq!(program.waveforms.len(), 1);
1198 assert_eq!(program.instructions.len(), 1);
1199
1200 assert_eq!(
1201 program.to_quil().unwrap(),
1202 "DECLARE ro BIT[5]
1203DEFFRAME 0 \"rx\":
1204 HARDWARE-OBJECT: \"hardware\"
1205DEFWAVEFORM custom:
1206 1, 2
1207DEFCAL I 0:
1208 DELAY 0 1
1209I 0
1210"
1211 );
1212 }
1213
1214 #[test]
1215 fn program_deterministic_ordering() {
1216 let input = "
1217DECLARE ro BIT
1218DECLARE anc BIT
1219DECLARE ec BIT
1220";
1221 let program1 = Program::from_str(input).unwrap().to_quil().unwrap();
1222 let program2 = Program::from_str(input).unwrap().to_quil().unwrap();
1223
1224 assert!(program1.lines().eq(program2.lines()));
1227 }
1228
1229 #[test]
1232 fn expand_calibrations() {
1233 let input = r#"DECLARE ro BIT[1]
1234DEFFRAME 0 "a":
1235 HARDWARE-OBJECT: "hardware"
1236
1237DEFCAL I 0:
1238 DECLAREMEM
1239 NOP
1240 NOP
1241
1242DEFCAL DECLAREMEM:
1243 DECLARE mem BIT[1]
1244 NOP
1245
1246I 0
1247PULSE 0 "a" custom_waveform
1248I 0
1249"#;
1250
1251 let expected = "DECLARE ro BIT[1]
1252DECLARE mem BIT[1]
1253DEFFRAME 0 \"a\":
1254 HARDWARE-OBJECT: \"hardware\"
1255DEFCAL I 0:
1256 DECLAREMEM
1257 NOP
1258 NOP
1259DEFCAL DECLAREMEM:
1260 DECLARE mem BIT[1]
1261 NOP
1262NOP
1263NOP
1264NOP
1265PULSE 0 \"a\" custom_waveform
1266NOP
1267NOP
1268NOP
1269";
1270
1271 let expected_source_map = SourceMap {
1272 entries: vec![
1273 SourceMapEntry {
1274 source_location: InstructionIndex(0),
1275 target_location: ExpansionResult::Rewritten(CalibrationExpansion {
1276 calibration_used: CalibrationIdentifier {
1277 name: "I".to_string(),
1278 qubits: vec![Qubit::Fixed(0)],
1279 modifiers: vec![],
1280 parameters: vec![],
1281 }
1282 .into(),
1283 range: InstructionIndex(0)..InstructionIndex(3),
1284 expansions: SourceMap {
1285 entries: vec![
1286 SourceMapEntry {
1287 source_location: InstructionIndex(0),
1288 target_location: ExpansionResult::Rewritten(
1289 CalibrationExpansion {
1290 calibration_used: CalibrationSource::Calibration(
1291 CalibrationIdentifier {
1292 modifiers: vec![],
1293 name: "DECLAREMEM".to_string(),
1294 parameters: vec![],
1295 qubits: vec![],
1296 },
1297 ),
1298 range: InstructionIndex(0)..InstructionIndex(1),
1299 expansions: SourceMap {
1300 entries: vec![
1301 SourceMapEntry {
1302 source_location: InstructionIndex(0),
1303 target_location:
1304 ExpansionResult::Unmodified(
1305 InstructionIndex(0),
1306 ),
1307 },
1308 SourceMapEntry {
1309 source_location: InstructionIndex(1),
1310 target_location:
1311 ExpansionResult::Unmodified(
1312 InstructionIndex(1),
1313 ),
1314 },
1315 ],
1316 },
1317 },
1318 ),
1319 },
1320 SourceMapEntry {
1321 source_location: InstructionIndex(1),
1322 target_location: ExpansionResult::Unmodified(InstructionIndex(
1323 2,
1324 )),
1325 },
1326 SourceMapEntry {
1327 source_location: InstructionIndex(2),
1328 target_location: ExpansionResult::Unmodified(InstructionIndex(
1329 3,
1330 )),
1331 },
1332 ],
1333 },
1334 }),
1335 },
1336 SourceMapEntry {
1337 source_location: InstructionIndex(1),
1338 target_location: ExpansionResult::Unmodified(InstructionIndex(3)),
1339 },
1340 SourceMapEntry {
1341 source_location: InstructionIndex(2),
1342 target_location: ExpansionResult::Rewritten(CalibrationExpansion {
1343 calibration_used: CalibrationIdentifier {
1344 name: "I".to_string(),
1345 qubits: vec![Qubit::Fixed(0)],
1346 modifiers: vec![],
1347 parameters: vec![],
1348 }
1349 .into(),
1350 range: InstructionIndex(4)..InstructionIndex(7),
1351 expansions: SourceMap {
1352 entries: vec![
1353 SourceMapEntry {
1354 source_location: InstructionIndex(0),
1355 target_location: ExpansionResult::Rewritten(
1356 CalibrationExpansion {
1357 calibration_used: CalibrationSource::Calibration(
1358 CalibrationIdentifier {
1359 modifiers: vec![],
1360 name: "DECLAREMEM".to_string(),
1361 parameters: vec![],
1362 qubits: vec![],
1363 },
1364 ),
1365 range: InstructionIndex(0)..InstructionIndex(1),
1366 expansions: SourceMap {
1367 entries: vec![
1368 SourceMapEntry {
1369 source_location: InstructionIndex(0),
1370 target_location:
1371 ExpansionResult::Unmodified(
1372 InstructionIndex(0),
1373 ),
1374 },
1375 SourceMapEntry {
1376 source_location: InstructionIndex(1),
1377 target_location:
1378 ExpansionResult::Unmodified(
1379 InstructionIndex(1),
1380 ),
1381 },
1382 ],
1383 },
1384 },
1385 ),
1386 },
1387 SourceMapEntry {
1388 source_location: InstructionIndex(1),
1389 target_location: ExpansionResult::Unmodified(InstructionIndex(
1390 2,
1391 )),
1392 },
1393 SourceMapEntry {
1394 source_location: InstructionIndex(2),
1395 target_location: ExpansionResult::Unmodified(InstructionIndex(
1396 3,
1397 )),
1398 },
1399 ],
1400 },
1401 }),
1402 },
1403 ],
1404 };
1405
1406 let program = Program::from_str(input).unwrap();
1407 let (expanded_program, source_map) = program.expand_calibrations_with_source_map().unwrap();
1408 pretty_assertions::assert_eq!(expanded_program.to_quil().unwrap(), expected);
1409 pretty_assertions::assert_eq!(source_map, expected_source_map);
1410 }
1411
1412 #[test]
1413 fn frame_blocking() {
1414 let input = "DEFFRAME 0 \"a\":
1415\tHARDWARE-OBJECT: \"hardware\"
1416
1417DEFFRAME 0 \"b\":
1418\tHARDWARE-OBJECT: \"hardware\"
1419
1420DEFFRAME 1 \"c\":
1421\tHARDWARE-OBJECT: \"hardware\"
1422
1423DEFFRAME 0 1 \"2q\":
1424\tHARDWARE-OBJECT: \"hardware\"
1425";
1426
1427 let program = Program::from_str(input).unwrap();
1428
1429 for (instruction_string, expected_used_frames, expected_blocked_frames) in vec![
1430 (
1432 r#"PULSE 0 "a" custom_waveform"#,
1433 vec![r#"0 "a""#],
1434 vec![r#"0 "b""#, r#"0 1 "2q""#],
1435 ),
1436 (
1437 r#"PULSE 1 "c" custom_waveform"#,
1438 vec![r#"1 "c""#],
1439 vec![r#"0 1 "2q""#],
1440 ),
1441 (r#"PULSE 2 "a" custom_waveform"#, vec![], vec![]),
1443 (
1445 r#"CAPTURE 0 "a" custom_waveform ro[0]"#,
1446 vec![r#"0 "a""#],
1447 vec![r#"0 "b""#, r#"0 1 "2q""#],
1448 ),
1449 (
1450 r#"CAPTURE 1 "c" custom_waveform ro[0]"#,
1451 vec![r#"1 "c""#],
1452 vec![r#"0 1 "2q""#],
1453 ),
1454 (r#"CAPTURE 2 "a" custom_waveform ro[0]"#, vec![], vec![]),
1455 (
1457 r#"RAW-CAPTURE 0 "a" 1e-6 ro[0]"#,
1458 vec![r#"0 "a""#],
1459 vec![r#"0 "b""#, r#"0 1 "2q""#],
1460 ),
1461 (
1462 r#"RAW-CAPTURE 1 "c" 1e-6 ro[0]"#,
1463 vec![r#"1 "c""#],
1464 vec![r#"0 1 "2q""#],
1465 ),
1466 (r#"RAW-CAPTURE 2 "a" 1e-6 ro[0]"#, vec![], vec![]),
1467 (
1469 r#"NONBLOCKING PULSE 0 "a" custom_waveform"#,
1470 vec![r#"0 "a""#],
1471 vec![],
1472 ),
1473 (
1474 r#"NONBLOCKING PULSE 1 "c" custom_waveform"#,
1475 vec![r#"1 "c""#],
1476 vec![],
1477 ),
1478 (
1479 r#"NONBLOCKING PULSE 0 1 "2q" custom_waveform"#,
1480 vec![r#"0 1 "2q""#],
1481 vec![],
1482 ),
1483 (r#"FENCE 1"#, vec![], vec![r#"1 "c""#, r#"0 1 "2q""#]),
1485 (
1487 r#"FENCE"#,
1488 vec![],
1489 vec![r#"0 "a""#, r#"0 "b""#, r#"1 "c""#, r#"0 1 "2q""#],
1490 ),
1491 (r#"DELAY 0 1.0"#, vec![r#"0 "a""#, r#"0 "b""#], vec![]),
1493 (r#"DELAY 1 1.0"#, vec![r#"1 "c""#], vec![]),
1494 (r#"DELAY 1 "c" 1.0"#, vec![r#"1 "c""#], vec![]),
1495 (r#"DELAY 0 1 1.0"#, vec![r#"0 1 "2q""#], vec![]),
1496 (
1497 r#"SWAP-PHASES 0 "a" 0 "b""#,
1498 vec![r#"0 "a""#, r#"0 "b""#],
1499 vec![],
1500 ),
1501 ] {
1502 let instruction = Instruction::parse_in_test(instruction_string).unwrap();
1503 let matched_frames = DefaultHandler
1504 .matching_frames(&program, &instruction)
1505 .unwrap();
1506 let used_frames: HashSet<String> = matched_frames
1507 .used
1508 .iter()
1509 .map(|f| f.to_quil_or_debug())
1510 .collect();
1511 let expected_used_frames: HashSet<String> = expected_used_frames
1512 .into_iter()
1513 .map(|el| el.to_owned())
1514 .collect();
1515 assert_eq!(
1516 used_frames, expected_used_frames,
1517 "Instruction {instruction} *used* frames `{used_frames:?}` but we expected `{expected_used_frames:?}`", instruction=instruction.to_quil_or_debug()
1518 );
1519
1520 let blocked_frames: HashSet<String> = matched_frames
1521 .blocked
1522 .iter()
1523 .map(|f| f.to_quil_or_debug())
1524 .collect();
1525 let expected_blocked_frames: HashSet<String> = expected_blocked_frames
1526 .into_iter()
1527 .map(|el| el.to_owned())
1528 .collect();
1529 assert_eq!(
1530 blocked_frames, expected_blocked_frames,
1531 "Instruction {instruction} *blocked* frames `{blocked_frames:?}` but we expected `{expected_blocked_frames:?}`", instruction=instruction.to_quil_or_debug()
1532 );
1533 }
1534 }
1535
1536 #[test]
1537 fn into_simplified() {
1538 let input = "
1539DEFCAL MEASURE 0 addr:
1540 CAPTURE 0 \"ro_rx\" custom addr
1541
1542DEFCAL MEASURE 1 addr:
1543 CAPTURE 1 \"ro_rx\" custom addr
1544
1545DEFFRAME 0 \"ro_rx\":
1546 ATTRIBUTE: \"value\"
1547
1548DEFFRAME 1 \"ro_rx\":
1549 ATTRIBUTE: \"other\"
1550
1551DEFWAVEFORM custom:
1552 0.0, 1.0
1553
1554DEFWAVEFORM other_custom:
1555 2.0, 3.0
1556
1557DECLARE ro BIT
1558MEASURE 0 ro
1559";
1560
1561 let expected = "
1562DECLARE ro BIT
1563
1564DEFFRAME 0 \"ro_rx\":
1565 ATTRIBUTE: \"value\"
1566
1567DEFWAVEFORM custom:
1568 0.0, 1.0
1569
1570CAPTURE 0 \"ro_rx\" custom ro
1571";
1572 let program = Program::from_str(input).map_err(|e| e.to_string()).unwrap();
1573 let program = program.simplify(&DefaultHandler).unwrap();
1574 assert_eq!(program, Program::from_str(expected).unwrap());
1575 }
1576
1577 #[test]
1578 fn test_get_qubits() {
1579 let input = "
1580DECLARE ro BIT
1581MEASURE q ro
1582JUMP-UNLESS @end-reset ro
1583X q
1584LABEL @end-reset
1585DEFCAL I 0:
1586 DELAY 0 1.0
1587DEFFRAME 0 \"rx\":
1588 HARDWARE-OBJECT: \"hardware\"
1589DEFWAVEFORM custom:
1590 1,2
1591I 0
1592";
1593 let program = Program::from_str(input).unwrap();
1594 let expected_owned = [Qubit::Fixed(0), Qubit::Variable("q".to_string())];
1595 let expected = expected_owned.iter().collect::<HashSet<_>>();
1596 let actual = program.get_used_qubits();
1597 assert_eq!(expected, actual.iter().collect());
1598 }
1599
1600 #[test]
1601 fn test_add_instructions() {
1602 let mut p = Program::new();
1603 let instrs = vec![Instruction::Nop(), Instruction::Nop()];
1604 p.add_instructions(instrs.clone());
1605 assert_eq!(p.instructions, instrs);
1606 }
1607
1608 #[test]
1609 fn test_add_programs() {
1610 let lhs_input = "
1611DECLARE ro BIT
1612
1613MEASURE q ro
1614X q
1615
1616DEFCAL I 0:
1617 DELAY 0 1.0
1618DEFFRAME 0 \"rx\":
1619 HARDWARE-OBJECT: \"hardware\"
1620DEFWAVEFORM custom:
1621 1,2
1622DEFGATE FOO:
1623 1, 0
1624 0, 1
1625I 0
1626";
1627 let rhs_input = "
1628DECLARE foo REAL
1629H 1
1630CNOT 2 3
1631
1632DEFCAL I 1:
1633 DELAY 0 1.0
1634DEFFRAME 1 \"rx\":
1635 HARDWARE-OBJECT: \"hardware\"
1636DEFWAVEFORM custom2:
1637 1,2
1638DEFGATE BAR:
1639 0, 1
1640 1, 0
1641";
1642 let lhs = Program::from_str(lhs_input).unwrap();
1643 let rhs = Program::from_str(rhs_input).unwrap();
1644
1645 let sum = lhs.clone() + rhs.clone();
1646 let mut in_place_sum = lhs.clone();
1647 in_place_sum += rhs;
1648
1649 let expected_qubits = [
1650 Qubit::Fixed(0),
1651 Qubit::Fixed(1),
1652 Qubit::Fixed(2),
1653 Qubit::Fixed(3),
1654 Qubit::Variable("q".to_string()),
1655 ];
1656
1657 let expected_qubits = expected_qubits.iter().collect::<HashSet<_>>();
1658 for program in [&sum, &in_place_sum] {
1659 assert_eq!(program.calibrations.len(), 2);
1660 assert_eq!(program.memory_regions.len(), 2);
1661 assert_eq!(program.frames.len(), 2);
1662 assert_eq!(program.waveforms.len(), 2);
1663 assert_eq!(program.instructions.len(), 5);
1664 assert_eq!(expected_qubits, sum.get_used_qubits().iter().collect());
1665 }
1666 }
1667
1668 #[test]
1669 fn test_from_vec_instructions() {
1670 let expected: Program = "NOP\nNOP".parse().expect("Should parse NOPs");
1671 let p: Program = expected.instructions.clone().into();
1672 assert_eq!(expected, p);
1673 }
1674
1675 #[test]
1676 fn test_clone_without_body_instructions() {
1677 let quil = "
1678DECLARE ro BIT
1679MEASURE q ro
1680JUMP-UNLESS @end-reset ro
1681X q
1682LABEL @end-reset
1683
1684DEFCAL I 0:
1685 DELAY 0 1.0
1686DEFFRAME 0 \"rx\":
1687 HARDWARE-OBJECT: \"hardware\"
1688DEFWAVEFORM custom:
1689 1,2
1690I 0
1691";
1692 let original = Program::from_str(quil).unwrap();
1694 assert!(!original.instructions.is_empty());
1695
1696 let mut cloned = original.clone_without_body_instructions();
1697 assert!(cloned.instructions.is_empty());
1699 assert!(cloned.used_qubits.is_empty());
1700
1701 cloned.add_instructions(original.instructions.clone());
1704 assert_eq!(original, cloned);
1705 }
1706
1707 static _0: Complex64 = real!(0.0);
1708 static _1: Complex64 = real!(1.0);
1709 static _I: Complex64 = imag!(1.0);
1710 static _1_SQRT_2: Complex64 = real!(std::f64::consts::FRAC_1_SQRT_2);
1711 static H: Lazy<Matrix> = Lazy::new(|| array![[_1, _1], [_1, -_1]] * _1_SQRT_2);
1712 static X: Lazy<Matrix> = Lazy::new(|| array![[_0, _1], [_1, _0]]);
1713 static Y: Lazy<Matrix> = Lazy::new(|| array![[_0, -_I], [_I, _0]]);
1714 static Z: Lazy<Matrix> = Lazy::new(|| array![[_1, _0], [_0, -_1]]);
1715 static CNOT: Lazy<Matrix> = Lazy::new(|| {
1716 array![
1717 [_1, _0, _0, _0],
1718 [_0, _1, _0, _0],
1719 [_0, _0, _0, _1],
1720 [_0, _0, _1, _0]
1721 ]
1722 });
1723 static I2: Lazy<Matrix> = Lazy::new(|| Array2::eye(2));
1724 static I4: Lazy<Matrix> = Lazy::new(|| Array2::eye(4));
1725
1726 #[rstest]
1727 #[case("H 0\nH 1\nH 0", 2, &kron(&H, &I2))]
1728 #[case("H 0\nX 1\nY 2\nZ 3", 4, &kron(&Z, &kron(&Y, &kron(&X, &H))))]
1729 #[case("X 2\nCNOT 2 1\nCNOT 1 0", 3, &kron(&I2, &CNOT).dot(&kron(&CNOT, &I2)).dot(&kron(&X, &I4)))]
1730 fn test_to_unitary(#[case] input: &str, #[case] n_qubits: u64, #[case] expected: &Matrix) {
1731 let program = Program::from_str(input);
1732 assert!(program.is_ok());
1733 let matrix = program.unwrap().to_unitary(n_qubits);
1734 assert!(matrix.is_ok());
1735 assert_abs_diff_eq!(matrix.as_ref().unwrap(), expected);
1736 }
1737
1738 #[test]
1741 fn test_to_instructions() {
1742 let input = format!(
1743 "DECLARE foo REAL[1]
1744DEFFRAME 1 \"rx\":
1745{INDENT}HARDWARE-OBJECT: \"hardware\"
1746DEFWAVEFORM custom2:
1747{INDENT}1, 2
1748DEFCAL I 1:
1749{INDENT}DELAY 0 1
1750DEFGATE BAR AS MATRIX:
1751{INDENT}0, 1
1752{INDENT}1, 0
1753
1754H 1
1755CNOT 2 3
1756"
1757 );
1758 let program = Program::from_str(&input).unwrap();
1759 assert_debug_snapshot!(program.to_instructions());
1760 assert_eq!(program.to_quil().unwrap(), input);
1761 assert_eq!(program.to_instructions(), program.into_instructions());
1762 }
1763
1764 #[test]
1765 fn placeholder_replacement() {
1766 let placeholder_1 = QubitPlaceholder::default();
1767 let placeholder_2 = QubitPlaceholder::default();
1768 let label_placeholder_1 = TargetPlaceholder::new(String::from("custom_label"));
1769 let label_placeholder_2 = TargetPlaceholder::new(String::from("custom_label"));
1770
1771 let mut program = Program::new();
1772
1773 program.add_instruction(Instruction::Label(Label {
1774 target: Target::Placeholder(label_placeholder_1.clone()),
1775 }));
1776
1777 program.add_instruction(Instruction::Jump(Jump {
1778 target: Target::Placeholder(label_placeholder_2.clone()),
1779 }));
1780
1781 program.add_instruction(Instruction::JumpWhen(JumpWhen {
1782 target: Target::Placeholder(label_placeholder_2.clone()),
1783 condition: MemoryReference {
1784 name: "ro".to_string(),
1785 index: 0,
1786 },
1787 }));
1788
1789 program.add_instruction(Instruction::JumpUnless(JumpUnless {
1790 target: Target::Placeholder(label_placeholder_2.clone()),
1791 condition: MemoryReference {
1792 name: "ro".to_string(),
1793 index: 0,
1794 },
1795 }));
1796
1797 program.add_instruction(Instruction::Gate(Gate {
1798 name: "X".to_string(),
1799 qubits: vec![Qubit::Placeholder(placeholder_1.clone())],
1800 parameters: vec![],
1801 modifiers: vec![],
1802 }));
1803
1804 program.add_instruction(Instruction::Gate(Gate {
1805 name: "Y".to_string(),
1806 qubits: vec![Qubit::Placeholder(placeholder_2.clone())],
1807 parameters: vec![],
1808 modifiers: vec![],
1809 }));
1810
1811 let mut auto_increment_resolved = program.clone();
1812 auto_increment_resolved.resolve_placeholders();
1813 assert_eq!(
1814 auto_increment_resolved.instructions,
1815 vec![
1816 Instruction::Label(Label {
1817 target: Target::Fixed("custom_label_0".to_string())
1818 }),
1819 Instruction::Jump(Jump {
1820 target: Target::Fixed("custom_label_1".to_string()),
1821 }),
1822 Instruction::JumpWhen(JumpWhen {
1823 target: Target::Fixed("custom_label_1".to_string()),
1824 condition: MemoryReference {
1825 name: "ro".to_string(),
1826 index: 0,
1827 },
1828 }),
1829 Instruction::JumpUnless(JumpUnless {
1830 target: Target::Fixed("custom_label_1".to_string()),
1831 condition: MemoryReference {
1832 name: "ro".to_string(),
1833 index: 0,
1834 },
1835 }),
1836 Instruction::Gate(Gate {
1837 name: "X".to_string(),
1838 qubits: vec![Qubit::Fixed(0)],
1839 parameters: vec![],
1840 modifiers: vec![],
1841 }),
1842 Instruction::Gate(Gate {
1843 name: "Y".to_string(),
1844 qubits: vec![Qubit::Fixed(1)],
1845 parameters: vec![],
1846 modifiers: vec![],
1847 }),
1848 ]
1849 );
1850
1851 let mut custom_resolved = program.clone();
1852 let custom_target_resolutions = HashMap::from([
1853 (label_placeholder_1, "new_label".to_string()),
1854 (label_placeholder_2, "other_new_label".to_string()),
1855 ]);
1856 let custom_qubit_resolutions = HashMap::from([(placeholder_1, 42), (placeholder_2, 10000)]);
1857 custom_resolved.resolve_placeholders_with_custom_resolvers(
1858 Box::new(move |placeholder| custom_target_resolutions.get(placeholder).cloned()),
1859 Box::new(move |placeholder| custom_qubit_resolutions.get(placeholder).copied()),
1860 );
1861 assert_eq!(
1862 custom_resolved.instructions,
1863 vec![
1864 Instruction::Label(Label {
1865 target: Target::Fixed("new_label".to_string())
1866 }),
1867 Instruction::Jump(Jump {
1868 target: Target::Fixed("other_new_label".to_string()),
1869 }),
1870 Instruction::JumpWhen(JumpWhen {
1871 target: Target::Fixed("other_new_label".to_string()),
1872 condition: MemoryReference {
1873 name: "ro".to_string(),
1874 index: 0,
1875 },
1876 }),
1877 Instruction::JumpUnless(JumpUnless {
1878 target: Target::Fixed("other_new_label".to_string()),
1879 condition: MemoryReference {
1880 name: "ro".to_string(),
1881 index: 0,
1882 },
1883 }),
1884 Instruction::Gate(Gate {
1885 name: "X".to_string(),
1886 qubits: vec![Qubit::Fixed(42)],
1887 parameters: vec![],
1888 modifiers: vec![],
1889 }),
1890 Instruction::Gate(Gate {
1891 name: "Y".to_string(),
1892 qubits: vec![Qubit::Fixed(10000)],
1893 parameters: vec![],
1894 modifiers: vec![],
1895 }),
1896 ]
1897 );
1898 }
1899
1900 #[test]
1901 fn test_filter_instructions() {
1902 let input = "DECLARE foo REAL[1]
1903DEFFRAME 1 \"rx\":
1904\tHARDWARE-OBJECT: \"hardware\"
1905DEFCAL I 1:
1906\tDELAY 0 1
1907DEFGATE BAR AS MATRIX:
1908\t0, 1
1909\t1, 0
1910
1911H 1
1912CNOT 2 3";
1913
1914 let program = Program::from_str(input).unwrap();
1915 let program_without_quil_t =
1916 program.filter_instructions(|instruction| !instruction.is_quil_t());
1917 assert_snapshot!(program_without_quil_t.to_quil().unwrap())
1918 }
1919
1920 #[test]
1921 fn test_wrap_in_loop() {
1922 let input = "DECLARE ro BIT
1923DECLARE shot_count INTEGER
1924MEASURE q ro
1925JUMP-UNLESS @end-reset ro
1926X q
1927LABEL @end-reset
1928
1929DEFCAL I 0:
1930 DELAY 0 1.0
1931DEFFRAME 0 \"rx\":
1932 HARDWARE-OBJECT: \"hardware\"
1933DEFWAVEFORM custom:
1934 1,2
1935I 0
1936";
1937 let program = Program::from_str(input).unwrap().wrap_in_loop(
1938 MemoryReference {
1939 name: "shot_count".to_string(),
1940 index: 0,
1941 },
1942 Target::Fixed("loop-start".to_string()),
1943 Target::Fixed("loop-end".to_string()),
1944 10,
1945 );
1946
1947 assert_snapshot!(program.to_quil().unwrap())
1948 }
1949
1950 #[test]
1951 fn test_equality() {
1952 let input = "DECLARE foo REAL[1]
1953DEFFRAME 1 \"rx\":
1954\tHARDWARE-OBJECT: \"hardware\"
1955DEFCAL I 0:
1956\tDELAY 0 1
1957DEFCAL I 1:
1958\tDELAY 0 1
1959DEFCAL I 2:
1960\tDELAY 0 1
1961DEFCAL MEASURE 0 addr:
1962\tCAPTURE 0 \"ro_rx\" custom addr
1963DEFCAL MEASURE 1 addr:
1964\tCAPTURE 1 \"ro_rx\" custom addr
1965DEFWAVEFORM custom:
1966\t1,2
1967DEFWAVEFORM custom2:
1968\t3,4
1969DEFWAVEFORM another1:
1970\t4,5
1971DEFGATE BAR AS MATRIX:
1972\t0, 1
1973\t1, 0
1974DEFGATE FOO AS MATRIX:
1975\t0, 1
1976\t1, 0
1977
1978H 1
1979CNOT 2 3";
1980
1981 let program = Program::from_str(input).unwrap();
1982
1983 let is_global_state_instruction = move |i: &Instruction| -> bool {
1986 matches!(
1987 i,
1988 |Instruction::WaveformDefinition(_)| Instruction::GateDefinition(_)
1989 | Instruction::FrameDefinition(_)
1990 )
1991 };
1992 let mut program2 = program.filter_instructions(|i| !is_global_state_instruction(i));
1996 let global_instructions: Vec<Instruction> = program
1997 .filter_instructions(is_global_state_instruction)
1998 .into_instructions()
1999 .into_iter()
2000 .rev()
2001 .collect();
2002 program2.add_instructions(global_instructions.clone());
2003 assert_eq!(program, program2);
2004
2005 let mut program3 = Program::from_instructions(
2008 program
2009 .filter_instructions(|i| !is_global_state_instruction(i))
2010 .into_instructions()
2011 .into_iter()
2012 .rev()
2013 .collect(),
2014 );
2015 program3.add_instructions(global_instructions);
2016 assert!(program != program3)
2017 }
2018
2019 #[test]
2020 fn test_deterministic_serialization() {
2021 let input = "DECLARE foo REAL[1]
2022DECLARE bar BIT[1]
2023DECLARE baz BIT[1]
2024RX(pi) 0
2025CNOT 0 1
2026DEFCAL I 0:
2027\tDELAY 0 1
2028\tDELAY 1 1
2029DEFCAL I 1:
2030\tDELAY 0 1
2031\tDELAY 1 2
2032DEFCAL I 2:
2033\tDELAY 2 1
2034\tDELAY 2 3
2035DEFCAL MEASURE 0 addr:
2036\tRX(pi) 0
2037\tCAPTURE 0 \"ro_rx\" custom addr
2038DEFCAL MEASURE 1 addr:
2039\tRX(pi/2) 1
2040\tCAPTURE 1 \"ro_rx\" custom addr
2041DEFCAL MEASURE 2 addr:
2042\tRX(pi/2) 2
2043\tCAPTURE 2 \"ro_rx\" custom addr
2044DEFWAVEFORM custom:
2045\t1,2
2046DEFWAVEFORM custom2:
2047\t3,4
2048DEFWAVEFORM another1(%a, %b):
2049\t%a,%b
2050PULSE 0 \"xy\" flat(duration: 1e-6, iq: 2+3i)
2051PULSE 0 \"xy\" another1(a: 1e-6, b: 2+3i)
2052DEFGATE HADAMARD AS MATRIX:
2053\t(1/sqrt(2)),(1/sqrt(2))
2054\t(1/sqrt(2)),((-1)/sqrt(2))
2055DEFGATE RX(%theta) AS MATRIX:
2056\tcos((%theta/2)),((-1i)*sin((%theta/2)))
2057\t((-1i)*sin((%theta/2))),cos((%theta/2))
2058DEFGATE Name AS PERMUTATION:
2059\t1, 0
2060DEFCIRCUIT SIMPLE:
2061\tX 0
2062\tX 1
2063DEFGATE BAR AS MATRIX:
2064\t0, 1
2065\t1, 0
2066DEFGATE FOO AS MATRIX:
2067\t0, 1
2068\t1, 0
2069DEFGATE BAZ AS MATRIX:
2070\t1, 0
2071\t0, 1
2072MEASURE 1 bar
2073MEASURE 0 foo
2074HALT
2075DEFCIRCUIT CIRCFOO:
2076\tLABEL @FOO_A
2077\tJUMP @FOO_A
2078DEFFRAME 0 \"xy\":
2079\tSAMPLE-RATE: 3000
2080DEFFRAME 0 \"xy\":
2081\tDIRECTION: \"rx\"
2082\tCENTER-FREQUENCY: 1000
2083\tHARDWARE-OBJECT: \"some object\"
2084\tINITIAL-FREQUENCY: 2000
2085\tSAMPLE-RATE: 3000";
2086 let program = Program::from_str(input).unwrap();
2087 let quil = program.to_quil().unwrap();
2088
2089 let iterations = 100;
2093 for _ in 0..iterations {
2094 let new_program = Program::from_str(input).unwrap();
2095 assert_eq!(new_program.to_quil().unwrap(), quil);
2096 }
2097 }
2098
2099 #[test]
2103 fn test_extern_call() {
2104 let input = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
2105DECLARE reals REAL[3]
2106DECLARE octets OCTET[3]
2107CALL foo octets[1] reals
2108"#;
2109 let program = Program::from_str(input).expect("should be able to parse program");
2110 let reserialized = program
2111 .to_quil()
2112 .expect("should be able to serialize program");
2113 assert_eq!(input, reserialized);
2114
2115 let pragma = crate::instruction::Pragma {
2116 name: RESERVED_PRAGMA_EXTERN.to_string(),
2117 arguments: vec![crate::instruction::PragmaArgument::Identifier(
2118 "foo".to_string(),
2119 )],
2120 data: Some("OCTET (params : mut REAL[3])".to_string()),
2121 };
2122 let call = Call {
2123 name: "foo".to_string(),
2124 arguments: vec![
2125 UnresolvedCallArgument::MemoryReference(MemoryReference {
2126 name: "octets".to_string(),
2127 index: 1,
2128 }),
2129 UnresolvedCallArgument::Identifier("reals".to_string()),
2130 ],
2131 };
2132 let expected_program = Program::from_instructions(vec![
2133 Instruction::Declaration(Declaration::new(
2134 "reals".to_string(),
2135 Vector::new(ScalarType::Real, 3),
2136 None,
2137 )),
2138 Instruction::Declaration(Declaration::new(
2139 "octets".to_string(),
2140 Vector::new(ScalarType::Octet, 3),
2141 None,
2142 )),
2143 Instruction::Pragma(pragma.clone()),
2144 Instruction::Call(call.clone()),
2145 ]);
2146 assert_eq!(expected_program, program);
2147
2148 let extern_signature_map = ExternSignatureMap::try_from(program.extern_pragma_map)
2149 .expect("should be able parse extern pragmas");
2150 assert_eq!(extern_signature_map.len(), 1);
2151
2152 assert_eq!(
2153 DefaultHandler
2154 .memory_accesses(&extern_signature_map, &Instruction::Pragma(pragma))
2155 .expect("should be able to get memory accesses"),
2156 MemoryAccesses::default()
2157 );
2158
2159 assert_eq!(
2160 DefaultHandler
2161 .memory_accesses(&extern_signature_map, &Instruction::Call(call))
2162 .expect("should be able to get memory accesses"),
2163 MemoryAccesses {
2164 reads: ["octets", "reals"].into_iter().map(String::from).collect(),
2165 writes: ["octets", "reals"].into_iter().map(String::from).collect(),
2166 ..MemoryAccesses::default()
2167 }
2168 );
2169 }
2170
2171 #[test]
2173 fn test_extern_call_simplification() {
2174 let input = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
2175PRAGMA EXTERN bar "OCTET (params : mut REAL[3])"
2176DECLARE reals REAL[3]
2177DECLARE octets OCTET[3]
2178CALL foo octets[1] reals
2179"#;
2180 let program = Program::from_str(input).expect("should be able to parse program");
2181
2182 let expected = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
2183DECLARE reals REAL[3]
2184DECLARE octets OCTET[3]
2185CALL foo octets[1] reals
2186"#;
2187
2188 let reserialized = program
2189 .expand_calibrations()
2190 .expect("should be able to expand calibrations")
2191 .simplify(&DefaultHandler)
2192 .expect("should be able to simplify program")
2193 .to_quil()
2194 .expect("should be able to serialize program");
2195 assert_eq!(expected, reserialized);
2196 }
2197
2198 #[test]
2201 fn test_defgate_as_sequence_mechanics() {
2202 let pi_divided_by_2 = Expression::Infix(InfixExpression {
2203 operator: InfixOperator::Slash,
2204 left: ArcIntern::new(Expression::PiConstant()),
2205 right: ArcIntern::new(Expression::Number(Complex64 { re: 2.0, im: 0.0 })),
2206 });
2207 let negate_variable = |variable: String| {
2208 Expression::Prefix(PrefixExpression {
2209 operator: PrefixOperator::Minus,
2210 expression: ArcIntern::new(Expression::Variable(variable)),
2211 })
2212 };
2213 let new_gate = |gate_name: &str, param: Expression, qubit: String| {
2214 Gate::new(gate_name, vec![param], vec![Qubit::Variable(qubit)], vec![])
2215 .expect("must be a valid gate")
2216 };
2217 let pmw3 = |param_prefix: String, qubit: String| {
2218 (0..3)
2219 .flat_map(|i| {
2220 vec![
2221 new_gate(
2222 "RZ",
2223 Expression::Variable(format!("{param_prefix}{i}")),
2224 qubit.clone(),
2225 ),
2226 new_gate("RX", pi_divided_by_2.clone(), qubit.clone()),
2227 new_gate(
2228 "RZ",
2229 negate_variable(format!("{param_prefix}{i}")),
2230 qubit.clone(),
2231 ),
2232 ]
2233 })
2234 .collect::<Vec<_>>()
2235 };
2236 let gate_sequence = DefGateSequence::try_new(
2237 ["q0", "q1"].map(String::from).to_vec(),
2238 pmw3("theta".to_string(), "q0".to_string())
2239 .into_iter()
2240 .chain(pmw3("phi".to_string(), "q1".to_string()))
2241 .collect(),
2242 )
2243 .expect("must be valid gate sequence");
2244 let gate_definition = GateDefinition {
2245 name: "PMW3".to_string(),
2246 parameters: vec![
2247 "theta0".to_string(),
2248 "theta1".to_string(),
2249 "theta2".to_string(),
2250 "phi0".to_string(),
2251 "phi1".to_string(),
2252 "phi2".to_string(),
2253 ],
2254 specification: GateSpecification::Sequence(gate_sequence),
2255 };
2256 let mut program = Program::new();
2257 program.add_instruction(Instruction::GateDefinition(gate_definition.clone()));
2258 assert_eq!(program.gate_definitions.len(), 1);
2259 assert_eq!(program.body_instructions().count(), 0);
2260
2261 let invocation = Gate::new(
2262 "PMW3",
2263 vec![
2264 Expression::Address(MemoryReference {
2265 name: "theta".to_string(),
2266 index: 0,
2267 }),
2268 Expression::Address(MemoryReference {
2269 name: "theta".to_string(),
2270 index: 1,
2271 }),
2272 Expression::Address(MemoryReference {
2273 name: "theta".to_string(),
2274 index: 2,
2275 }),
2276 Expression::Address(MemoryReference {
2277 name: "phi".to_string(),
2278 index: 0,
2279 }),
2280 Expression::Address(MemoryReference {
2281 name: "phi".to_string(),
2282 index: 1,
2283 }),
2284 Expression::Address(MemoryReference {
2285 name: "phi".to_string(),
2286 index: 2,
2287 }),
2288 ],
2289 vec![Qubit::Fixed(0), Qubit::Fixed(1)],
2290 vec![],
2291 )
2292 .expect("must be a valid gate");
2293 program.add_instruction(Instruction::Gate(invocation));
2294 assert_eq!(program.body_instructions().count(), 1);
2295
2296 let program_copy = program.clone_without_body_instructions();
2297 assert_eq!(program_copy.gate_definitions.len(), 1);
2298 assert_eq!(
2299 program_copy
2300 .gate_definitions
2301 .get("PMW3")
2302 .expect("must exist"),
2303 &gate_definition
2304 );
2305 assert_eq!(program_copy.body_instructions().count(), 0);
2306 }
2307
2308 #[test]
2313 fn test_gate_sequence_expansion() {
2314 const QUIL: &str = r"
2315DECLARE ro BIT[2]
2316
2317DEFGATE seq1(%param1) a AS SEQUENCE:
2318 RZ(%param1) a
2319
2320DEFGATE seq2() a AS SEQUENCE:
2321 X a
2322
2323seq1(pi/2) 0
2324seq2 1
2325
2326MEASURE 0 ro[0]
2327MEASURE 1 ro[1]
2328";
2329 let program = Program::from_str(QUIL).expect("should parse program");
2330 let exclude = ["seq1"]
2331 .into_iter()
2332 .map(String::from)
2333 .collect::<HashSet<_>>();
2334 let filter = |key: &str| !exclude.contains(key);
2335 let expanded_program = program
2336 .expand_defgate_sequences(filter)
2337 .expect("should expand gate sequences");
2338 const EXPECTED: &str = r"
2339DECLARE ro BIT[2]
2340DEFGATE seq1(%param1) a AS SEQUENCE:
2341 RZ(%param1) a
2342
2343seq1(pi/2) 0
2344X 1
2345MEASURE 0 ro[0]
2346MEASURE 1 ro[1]
2347";
2348 let expected_program = Program::from_str(EXPECTED).expect("should parse expected program");
2349 pretty_assertions::assert_eq!(expanded_program, expected_program);
2350 }
2351
2352 #[test]
2355 fn test_gate_sequence_expansion_preserves_referred_gates() {
2356 const QUIL: &str = r"
2357DECLARE ro BIT[2]
2358
2359DEFGATE seq1(%param1) a AS SEQUENCE:
2360 RZ(%param1) a
2361 seq2() a
2362
2363DEFGATE seq2() a AS SEQUENCE:
2364 H a
2365
2366DEFGATE seq3() a AS SEQUENCE:
2367 X a
2368
2369seq1(pi/2) 0
2370seq2() 1
2371seq3 2
2372
2373MEASURE 0 ro[0]
2374MEASURE 1 ro[1]
2375";
2376 let program = Program::from_str(QUIL).expect("should parse program");
2377 let exclude = ["seq1"]
2378 .into_iter()
2379 .map(String::from)
2380 .collect::<HashSet<_>>();
2381 let filter = |key: &str| !exclude.contains(key);
2382 let expanded_program = program
2383 .expand_defgate_sequences(filter)
2384 .expect("should expand gate sequences");
2385 const EXPECTED: &str = r"
2386DECLARE ro BIT[2]
2387DEFGATE seq1(%param1) a AS SEQUENCE:
2388 RZ(%param1) a
2389 seq2() a
2390
2391DEFGATE seq2() a AS SEQUENCE:
2392 H a
2393
2394seq1(pi/2) 0
2395H 1
2396X 2
2397MEASURE 0 ro[0]
2398MEASURE 1 ro[1]
2399";
2400 let expected_program = Program::from_str(EXPECTED).expect("should parse expected program");
2401 pretty_assertions::assert_eq!(expanded_program, expected_program);
2402 }
2403}