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