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, CircuitDefinition, 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)]
175#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
176#[cfg_attr(feature = "python", pyo3::pyclass(module = "quil.program", eq))]
177#[cfg_attr(not(feature = "python"), strip_pyo3)]
178pub struct Program {
179 #[pyo3(get, set)]
180 pub calibrations: Calibrations,
181 #[pyo3(get, name = "pragma_extern_map")]
182 pub extern_pragma_map: ExternPragmaMap,
183 #[pyo3(get, set)]
184 pub frames: FrameSet,
185 #[pyo3(get, set)]
186 pub memory_regions: IndexMap<String, MemoryRegion>,
187 #[pyo3(get, set)]
188 pub waveforms: IndexMap<String, Waveform>,
189 #[pyo3(get, set)]
190 pub gate_definitions: IndexMap<String, GateDefinition>,
191 #[pyo3(get, set)]
192 pub circuits: IndexMap<String, CircuitDefinition>,
193 #[pyo3(get, set)]
194 instructions: Vec<Instruction>,
195 #[pyo3(get)]
197 used_qubits: HashSet<Qubit>,
198}
199
200#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
201#[cfg_attr(feature = "python", pyo3::pymethods)]
202#[cfg_attr(not(feature = "python"), strip_pyo3)]
203impl Program {
204 #[new]
205 pub fn new() -> Self {
206 Program::default()
207 }
208
209 pub fn clone_without_body_instructions(&self) -> Self {
211 Self {
212 calibrations: self.calibrations.clone(),
213 extern_pragma_map: self.extern_pragma_map.clone(),
214 frames: self.frames.clone(),
215 memory_regions: self.memory_regions.clone(),
216 waveforms: self.waveforms.clone(),
217 gate_definitions: self.gate_definitions.clone(),
218 circuits: self.circuits.clone(),
219 instructions: Vec::new(),
220 used_qubits: HashSet::new(),
221 }
222 }
223
224 pub fn add_instruction(&mut self, instruction: Instruction) {
231 self.used_qubits
232 .extend(instruction.get_qubits().into_iter().cloned());
233
234 match instruction {
235 Instruction::CalibrationDefinition(calibration) => {
236 self.calibrations.insert_calibration(calibration);
237 }
238 Instruction::CircuitDefinition(circuit) => {
239 self.circuits.insert(circuit.name.clone(), circuit);
240 }
241 Instruction::FrameDefinition(FrameDefinition {
242 identifier,
243 attributes,
244 }) => {
245 self.frames.insert(identifier, attributes);
246 }
247 Instruction::Declaration(Declaration {
248 name,
249 size,
250 sharing,
251 }) => {
252 self.memory_regions
253 .insert(name, MemoryRegion { size, sharing });
254 }
255 Instruction::GateDefinition(gate_definition) => {
256 self.gate_definitions
257 .insert(gate_definition.name.clone(), gate_definition);
258 }
259 Instruction::MeasureCalibrationDefinition(calibration) => {
260 self.calibrations
261 .insert_measurement_calibration(calibration);
262 }
263 Instruction::WaveformDefinition(WaveformDefinition { name, definition }) => {
264 self.waveforms.insert(name, definition);
265 }
266 Instruction::Gate(gate) => {
267 self.instructions.push(Instruction::Gate(gate));
268 }
269 Instruction::Measurement(measurement) => {
270 self.instructions
271 .push(Instruction::Measurement(measurement));
272 }
273 Instruction::Reset(reset) => {
274 self.instructions.push(Instruction::Reset(reset));
275 }
276 Instruction::Delay(delay) => {
277 self.instructions.push(Instruction::Delay(delay));
278 }
279 Instruction::Fence(fence) => {
280 self.instructions.push(Instruction::Fence(fence));
281 }
282 Instruction::Capture(capture) => {
283 self.instructions.push(Instruction::Capture(capture));
284 }
285 Instruction::Pulse(pulse) => {
286 self.instructions.push(Instruction::Pulse(pulse));
287 }
288 Instruction::Pragma(pragma) if pragma.name == RESERVED_PRAGMA_EXTERN => {
289 self.extern_pragma_map.insert(pragma);
290 }
291 Instruction::RawCapture(raw_capture) => {
292 self.instructions.push(Instruction::RawCapture(raw_capture));
293 }
294 other => self.instructions.push(other),
295 }
296 }
297
298 pub fn dagger(&self) -> Result<Self> {
305 self.to_instructions().into_iter().try_rfold(
306 Program::new(),
307 |mut new_program, instruction| match instruction {
308 Instruction::Gate(gate) => {
309 new_program.add_instruction(Instruction::Gate(gate.dagger()));
310 Ok(new_program)
311 }
312 _ => Err(ProgramError::UnsupportedOperation(instruction)),
313 },
314 )
315 }
316
317 pub fn expand_calibrations(&self) -> Result<Self> {
325 self.expand_calibrations_inner(None)
326 }
327
328 pub fn wrap_in_loop(
343 &self,
344 loop_count_reference: MemoryReference,
345 start_target: Target,
346 end_target: Target,
347 iterations: u32,
348 ) -> Self {
349 if iterations == 0 {
350 return self.clone();
351 }
352
353 let mut looped_program = self.clone_without_body_instructions();
354
355 looped_program.add_instructions(
356 vec![
357 Instruction::Declaration(Declaration {
358 name: loop_count_reference.name.clone(),
359 size: Vector {
360 data_type: ScalarType::Integer,
361 length: 1,
362 },
363 sharing: None,
364 }),
365 Instruction::Move(Move {
366 destination: loop_count_reference.clone(),
367 source: ArithmeticOperand::LiteralInteger(iterations.into()),
368 }),
369 Instruction::Label(Label {
370 target: start_target.clone(),
371 }),
372 ]
373 .into_iter()
374 .chain(self.body_instructions().cloned())
375 .chain(vec![
376 Instruction::Arithmetic(Arithmetic {
377 operator: ArithmeticOperator::Subtract,
378 destination: MemoryReference {
379 name: loop_count_reference.name.clone(),
380 index: 0,
381 },
382 source: ArithmeticOperand::LiteralInteger(1),
383 }),
384 Instruction::JumpUnless(JumpUnless {
385 target: end_target.clone(),
386 condition: loop_count_reference,
387 }),
388 Instruction::Jump(Jump {
389 target: start_target,
390 }),
391 Instruction::Label(Label { target: end_target }),
392 ])
393 .collect::<Vec<Instruction>>(),
394 );
395
396 looped_program
397 }
398
399 pub fn resolve_placeholders(&mut self) {
405 self.resolve_placeholders_with_custom_resolvers(
406 self.default_target_resolver(),
407 self.default_qubit_resolver(),
408 )
409 }
410
411 pub fn to_instructions(&self) -> Vec<Instruction> {
413 let mut instructions: Vec<Instruction> = Vec::with_capacity(self.len());
414
415 instructions.extend(self.extern_pragma_map.to_instructions());
416 instructions.extend(self.memory_regions.iter().map(|(name, descriptor)| {
417 Instruction::Declaration(Declaration {
418 name: name.clone(),
419 size: descriptor.size.clone(),
420 sharing: descriptor.sharing.clone(),
421 })
422 }));
423 instructions.extend(self.frames.to_instructions());
424 instructions.extend(self.waveforms.iter().map(|(name, definition)| {
425 Instruction::WaveformDefinition(WaveformDefinition {
426 name: name.clone(),
427 definition: definition.clone(),
428 })
429 }));
430 instructions.extend(self.calibrations.to_instructions());
431 instructions.extend(
432 self.gate_definitions
433 .values()
434 .cloned()
435 .map(Instruction::GateDefinition),
436 );
437 instructions.extend(
438 self.circuits
439 .values()
440 .cloned()
441 .map(Instruction::CircuitDefinition),
442 );
443 instructions.extend(self.instructions.clone());
444 instructions
445 }
446}
447
448impl Program {
449 pub fn body_instructions(&self) -> impl Iterator<Item = &Instruction> {
451 self.instructions.iter()
452 }
453
454 pub fn into_body_instructions(self) -> impl Iterator<Item = Instruction> {
455 self.instructions.into_iter()
456 }
457
458 #[cfg(test)]
460 pub(crate) fn for_each_body_instruction<F>(&mut self, closure: F)
461 where
462 F: Fn(&mut Instruction),
463 {
464 let mut instructions = std::mem::take(&mut self.instructions);
465 self.used_qubits.clear();
466
467 instructions.iter_mut().for_each(closure);
468
469 self.add_instructions(instructions);
470 }
471
472 pub fn add_instructions<I>(&mut self, instructions: I)
473 where
474 I: IntoIterator<Item = Instruction>,
475 {
476 instructions
477 .into_iter()
478 .for_each(|i| self.add_instruction(i));
479 }
480
481 pub fn filter_instructions(&self, predicate: impl FnMut(&Instruction) -> bool) -> Program {
484 Program::from_instructions(
485 self.to_instructions()
486 .into_iter()
487 .filter(predicate)
488 .collect(),
489 )
490 }
491
492 pub fn expand_calibrations_with_source_map(
495 &self,
496 ) -> Result<(
497 Program,
498 SourceMap<InstructionIndex, ExpansionResult<CalibrationExpansion>>,
499 )> {
500 let mut source_mapping = ProgramCalibrationExpansionSourceMap::default();
501 let new_program = self.expand_calibrations_inner(Some(&mut source_mapping))?;
502
503 Ok((new_program, source_mapping))
504 }
505
506 fn expand_calibrations_inner(
512 &self,
513 mut source_mapping: Option<&mut ProgramCalibrationExpansionSourceMap>,
514 ) -> Result<Self> {
515 let mut new_program = self.clone_without_body_instructions();
516
517 for (index, instruction) in self.instructions.iter().enumerate() {
518 let index = InstructionIndex(index);
519
520 match self.calibrations.expand_with_detail(instruction, &[])? {
521 Some(expanded) => {
522 new_program.append_calibration_expansion_output_inner(
523 expanded,
524 index,
525 &mut source_mapping,
526 );
527 }
528 None => {
529 new_program.add_instruction(instruction.clone());
530 if let Some(source_mapping) = source_mapping.as_mut() {
531 source_mapping.entries.push(SourceMapEntry {
532 source_location: index,
533 target_location: ExpansionResult::Unmodified(InstructionIndex(
534 new_program.instructions.len() - 1,
535 )),
536 });
537 }
538 }
539 }
540 }
541
542 Ok(new_program)
543 }
544
545 pub fn expand_defgate_sequences<F>(self, filter: F) -> Result<Self>
635 where
636 F: Fn(&str) -> bool,
637 {
638 let (expansion, gate_definitions) = self.initialize_defgate_sequence_expander(filter);
639 let new_instructions = expansion.expand(&self.instructions)?;
640
641 let mut new_program = Self {
642 calibrations: self.calibrations,
643 extern_pragma_map: self.extern_pragma_map,
644 frames: self.frames,
645 memory_regions: self.memory_regions,
646 waveforms: self.waveforms,
647 gate_definitions,
648 circuits: self.circuits.clone(),
649 instructions: Vec::new(),
650 used_qubits: HashSet::new(),
651 };
652 new_program.add_instructions(new_instructions);
653 Ok(new_program)
654 }
655
656 pub fn expand_defgate_sequences_with_source_map<F>(
669 &self,
670 filter: F,
671 ) -> Result<(
672 Self,
673 SourceMap<InstructionIndex, ExpansionResult<DefGateSequenceExpansion<'_>>>,
674 )>
675 where
676 F: Fn(&str) -> bool,
677 {
678 let (expander, gate_definitions) = self.initialize_defgate_sequence_expander(filter);
679 let ExpandedInstructionsWithSourceMap {
680 instructions: new_instructions,
681 source_map,
682 } = expander.expand_with_source_map(&self.instructions)?;
683
684 let mut new_program = Self {
685 calibrations: self.calibrations.clone(),
686 extern_pragma_map: self.extern_pragma_map.clone(),
687 frames: self.frames.clone(),
688 memory_regions: self.memory_regions.clone(),
689 waveforms: self.waveforms.clone(),
690 gate_definitions,
691 circuits: self.circuits.clone(),
692 instructions: Vec::new(),
693 used_qubits: HashSet::new(),
694 };
695 new_program.add_instructions(new_instructions);
696 Ok((new_program, source_map))
697 }
698
699 fn initialize_defgate_sequence_expander<F>(
700 &self,
701 filter: F,
702 ) -> (
703 ProgramDefGateSequenceExpander<'_, F>,
704 IndexMap<String, GateDefinition>,
705 )
706 where
707 F: Fn(&str) -> bool,
708 {
709 let gate_definitions_to_keep =
710 filter_sequence_gate_definitions_to_keep(&self.gate_definitions, &filter);
711 let expansion = ProgramDefGateSequenceExpander::new(&self.gate_definitions, filter);
712 (expansion, gate_definitions_to_keep)
713 }
714
715 fn append_calibration_expansion_output_inner(
721 &mut self,
722 mut expansion_output: CalibrationExpansionOutput,
723 source_index: InstructionIndex,
724 source_mapping: &mut Option<&mut ProgramCalibrationExpansionSourceMap>,
725 ) {
726 if let Some(source_mapping) = source_mapping.as_mut() {
727 let previous_program_instruction_body_length = self.instructions.len();
728
729 for instruction in expansion_output.new_instructions {
730 let start_length = self.instructions.len();
731 self.add_instruction(instruction.clone());
732 let end_length = self.instructions.len();
733
734 if start_length == end_length {
737 let relative_target_index =
738 InstructionIndex(start_length - previous_program_instruction_body_length);
739 expansion_output
740 .detail
741 .remove_target_index(relative_target_index);
742 }
743 }
744
745 expansion_output.detail.range =
746 InstructionIndex(previous_program_instruction_body_length)
747 ..InstructionIndex(self.instructions.len());
748
749 if !expansion_output.detail.range.is_empty() {
750 source_mapping.entries.push(SourceMapEntry {
751 source_location: source_index,
752 target_location: ExpansionResult::Rewritten(expansion_output.detail),
753 });
754 }
755 } else {
756 self.add_instructions(expansion_output.new_instructions);
757 }
758 }
759
760 pub fn from_instructions(instructions: Vec<Instruction>) -> Self {
762 let mut program = Self::default();
763 for instruction in instructions {
764 program.add_instruction(instruction);
765 }
766 program
767 }
768
769 fn get_targets(&self) -> Vec<&Target> {
771 self.instructions
772 .iter()
773 .filter_map(|i| match i {
774 Instruction::Label(label) => Some(&label.target),
775 Instruction::Jump(jump) => Some(&jump.target),
776 Instruction::JumpWhen(jump_when) => Some(&jump_when.target),
777 Instruction::JumpUnless(jump_unless) => Some(&jump_unless.target),
778 _ => None,
779 })
780 .collect()
781 }
782
783 pub fn get_used_qubits(&self) -> &HashSet<Qubit> {
785 &self.used_qubits
786 }
787
788 fn rebuild_used_qubits(&mut self) {
790 self.used_qubits = self
791 .to_instructions()
792 .iter()
793 .flat_map(|instruction| instruction.get_qubits().into_iter().cloned())
794 .collect()
795 }
796
797 pub fn into_instructions(self) -> Vec<Instruction> {
799 let mut instructions: Vec<Instruction> = Vec::with_capacity(self.len());
800
801 instructions.extend(self.memory_regions.into_iter().map(|(name, descriptor)| {
802 Instruction::Declaration(Declaration {
803 name,
804 size: descriptor.size,
805 sharing: descriptor.sharing,
806 })
807 }));
808 instructions.extend(self.frames.into_instructions());
809 instructions.extend(self.waveforms.into_iter().map(|(name, definition)| {
810 Instruction::WaveformDefinition(WaveformDefinition { name, definition })
811 }));
812 instructions.extend(self.calibrations.to_instructions());
813 instructions.extend(
814 self.gate_definitions
815 .into_values()
816 .map(Instruction::GateDefinition),
817 );
818 instructions.extend(
819 self.circuits
820 .into_values()
821 .map(Instruction::CircuitDefinition),
822 );
823 instructions.extend(self.extern_pragma_map.into_instructions());
824 instructions.extend(self.instructions);
825 instructions
826 }
827
828 pub fn simplify<H: InstructionHandler>(&self, handler: &H) -> Result<Self> {
840 let mut expanded_program = self.expand_calibrations()?;
841 expanded_program.calibrations = Calibrations::default();
845
846 let mut frames_used: HashSet<&FrameIdentifier> = HashSet::new();
847 let mut waveforms_used: HashSet<&String> = HashSet::new();
848 let mut extern_signatures_used: HashSet<&String> = HashSet::new();
849
850 for instruction in &expanded_program.instructions {
851 if let Some(matched_frames) = handler.matching_frames(&expanded_program, instruction) {
852 frames_used.extend(matched_frames.used)
853 }
854
855 if let Some(waveform) = instruction.get_waveform_invocation() {
856 waveforms_used.insert(&waveform.name);
857 }
858
859 if let Instruction::Call(Call { name, .. }) = instruction {
860 extern_signatures_used.insert(name);
861 }
862 }
863
864 expanded_program.frames = self.frames.intersection(&frames_used);
865 expanded_program
866 .waveforms
867 .retain(|name, _definition| waveforms_used.contains(name));
868 expanded_program
869 .extern_pragma_map
870 .retain(|name, _signature| {
871 name.as_ref()
872 .map(|name| extern_signatures_used.contains(name))
873 .unwrap_or(false)
874 });
875
876 Ok(expanded_program)
877 }
878
879 #[allow(clippy::type_complexity)]
889 pub fn resolve_placeholders_with_custom_resolvers(
890 &mut self,
891 target_resolver: Box<dyn Fn(&TargetPlaceholder) -> Option<String>>,
892 qubit_resolver: Box<dyn Fn(&QubitPlaceholder) -> Option<u64>>,
893 ) {
894 for instruction in &mut self.instructions {
895 instruction.resolve_placeholders(&target_resolver, &qubit_resolver);
896 }
897 self.rebuild_used_qubits()
898 }
899
900 #[allow(clippy::type_complexity)]
903 pub fn default_target_resolver(&self) -> Box<dyn Fn(&TargetPlaceholder) -> Option<String>> {
904 let mut fixed_labels = HashSet::new();
905 let mut label_placeholders = IndexSet::new();
906 for target in self.get_targets() {
907 match target {
908 Target::Fixed(fixed) => {
909 fixed_labels.insert(fixed.clone());
910 }
911 Target::Placeholder(placeholder) => {
912 label_placeholders.insert(placeholder.clone());
913 }
914 }
915 }
916
917 let target_resolutions: HashMap<TargetPlaceholder, String> = label_placeholders
918 .into_iter()
919 .map(|p| {
920 let base_label = p.as_inner();
921 let mut next_label = format!("{base_label}_0");
922 let mut next_suffix = 1;
923
924 while fixed_labels.contains(&next_label) {
925 next_label = format!("{base_label}_{next_suffix}");
926 next_suffix += 1;
927 }
928 fixed_labels.insert(next_label.clone());
929
930 (p, next_label)
931 })
932 .collect();
933
934 Box::new(move |key| target_resolutions.get(key).cloned())
935 }
936
937 #[allow(clippy::type_complexity)]
940 pub fn default_qubit_resolver(&self) -> Box<dyn Fn(&QubitPlaceholder) -> Option<u64>> {
941 let mut qubits_used: HashSet<u64> = HashSet::new();
942 let mut qubit_placeholders: IndexSet<QubitPlaceholder> = IndexSet::new();
943
944 for instruction in &self.instructions {
946 let qubits = instruction.get_qubits();
947
948 for qubit in qubits {
949 match qubit {
950 Qubit::Fixed(index) => {
951 qubits_used.insert(*index);
952 }
953 Qubit::Placeholder(placeholder) => {
954 qubit_placeholders.insert(placeholder.clone());
955 }
956 Qubit::Variable(_) => {}
957 }
958 }
959 }
960
961 let qubit_iterator = (0u64..).filter(|index| !qubits_used.contains(index));
962 let qubit_resolutions: HashMap<QubitPlaceholder, u64> =
963 qubit_placeholders.into_iter().zip(qubit_iterator).collect();
964
965 Box::new(move |key| qubit_resolutions.get(key).copied())
966 }
967
968 pub fn is_empty(&self) -> bool {
969 self.len() == 0
970 }
971
972 pub fn len(&self) -> usize {
973 self.memory_regions.len()
974 + self.frames.len()
975 + self.waveforms.len()
976 + self.gate_definitions.len()
977 + self.circuits.len()
978 + self.instructions.len()
979 + self.extern_pragma_map.len()
980 }
981
982 pub fn to_unitary(&self, n_qubits: u64) -> Result<Matrix> {
988 let mut umat = Array2::eye(2usize.pow(n_qubits as u32));
989 for instruction in self.instructions.clone() {
990 match instruction {
991 Instruction::Halt() => {}
992 Instruction::Gate(mut gate) => {
993 umat = gate.to_unitary(n_qubits)?.dot(&umat);
994 }
995 _ => return Err(ProgramError::UnsupportedForUnitary(instruction)),
996 }
997 }
998 Ok(umat)
999 }
1000
1001 pub fn get_instruction(&self, index: usize) -> Option<&Instruction> {
1003 self.instructions.get(index)
1004 }
1005
1006 pub fn try_extern_signature_map_from_pragma_map(
1012 &self,
1013 ) -> std::result::Result<ExternSignatureMap, (Pragma, ExternError)> {
1014 ExternSignatureMap::try_from(self.extern_pragma_map.clone())
1015 }
1016}
1017
1018fn filter_sequence_gate_definitions_to_keep<F>(
1024 gate_definitions: &IndexMap<String, GateDefinition>,
1025 filter: &F,
1026) -> IndexMap<String, GateDefinition>
1027where
1028 F: Fn(&str) -> bool,
1029{
1030 let mut graph: Graph<usize, u8> = Graph::new();
1031 let gate_sequence_definitions = gate_definitions
1032 .iter()
1033 .filter_map(|(gate_name, definition)| {
1034 if let GateSpecification::Sequence(sequence) = &definition.specification {
1035 Some((gate_name.clone(), sequence.clone()))
1036 } else {
1037 None
1038 }
1039 })
1040 .map(|(gate_name, sequence)| (gate_name, (graph.add_node(1), sequence)))
1041 .collect::<HashMap<_, _>>();
1042
1043 gate_sequence_definitions
1044 .values()
1045 .flat_map(|(i, sequence)| {
1046 sequence.gates.iter().filter_map(|gate| {
1047 if let Some((j, _)) = gate_sequence_definitions.get(&gate.name) {
1048 Some((*i, *j))
1049 } else {
1050 None
1051 }
1052 })
1053 })
1054 .for_each(|edge| {
1055 graph.add_edge(edge.0, edge.1, 1);
1056 });
1057
1058 let mut space = DfsSpace::new(&graph);
1059 let mut seq_defgates_referenced_by_unfiltered_seq_defgates = HashSet::new();
1060
1061 for (_, (i, _)) in gate_sequence_definitions
1062 .iter()
1063 .filter(|(name, _)| !filter(name))
1064 {
1065 for (gate_name, (j, _)) in &gate_sequence_definitions {
1066 if petgraph::algo::has_path_connecting(&graph, *i, *j, Some(&mut space)) {
1067 seq_defgates_referenced_by_unfiltered_seq_defgates.insert(gate_name.clone());
1068 }
1069 }
1070 }
1071
1072 gate_definitions
1073 .iter()
1074 .filter(|(gate_name, definition)| {
1075 if let GateSpecification::Sequence(_) = definition.specification {
1076 !filter(gate_name)
1077 || seq_defgates_referenced_by_unfiltered_seq_defgates.contains(*gate_name)
1078 } else {
1079 true
1080 }
1081 })
1082 .map(|(gate_name, definition)| (gate_name.clone(), definition.clone()))
1083 .collect()
1084}
1085
1086impl Quil for Program {
1087 fn write(
1088 &self,
1089 writer: &mut impl std::fmt::Write,
1090 fall_back_to_debug: bool,
1091 ) -> std::result::Result<(), crate::quil::ToQuilError> {
1092 for instruction in self.to_instructions() {
1093 instruction.write(writer, fall_back_to_debug)?;
1094 writeln!(writer)?;
1095 }
1096 Ok(())
1097 }
1098}
1099
1100impl FromStr for Program {
1101 type Err = ProgramError;
1102 fn from_str(s: &str) -> Result<Self> {
1103 let input = LocatedSpan::new(s);
1104 let lexed = lex(input).map_err(ParseProgramError::<Self>::from)?;
1105 map_parsed(
1106 disallow_leftover(
1107 parse_instructions(&lexed).map_err(ParseError::from_nom_internal_err),
1108 ),
1109 |instructions| {
1110 let mut program = Self::new();
1111 program.add_instructions(instructions);
1112 program
1113 },
1114 )
1115 .map_err(ProgramError::from)
1116 }
1117}
1118
1119impl From<Vec<Instruction>> for Program {
1120 fn from(instructions: Vec<Instruction>) -> Self {
1121 let mut p = Program::new();
1122 p.add_instructions(instructions);
1123 p
1124 }
1125}
1126
1127impl ops::Add<Program> for Program {
1128 type Output = Program;
1129
1130 fn add(mut self, rhs: Program) -> Program {
1131 self += rhs;
1132 self
1133 }
1134}
1135
1136impl ops::AddAssign<Program> for Program {
1137 fn add_assign(&mut self, rhs: Program) {
1138 self.calibrations.extend(rhs.calibrations);
1139 self.memory_regions.extend(rhs.memory_regions);
1140 self.frames.merge(rhs.frames);
1141 self.waveforms.extend(rhs.waveforms);
1142 self.gate_definitions.extend(rhs.gate_definitions);
1143 self.circuits.extend(rhs.circuits);
1144 self.extern_pragma_map.extend(rhs.extern_pragma_map);
1145 self.instructions.extend(rhs.instructions);
1146 self.used_qubits.extend(rhs.used_qubits);
1147 }
1148}
1149
1150#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
1151#[cfg_attr(
1152 feature = "python",
1153 derive(pyo3::FromPyObject, pyo3::IntoPyObject, pyo3::IntoPyObjectRef)
1154)]
1155pub struct InstructionIndex(pub usize);
1156
1157impl InstructionIndex {
1158 fn map(self, f: impl FnOnce(usize) -> usize) -> Self {
1159 Self(f(self.0))
1160 }
1161}
1162
1163type ProgramCalibrationExpansionSourceMap =
1164 SourceMap<InstructionIndex, ExpansionResult<CalibrationExpansion>>;
1165
1166#[cfg(test)]
1167mod tests {
1168 use super::Program;
1169 use crate::{
1170 expression::{
1171 Expression, InfixExpression, InfixOperator, PrefixExpression, PrefixOperator,
1172 },
1173 imag,
1174 instruction::{
1175 CalibrationIdentifier, Call, Declaration, DefGateSequence, DefaultHandler,
1176 ExternSignatureMap, Gate, GateDefinition, GateSpecification, Instruction,
1177 InstructionHandler, Jump, JumpUnless, JumpWhen, Label, Matrix, MemoryReference, Qubit,
1178 QubitPlaceholder, ScalarType, Target, TargetPlaceholder, UnresolvedCallArgument,
1179 Vector, RESERVED_PRAGMA_EXTERN,
1180 },
1181 program::{
1182 calibration::{CalibrationExpansion, CalibrationSource},
1183 source_map::{ExpansionResult, SourceMap, SourceMapEntry},
1184 InstructionIndex, MemoryAccesses,
1185 },
1186 quil::{Quil, INDENT},
1187 real,
1188 };
1189 use approx::assert_abs_diff_eq;
1190 use insta::{assert_debug_snapshot, assert_snapshot};
1191 use internment::ArcIntern;
1192 use ndarray::{array, linalg::kron, Array2};
1193 use num_complex::Complex64;
1194 use once_cell::sync::Lazy;
1195
1196 use rstest::rstest;
1197 use std::{
1198 collections::{HashMap, HashSet},
1199 str::FromStr,
1200 };
1201
1202 #[test]
1203 fn program_eq() {
1204 let input = "
1205DECLARE ro BIT
1206MEASURE q ro
1207JUMP-UNLESS @end-reset ro
1208X q
1209LABEL @end-reset
1210
1211DEFCAL I 0:
1212 DELAY 0 1.0
1213DEFFRAME 0 \"rx\":
1214 HARDWARE-OBJECT: \"hardware\"
1215DEFWAVEFORM custom:
1216 1,2
1217I 0
1218";
1219 let a = Program::from_str(input).unwrap();
1220 let b = Program::from_str(input).unwrap();
1221 assert_eq!(a, b);
1222 }
1223
1224 #[test]
1225 fn program_neq() {
1226 let input_a = "
1227DECLARE ro BIT
1228MEASURE q ro
1229JUMP-UNLESS @end-reset ro
1230X q
1231LABEL @end-reset
1232
1233DEFCAL I 0:
1234 DELAY 0 1.0
1235DEFFRAME 0 \"rx\":
1236 HARDWARE-OBJECT: \"hardware\"
1237DEFWAVEFORM custom:
1238 1,2
1239I 0
1240";
1241 let input_b = "
1242DECLARE readout BIT
1243MEASURE q readout
1244JUMP-UNLESS @end-reset readout
1245X q
1246LABEL @end-reset
1247
1248DEFCAL I 1:
1249 DELAY 1 1.0
1250DEFFRAME 1 \"rx\":
1251 HARDWARE-OBJECT: \"hardware\"
1252DEFWAVEFORM custom:
1253 1,2
1254I 0
1255";
1256 let a = Program::from_str(input_a).unwrap();
1257 let b = Program::from_str(input_b).unwrap();
1258 assert_ne!(a, b);
1259 }
1260
1261 #[test]
1264 fn program_headers() {
1265 let input = "
1266DECLARE ro BIT[5]
1267DEFCAL I 0:
1268 DELAY 0 1.0
1269DEFFRAME 0 \"rx\":
1270 HARDWARE-OBJECT: \"hardware\"
1271DEFWAVEFORM custom:
1272 1, 2
1273DEFGATE FOO:
1274 1, 0
1275 0, 1
1276
1277DEFCIRCUIT BELL q0 q1:
1278 H q1
1279 CNOT q1 q0
1280
1281I 0
1282";
1283 let program = Program::from_str(input).unwrap();
1284 assert_eq!(program.calibrations.len(), 1);
1285 assert_eq!(program.memory_regions.len(), 1);
1286 assert_eq!(program.frames.len(), 1);
1287 assert_eq!(program.waveforms.len(), 1);
1288 assert_eq!(program.gate_definitions.len(), 1);
1289 assert_eq!(program.circuits.len(), 1);
1290 assert_eq!(program.instructions.len(), 1);
1291
1292 assert_eq!(
1293 program.to_quil().unwrap(),
1294 "DECLARE ro BIT[5]
1295DEFFRAME 0 \"rx\":
1296 HARDWARE-OBJECT: \"hardware\"
1297DEFWAVEFORM custom:
1298 1, 2
1299DEFCAL I 0:
1300 DELAY 0 1
1301DEFGATE FOO AS MATRIX:
1302 1, 0
1303 0, 1
1304
1305DEFCIRCUIT BELL q0 q1:
1306 H q1
1307 CNOT q1 q0
1308
1309I 0
1310"
1311 );
1312 }
1313
1314 #[test]
1315 fn program_deterministic_ordering() {
1316 let input = "
1317DECLARE ro BIT
1318DECLARE anc BIT
1319DECLARE ec BIT
1320";
1321 let program1 = Program::from_str(input).unwrap().to_quil().unwrap();
1322 let program2 = Program::from_str(input).unwrap().to_quil().unwrap();
1323
1324 assert!(program1.lines().eq(program2.lines()));
1327 }
1328
1329 #[test]
1332 fn expand_calibrations() {
1333 let input = r#"DECLARE ro BIT[1]
1334DEFFRAME 0 "a":
1335 HARDWARE-OBJECT: "hardware"
1336
1337DEFCAL I 0:
1338 DECLAREMEM
1339 NOP
1340 NOP
1341
1342DEFCAL DECLAREMEM:
1343 DECLARE mem BIT[1]
1344 NOP
1345
1346I 0
1347PULSE 0 "a" custom_waveform
1348I 0
1349"#;
1350
1351 let expected = "DECLARE ro BIT[1]
1352DECLARE mem BIT[1]
1353DEFFRAME 0 \"a\":
1354 HARDWARE-OBJECT: \"hardware\"
1355DEFCAL I 0:
1356 DECLAREMEM
1357 NOP
1358 NOP
1359DEFCAL DECLAREMEM:
1360 DECLARE mem BIT[1]
1361 NOP
1362NOP
1363NOP
1364NOP
1365PULSE 0 \"a\" custom_waveform
1366NOP
1367NOP
1368NOP
1369";
1370
1371 let expected_source_map = SourceMap {
1372 entries: vec![
1373 SourceMapEntry {
1374 source_location: InstructionIndex(0),
1375 target_location: ExpansionResult::Rewritten(CalibrationExpansion {
1376 calibration_used: CalibrationIdentifier {
1377 name: "I".to_string(),
1378 qubits: vec![Qubit::Fixed(0)],
1379 modifiers: vec![],
1380 parameters: vec![],
1381 }
1382 .into(),
1383 range: InstructionIndex(0)..InstructionIndex(3),
1384 expansions: SourceMap {
1385 entries: vec![
1386 SourceMapEntry {
1387 source_location: InstructionIndex(0),
1388 target_location: ExpansionResult::Rewritten(
1389 CalibrationExpansion {
1390 calibration_used: CalibrationSource::Calibration(
1391 CalibrationIdentifier {
1392 modifiers: vec![],
1393 name: "DECLAREMEM".to_string(),
1394 parameters: vec![],
1395 qubits: vec![],
1396 },
1397 ),
1398 range: InstructionIndex(0)..InstructionIndex(1),
1399 expansions: SourceMap {
1400 entries: vec![
1401 SourceMapEntry {
1402 source_location: InstructionIndex(0),
1403 target_location:
1404 ExpansionResult::Unmodified(
1405 InstructionIndex(0),
1406 ),
1407 },
1408 SourceMapEntry {
1409 source_location: InstructionIndex(1),
1410 target_location:
1411 ExpansionResult::Unmodified(
1412 InstructionIndex(1),
1413 ),
1414 },
1415 ],
1416 },
1417 },
1418 ),
1419 },
1420 SourceMapEntry {
1421 source_location: InstructionIndex(1),
1422 target_location: ExpansionResult::Unmodified(InstructionIndex(
1423 2,
1424 )),
1425 },
1426 SourceMapEntry {
1427 source_location: InstructionIndex(2),
1428 target_location: ExpansionResult::Unmodified(InstructionIndex(
1429 3,
1430 )),
1431 },
1432 ],
1433 },
1434 }),
1435 },
1436 SourceMapEntry {
1437 source_location: InstructionIndex(1),
1438 target_location: ExpansionResult::Unmodified(InstructionIndex(3)),
1439 },
1440 SourceMapEntry {
1441 source_location: InstructionIndex(2),
1442 target_location: ExpansionResult::Rewritten(CalibrationExpansion {
1443 calibration_used: CalibrationIdentifier {
1444 name: "I".to_string(),
1445 qubits: vec![Qubit::Fixed(0)],
1446 modifiers: vec![],
1447 parameters: vec![],
1448 }
1449 .into(),
1450 range: InstructionIndex(4)..InstructionIndex(7),
1451 expansions: SourceMap {
1452 entries: vec![
1453 SourceMapEntry {
1454 source_location: InstructionIndex(0),
1455 target_location: ExpansionResult::Rewritten(
1456 CalibrationExpansion {
1457 calibration_used: CalibrationSource::Calibration(
1458 CalibrationIdentifier {
1459 modifiers: vec![],
1460 name: "DECLAREMEM".to_string(),
1461 parameters: vec![],
1462 qubits: vec![],
1463 },
1464 ),
1465 range: InstructionIndex(0)..InstructionIndex(1),
1466 expansions: SourceMap {
1467 entries: vec![
1468 SourceMapEntry {
1469 source_location: InstructionIndex(0),
1470 target_location:
1471 ExpansionResult::Unmodified(
1472 InstructionIndex(0),
1473 ),
1474 },
1475 SourceMapEntry {
1476 source_location: InstructionIndex(1),
1477 target_location:
1478 ExpansionResult::Unmodified(
1479 InstructionIndex(1),
1480 ),
1481 },
1482 ],
1483 },
1484 },
1485 ),
1486 },
1487 SourceMapEntry {
1488 source_location: InstructionIndex(1),
1489 target_location: ExpansionResult::Unmodified(InstructionIndex(
1490 2,
1491 )),
1492 },
1493 SourceMapEntry {
1494 source_location: InstructionIndex(2),
1495 target_location: ExpansionResult::Unmodified(InstructionIndex(
1496 3,
1497 )),
1498 },
1499 ],
1500 },
1501 }),
1502 },
1503 ],
1504 };
1505
1506 let program = Program::from_str(input).unwrap();
1507 let (expanded_program, source_map) = program.expand_calibrations_with_source_map().unwrap();
1508 pretty_assertions::assert_eq!(expanded_program.to_quil().unwrap(), expected);
1509 pretty_assertions::assert_eq!(source_map, expected_source_map);
1510 }
1511
1512 #[test]
1513 fn frame_blocking() {
1514 let input = "DEFFRAME 0 \"a\":
1515\tHARDWARE-OBJECT: \"hardware\"
1516
1517DEFFRAME 0 \"b\":
1518\tHARDWARE-OBJECT: \"hardware\"
1519
1520DEFFRAME 1 \"c\":
1521\tHARDWARE-OBJECT: \"hardware\"
1522
1523DEFFRAME 0 1 \"2q\":
1524\tHARDWARE-OBJECT: \"hardware\"
1525";
1526
1527 let program = Program::from_str(input).unwrap();
1528
1529 for (instruction_string, expected_used_frames, expected_blocked_frames) in vec![
1530 (
1532 r#"PULSE 0 "a" custom_waveform"#,
1533 vec![r#"0 "a""#],
1534 vec![r#"0 "b""#, r#"0 1 "2q""#],
1535 ),
1536 (
1537 r#"PULSE 1 "c" custom_waveform"#,
1538 vec![r#"1 "c""#],
1539 vec![r#"0 1 "2q""#],
1540 ),
1541 (r#"PULSE 2 "a" custom_waveform"#, vec![], vec![]),
1543 (
1545 r#"CAPTURE 0 "a" custom_waveform ro[0]"#,
1546 vec![r#"0 "a""#],
1547 vec![r#"0 "b""#, r#"0 1 "2q""#],
1548 ),
1549 (
1550 r#"CAPTURE 1 "c" custom_waveform ro[0]"#,
1551 vec![r#"1 "c""#],
1552 vec![r#"0 1 "2q""#],
1553 ),
1554 (r#"CAPTURE 2 "a" custom_waveform ro[0]"#, vec![], vec![]),
1555 (
1557 r#"RAW-CAPTURE 0 "a" 1e-6 ro[0]"#,
1558 vec![r#"0 "a""#],
1559 vec![r#"0 "b""#, r#"0 1 "2q""#],
1560 ),
1561 (
1562 r#"RAW-CAPTURE 1 "c" 1e-6 ro[0]"#,
1563 vec![r#"1 "c""#],
1564 vec![r#"0 1 "2q""#],
1565 ),
1566 (r#"RAW-CAPTURE 2 "a" 1e-6 ro[0]"#, vec![], vec![]),
1567 (
1569 r#"NONBLOCKING PULSE 0 "a" custom_waveform"#,
1570 vec![r#"0 "a""#],
1571 vec![],
1572 ),
1573 (
1574 r#"NONBLOCKING PULSE 1 "c" custom_waveform"#,
1575 vec![r#"1 "c""#],
1576 vec![],
1577 ),
1578 (
1579 r#"NONBLOCKING PULSE 0 1 "2q" custom_waveform"#,
1580 vec![r#"0 1 "2q""#],
1581 vec![],
1582 ),
1583 (r#"FENCE 1"#, vec![], vec![r#"1 "c""#, r#"0 1 "2q""#]),
1585 (
1587 r#"FENCE"#,
1588 vec![],
1589 vec![r#"0 "a""#, r#"0 "b""#, r#"1 "c""#, r#"0 1 "2q""#],
1590 ),
1591 (r#"DELAY 0 1.0"#, vec![r#"0 "a""#, r#"0 "b""#], vec![]),
1593 (r#"DELAY 1 1.0"#, vec![r#"1 "c""#], vec![]),
1594 (r#"DELAY 1 "c" 1.0"#, vec![r#"1 "c""#], vec![]),
1595 (r#"DELAY 0 1 1.0"#, vec![r#"0 1 "2q""#], vec![]),
1596 (
1597 r#"SWAP-PHASES 0 "a" 0 "b""#,
1598 vec![r#"0 "a""#, r#"0 "b""#],
1599 vec![],
1600 ),
1601 ] {
1602 let instruction = Instruction::parse_in_test(instruction_string).unwrap();
1603 let matched_frames = DefaultHandler
1604 .matching_frames(&program, &instruction)
1605 .unwrap();
1606 let used_frames: HashSet<String> = matched_frames
1607 .used
1608 .iter()
1609 .map(|f| f.to_quil_or_debug())
1610 .collect();
1611 let expected_used_frames: HashSet<String> = expected_used_frames
1612 .into_iter()
1613 .map(|el| el.to_owned())
1614 .collect();
1615 assert_eq!(
1616 used_frames, expected_used_frames,
1617 "Instruction {instruction} *used* frames `{used_frames:?}` but we expected `{expected_used_frames:?}`", instruction=instruction.to_quil_or_debug()
1618 );
1619
1620 let blocked_frames: HashSet<String> = matched_frames
1621 .blocked
1622 .iter()
1623 .map(|f| f.to_quil_or_debug())
1624 .collect();
1625 let expected_blocked_frames: HashSet<String> = expected_blocked_frames
1626 .into_iter()
1627 .map(|el| el.to_owned())
1628 .collect();
1629 assert_eq!(
1630 blocked_frames, expected_blocked_frames,
1631 "Instruction {instruction} *blocked* frames `{blocked_frames:?}` but we expected `{expected_blocked_frames:?}`", instruction=instruction.to_quil_or_debug()
1632 );
1633 }
1634 }
1635
1636 #[test]
1637 fn into_simplified() {
1638 let input = "
1639DEFCAL MEASURE 0 addr:
1640 CAPTURE 0 \"ro_rx\" custom addr
1641
1642DEFCAL MEASURE 1 addr:
1643 CAPTURE 1 \"ro_rx\" custom addr
1644
1645DEFFRAME 0 \"ro_rx\":
1646 ATTRIBUTE: \"value\"
1647
1648DEFFRAME 1 \"ro_rx\":
1649 ATTRIBUTE: \"other\"
1650
1651DEFWAVEFORM custom:
1652 0.0, 1.0
1653
1654DEFWAVEFORM other_custom:
1655 2.0, 3.0
1656
1657DECLARE ro BIT
1658MEASURE 0 ro
1659";
1660
1661 let expected = "
1662DECLARE ro BIT
1663
1664DEFFRAME 0 \"ro_rx\":
1665 ATTRIBUTE: \"value\"
1666
1667DEFWAVEFORM custom:
1668 0.0, 1.0
1669
1670CAPTURE 0 \"ro_rx\" custom ro
1671";
1672 let program = Program::from_str(input).map_err(|e| e.to_string()).unwrap();
1673 let program = program.simplify(&DefaultHandler).unwrap();
1674 assert_eq!(program, Program::from_str(expected).unwrap());
1675 }
1676
1677 #[test]
1678 fn test_get_qubits() {
1679 let input = "
1680DECLARE ro BIT
1681MEASURE q ro
1682JUMP-UNLESS @end-reset ro
1683X q
1684LABEL @end-reset
1685DEFCAL I 0:
1686 DELAY 0 1.0
1687DEFFRAME 0 \"rx\":
1688 HARDWARE-OBJECT: \"hardware\"
1689DEFWAVEFORM custom:
1690 1,2
1691I 0
1692";
1693 let program = Program::from_str(input).unwrap();
1694 let expected_owned = [Qubit::Fixed(0), Qubit::Variable("q".to_string())];
1695 let expected = expected_owned.iter().collect::<HashSet<_>>();
1696 let actual = program.get_used_qubits();
1697 assert_eq!(expected, actual.iter().collect());
1698 }
1699
1700 #[test]
1701 fn test_add_instructions() {
1702 let mut p = Program::new();
1703 let instrs = vec![Instruction::Nop(), Instruction::Nop()];
1704 p.add_instructions(instrs.clone());
1705 assert_eq!(p.instructions, instrs);
1706 }
1707
1708 #[test]
1709 fn test_add_programs() {
1710 let lhs_input = "
1711DECLARE ro BIT
1712
1713MEASURE q ro
1714X q
1715
1716DEFCAL I 0:
1717 DELAY 0 1.0
1718DEFFRAME 0 \"rx\":
1719 HARDWARE-OBJECT: \"hardware\"
1720DEFWAVEFORM custom:
1721 1,2
1722DEFGATE FOO:
1723 1, 0
1724 0, 1
1725
1726DEFCIRCUIT BELL q0 q1:
1727 H q1
1728 CNOT q1 q0
1729
1730I 0
1731";
1732 let rhs_input = "
1733DECLARE foo REAL
1734H 1
1735CNOT 2 3
1736
1737DEFCAL I 1:
1738 DELAY 0 1.0
1739DEFFRAME 1 \"rx\":
1740 HARDWARE-OBJECT: \"hardware\"
1741DEFWAVEFORM custom2:
1742 1,2
1743DEFGATE BAR:
1744 0, 1
1745 1, 0
1746
1747DEFCIRCUIT BELL2 q0 q1:
1748 H q1
1749 CNOT q1 q0
1750 X q1
1751";
1752 let lhs = Program::from_str(lhs_input).unwrap();
1753 let rhs = Program::from_str(rhs_input).unwrap();
1754
1755 let sum = lhs.clone() + rhs.clone();
1756 let mut in_place_sum = lhs.clone();
1757 in_place_sum += rhs;
1758
1759 let expected_qubits = [
1760 Qubit::Fixed(0),
1761 Qubit::Fixed(1),
1762 Qubit::Fixed(2),
1763 Qubit::Fixed(3),
1764 Qubit::Variable("q".to_string()),
1765 ];
1766
1767 let expected_qubits = expected_qubits.iter().collect::<HashSet<_>>();
1768 for program in [&sum, &in_place_sum] {
1769 assert_eq!(program.calibrations.len(), 2);
1770 assert_eq!(program.memory_regions.len(), 2);
1771 assert_eq!(program.frames.len(), 2);
1772 assert_eq!(program.waveforms.len(), 2);
1773 assert_eq!(program.gate_definitions.len(), 2);
1774 assert_eq!(program.circuits.len(), 2);
1775 assert_eq!(program.instructions.len(), 5);
1776 assert_eq!(expected_qubits, sum.get_used_qubits().iter().collect());
1777 }
1778 }
1779
1780 #[test]
1781 fn test_from_vec_instructions() {
1782 let expected: Program = "NOP\nNOP".parse().expect("Should parse NOPs");
1783 let p: Program = expected.instructions.clone().into();
1784 assert_eq!(expected, p);
1785 }
1786
1787 #[test]
1788 fn test_clone_without_body_instructions() {
1789 let quil = "
1790DECLARE ro BIT
1791MEASURE q ro
1792JUMP-UNLESS @end-reset ro
1793X q
1794LABEL @end-reset
1795
1796DEFCAL I 0:
1797 DELAY 0 1.0
1798DEFFRAME 0 \"rx\":
1799 HARDWARE-OBJECT: \"hardware\"
1800DEFWAVEFORM custom:
1801 1,2
1802
1803DEFCIRCUIT BELL q0 q1:
1804 H q1
1805 CNOT q1 q0
1806
1807I 0
1808";
1809 let original = Program::from_str(quil).unwrap();
1811 assert!(!original.instructions.is_empty());
1812
1813 let mut cloned = original.clone_without_body_instructions();
1814 assert!(cloned.instructions.is_empty());
1816 assert!(cloned.used_qubits.is_empty());
1817
1818 cloned.add_instructions(original.instructions.clone());
1821 assert_eq!(original, cloned);
1822 }
1823
1824 static _0: Complex64 = real!(0.0);
1825 static _1: Complex64 = real!(1.0);
1826 static _I: Complex64 = imag!(1.0);
1827 static _1_SQRT_2: Complex64 = real!(std::f64::consts::FRAC_1_SQRT_2);
1828 static H: Lazy<Matrix> = Lazy::new(|| array![[_1, _1], [_1, -_1]] * _1_SQRT_2);
1829 static X: Lazy<Matrix> = Lazy::new(|| array![[_0, _1], [_1, _0]]);
1830 static Y: Lazy<Matrix> = Lazy::new(|| array![[_0, -_I], [_I, _0]]);
1831 static Z: Lazy<Matrix> = Lazy::new(|| array![[_1, _0], [_0, -_1]]);
1832 static CNOT: Lazy<Matrix> = Lazy::new(|| {
1833 array![
1834 [_1, _0, _0, _0],
1835 [_0, _1, _0, _0],
1836 [_0, _0, _0, _1],
1837 [_0, _0, _1, _0]
1838 ]
1839 });
1840 static I2: Lazy<Matrix> = Lazy::new(|| Array2::eye(2));
1841 static I4: Lazy<Matrix> = Lazy::new(|| Array2::eye(4));
1842
1843 #[rstest]
1844 #[case("H 0\nH 1\nH 0", 2, &kron(&H, &I2))]
1845 #[case("H 0\nX 1\nY 2\nZ 3", 4, &kron(&Z, &kron(&Y, &kron(&X, &H))))]
1846 #[case("X 2\nCNOT 2 1\nCNOT 1 0", 3, &kron(&I2, &CNOT).dot(&kron(&CNOT, &I2)).dot(&kron(&X, &I4)))]
1847 fn test_to_unitary(#[case] input: &str, #[case] n_qubits: u64, #[case] expected: &Matrix) {
1848 let program = Program::from_str(input);
1849 assert!(program.is_ok());
1850 let matrix = program.unwrap().to_unitary(n_qubits);
1851 assert!(matrix.is_ok());
1852 assert_abs_diff_eq!(matrix.as_ref().unwrap(), expected);
1853 }
1854
1855 #[test]
1858 fn test_to_instructions() {
1859 let input = format!(
1860 "DECLARE foo REAL[1]
1861DEFFRAME 1 \"rx\":
1862{INDENT}HARDWARE-OBJECT: \"hardware\"
1863DEFWAVEFORM custom2:
1864{INDENT}1, 2
1865DEFCAL I 1:
1866{INDENT}DELAY 0 1
1867DEFGATE BAR AS MATRIX:
1868{INDENT}0, 1
1869{INDENT}1, 0
1870
1871DEFCIRCUIT BELL q0 q1:
1872{INDENT}H q1
1873{INDENT}CNOT q1 q0
1874
1875H 1
1876CNOT 2 3
1877"
1878 );
1879 let program = Program::from_str(&input).unwrap();
1880 assert_debug_snapshot!(program.to_instructions());
1881 assert_eq!(program.to_quil().unwrap(), input);
1882 assert_eq!(program.to_instructions(), program.into_instructions());
1883 }
1884
1885 #[test]
1886 fn placeholder_replacement() {
1887 let placeholder_1 = QubitPlaceholder::default();
1888 let placeholder_2 = QubitPlaceholder::default();
1889 let label_placeholder_1 = TargetPlaceholder::new(String::from("custom_label"));
1890 let label_placeholder_2 = TargetPlaceholder::new(String::from("custom_label"));
1891
1892 let mut program = Program::new();
1893
1894 program.add_instruction(Instruction::Label(Label {
1895 target: Target::Placeholder(label_placeholder_1.clone()),
1896 }));
1897
1898 program.add_instruction(Instruction::Jump(Jump {
1899 target: Target::Placeholder(label_placeholder_2.clone()),
1900 }));
1901
1902 program.add_instruction(Instruction::JumpWhen(JumpWhen {
1903 target: Target::Placeholder(label_placeholder_2.clone()),
1904 condition: MemoryReference {
1905 name: "ro".to_string(),
1906 index: 0,
1907 },
1908 }));
1909
1910 program.add_instruction(Instruction::JumpUnless(JumpUnless {
1911 target: Target::Placeholder(label_placeholder_2.clone()),
1912 condition: MemoryReference {
1913 name: "ro".to_string(),
1914 index: 0,
1915 },
1916 }));
1917
1918 program.add_instruction(Instruction::Gate(Gate {
1919 name: "X".to_string(),
1920 qubits: vec![Qubit::Placeholder(placeholder_1.clone())],
1921 parameters: vec![],
1922 modifiers: vec![],
1923 }));
1924
1925 program.add_instruction(Instruction::Gate(Gate {
1926 name: "Y".to_string(),
1927 qubits: vec![Qubit::Placeholder(placeholder_2.clone())],
1928 parameters: vec![],
1929 modifiers: vec![],
1930 }));
1931
1932 let mut auto_increment_resolved = program.clone();
1933 auto_increment_resolved.resolve_placeholders();
1934 assert_eq!(
1935 auto_increment_resolved.instructions,
1936 vec![
1937 Instruction::Label(Label {
1938 target: Target::Fixed("custom_label_0".to_string())
1939 }),
1940 Instruction::Jump(Jump {
1941 target: Target::Fixed("custom_label_1".to_string()),
1942 }),
1943 Instruction::JumpWhen(JumpWhen {
1944 target: Target::Fixed("custom_label_1".to_string()),
1945 condition: MemoryReference {
1946 name: "ro".to_string(),
1947 index: 0,
1948 },
1949 }),
1950 Instruction::JumpUnless(JumpUnless {
1951 target: Target::Fixed("custom_label_1".to_string()),
1952 condition: MemoryReference {
1953 name: "ro".to_string(),
1954 index: 0,
1955 },
1956 }),
1957 Instruction::Gate(Gate {
1958 name: "X".to_string(),
1959 qubits: vec![Qubit::Fixed(0)],
1960 parameters: vec![],
1961 modifiers: vec![],
1962 }),
1963 Instruction::Gate(Gate {
1964 name: "Y".to_string(),
1965 qubits: vec![Qubit::Fixed(1)],
1966 parameters: vec![],
1967 modifiers: vec![],
1968 }),
1969 ]
1970 );
1971
1972 let mut custom_resolved = program.clone();
1973 let custom_target_resolutions = HashMap::from([
1974 (label_placeholder_1, "new_label".to_string()),
1975 (label_placeholder_2, "other_new_label".to_string()),
1976 ]);
1977 let custom_qubit_resolutions = HashMap::from([(placeholder_1, 42), (placeholder_2, 10000)]);
1978 custom_resolved.resolve_placeholders_with_custom_resolvers(
1979 Box::new(move |placeholder| custom_target_resolutions.get(placeholder).cloned()),
1980 Box::new(move |placeholder| custom_qubit_resolutions.get(placeholder).copied()),
1981 );
1982 assert_eq!(
1983 custom_resolved.instructions,
1984 vec![
1985 Instruction::Label(Label {
1986 target: Target::Fixed("new_label".to_string())
1987 }),
1988 Instruction::Jump(Jump {
1989 target: Target::Fixed("other_new_label".to_string()),
1990 }),
1991 Instruction::JumpWhen(JumpWhen {
1992 target: Target::Fixed("other_new_label".to_string()),
1993 condition: MemoryReference {
1994 name: "ro".to_string(),
1995 index: 0,
1996 },
1997 }),
1998 Instruction::JumpUnless(JumpUnless {
1999 target: Target::Fixed("other_new_label".to_string()),
2000 condition: MemoryReference {
2001 name: "ro".to_string(),
2002 index: 0,
2003 },
2004 }),
2005 Instruction::Gate(Gate {
2006 name: "X".to_string(),
2007 qubits: vec![Qubit::Fixed(42)],
2008 parameters: vec![],
2009 modifiers: vec![],
2010 }),
2011 Instruction::Gate(Gate {
2012 name: "Y".to_string(),
2013 qubits: vec![Qubit::Fixed(10000)],
2014 parameters: vec![],
2015 modifiers: vec![],
2016 }),
2017 ]
2018 );
2019 }
2020
2021 #[test]
2022 fn test_filter_instructions() {
2023 let input = "DECLARE foo REAL[1]
2024DEFFRAME 1 \"rx\":
2025\tHARDWARE-OBJECT: \"hardware\"
2026DEFCAL I 1:
2027\tDELAY 0 1
2028DEFGATE BAR AS MATRIX:
2029\t0, 1
2030\t1, 0
2031
2032H 1
2033CNOT 2 3";
2034
2035 let program = Program::from_str(input).unwrap();
2036 let program_without_quil_t =
2037 program.filter_instructions(|instruction| !instruction.is_quil_t());
2038 assert_snapshot!(program_without_quil_t.to_quil().unwrap())
2039 }
2040
2041 #[test]
2042 fn test_wrap_in_loop() {
2043 let input = "DECLARE ro BIT
2044DECLARE shot_count INTEGER
2045MEASURE q ro
2046JUMP-UNLESS @end-reset ro
2047X q
2048LABEL @end-reset
2049
2050DEFCAL I 0:
2051 DELAY 0 1.0
2052DEFFRAME 0 \"rx\":
2053 HARDWARE-OBJECT: \"hardware\"
2054DEFWAVEFORM custom:
2055 1,2
2056I 0
2057";
2058 let program = Program::from_str(input).unwrap().wrap_in_loop(
2059 MemoryReference {
2060 name: "shot_count".to_string(),
2061 index: 0,
2062 },
2063 Target::Fixed("loop-start".to_string()),
2064 Target::Fixed("loop-end".to_string()),
2065 10,
2066 );
2067
2068 assert_snapshot!(program.to_quil().unwrap())
2069 }
2070
2071 #[test]
2072 fn test_equality() {
2073 let input = "DECLARE foo REAL[1]
2074DEFFRAME 1 \"rx\":
2075\tHARDWARE-OBJECT: \"hardware\"
2076DEFCAL I 0:
2077\tDELAY 0 1
2078DEFCAL I 1:
2079\tDELAY 0 1
2080DEFCAL I 2:
2081\tDELAY 0 1
2082DEFCAL MEASURE 0 addr:
2083\tCAPTURE 0 \"ro_rx\" custom addr
2084DEFCAL MEASURE 1 addr:
2085\tCAPTURE 1 \"ro_rx\" custom addr
2086DEFWAVEFORM custom:
2087\t1,2
2088DEFWAVEFORM custom2:
2089\t3,4
2090DEFWAVEFORM another1:
2091\t4,5
2092DEFGATE BAR AS MATRIX:
2093\t0, 1
2094\t1, 0
2095DEFGATE FOO AS MATRIX:
2096\t0, 1
2097\t1, 0
2098
2099H 1
2100CNOT 2 3";
2101
2102 let program = Program::from_str(input).unwrap();
2103
2104 let is_global_state_instruction = move |i: &Instruction| -> bool {
2107 matches!(
2108 i,
2109 |Instruction::WaveformDefinition(_)| Instruction::GateDefinition(_)
2110 | Instruction::FrameDefinition(_)
2111 )
2112 };
2113 let mut program2 = program.filter_instructions(|i| !is_global_state_instruction(i));
2117 let global_instructions: Vec<Instruction> = program
2118 .filter_instructions(is_global_state_instruction)
2119 .into_instructions()
2120 .into_iter()
2121 .rev()
2122 .collect();
2123 program2.add_instructions(global_instructions.clone());
2124 assert_eq!(program, program2);
2125
2126 let mut program3 = Program::from_instructions(
2129 program
2130 .filter_instructions(|i| !is_global_state_instruction(i))
2131 .into_instructions()
2132 .into_iter()
2133 .rev()
2134 .collect(),
2135 );
2136 program3.add_instructions(global_instructions);
2137 assert!(program != program3)
2138 }
2139
2140 #[test]
2141 fn test_deterministic_serialization() {
2142 let input = "DECLARE foo REAL[1]
2143DECLARE bar BIT[1]
2144DECLARE baz BIT[1]
2145RX(pi) 0
2146CNOT 0 1
2147DEFCAL I 0:
2148\tDELAY 0 1
2149\tDELAY 1 1
2150DEFCAL I 1:
2151\tDELAY 0 1
2152\tDELAY 1 2
2153DEFCAL I 2:
2154\tDELAY 2 1
2155\tDELAY 2 3
2156DEFCAL MEASURE 0 addr:
2157\tRX(pi) 0
2158\tCAPTURE 0 \"ro_rx\" custom addr
2159DEFCAL MEASURE 1 addr:
2160\tRX(pi/2) 1
2161\tCAPTURE 1 \"ro_rx\" custom addr
2162DEFCAL MEASURE 2 addr:
2163\tRX(pi/2) 2
2164\tCAPTURE 2 \"ro_rx\" custom addr
2165DEFWAVEFORM custom:
2166\t1,2
2167DEFWAVEFORM custom2:
2168\t3,4
2169DEFWAVEFORM another1(%a, %b):
2170\t%a,%b
2171PULSE 0 \"xy\" flat(duration: 1e-6, iq: 2+3i)
2172PULSE 0 \"xy\" another1(a: 1e-6, b: 2+3i)
2173DEFGATE HADAMARD AS MATRIX:
2174\t(1/sqrt(2)),(1/sqrt(2))
2175\t(1/sqrt(2)),((-1)/sqrt(2))
2176DEFGATE RX(%theta) AS MATRIX:
2177\tcos((%theta/2)),((-1i)*sin((%theta/2)))
2178\t((-1i)*sin((%theta/2))),cos((%theta/2))
2179DEFGATE Name AS PERMUTATION:
2180\t1, 0
2181DEFCIRCUIT SIMPLE:
2182\tX 0
2183\tX 1
2184DEFGATE BAR AS MATRIX:
2185\t0, 1
2186\t1, 0
2187DEFGATE FOO AS MATRIX:
2188\t0, 1
2189\t1, 0
2190DEFGATE BAZ AS MATRIX:
2191\t1, 0
2192\t0, 1
2193MEASURE 1 bar
2194MEASURE 0 foo
2195HALT
2196DEFCIRCUIT CIRCFOO:
2197\tLABEL @FOO_A
2198\tJUMP @FOO_A
2199DEFFRAME 0 \"xy\":
2200\tSAMPLE-RATE: 3000
2201DEFFRAME 0 \"xy\":
2202\tDIRECTION: \"rx\"
2203\tCENTER-FREQUENCY: 1000
2204\tHARDWARE-OBJECT: \"some object\"
2205\tINITIAL-FREQUENCY: 2000
2206\tSAMPLE-RATE: 3000";
2207 let program = Program::from_str(input).unwrap();
2208 let quil = program.to_quil().unwrap();
2209
2210 let iterations = 100;
2214 for _ in 0..iterations {
2215 let new_program = Program::from_str(input).unwrap();
2216 assert_eq!(new_program.to_quil().unwrap(), quil);
2217 }
2218 }
2219
2220 #[test]
2224 fn test_extern_call() {
2225 let input = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
2226DECLARE reals REAL[3]
2227DECLARE octets OCTET[3]
2228CALL foo octets[1] reals
2229"#;
2230 let program = Program::from_str(input).expect("should be able to parse program");
2231 let reserialized = program
2232 .to_quil()
2233 .expect("should be able to serialize program");
2234 assert_eq!(input, reserialized);
2235
2236 let pragma = crate::instruction::Pragma {
2237 name: RESERVED_PRAGMA_EXTERN.to_string(),
2238 arguments: vec![crate::instruction::PragmaArgument::Identifier(
2239 "foo".to_string(),
2240 )],
2241 data: Some("OCTET (params : mut REAL[3])".to_string()),
2242 };
2243 let call = Call {
2244 name: "foo".to_string(),
2245 arguments: vec![
2246 UnresolvedCallArgument::MemoryReference(MemoryReference {
2247 name: "octets".to_string(),
2248 index: 1,
2249 }),
2250 UnresolvedCallArgument::Identifier("reals".to_string()),
2251 ],
2252 };
2253 let expected_program = Program::from_instructions(vec![
2254 Instruction::Declaration(Declaration::new(
2255 "reals".to_string(),
2256 Vector::new(ScalarType::Real, 3),
2257 None,
2258 )),
2259 Instruction::Declaration(Declaration::new(
2260 "octets".to_string(),
2261 Vector::new(ScalarType::Octet, 3),
2262 None,
2263 )),
2264 Instruction::Pragma(pragma.clone()),
2265 Instruction::Call(call.clone()),
2266 ]);
2267 assert_eq!(expected_program, program);
2268
2269 let extern_signature_map = ExternSignatureMap::try_from(program.extern_pragma_map)
2270 .expect("should be able parse extern pragmas");
2271 assert_eq!(extern_signature_map.len(), 1);
2272
2273 assert_eq!(
2274 DefaultHandler
2275 .memory_accesses(&extern_signature_map, &Instruction::Pragma(pragma))
2276 .expect("should be able to get memory accesses"),
2277 MemoryAccesses::default()
2278 );
2279
2280 assert_eq!(
2281 DefaultHandler
2282 .memory_accesses(&extern_signature_map, &Instruction::Call(call))
2283 .expect("should be able to get memory accesses"),
2284 MemoryAccesses {
2285 reads: ["octets", "reals"].into_iter().map(String::from).collect(),
2286 writes: ["octets", "reals"].into_iter().map(String::from).collect(),
2287 ..MemoryAccesses::default()
2288 }
2289 );
2290 }
2291
2292 #[test]
2294 fn test_extern_call_simplification() {
2295 let input = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
2296PRAGMA EXTERN bar "OCTET (params : mut REAL[3])"
2297DECLARE reals REAL[3]
2298DECLARE octets OCTET[3]
2299CALL foo octets[1] reals
2300"#;
2301 let program = Program::from_str(input).expect("should be able to parse program");
2302
2303 let expected = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
2304DECLARE reals REAL[3]
2305DECLARE octets OCTET[3]
2306CALL foo octets[1] reals
2307"#;
2308
2309 let reserialized = program
2310 .expand_calibrations()
2311 .expect("should be able to expand calibrations")
2312 .simplify(&DefaultHandler)
2313 .expect("should be able to simplify program")
2314 .to_quil()
2315 .expect("should be able to serialize program");
2316 assert_eq!(expected, reserialized);
2317 }
2318
2319 #[test]
2322 fn test_defgate_as_sequence_mechanics() {
2323 let pi_divided_by_2 = Expression::Infix(InfixExpression {
2324 operator: InfixOperator::Slash,
2325 left: ArcIntern::new(Expression::PiConstant()),
2326 right: ArcIntern::new(Expression::Number(Complex64 { re: 2.0, im: 0.0 })),
2327 });
2328 let negate_variable = |variable: String| {
2329 Expression::Prefix(PrefixExpression {
2330 operator: PrefixOperator::Minus,
2331 expression: ArcIntern::new(Expression::Variable(variable)),
2332 })
2333 };
2334 let new_gate = |gate_name: &str, param: Expression, qubit: String| {
2335 Gate::new(gate_name, vec![param], vec![Qubit::Variable(qubit)], vec![])
2336 .expect("must be a valid gate")
2337 };
2338 let pmw3 = |param_prefix: String, qubit: String| {
2339 (0..3)
2340 .flat_map(|i| {
2341 vec![
2342 new_gate(
2343 "RZ",
2344 Expression::Variable(format!("{param_prefix}{i}")),
2345 qubit.clone(),
2346 ),
2347 new_gate("RX", pi_divided_by_2.clone(), qubit.clone()),
2348 new_gate(
2349 "RZ",
2350 negate_variable(format!("{param_prefix}{i}")),
2351 qubit.clone(),
2352 ),
2353 ]
2354 })
2355 .collect::<Vec<_>>()
2356 };
2357 let gate_sequence = DefGateSequence::try_new(
2358 ["q0", "q1"].map(String::from).to_vec(),
2359 pmw3("theta".to_string(), "q0".to_string())
2360 .into_iter()
2361 .chain(pmw3("phi".to_string(), "q1".to_string()))
2362 .collect(),
2363 )
2364 .expect("must be valid gate sequence");
2365 let gate_definition = GateDefinition {
2366 name: "PMW3".to_string(),
2367 parameters: vec![
2368 "theta0".to_string(),
2369 "theta1".to_string(),
2370 "theta2".to_string(),
2371 "phi0".to_string(),
2372 "phi1".to_string(),
2373 "phi2".to_string(),
2374 ],
2375 specification: GateSpecification::Sequence(gate_sequence),
2376 };
2377 let mut program = Program::new();
2378 program.add_instruction(Instruction::GateDefinition(gate_definition.clone()));
2379 assert_eq!(program.gate_definitions.len(), 1);
2380 assert_eq!(program.body_instructions().count(), 0);
2381
2382 let invocation = Gate::new(
2383 "PMW3",
2384 vec![
2385 Expression::Address(MemoryReference {
2386 name: "theta".to_string(),
2387 index: 0,
2388 }),
2389 Expression::Address(MemoryReference {
2390 name: "theta".to_string(),
2391 index: 1,
2392 }),
2393 Expression::Address(MemoryReference {
2394 name: "theta".to_string(),
2395 index: 2,
2396 }),
2397 Expression::Address(MemoryReference {
2398 name: "phi".to_string(),
2399 index: 0,
2400 }),
2401 Expression::Address(MemoryReference {
2402 name: "phi".to_string(),
2403 index: 1,
2404 }),
2405 Expression::Address(MemoryReference {
2406 name: "phi".to_string(),
2407 index: 2,
2408 }),
2409 ],
2410 vec![Qubit::Fixed(0), Qubit::Fixed(1)],
2411 vec![],
2412 )
2413 .expect("must be a valid gate");
2414 program.add_instruction(Instruction::Gate(invocation));
2415 assert_eq!(program.body_instructions().count(), 1);
2416
2417 let program_copy = program.clone_without_body_instructions();
2418 assert_eq!(program_copy.gate_definitions.len(), 1);
2419 assert_eq!(
2420 program_copy
2421 .gate_definitions
2422 .get("PMW3")
2423 .expect("must exist"),
2424 &gate_definition
2425 );
2426 assert_eq!(program_copy.body_instructions().count(), 0);
2427 }
2428
2429 #[test]
2434 fn test_gate_sequence_expansion() {
2435 const QUIL: &str = r"
2436DECLARE ro BIT[2]
2437
2438DEFGATE seq1(%param1) a AS SEQUENCE:
2439 RZ(%param1) a
2440
2441DEFGATE seq2() a AS SEQUENCE:
2442 X a
2443
2444seq1(pi/2) 0
2445seq2 1
2446
2447MEASURE 0 ro[0]
2448MEASURE 1 ro[1]
2449";
2450 let program = Program::from_str(QUIL).expect("should parse program");
2451 let exclude = ["seq1"]
2452 .into_iter()
2453 .map(String::from)
2454 .collect::<HashSet<_>>();
2455 let filter = |key: &str| !exclude.contains(key);
2456 let expanded_program = program
2457 .expand_defgate_sequences(filter)
2458 .expect("should expand gate sequences");
2459 const EXPECTED: &str = r"
2460DECLARE ro BIT[2]
2461DEFGATE seq1(%param1) a AS SEQUENCE:
2462 RZ(%param1) a
2463
2464seq1(pi/2) 0
2465X 1
2466MEASURE 0 ro[0]
2467MEASURE 1 ro[1]
2468";
2469 let expected_program = Program::from_str(EXPECTED).expect("should parse expected program");
2470 pretty_assertions::assert_eq!(expanded_program, expected_program);
2471 }
2472
2473 #[test]
2476 fn test_gate_sequence_expansion_preserves_referred_gates() {
2477 const QUIL: &str = r"
2478DECLARE ro BIT[2]
2479
2480DEFGATE seq1(%param1) a AS SEQUENCE:
2481 RZ(%param1) a
2482 seq2() a
2483
2484DEFGATE seq2() a AS SEQUENCE:
2485 H a
2486
2487DEFGATE seq3() a AS SEQUENCE:
2488 X a
2489
2490seq1(pi/2) 0
2491seq2() 1
2492seq3 2
2493
2494MEASURE 0 ro[0]
2495MEASURE 1 ro[1]
2496";
2497 let program = Program::from_str(QUIL).expect("should parse program");
2498 let exclude = ["seq1"]
2499 .into_iter()
2500 .map(String::from)
2501 .collect::<HashSet<_>>();
2502 let filter = |key: &str| !exclude.contains(key);
2503 let expanded_program = program
2504 .expand_defgate_sequences(filter)
2505 .expect("should expand gate sequences");
2506 const EXPECTED: &str = r"
2507DECLARE ro BIT[2]
2508DEFGATE seq1(%param1) a AS SEQUENCE:
2509 RZ(%param1) a
2510 seq2() a
2511
2512DEFGATE seq2() a AS SEQUENCE:
2513 H a
2514
2515seq1(pi/2) 0
2516H 1
2517X 2
2518MEASURE 0 ro[0]
2519MEASURE 1 ro[1]
2520";
2521 let expected_program = Program::from_str(EXPECTED).expect("should parse expected program");
2522 pretty_assertions::assert_eq!(expanded_program, expected_program);
2523 }
2524}