Skip to main content

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