quil_rs/program/
mod.rs

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