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