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