1use std::collections::{HashMap, HashSet};
16use std::ops::{self};
17use std::str::FromStr;
18
19use indexmap::{IndexMap, IndexSet};
20use ndarray::Array2;
21use nom_locate::LocatedSpan;
22
23use crate::instruction::{
24 Arithmetic, ArithmeticOperand, ArithmeticOperator, Call, Declaration, ExternError,
25 ExternPragmaMap, ExternSignatureMap, FrameDefinition, FrameIdentifier, GateDefinition,
26 GateError, Instruction, InstructionHandler, Jump, JumpUnless, Label, Matrix, MemoryReference,
27 Move, Pragma, Qubit, QubitPlaceholder, ScalarType, Target, TargetPlaceholder, Vector, Waveform,
28 WaveformDefinition, RESERVED_PRAGMA_EXTERN,
29};
30use crate::parser::{lex, parse_instructions, ParseError};
31use crate::quil::Quil;
32
33pub use self::calibration::Calibrations;
34pub use self::calibration::{
35 CalibrationExpansion, CalibrationExpansionOutput, CalibrationSource, MaybeCalibrationExpansion,
36};
37pub use self::calibration_set::CalibrationSet;
38pub use self::error::{
39 disallow_leftover, map_parsed, recover, LeftoverError, ParseProgramError, SyntaxError,
40};
41pub use self::frame::FrameSet;
42pub use self::frame::MatchedFrames;
43pub use self::memory::{
44 MemoryAccess, MemoryAccesses, MemoryAccessesError, MemoryAccessesResult, MemoryRegion,
45};
46pub use self::source_map::{SourceMap, SourceMapEntry};
47
48pub mod analysis;
49mod calibration;
50mod calibration_set;
51mod error;
52pub(crate) mod frame;
53mod memory;
54pub mod scheduling;
55mod source_map;
56pub mod type_check;
57
58#[derive(Clone, Debug, PartialEq, thiserror::Error)]
59pub enum ProgramError {
60 #[error("{0}")]
61 ParsingError(#[from] ParseProgramError<Program>),
62
63 #[error("this operation isn't supported on instruction: {}", .0.to_quil_or_debug())]
64 UnsupportedOperation(Instruction),
65
66 #[error("instruction {} expands into itself", .0.to_quil_or_debug())]
67 RecursiveCalibration(Instruction),
68
69 #[error("{0}")]
70 GateError(#[from] GateError),
71
72 #[error("can only compute program unitary for programs composed of `Gate`s; found unsupported instruction: {}", .0.to_quil_or_debug())]
73 UnsupportedForUnitary(Instruction),
74}
75
76type Result<T> = std::result::Result<T, ProgramError>;
77
78#[derive(Clone, Debug, Default, PartialEq)]
84pub struct Program {
85 pub calibrations: Calibrations,
86 pub extern_pragma_map: ExternPragmaMap,
87 pub frames: FrameSet,
88 pub memory_regions: IndexMap<String, MemoryRegion>,
89 pub waveforms: IndexMap<String, Waveform>,
90 pub gate_definitions: IndexMap<String, GateDefinition>,
91 instructions: Vec<Instruction>,
92 used_qubits: HashSet<Qubit>,
94}
95
96impl Program {
97 pub fn new() -> Self {
98 Program {
99 calibrations: Calibrations::default(),
100 extern_pragma_map: ExternPragmaMap::default(),
101 frames: FrameSet::new(),
102 memory_regions: IndexMap::new(),
103 waveforms: IndexMap::new(),
104 gate_definitions: IndexMap::new(),
105 instructions: vec![],
106 used_qubits: HashSet::new(),
107 }
108 }
109
110 pub fn body_instructions(&self) -> impl Iterator<Item = &Instruction> {
112 self.instructions.iter()
113 }
114
115 pub fn into_body_instructions(self) -> impl Iterator<Item = Instruction> {
116 self.instructions.into_iter()
117 }
118
119 #[cfg(test)]
121 pub(crate) fn for_each_body_instruction<F>(&mut self, closure: F)
122 where
123 F: FnMut(&mut Instruction),
124 {
125 let mut instructions = std::mem::take(&mut self.instructions);
126 self.used_qubits.clear();
127
128 instructions.iter_mut().for_each(closure);
129
130 self.add_instructions(instructions);
131 }
132
133 pub fn clone_without_body_instructions(&self) -> Self {
135 Self {
136 instructions: Vec::new(),
137 calibrations: self.calibrations.clone(),
138 extern_pragma_map: self.extern_pragma_map.clone(),
139 frames: self.frames.clone(),
140 memory_regions: self.memory_regions.clone(),
141 gate_definitions: self.gate_definitions.clone(),
142 waveforms: self.waveforms.clone(),
143 used_qubits: HashSet::new(),
144 }
145 }
146
147 pub fn add_instruction(&mut self, instruction: Instruction) {
154 self.used_qubits
155 .extend(instruction.get_qubits().into_iter().cloned());
156
157 match instruction {
158 Instruction::CalibrationDefinition(calibration) => {
159 self.calibrations.insert_calibration(calibration);
160 }
161 Instruction::FrameDefinition(FrameDefinition {
162 identifier,
163 attributes,
164 }) => {
165 self.frames.insert(identifier, attributes);
166 }
167 Instruction::Declaration(Declaration {
168 name,
169 size,
170 sharing,
171 }) => {
172 self.memory_regions
173 .insert(name, MemoryRegion { size, sharing });
174 }
175 Instruction::GateDefinition(gate_definition) => {
176 self.gate_definitions
177 .insert(gate_definition.name.clone(), gate_definition);
178 }
179 Instruction::MeasureCalibrationDefinition(calibration) => {
180 self.calibrations
181 .insert_measurement_calibration(calibration);
182 }
183 Instruction::WaveformDefinition(WaveformDefinition { name, definition }) => {
184 self.waveforms.insert(name, definition);
185 }
186 Instruction::Gate(gate) => {
187 self.instructions.push(Instruction::Gate(gate));
188 }
189 Instruction::Measurement(measurement) => {
190 self.instructions
191 .push(Instruction::Measurement(measurement));
192 }
193 Instruction::Reset(reset) => {
194 self.instructions.push(Instruction::Reset(reset));
195 }
196 Instruction::Delay(delay) => {
197 self.instructions.push(Instruction::Delay(delay));
198 }
199 Instruction::Fence(fence) => {
200 self.instructions.push(Instruction::Fence(fence));
201 }
202 Instruction::Capture(capture) => {
203 self.instructions.push(Instruction::Capture(capture));
204 }
205 Instruction::Pulse(pulse) => {
206 self.instructions.push(Instruction::Pulse(pulse));
207 }
208 Instruction::Pragma(pragma) if pragma.name == RESERVED_PRAGMA_EXTERN => {
209 self.extern_pragma_map.insert(pragma);
210 }
211 Instruction::RawCapture(raw_capture) => {
212 self.instructions.push(Instruction::RawCapture(raw_capture));
213 }
214 other => self.instructions.push(other),
215 }
216 }
217
218 pub fn add_instructions<I>(&mut self, instructions: I)
219 where
220 I: IntoIterator<Item = Instruction>,
221 {
222 instructions
223 .into_iter()
224 .for_each(|i| self.add_instruction(i));
225 }
226
227 pub fn filter_instructions(&self, predicate: impl FnMut(&Instruction) -> bool) -> Program {
230 Program::from_instructions(
231 self.to_instructions()
232 .into_iter()
233 .filter(predicate)
234 .collect(),
235 )
236 }
237
238 pub fn dagger(&self) -> Result<Self> {
246 self.to_instructions().into_iter().try_rfold(
247 Program::new(),
248 |mut new_program, instruction| match instruction {
249 Instruction::Gate(gate) => {
250 new_program.add_instruction(Instruction::Gate(gate.dagger()));
251 Ok(new_program)
252 }
253 _ => Err(ProgramError::UnsupportedOperation(instruction)),
254 },
255 )
256 }
257
258 pub fn expand_calibrations(&self) -> Result<Self> {
265 self.expand_calibrations_inner(None)
266 }
267
268 pub fn expand_calibrations_with_source_map(&self) -> Result<ProgramCalibrationExpansion> {
271 let mut source_mapping = ProgramCalibrationExpansionSourceMap::default();
272 let new_program = self.expand_calibrations_inner(Some(&mut source_mapping))?;
273
274 Ok(ProgramCalibrationExpansion {
275 program: new_program,
276 source_map: source_mapping,
277 })
278 }
279
280 fn expand_calibrations_inner(
286 &self,
287 mut source_mapping: Option<&mut ProgramCalibrationExpansionSourceMap>,
288 ) -> Result<Self> {
289 let mut new_program = Self {
290 calibrations: self.calibrations.clone(),
291 extern_pragma_map: self.extern_pragma_map.clone(),
292 frames: self.frames.clone(),
293 memory_regions: self.memory_regions.clone(),
294 waveforms: self.waveforms.clone(),
295 gate_definitions: self.gate_definitions.clone(),
296 instructions: Vec::new(),
297 used_qubits: HashSet::new(),
298 };
299
300 for (index, instruction) in self.instructions.iter().enumerate() {
301 let index = InstructionIndex(index);
302
303 match self.calibrations.expand_with_detail(instruction, &[])? {
304 Some(expanded) => {
305 new_program.append_calibration_expansion_output_inner(
306 expanded,
307 index,
308 &mut source_mapping,
309 );
310 }
311 None => {
312 new_program.add_instruction(instruction.clone());
313 if let Some(source_mapping) = source_mapping.as_mut() {
314 source_mapping.entries.push(SourceMapEntry {
315 source_location: index,
316 target_location: MaybeCalibrationExpansion::Unexpanded(
317 InstructionIndex(new_program.instructions.len() - 1),
318 ),
319 });
320 }
321 }
322 }
323 }
324
325 Ok(new_program)
326 }
327
328 fn append_calibration_expansion_output_inner(
334 &mut self,
335 mut expansion_output: CalibrationExpansionOutput,
336 source_index: InstructionIndex,
337 source_mapping: &mut Option<&mut ProgramCalibrationExpansionSourceMap>,
338 ) {
339 if let Some(source_mapping) = source_mapping.as_mut() {
340 let previous_program_instruction_body_length = self.instructions.len();
341
342 for instruction in expansion_output.new_instructions {
343 let start_length = self.instructions.len();
344 self.add_instruction(instruction.clone());
345 let end_length = self.instructions.len();
346
347 if start_length == end_length {
350 let relative_target_index =
351 InstructionIndex(start_length - previous_program_instruction_body_length);
352 expansion_output
353 .detail
354 .remove_target_index(relative_target_index);
355 }
356 }
357
358 expansion_output.detail.range =
359 InstructionIndex(previous_program_instruction_body_length)
360 ..InstructionIndex(self.instructions.len());
361
362 if !expansion_output.detail.range.is_empty() {
363 source_mapping.entries.push(SourceMapEntry {
364 source_location: source_index,
365 target_location: MaybeCalibrationExpansion::Expanded(expansion_output.detail),
366 });
367 }
368 } else {
369 self.add_instructions(expansion_output.new_instructions);
370 }
371 }
372
373 pub fn from_instructions(instructions: Vec<Instruction>) -> Self {
375 let mut program = Self::default();
376 for instruction in instructions {
377 program.add_instruction(instruction);
378 }
379 program
380 }
381
382 pub fn get_frames_for_instruction<'a>(
393 &'a self,
394 instruction: &'a Instruction,
395 ) -> Option<MatchedFrames<'a>> {
396 let qubits_used_by_program = self.get_used_qubits();
397
398 instruction
399 .get_frame_match_condition(qubits_used_by_program)
400 .map(|condition| self.frames.get_matching_keys_for_conditions(condition))
401 }
402
403 fn get_targets(&self) -> Vec<&Target> {
405 self.instructions
406 .iter()
407 .filter_map(|i| match i {
408 Instruction::Label(label) => Some(&label.target),
409 Instruction::Jump(jump) => Some(&jump.target),
410 Instruction::JumpWhen(jump_when) => Some(&jump_when.target),
411 Instruction::JumpUnless(jump_unless) => Some(&jump_unless.target),
412 _ => None,
413 })
414 .collect()
415 }
416
417 pub fn get_used_qubits(&self) -> &HashSet<Qubit> {
419 &self.used_qubits
420 }
421
422 fn rebuild_used_qubits(&mut self) {
424 self.used_qubits = self
425 .to_instructions()
426 .iter()
427 .flat_map(|instruction| instruction.get_qubits().into_iter().cloned())
428 .collect()
429 }
430
431 pub fn into_instructions(self) -> Vec<Instruction> {
433 let mut instructions: Vec<Instruction> = Vec::with_capacity(self.len());
434
435 instructions.extend(self.memory_regions.into_iter().map(|(name, descriptor)| {
436 Instruction::Declaration(Declaration {
437 name,
438 size: descriptor.size,
439 sharing: descriptor.sharing,
440 })
441 }));
442 instructions.extend(self.frames.into_instructions());
443 instructions.extend(self.waveforms.into_iter().map(|(name, definition)| {
444 Instruction::WaveformDefinition(WaveformDefinition { name, definition })
445 }));
446 instructions.extend(self.calibrations.to_instructions());
447 instructions.extend(
448 self.gate_definitions
449 .into_values()
450 .map(Instruction::GateDefinition),
451 );
452 instructions.extend(self.extern_pragma_map.into_instructions());
453 instructions.extend(self.instructions);
454 instructions
455 }
456
457 pub(crate) fn simplify_with_handler(
458 &self,
459 instruction_handler: &mut InstructionHandler,
460 ) -> Result<Self> {
461 let mut expanded_program = self.expand_calibrations()?;
462 expanded_program.calibrations = Calibrations::default();
466
467 let mut frames_used: HashSet<&FrameIdentifier> = HashSet::new();
468 let mut waveforms_used: HashSet<&String> = HashSet::new();
469 let mut extern_signatures_used: HashSet<&String> = HashSet::new();
470
471 for instruction in &expanded_program.instructions {
472 if let Some(matched_frames) =
473 instruction_handler.matching_frames(instruction, &expanded_program)
474 {
475 frames_used.extend(matched_frames.used())
476 }
477
478 if let Some(waveform) = instruction.get_waveform_invocation() {
479 waveforms_used.insert(&waveform.name);
480 }
481
482 if let Instruction::Call(Call { name, .. }) = instruction {
483 extern_signatures_used.insert(name);
484 }
485 }
486
487 expanded_program.frames = self.frames.intersection(&frames_used);
488 expanded_program
489 .waveforms
490 .retain(|name, _definition| waveforms_used.contains(name));
491 expanded_program
492 .extern_pragma_map
493 .retain(|name, _signature| {
494 name.as_ref()
495 .map(|name| extern_signatures_used.contains(name))
496 .unwrap_or(false)
497 });
498
499 Ok(expanded_program)
500 }
501
502 pub fn into_simplified(&self) -> Result<Self> {
519 self.simplify_with_handler(&mut InstructionHandler::default())
520 }
521
522 pub fn wrap_in_loop(
537 &self,
538 loop_count_reference: MemoryReference,
539 start_target: Target,
540 end_target: Target,
541 iterations: u32,
542 ) -> Self {
543 if iterations == 0 {
544 return self.clone();
545 }
546
547 let mut looped_program = self.clone_without_body_instructions();
548
549 looped_program.add_instructions(
550 vec![
551 Instruction::Declaration(Declaration {
552 name: loop_count_reference.name.clone(),
553 size: Vector {
554 data_type: ScalarType::Integer,
555 length: 1,
556 },
557 sharing: None,
558 }),
559 Instruction::Move(Move {
560 destination: loop_count_reference.clone(),
561 source: ArithmeticOperand::LiteralInteger(iterations.into()),
562 }),
563 Instruction::Label(Label {
564 target: start_target.clone(),
565 }),
566 ]
567 .into_iter()
568 .chain(self.body_instructions().cloned())
569 .chain(vec![
570 Instruction::Arithmetic(Arithmetic {
571 operator: ArithmeticOperator::Subtract,
572 destination: MemoryReference {
573 name: loop_count_reference.name.clone(),
574 index: 0,
575 },
576 source: ArithmeticOperand::LiteralInteger(1),
577 }),
578 Instruction::JumpUnless(JumpUnless {
579 target: end_target.clone(),
580 condition: loop_count_reference,
581 }),
582 Instruction::Jump(Jump {
583 target: start_target,
584 }),
585 Instruction::Label(Label { target: end_target }),
586 ])
587 .collect::<Vec<Instruction>>(),
588 );
589
590 looped_program
591 }
592
593 pub fn resolve_placeholders(&mut self) {
599 self.resolve_placeholders_with_custom_resolvers(
600 self.default_target_resolver(),
601 self.default_qubit_resolver(),
602 )
603 }
604
605 #[allow(clippy::type_complexity)]
615 pub fn resolve_placeholders_with_custom_resolvers(
616 &mut self,
617 target_resolver: Box<dyn Fn(&TargetPlaceholder) -> Option<String>>,
618 qubit_resolver: Box<dyn Fn(&QubitPlaceholder) -> Option<u64>>,
619 ) {
620 for instruction in &mut self.instructions {
621 instruction.resolve_placeholders(&target_resolver, &qubit_resolver);
622 }
623 self.rebuild_used_qubits()
624 }
625
626 #[allow(clippy::type_complexity)]
629 pub fn default_target_resolver(&self) -> Box<dyn Fn(&TargetPlaceholder) -> Option<String>> {
630 let mut fixed_labels = HashSet::new();
631 let mut label_placeholders = IndexSet::new();
632 for target in self.get_targets() {
633 match target {
634 Target::Fixed(fixed) => {
635 fixed_labels.insert(fixed.clone());
636 }
637 Target::Placeholder(placeholder) => {
638 label_placeholders.insert(placeholder.clone());
639 }
640 }
641 }
642
643 let target_resolutions: HashMap<TargetPlaceholder, String> = label_placeholders
644 .into_iter()
645 .map(|p| {
646 let base_label = p.as_inner();
647 let mut next_label = format!("{base_label}_0");
648 let mut next_suffix = 1;
649
650 while fixed_labels.contains(&next_label) {
651 next_label = format!("{base_label}_{next_suffix}");
652 next_suffix += 1;
653 }
654 fixed_labels.insert(next_label.clone());
655
656 (p, next_label)
657 })
658 .collect();
659
660 Box::new(move |key| target_resolutions.get(key).cloned())
661 }
662
663 #[allow(clippy::type_complexity)]
666 pub fn default_qubit_resolver(&self) -> Box<dyn Fn(&QubitPlaceholder) -> Option<u64>> {
667 let mut qubits_used: HashSet<u64> = HashSet::new();
668 let mut qubit_placeholders: IndexSet<QubitPlaceholder> = IndexSet::new();
669
670 for instruction in &self.instructions {
672 let qubits = instruction.get_qubits();
673
674 for qubit in qubits {
675 match qubit {
676 Qubit::Fixed(index) => {
677 qubits_used.insert(*index);
678 }
679 Qubit::Placeholder(placeholder) => {
680 qubit_placeholders.insert(placeholder.clone());
681 }
682 Qubit::Variable(_) => {}
683 }
684 }
685 }
686
687 let qubit_iterator = (0u64..).filter(|index| !qubits_used.contains(index));
688 let qubit_resolutions: HashMap<QubitPlaceholder, u64> =
689 qubit_placeholders.into_iter().zip(qubit_iterator).collect();
690
691 Box::new(move |key| qubit_resolutions.get(key).copied())
692 }
693
694 pub fn is_empty(&self) -> bool {
695 self.len() == 0
696 }
697
698 pub fn len(&self) -> usize {
699 self.memory_regions.len()
700 + self.frames.len()
701 + self.waveforms.len()
702 + self.gate_definitions.len()
703 + self.instructions.len()
704 + self.extern_pragma_map.len()
705 }
706
707 pub fn to_instructions(&self) -> Vec<Instruction> {
709 let mut instructions: Vec<Instruction> = Vec::with_capacity(self.len());
710
711 instructions.extend(self.extern_pragma_map.to_instructions());
712 instructions.extend(self.memory_regions.iter().map(|(name, descriptor)| {
713 Instruction::Declaration(Declaration {
714 name: name.clone(),
715 size: descriptor.size.clone(),
716 sharing: descriptor.sharing.clone(),
717 })
718 }));
719 instructions.extend(self.frames.to_instructions());
720 instructions.extend(self.waveforms.iter().map(|(name, definition)| {
721 Instruction::WaveformDefinition(WaveformDefinition {
722 name: name.clone(),
723 definition: definition.clone(),
724 })
725 }));
726 instructions.extend(self.calibrations.to_instructions());
727 instructions.extend(
728 self.gate_definitions
729 .values()
730 .cloned()
731 .map(Instruction::GateDefinition),
732 );
733 instructions.extend(self.instructions.clone());
734 instructions
735 }
736
737 pub fn to_unitary(&self, n_qubits: u64) -> Result<Matrix> {
743 let mut umat = Array2::eye(2usize.pow(n_qubits as u32));
744 for instruction in self.instructions.clone() {
745 match instruction {
746 Instruction::Halt => {}
747 Instruction::Gate(mut gate) => {
748 umat = gate.to_unitary(n_qubits)?.dot(&umat);
749 }
750 _ => return Err(ProgramError::UnsupportedForUnitary(instruction)),
751 }
752 }
753 Ok(umat)
754 }
755
756 pub fn get_instruction(&self, index: usize) -> Option<&Instruction> {
758 self.instructions.get(index)
759 }
760
761 pub fn try_extern_signature_map_from_pragma_map(
767 &self,
768 ) -> std::result::Result<ExternSignatureMap, (Pragma, ExternError)> {
769 ExternSignatureMap::try_from(self.extern_pragma_map.clone())
770 }
771}
772
773impl Quil for Program {
774 fn write(
775 &self,
776 writer: &mut impl std::fmt::Write,
777 fall_back_to_debug: bool,
778 ) -> std::result::Result<(), crate::quil::ToQuilError> {
779 for instruction in self.to_instructions() {
780 instruction.write(writer, fall_back_to_debug)?;
781 writeln!(writer)?;
782 }
783 Ok(())
784 }
785}
786
787impl FromStr for Program {
788 type Err = ProgramError;
789 fn from_str(s: &str) -> Result<Self> {
790 let input = LocatedSpan::new(s);
791 let lexed = lex(input).map_err(ParseProgramError::<Self>::from)?;
792 map_parsed(
793 disallow_leftover(
794 parse_instructions(&lexed).map_err(ParseError::from_nom_internal_err),
795 ),
796 |instructions| {
797 let mut program = Self::new();
798 program.add_instructions(instructions);
799 program
800 },
801 )
802 .map_err(ProgramError::from)
803 }
804}
805
806impl From<Vec<Instruction>> for Program {
807 fn from(instructions: Vec<Instruction>) -> Self {
808 let mut p = Program::new();
809 p.add_instructions(instructions);
810 p
811 }
812}
813
814impl ops::Add<Program> for Program {
815 type Output = Program;
816
817 fn add(mut self, rhs: Program) -> Program {
818 self += rhs;
819 self
820 }
821}
822
823impl ops::AddAssign<Program> for Program {
824 fn add_assign(&mut self, rhs: Program) {
825 self.calibrations.extend(rhs.calibrations);
826 self.memory_regions.extend(rhs.memory_regions);
827 self.frames.merge(rhs.frames);
828 self.waveforms.extend(rhs.waveforms);
829 self.gate_definitions.extend(rhs.gate_definitions);
830 self.extern_pragma_map.extend(rhs.extern_pragma_map);
831 self.instructions.extend(rhs.instructions);
832 self.used_qubits.extend(rhs.used_qubits);
833 }
834}
835
836#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
837pub struct InstructionIndex(pub usize);
838
839impl InstructionIndex {
840 fn map(self, f: impl FnOnce(usize) -> usize) -> Self {
841 Self(f(self.0))
842 }
843}
844
845pub type ProgramCalibrationExpansionSourceMap =
846 SourceMap<InstructionIndex, MaybeCalibrationExpansion>;
847
848#[derive(Clone, Debug, PartialEq)]
849pub struct ProgramCalibrationExpansion {
850 program: Program,
851 source_map: ProgramCalibrationExpansionSourceMap,
852}
853
854impl ProgramCalibrationExpansion {
855 pub fn program(&self) -> &Program {
856 &self.program
857 }
858
859 pub fn source_map(&self) -> &ProgramCalibrationExpansionSourceMap {
860 &self.source_map
861 }
862}
863
864#[cfg(test)]
865mod tests {
866 use super::Program;
867 use crate::{
868 imag,
869 instruction::{
870 CalibrationIdentifier, Call, Declaration, ExternSignatureMap, Gate, Instruction, Jump,
871 JumpUnless, JumpWhen, Label, Matrix, MemoryReference, Qubit, QubitPlaceholder,
872 ScalarType, Target, TargetPlaceholder, UnresolvedCallArgument, Vector,
873 RESERVED_PRAGMA_EXTERN,
874 },
875 program::{
876 calibration::{CalibrationExpansion, CalibrationSource, MaybeCalibrationExpansion},
877 source_map::{SourceMap, SourceMapEntry},
878 InstructionIndex, MemoryAccesses,
879 },
880 quil::{Quil, INDENT},
881 real,
882 };
883 use approx::assert_abs_diff_eq;
884 use insta::{assert_debug_snapshot, assert_snapshot};
885 use ndarray::{array, linalg::kron, Array2};
886 use num_complex::Complex64;
887 use once_cell::sync::Lazy;
888 use rstest::rstest;
889 use std::{
890 collections::{HashMap, HashSet},
891 str::FromStr,
892 };
893
894 #[test]
895 fn program_eq() {
896 let input = "
897DECLARE ro BIT
898MEASURE q ro
899JUMP-UNLESS @end-reset ro
900X q
901LABEL @end-reset
902
903DEFCAL I 0:
904 DELAY 0 1.0
905DEFFRAME 0 \"rx\":
906 HARDWARE-OBJECT: \"hardware\"
907DEFWAVEFORM custom:
908 1,2
909I 0
910";
911 let a = Program::from_str(input).unwrap();
912 let b = Program::from_str(input).unwrap();
913 assert_eq!(a, b);
914 }
915
916 #[test]
917 fn program_neq() {
918 let input_a = "
919DECLARE ro BIT
920MEASURE q ro
921JUMP-UNLESS @end-reset ro
922X q
923LABEL @end-reset
924
925DEFCAL I 0:
926 DELAY 0 1.0
927DEFFRAME 0 \"rx\":
928 HARDWARE-OBJECT: \"hardware\"
929DEFWAVEFORM custom:
930 1,2
931I 0
932";
933 let input_b = "
934DECLARE readout BIT
935MEASURE q readout
936JUMP-UNLESS @end-reset readout
937X q
938LABEL @end-reset
939
940DEFCAL I 1:
941 DELAY 1 1.0
942DEFFRAME 1 \"rx\":
943 HARDWARE-OBJECT: \"hardware\"
944DEFWAVEFORM custom:
945 1,2
946I 0
947";
948 let a = Program::from_str(input_a).unwrap();
949 let b = Program::from_str(input_b).unwrap();
950 assert_ne!(a, b);
951 }
952
953 #[test]
956 fn program_headers() {
957 let input = "
958DECLARE ro BIT[5]
959DEFCAL I 0:
960 DELAY 0 1.0
961DEFFRAME 0 \"rx\":
962 HARDWARE-OBJECT: \"hardware\"
963DEFWAVEFORM custom:
964 1, 2
965I 0
966";
967 let program = Program::from_str(input).unwrap();
968 assert_eq!(program.calibrations.len(), 1);
969 assert_eq!(program.memory_regions.len(), 1);
970 assert_eq!(program.frames.len(), 1);
971 assert_eq!(program.waveforms.len(), 1);
972 assert_eq!(program.instructions.len(), 1);
973
974 assert_eq!(
975 program.to_quil().unwrap(),
976 "DECLARE ro BIT[5]
977DEFFRAME 0 \"rx\":
978 HARDWARE-OBJECT: \"hardware\"
979DEFWAVEFORM custom:
980 1, 2
981DEFCAL I 0:
982 DELAY 0 1
983I 0
984"
985 );
986 }
987
988 #[test]
989 fn program_deterministic_ordering() {
990 let input = "
991DECLARE ro BIT
992DECLARE anc BIT
993DECLARE ec BIT
994";
995 let program1 = Program::from_str(input).unwrap().to_quil().unwrap();
996 let program2 = Program::from_str(input).unwrap().to_quil().unwrap();
997
998 assert!(program1.lines().eq(program2.lines()));
1001 }
1002
1003 #[test]
1006 fn expand_calibrations() {
1007 let input = r#"DECLARE ro BIT[1]
1008DEFFRAME 0 "a":
1009 HARDWARE-OBJECT: "hardware"
1010
1011DEFCAL I 0:
1012 DECLAREMEM
1013 NOP
1014 NOP
1015
1016DEFCAL DECLAREMEM:
1017 DECLARE mem BIT[1]
1018 NOP
1019
1020I 0
1021PULSE 0 "a" custom_waveform
1022I 0
1023"#;
1024
1025 let expected = "DECLARE ro BIT[1]
1026DECLARE mem BIT[1]
1027DEFFRAME 0 \"a\":
1028 HARDWARE-OBJECT: \"hardware\"
1029DEFCAL I 0:
1030 DECLAREMEM
1031 NOP
1032 NOP
1033DEFCAL DECLAREMEM:
1034 DECLARE mem BIT[1]
1035 NOP
1036NOP
1037NOP
1038NOP
1039PULSE 0 \"a\" custom_waveform
1040NOP
1041NOP
1042NOP
1043";
1044
1045 let expected_source_map = SourceMap {
1046 entries: vec![
1047 SourceMapEntry {
1048 source_location: InstructionIndex(0),
1049 target_location: MaybeCalibrationExpansion::Expanded(CalibrationExpansion {
1050 calibration_used: CalibrationIdentifier {
1051 name: "I".to_string(),
1052 qubits: vec![Qubit::Fixed(0)],
1053 ..CalibrationIdentifier::default()
1054 }
1055 .into(),
1056 range: InstructionIndex(0)..InstructionIndex(3),
1057 expansions: SourceMap {
1058 entries: vec![SourceMapEntry {
1059 source_location: InstructionIndex(0),
1060 target_location: CalibrationExpansion {
1061 calibration_used: CalibrationSource::Calibration(
1062 CalibrationIdentifier {
1063 modifiers: vec![],
1064 name: "DECLAREMEM".to_string(),
1065 parameters: vec![],
1066 qubits: vec![],
1067 },
1068 ),
1069 range: InstructionIndex(0)..InstructionIndex(1),
1070 expansions: SourceMap { entries: vec![] },
1071 },
1072 }],
1073 },
1074 }),
1075 },
1076 SourceMapEntry {
1077 source_location: InstructionIndex(1),
1078 target_location: MaybeCalibrationExpansion::Unexpanded(InstructionIndex(3)),
1079 },
1080 SourceMapEntry {
1081 source_location: InstructionIndex(2),
1082 target_location: MaybeCalibrationExpansion::Expanded(CalibrationExpansion {
1083 calibration_used: CalibrationIdentifier {
1084 name: "I".to_string(),
1085 qubits: vec![Qubit::Fixed(0)],
1086 ..CalibrationIdentifier::default()
1087 }
1088 .into(),
1089 range: InstructionIndex(4)..InstructionIndex(7),
1090 expansions: SourceMap {
1091 entries: vec![SourceMapEntry {
1092 source_location: InstructionIndex(0),
1093 target_location: CalibrationExpansion {
1094 calibration_used: CalibrationSource::Calibration(
1095 CalibrationIdentifier {
1096 modifiers: vec![],
1097 name: "DECLAREMEM".to_string(),
1098 parameters: vec![],
1099 qubits: vec![],
1100 },
1101 ),
1102 range: InstructionIndex(0)..InstructionIndex(1),
1103 expansions: SourceMap { entries: vec![] },
1104 },
1105 }],
1106 },
1107 }),
1108 },
1109 ],
1110 };
1111
1112 let program = Program::from_str(input).unwrap();
1113 let expanded_program = program.expand_calibrations_with_source_map().unwrap();
1114 pretty_assertions::assert_eq!(expanded_program.program.to_quil().unwrap(), expected);
1115 pretty_assertions::assert_eq!(expanded_program.source_map, expected_source_map);
1116 }
1117
1118 #[test]
1119 fn frame_blocking() {
1120 let input = "DEFFRAME 0 \"a\":
1121\tHARDWARE-OBJECT: \"hardware\"
1122
1123DEFFRAME 0 \"b\":
1124\tHARDWARE-OBJECT: \"hardware\"
1125
1126DEFFRAME 1 \"c\":
1127\tHARDWARE-OBJECT: \"hardware\"
1128
1129DEFFRAME 0 1 \"2q\":
1130\tHARDWARE-OBJECT: \"hardware\"
1131";
1132
1133 let program = Program::from_str(input).unwrap();
1134
1135 for (instruction_string, expected_used_frames, expected_blocked_frames) in vec![
1136 (
1138 r#"PULSE 0 "a" custom_waveform"#,
1139 vec![r#"0 "a""#],
1140 vec![r#"0 "b""#, r#"0 1 "2q""#],
1141 ),
1142 (
1143 r#"PULSE 1 "c" custom_waveform"#,
1144 vec![r#"1 "c""#],
1145 vec![r#"0 1 "2q""#],
1146 ),
1147 (r#"PULSE 2 "a" custom_waveform"#, vec![], vec![]),
1149 (
1151 r#"CAPTURE 0 "a" custom_waveform ro[0]"#,
1152 vec![r#"0 "a""#],
1153 vec![r#"0 "b""#, r#"0 1 "2q""#],
1154 ),
1155 (
1156 r#"CAPTURE 1 "c" custom_waveform ro[0]"#,
1157 vec![r#"1 "c""#],
1158 vec![r#"0 1 "2q""#],
1159 ),
1160 (r#"CAPTURE 2 "a" custom_waveform ro[0]"#, vec![], vec![]),
1161 (
1163 r#"RAW-CAPTURE 0 "a" 1e-6 ro[0]"#,
1164 vec![r#"0 "a""#],
1165 vec![r#"0 "b""#, r#"0 1 "2q""#],
1166 ),
1167 (
1168 r#"RAW-CAPTURE 1 "c" 1e-6 ro[0]"#,
1169 vec![r#"1 "c""#],
1170 vec![r#"0 1 "2q""#],
1171 ),
1172 (r#"RAW-CAPTURE 2 "a" 1e-6 ro[0]"#, vec![], vec![]),
1173 (
1175 r#"NONBLOCKING PULSE 0 "a" custom_waveform"#,
1176 vec![r#"0 "a""#],
1177 vec![],
1178 ),
1179 (
1180 r#"NONBLOCKING PULSE 1 "c" custom_waveform"#,
1181 vec![r#"1 "c""#],
1182 vec![],
1183 ),
1184 (
1185 r#"NONBLOCKING PULSE 0 1 "2q" custom_waveform"#,
1186 vec![r#"0 1 "2q""#],
1187 vec![],
1188 ),
1189 (r#"FENCE 1"#, vec![], vec![r#"1 "c""#, r#"0 1 "2q""#]),
1191 (
1193 r#"FENCE"#,
1194 vec![],
1195 vec![r#"0 "a""#, r#"0 "b""#, r#"1 "c""#, r#"0 1 "2q""#],
1196 ),
1197 (r#"DELAY 0 1.0"#, vec![r#"0 "a""#, r#"0 "b""#], vec![]),
1199 (r#"DELAY 1 1.0"#, vec![r#"1 "c""#], vec![]),
1200 (r#"DELAY 1 "c" 1.0"#, vec![r#"1 "c""#], vec![]),
1201 (r#"DELAY 0 1 1.0"#, vec![r#"0 1 "2q""#], vec![]),
1202 (
1203 r#"SWAP-PHASES 0 "a" 0 "b""#,
1204 vec![r#"0 "a""#, r#"0 "b""#],
1205 vec![],
1206 ),
1207 ] {
1208 let instruction = Instruction::parse(instruction_string).unwrap();
1209 let matched_frames = program.get_frames_for_instruction(&instruction).unwrap();
1210 let used_frames: HashSet<String> = matched_frames
1211 .used()
1212 .iter()
1213 .map(|f| f.to_quil_or_debug())
1214 .collect();
1215 let expected_used_frames: HashSet<String> = expected_used_frames
1216 .into_iter()
1217 .map(|el| el.to_owned())
1218 .collect();
1219 assert_eq!(
1220 used_frames, expected_used_frames,
1221 "Instruction {instruction} *used* frames `{used_frames:?}` but we expected `{expected_used_frames:?}`", instruction=instruction.to_quil_or_debug()
1222 );
1223
1224 let blocked_frames: HashSet<String> = matched_frames
1225 .blocked()
1226 .iter()
1227 .map(|f| f.to_quil_or_debug())
1228 .collect();
1229 let expected_blocked_frames: HashSet<String> = expected_blocked_frames
1230 .into_iter()
1231 .map(|el| el.to_owned())
1232 .collect();
1233 assert_eq!(
1234 blocked_frames, expected_blocked_frames,
1235 "Instruction {instruction} *blocked* frames `{blocked_frames:?}` but we expected `{expected_blocked_frames:?}`", instruction=instruction.to_quil_or_debug()
1236 );
1237 }
1238 }
1239
1240 #[test]
1241 fn into_simplified() {
1242 let input = "
1243DEFCAL MEASURE 0 addr:
1244 CAPTURE 0 \"ro_rx\" custom addr
1245
1246DEFCAL MEASURE 1 addr:
1247 CAPTURE 1 \"ro_rx\" custom addr
1248
1249DEFFRAME 0 \"ro_rx\":
1250 ATTRIBUTE: \"value\"
1251
1252DEFFRAME 1 \"ro_rx\":
1253 ATTRIBUTE: \"other\"
1254
1255DEFWAVEFORM custom:
1256 0.0, 1.0
1257
1258DEFWAVEFORM other_custom:
1259 2.0, 3.0
1260
1261DECLARE ro BIT
1262MEASURE 0 ro
1263";
1264
1265 let expected = "
1266DECLARE ro BIT
1267
1268DEFFRAME 0 \"ro_rx\":
1269 ATTRIBUTE: \"value\"
1270
1271DEFWAVEFORM custom:
1272 0.0, 1.0
1273
1274CAPTURE 0 \"ro_rx\" custom ro
1275";
1276 let program = Program::from_str(input).map_err(|e| e.to_string()).unwrap();
1277 let program = program.into_simplified().unwrap();
1278 assert_eq!(program, Program::from_str(expected).unwrap());
1279 }
1280
1281 #[test]
1282 fn test_get_qubits() {
1283 let input = "
1284DECLARE ro BIT
1285MEASURE q ro
1286JUMP-UNLESS @end-reset ro
1287X q
1288LABEL @end-reset
1289DEFCAL I 0:
1290 DELAY 0 1.0
1291DEFFRAME 0 \"rx\":
1292 HARDWARE-OBJECT: \"hardware\"
1293DEFWAVEFORM custom:
1294 1,2
1295I 0
1296";
1297 let program = Program::from_str(input).unwrap();
1298 let expected_owned = [Qubit::Fixed(0), Qubit::Variable("q".to_string())];
1299 let expected = expected_owned.iter().collect::<HashSet<_>>();
1300 let actual = program.get_used_qubits();
1301 assert_eq!(expected, actual.iter().collect());
1302 }
1303
1304 #[test]
1305 fn test_add_instructions() {
1306 let mut p = Program::new();
1307 let instrs = vec![Instruction::Nop, Instruction::Nop];
1308 p.add_instructions(instrs.clone());
1309 assert_eq!(p.instructions, instrs);
1310 }
1311
1312 #[test]
1313 fn test_add_programs() {
1314 let lhs_input = "
1315DECLARE ro BIT
1316
1317MEASURE q ro
1318X q
1319
1320DEFCAL I 0:
1321 DELAY 0 1.0
1322DEFFRAME 0 \"rx\":
1323 HARDWARE-OBJECT: \"hardware\"
1324DEFWAVEFORM custom:
1325 1,2
1326DEFGATE FOO:
1327 1, 0
1328 0, 1
1329I 0
1330";
1331 let rhs_input = "
1332DECLARE foo REAL
1333H 1
1334CNOT 2 3
1335
1336DEFCAL I 1:
1337 DELAY 0 1.0
1338DEFFRAME 1 \"rx\":
1339 HARDWARE-OBJECT: \"hardware\"
1340DEFWAVEFORM custom2:
1341 1,2
1342DEFGATE BAR:
1343 0, 1
1344 1, 0
1345";
1346 let lhs = Program::from_str(lhs_input).unwrap();
1347 let rhs = Program::from_str(rhs_input).unwrap();
1348
1349 let sum = lhs.clone() + rhs.clone();
1350 let mut in_place_sum = lhs.clone();
1351 in_place_sum += rhs;
1352
1353 let expected_qubits = [
1354 Qubit::Fixed(0),
1355 Qubit::Fixed(1),
1356 Qubit::Fixed(2),
1357 Qubit::Fixed(3),
1358 Qubit::Variable("q".to_string()),
1359 ];
1360
1361 let expected_qubits = expected_qubits.iter().collect::<HashSet<_>>();
1362 for program in [&sum, &in_place_sum] {
1363 assert_eq!(program.calibrations.len(), 2);
1364 assert_eq!(program.memory_regions.len(), 2);
1365 assert_eq!(program.frames.len(), 2);
1366 assert_eq!(program.waveforms.len(), 2);
1367 assert_eq!(program.instructions.len(), 5);
1368 assert_eq!(expected_qubits, sum.get_used_qubits().iter().collect());
1369 }
1370 }
1371
1372 #[test]
1373 fn test_from_vec_instructions() {
1374 let expected: Program = "NOP\nNOP".parse().expect("Should parse NOPs");
1375 let p: Program = expected.instructions.clone().into();
1376 assert_eq!(expected, p);
1377 }
1378
1379 #[test]
1380 fn test_clone_without_body_instructions() {
1381 let quil = "
1382DECLARE ro BIT
1383MEASURE q ro
1384JUMP-UNLESS @end-reset ro
1385X q
1386LABEL @end-reset
1387
1388DEFCAL I 0:
1389 DELAY 0 1.0
1390DEFFRAME 0 \"rx\":
1391 HARDWARE-OBJECT: \"hardware\"
1392DEFWAVEFORM custom:
1393 1,2
1394I 0
1395";
1396 let original = Program::from_str(quil).unwrap();
1398 assert!(!original.instructions.is_empty());
1399
1400 let mut cloned = original.clone_without_body_instructions();
1401 assert!(cloned.instructions.is_empty());
1403 assert!(cloned.used_qubits.is_empty());
1404
1405 cloned.add_instructions(original.instructions.clone());
1408 assert_eq!(original, cloned);
1409 }
1410
1411 static _0: Complex64 = real!(0.0);
1412 static _1: Complex64 = real!(1.0);
1413 static _I: Complex64 = imag!(1.0);
1414 static _1_SQRT_2: Complex64 = real!(std::f64::consts::FRAC_1_SQRT_2);
1415 static H: Lazy<Matrix> = Lazy::new(|| array![[_1, _1], [_1, -_1]] * _1_SQRT_2);
1416 static X: Lazy<Matrix> = Lazy::new(|| array![[_0, _1], [_1, _0]]);
1417 static Y: Lazy<Matrix> = Lazy::new(|| array![[_0, -_I], [_I, _0]]);
1418 static Z: Lazy<Matrix> = Lazy::new(|| array![[_1, _0], [_0, -_1]]);
1419 static CNOT: Lazy<Matrix> = Lazy::new(|| {
1420 array![
1421 [_1, _0, _0, _0],
1422 [_0, _1, _0, _0],
1423 [_0, _0, _0, _1],
1424 [_0, _0, _1, _0]
1425 ]
1426 });
1427 static I2: Lazy<Matrix> = Lazy::new(|| Array2::eye(2));
1428 static I4: Lazy<Matrix> = Lazy::new(|| Array2::eye(4));
1429
1430 #[rstest]
1431 #[case("H 0\nH 1\nH 0", 2, &kron(&H, &I2))]
1432 #[case("H 0\nX 1\nY 2\nZ 3", 4, &kron(&Z, &kron(&Y, &kron(&X, &H))))]
1433 #[case("X 2\nCNOT 2 1\nCNOT 1 0", 3, &kron(&I2, &CNOT).dot(&kron(&CNOT, &I2)).dot(&kron(&X, &I4)))]
1434 fn test_to_unitary(#[case] input: &str, #[case] n_qubits: u64, #[case] expected: &Matrix) {
1435 let program = Program::from_str(input);
1436 assert!(program.is_ok());
1437 let matrix = program.unwrap().to_unitary(n_qubits);
1438 assert!(matrix.is_ok());
1439 assert_abs_diff_eq!(matrix.as_ref().unwrap(), expected);
1440 }
1441
1442 #[test]
1445 fn test_to_instructions() {
1446 let input = format!(
1447 "DECLARE foo REAL[1]
1448DEFFRAME 1 \"rx\":
1449{INDENT}HARDWARE-OBJECT: \"hardware\"
1450DEFWAVEFORM custom2:
1451{INDENT}1, 2
1452DEFCAL I 1:
1453{INDENT}DELAY 0 1
1454DEFGATE BAR AS MATRIX:
1455{INDENT}0, 1
1456{INDENT}1, 0
1457
1458H 1
1459CNOT 2 3
1460"
1461 );
1462 let program = Program::from_str(&input).unwrap();
1463 assert_debug_snapshot!(program.to_instructions());
1464 assert_eq!(program.to_quil().unwrap(), input);
1465 assert_eq!(program.to_instructions(), program.into_instructions());
1466 }
1467
1468 #[test]
1469 fn placeholder_replacement() {
1470 let placeholder_1 = QubitPlaceholder::default();
1471 let placeholder_2 = QubitPlaceholder::default();
1472 let label_placeholder_1 = TargetPlaceholder::new(String::from("custom_label"));
1473 let label_placeholder_2 = TargetPlaceholder::new(String::from("custom_label"));
1474
1475 let mut program = Program::new();
1476
1477 program.add_instruction(Instruction::Label(Label {
1478 target: Target::Placeholder(label_placeholder_1.clone()),
1479 }));
1480
1481 program.add_instruction(Instruction::Jump(Jump {
1482 target: Target::Placeholder(label_placeholder_2.clone()),
1483 }));
1484
1485 program.add_instruction(Instruction::JumpWhen(JumpWhen {
1486 target: Target::Placeholder(label_placeholder_2.clone()),
1487 condition: MemoryReference {
1488 name: "ro".to_string(),
1489 index: 0,
1490 },
1491 }));
1492
1493 program.add_instruction(Instruction::JumpUnless(JumpUnless {
1494 target: Target::Placeholder(label_placeholder_2.clone()),
1495 condition: MemoryReference {
1496 name: "ro".to_string(),
1497 index: 0,
1498 },
1499 }));
1500
1501 program.add_instruction(Instruction::Gate(Gate {
1502 name: "X".to_string(),
1503 qubits: vec![Qubit::Placeholder(placeholder_1.clone())],
1504 parameters: vec![],
1505 modifiers: vec![],
1506 }));
1507
1508 program.add_instruction(Instruction::Gate(Gate {
1509 name: "Y".to_string(),
1510 qubits: vec![Qubit::Placeholder(placeholder_2.clone())],
1511 parameters: vec![],
1512 modifiers: vec![],
1513 }));
1514
1515 let mut auto_increment_resolved = program.clone();
1516 auto_increment_resolved.resolve_placeholders();
1517 assert_eq!(
1518 auto_increment_resolved.instructions,
1519 vec![
1520 Instruction::Label(Label {
1521 target: Target::Fixed("custom_label_0".to_string())
1522 }),
1523 Instruction::Jump(Jump {
1524 target: Target::Fixed("custom_label_1".to_string()),
1525 }),
1526 Instruction::JumpWhen(JumpWhen {
1527 target: Target::Fixed("custom_label_1".to_string()),
1528 condition: MemoryReference {
1529 name: "ro".to_string(),
1530 index: 0,
1531 },
1532 }),
1533 Instruction::JumpUnless(JumpUnless {
1534 target: Target::Fixed("custom_label_1".to_string()),
1535 condition: MemoryReference {
1536 name: "ro".to_string(),
1537 index: 0,
1538 },
1539 }),
1540 Instruction::Gate(Gate {
1541 name: "X".to_string(),
1542 qubits: vec![Qubit::Fixed(0)],
1543 parameters: vec![],
1544 modifiers: vec![],
1545 }),
1546 Instruction::Gate(Gate {
1547 name: "Y".to_string(),
1548 qubits: vec![Qubit::Fixed(1)],
1549 parameters: vec![],
1550 modifiers: vec![],
1551 }),
1552 ]
1553 );
1554
1555 let mut custom_resolved = program.clone();
1556 let custom_target_resolutions = HashMap::from([
1557 (label_placeholder_1, "new_label".to_string()),
1558 (label_placeholder_2, "other_new_label".to_string()),
1559 ]);
1560 let custom_qubit_resolutions = HashMap::from([(placeholder_1, 42), (placeholder_2, 10000)]);
1561 custom_resolved.resolve_placeholders_with_custom_resolvers(
1562 Box::new(move |placeholder| custom_target_resolutions.get(placeholder).cloned()),
1563 Box::new(move |placeholder| custom_qubit_resolutions.get(placeholder).copied()),
1564 );
1565 assert_eq!(
1566 custom_resolved.instructions,
1567 vec![
1568 Instruction::Label(Label {
1569 target: Target::Fixed("new_label".to_string())
1570 }),
1571 Instruction::Jump(Jump {
1572 target: Target::Fixed("other_new_label".to_string()),
1573 }),
1574 Instruction::JumpWhen(JumpWhen {
1575 target: Target::Fixed("other_new_label".to_string()),
1576 condition: MemoryReference {
1577 name: "ro".to_string(),
1578 index: 0,
1579 },
1580 }),
1581 Instruction::JumpUnless(JumpUnless {
1582 target: Target::Fixed("other_new_label".to_string()),
1583 condition: MemoryReference {
1584 name: "ro".to_string(),
1585 index: 0,
1586 },
1587 }),
1588 Instruction::Gate(Gate {
1589 name: "X".to_string(),
1590 qubits: vec![Qubit::Fixed(42)],
1591 parameters: vec![],
1592 modifiers: vec![],
1593 }),
1594 Instruction::Gate(Gate {
1595 name: "Y".to_string(),
1596 qubits: vec![Qubit::Fixed(10000)],
1597 parameters: vec![],
1598 modifiers: vec![],
1599 }),
1600 ]
1601 );
1602 }
1603
1604 #[test]
1605 fn test_filter_instructions() {
1606 let input = "DECLARE foo REAL[1]
1607DEFFRAME 1 \"rx\":
1608\tHARDWARE-OBJECT: \"hardware\"
1609DEFCAL I 1:
1610\tDELAY 0 1
1611DEFGATE BAR AS MATRIX:
1612\t0, 1
1613\t1, 0
1614
1615H 1
1616CNOT 2 3";
1617
1618 let program = Program::from_str(input).unwrap();
1619 let program_without_quil_t =
1620 program.filter_instructions(|instruction| !instruction.is_quil_t());
1621 assert_snapshot!(program_without_quil_t.to_quil().unwrap())
1622 }
1623
1624 #[test]
1625 fn test_wrap_in_loop() {
1626 let input = "DECLARE ro BIT
1627DECLARE shot_count INTEGER
1628MEASURE q ro
1629JUMP-UNLESS @end-reset ro
1630X q
1631LABEL @end-reset
1632
1633DEFCAL I 0:
1634 DELAY 0 1.0
1635DEFFRAME 0 \"rx\":
1636 HARDWARE-OBJECT: \"hardware\"
1637DEFWAVEFORM custom:
1638 1,2
1639I 0
1640";
1641 let program = Program::from_str(input).unwrap().wrap_in_loop(
1642 MemoryReference {
1643 name: "shot_count".to_string(),
1644 index: 0,
1645 },
1646 Target::Fixed("loop-start".to_string()),
1647 Target::Fixed("loop-end".to_string()),
1648 10,
1649 );
1650
1651 assert_snapshot!(program.to_quil().unwrap())
1652 }
1653
1654 #[test]
1655 fn test_equality() {
1656 let input = "DECLARE foo REAL[1]
1657DEFFRAME 1 \"rx\":
1658\tHARDWARE-OBJECT: \"hardware\"
1659DEFCAL I 0:
1660\tDELAY 0 1
1661DEFCAL I 1:
1662\tDELAY 0 1
1663DEFCAL I 2:
1664\tDELAY 0 1
1665DEFCAL MEASURE 0 addr:
1666\tCAPTURE 0 \"ro_rx\" custom addr
1667DEFCAL MEASURE 1 addr:
1668\tCAPTURE 1 \"ro_rx\" custom addr
1669DEFWAVEFORM custom:
1670\t1,2
1671DEFWAVEFORM custom2:
1672\t3,4
1673DEFWAVEFORM another1:
1674\t4,5
1675DEFGATE BAR AS MATRIX:
1676\t0, 1
1677\t1, 0
1678DEFGATE FOO AS MATRIX:
1679\t0, 1
1680\t1, 0
1681
1682H 1
1683CNOT 2 3";
1684
1685 let program = Program::from_str(input).unwrap();
1686
1687 let is_global_state_instruction = move |i: &Instruction| -> bool {
1690 matches!(
1691 i,
1692 |Instruction::WaveformDefinition(_)| Instruction::GateDefinition(_)
1693 | Instruction::FrameDefinition(_)
1694 )
1695 };
1696 let mut program2 = program.filter_instructions(|i| !is_global_state_instruction(i));
1700 let global_instructions: Vec<Instruction> = program
1701 .filter_instructions(is_global_state_instruction)
1702 .into_instructions()
1703 .into_iter()
1704 .rev()
1705 .collect();
1706 program2.add_instructions(global_instructions.clone());
1707 assert_eq!(program, program2);
1708
1709 let mut program3 = Program::from_instructions(
1712 program
1713 .filter_instructions(|i| !is_global_state_instruction(i))
1714 .into_instructions()
1715 .into_iter()
1716 .rev()
1717 .collect(),
1718 );
1719 program3.add_instructions(global_instructions);
1720 assert!(program != program3)
1721 }
1722
1723 #[test]
1724 fn test_deterministic_serialization() {
1725 let input = "DECLARE foo REAL[1]
1726DECLARE bar BIT[1]
1727DECLARE baz BIT[1]
1728RX(pi) 0
1729CNOT 0 1
1730DEFCAL I 0:
1731\tDELAY 0 1
1732\tDELAY 1 1
1733DEFCAL I 1:
1734\tDELAY 0 1
1735\tDELAY 1 2
1736DEFCAL I 2:
1737\tDELAY 2 1
1738\tDELAY 2 3
1739DEFCAL MEASURE 0 addr:
1740\tRX(pi) 0
1741\tCAPTURE 0 \"ro_rx\" custom addr
1742DEFCAL MEASURE 1 addr:
1743\tRX(pi/2) 1
1744\tCAPTURE 1 \"ro_rx\" custom addr
1745DEFCAL MEASURE 2 addr:
1746\tRX(pi/2) 2
1747\tCAPTURE 2 \"ro_rx\" custom addr
1748DEFWAVEFORM custom:
1749\t1,2
1750DEFWAVEFORM custom2:
1751\t3,4
1752DEFWAVEFORM another1(%a, %b):
1753\t%a,%b
1754PULSE 0 \"xy\" flat(duration: 1e-6, iq: 2+3i)
1755PULSE 0 \"xy\" another1(a: 1e-6, b: 2+3i)
1756DEFGATE HADAMARD AS MATRIX:
1757\t(1/sqrt(2)),(1/sqrt(2))
1758\t(1/sqrt(2)),((-1)/sqrt(2))
1759DEFGATE RX(%theta) AS MATRIX:
1760\tcos((%theta/2)),((-1i)*sin((%theta/2)))
1761\t((-1i)*sin((%theta/2))),cos((%theta/2))
1762DEFGATE Name AS PERMUTATION:
1763\t1, 0
1764DEFCIRCUIT SIMPLE:
1765\tX 0
1766\tX 1
1767DEFGATE BAR AS MATRIX:
1768\t0, 1
1769\t1, 0
1770DEFGATE FOO AS MATRIX:
1771\t0, 1
1772\t1, 0
1773DEFGATE BAZ AS MATRIX:
1774\t1, 0
1775\t0, 1
1776MEASURE 1 bar
1777MEASURE 0 foo
1778HALT
1779DEFCIRCUIT CIRCFOO:
1780\tLABEL @FOO_A
1781\tJUMP @FOO_A
1782DEFFRAME 0 \"xy\":
1783\tSAMPLE-RATE: 3000
1784DEFFRAME 0 \"xy\":
1785\tDIRECTION: \"rx\"
1786\tCENTER-FREQUENCY: 1000
1787\tHARDWARE-OBJECT: \"some object\"
1788\tINITIAL-FREQUENCY: 2000
1789\tSAMPLE-RATE: 3000";
1790 let program = Program::from_str(input).unwrap();
1791 let quil = program.to_quil().unwrap();
1792
1793 let iterations = 100;
1797 for _ in 0..iterations {
1798 let new_program = Program::from_str(input).unwrap();
1799 assert_eq!(new_program.to_quil().unwrap(), quil);
1800 }
1801 }
1802
1803 #[test]
1807 fn test_extern_call() {
1808 let input = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
1809DECLARE reals REAL[3]
1810DECLARE octets OCTET[3]
1811CALL foo octets[1] reals
1812"#;
1813 let program = Program::from_str(input).expect("should be able to parse program");
1814 let reserialized = program
1815 .to_quil()
1816 .expect("should be able to serialize program");
1817 assert_eq!(input, reserialized);
1818
1819 let pragma = crate::instruction::Pragma {
1820 name: RESERVED_PRAGMA_EXTERN.to_string(),
1821 arguments: vec![crate::instruction::PragmaArgument::Identifier(
1822 "foo".to_string(),
1823 )],
1824 data: Some("OCTET (params : mut REAL[3])".to_string()),
1825 };
1826 let call = Call {
1827 name: "foo".to_string(),
1828 arguments: vec![
1829 UnresolvedCallArgument::MemoryReference(MemoryReference {
1830 name: "octets".to_string(),
1831 index: 1,
1832 }),
1833 UnresolvedCallArgument::Identifier("reals".to_string()),
1834 ],
1835 };
1836 let expected_program = Program::from_instructions(vec![
1837 Instruction::Declaration(Declaration::new(
1838 "reals".to_string(),
1839 Vector::new(ScalarType::Real, 3),
1840 None,
1841 )),
1842 Instruction::Declaration(Declaration::new(
1843 "octets".to_string(),
1844 Vector::new(ScalarType::Octet, 3),
1845 None,
1846 )),
1847 Instruction::Pragma(pragma.clone()),
1848 Instruction::Call(call.clone()),
1849 ]);
1850 assert_eq!(expected_program, program);
1851
1852 let extern_signature_map = ExternSignatureMap::try_from(program.extern_pragma_map)
1853 .expect("should be able parse extern pragmas");
1854 assert_eq!(extern_signature_map.len(), 1);
1855
1856 assert_eq!(
1857 Instruction::Pragma(pragma)
1858 .get_memory_accesses(&extern_signature_map)
1859 .expect("should be able to get memory accesses"),
1860 MemoryAccesses::default()
1861 );
1862
1863 assert_eq!(
1864 call.get_memory_accesses(&extern_signature_map)
1865 .expect("should be able to get memory accesses"),
1866 MemoryAccesses {
1867 reads: ["octets", "reals"].into_iter().map(String::from).collect(),
1868 writes: ["octets", "reals"].into_iter().map(String::from).collect(),
1869 ..MemoryAccesses::default()
1870 }
1871 );
1872 }
1873
1874 #[test]
1876 fn test_extern_call_simplification() {
1877 let input = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
1878PRAGMA EXTERN bar "OCTET (params : mut REAL[3])"
1879DECLARE reals REAL[3]
1880DECLARE octets OCTET[3]
1881CALL foo octets[1] reals
1882"#;
1883 let program = Program::from_str(input).expect("should be able to parse program");
1884
1885 let expected = r#"PRAGMA EXTERN foo "OCTET (params : mut REAL[3])"
1886DECLARE reals REAL[3]
1887DECLARE octets OCTET[3]
1888CALL foo octets[1] reals
1889"#;
1890
1891 let reserialized = program
1892 .expand_calibrations()
1893 .expect("should be able to expand calibrations")
1894 .into_simplified()
1895 .expect("should be able to simplify program")
1896 .to_quil()
1897 .expect("should be able to serialize program");
1898 assert_eq!(expected, reserialized);
1899 }
1900}