quil_rs/instruction/
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::HashSet, fmt, iter, str::FromStr};
16
17use itertools::Itertools as _;
18use nom_locate::LocatedSpan;
19
20#[cfg(feature = "stubs")]
21use pyo3_stub_gen::derive::{gen_stub_pyclass_complex_enum, gen_stub_pymethods};
22
23use crate::{
24    expression::Expression,
25    parser::{lex, parse_instructions},
26    program::{
27        frame::{FrameMatchCondition, FrameMatchConditions},
28        MatchedFrames, MemoryAccesses, MemoryAccessesError,
29    },
30    quil::{write_join_quil, Quil, ToQuilResult},
31    Program,
32};
33
34#[cfg(feature = "python")]
35pub(crate) mod quilpy;
36
37mod calibration;
38mod circuit;
39mod classical;
40mod control_flow;
41mod declaration;
42mod extern_call;
43mod frame;
44mod gate;
45mod gate_sequence;
46mod measurement;
47mod pragma;
48mod qubit;
49mod reset;
50mod timing;
51mod waveform;
52
53pub use self::{
54    calibration::{
55        CalibrationDefinition, CalibrationIdentifier, CalibrationSignature,
56        MeasureCalibrationDefinition, MeasureCalibrationIdentifier,
57    },
58    circuit::CircuitDefinition,
59    classical::{
60        Arithmetic, ArithmeticOperand, ArithmeticOperator, BinaryLogic, BinaryOperand,
61        BinaryOperator, ClassicalOperand, Comparison, ComparisonOperand, ComparisonOperator,
62        Convert, Exchange, Move, UnaryLogic, UnaryOperator,
63    },
64    control_flow::{Jump, JumpUnless, JumpWhen, Label, Target, TargetPlaceholder},
65    declaration::{Declaration, Load, MemoryReference, Offset, ScalarType, Sharing, Store, Vector},
66    extern_call::*,
67    frame::{
68        AttributeValue, Capture, FrameAttributes, FrameDefinition, FrameIdentifier, Pulse,
69        RawCapture, SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, SwapPhases,
70    },
71    gate::{
72        Gate, GateDefinition, GateError, GateModifier, GateSpecification, GateType, Matrix,
73        PauliGate, PauliSum, PauliTerm,
74    },
75    gate_sequence::{DefGateSequence, DefGateSequenceError, DefGateSequenceExpansionError},
76    measurement::Measurement,
77    pragma::{Include, Pragma, PragmaArgument, RESERVED_PRAGMA_EXTERN},
78    qubit::{Qubit, QubitPlaceholder},
79    reset::Reset,
80    timing::{Delay, Fence},
81    waveform::{Waveform, WaveformDefinition, WaveformInvocation, WaveformParameters},
82};
83
84pub(crate) use self::gate::GateSignature;
85
86#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
87pub enum ValidationError {
88    #[error(transparent)]
89    GateError(#[from] GateError),
90    #[error(transparent)]
91    DefGateSequenceError(#[from] DefGateSequenceError),
92}
93
94/// A Quil instruction.
95///
96/// Each variant (for Python users, each nested subclass)
97/// corresponds to a possible type of Quil instruction,
98/// which is accessible as a member within the variant.
99///
100/// # Python Users
101///
102/// The subclasses of this class are class attributes defined on it,
103/// and can be used to "wrap" instructions when they should be stored together.
104/// In particular, they are *NOT* the instruction classes you'd typically create,
105/// and instances of instruction classes are *NOT* subclasses of this class:
106///
107/// ```python
108/// >>> from quil.instructions import Instruction, Gate, Qubit
109/// >>> issubclass(Instruction.Gate, Instruction)
110/// True
111/// >>> issubclass(Gate, Instruction)
112/// False
113/// >>> g = Gate("X", (), (Qubit.Fixed(0),), ())
114/// >>> isinstance(g, Gate)
115/// True
116/// >>> isinstance(g, Instruction.Gate)
117/// False
118/// >>> g_instr = Instruction.Gate(g)
119/// >>> isinstance(g_instr, Gate)
120/// False
121/// >>> isinstance(g_instr, Instruction.Gate)
122/// True
123/// >>> isinstance(g_instr._0, Gate)
124/// True
125/// >>> g_instr._0 == g
126/// True
127/// ```
128///
129/// The point of this class is to wrap different kinds of instructions
130/// when stored together in a collection, all of which are of type `Instruction`.
131/// You can check for different instruction variants and destructure them using `match`:
132///
133/// ```python
134/// match g_instr:
135///     case Instruction.Gate(gate):
136///         assert isinstance(gate, Gate)
137///     case Instruction.Wait() | Instruction.Nop():
138///         # note the `()` -- these aren't like Python's enumerations!
139/// ```
140#[derive(Clone, Debug, PartialEq)]
141#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
142#[cfg_attr(
143    feature = "python",
144    pyo3::pyclass(module = "quil.instructions", eq, frozen)
145)]
146pub enum Instruction {
147    Arithmetic(Arithmetic),
148    BinaryLogic(BinaryLogic),
149    CalibrationDefinition(CalibrationDefinition),
150    Call(Call),
151    Capture(Capture),
152    CircuitDefinition(CircuitDefinition),
153    Convert(Convert),
154    Comparison(Comparison),
155    Declaration(Declaration),
156    Delay(Delay),
157    Exchange(Exchange),
158    Fence(Fence),
159    FrameDefinition(FrameDefinition),
160    Gate(Gate),
161    GateDefinition(GateDefinition),
162    // Developer note: In Rust, this could be just `Halt`,
163    // but to be compatible with PyO3's "complex enums",
164    // it has to be an empty tuple variant.
165    // The same restriction applies `Nop` and `Wait`,
166    // as well as those in the `Expression` enumeration.
167    Halt(),
168    Include(Include),
169    Jump(Jump),
170    JumpUnless(JumpUnless),
171    JumpWhen(JumpWhen),
172    Label(Label),
173    Load(Load),
174    MeasureCalibrationDefinition(MeasureCalibrationDefinition),
175    Measurement(Measurement),
176    Move(Move),
177    Nop(),
178    Pragma(Pragma),
179    Pulse(Pulse),
180    RawCapture(RawCapture),
181    Reset(Reset),
182    SetFrequency(SetFrequency),
183    SetPhase(SetPhase),
184    SetScale(SetScale),
185    ShiftFrequency(ShiftFrequency),
186    ShiftPhase(ShiftPhase),
187    Store(Store),
188    SwapPhases(SwapPhases),
189    UnaryLogic(UnaryLogic),
190    WaveformDefinition(WaveformDefinition),
191    Wait(),
192}
193
194#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
195#[cfg_attr(feature = "python", pyo3::pymethods)]
196impl Instruction {
197    /// Returns true if the instruction is a Quil-T instruction.
198    pub fn is_quil_t(&self) -> bool {
199        match self {
200            Instruction::Capture(_)
201            | Instruction::CalibrationDefinition(_)
202            | Instruction::Delay(_)
203            | Instruction::Fence(_)
204            | Instruction::FrameDefinition(_)
205            | Instruction::MeasureCalibrationDefinition(_)
206            | Instruction::Pulse(_)
207            | Instruction::RawCapture(_)
208            | Instruction::SetFrequency(_)
209            | Instruction::SetPhase(_)
210            | Instruction::SetScale(_)
211            | Instruction::ShiftFrequency(_)
212            | Instruction::ShiftPhase(_)
213            | Instruction::SwapPhases(_)
214            | Instruction::WaveformDefinition(_) => true,
215
216            Instruction::Arithmetic(_)
217            | Instruction::BinaryLogic(_)
218            | Instruction::Call(_)
219            | Instruction::CircuitDefinition(_)
220            | Instruction::Convert(_)
221            | Instruction::Comparison(_)
222            | Instruction::Declaration(_)
223            | Instruction::Exchange(_)
224            | Instruction::Gate(_)
225            | Instruction::GateDefinition(_)
226            | Instruction::Halt()
227            | Instruction::Include(_)
228            | Instruction::Jump(_)
229            | Instruction::JumpUnless(_)
230            | Instruction::JumpWhen(_)
231            | Instruction::Label(_)
232            | Instruction::Load(_)
233            | Instruction::Measurement(_)
234            | Instruction::Move(_)
235            | Instruction::Nop()
236            | Instruction::Pragma(_)
237            | Instruction::Reset(_)
238            | Instruction::Store(_)
239            | Instruction::Wait()
240            | Instruction::UnaryLogic(_) => false,
241        }
242    }
243}
244
245/// What purpose an instruction serves in the program from a [Quil-T] perspective.
246///
247/// [Quil-T]: https://quil-lang.github.io/#12Annex-T--Pulse-Level-Control
248#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
249pub enum InstructionRole {
250    /// An instruction that is relevant to the superstructure of the program but not to
251    /// [Quil-T][]–level execution; for example, [`DECLARE`][Instruction::Declare].
252    ///
253    /// Note the callout of Quil-T above: the most surprising entries in this category, [by
254    /// default][DefaultHandler], are *[gate application][Instruction::Gate] and
255    /// [`MEASURE`][Instruction::Measurement]*.  This is because Quil-T expects all
256    /// gates/measurements to be expanded through
257    /// [`DEFCAL`][Instruction::CalibrationDefinition]/[`DEFCAL
258    /// MEASURE`][Instruction::MeasureCalibrationDefinition] (or, for gates, through
259    /// [`DEFGATE`][Instruction::GateDefinition`] or [`DEFCIRCUIT`][Instruction::CircuitDefinition]
260    /// until they can be calibrated).
261    ///
262    /// [Quil-T]: https://quil-lang.github.io/#12Annex-T--Pulse-Level-Control
263    ProgramComposition,
264
265    /// An instruction affecting only classical state, such as [`ADD`][Instruction:ADD`].
266    ClassicalCompute,
267
268    /// An instruction affecting the pulse level portion of the program, such as
269    /// [`PULSE`][Instruction::Pulse].  The RF stands for Radio Frequency.
270    ///
271    /// Unlike for [`MEASURE`][Instruction::Measurement], [`RESET`] is, [by
272    /// default][DefaultHandler], considered an RF control instruction, as it is not realized
273    /// through calibration into a lower-level instruction.
274    RFControl,
275
276    /// An instruction that can perform control flow, such as [`JUMP-WHEN`][Instruction::JumpWhen].
277    ControlFlow,
278}
279
280pub fn write_instruction_block<'i, I, Q>(
281    f: &mut impl fmt::Write,
282    fall_back_to_debug: bool,
283    values: I,
284) -> crate::quil::ToQuilResult<()>
285where
286    I: IntoIterator<Item = &'i Q>,
287    Q: Quil + 'i,
288{
289    write_join_quil(f, fall_back_to_debug, values, "\n", "\t")
290}
291
292pub(crate) fn write_join(
293    f: &mut impl fmt::Write,
294    values: &[impl fmt::Display],
295    separator: &str,
296    prefix: &str,
297) -> fmt::Result {
298    let mut iter = values.iter();
299    if let Some(first) = iter.next() {
300        write!(f, "{prefix}{first}")?;
301
302        for value in iter {
303            write!(f, "{separator}{prefix}{value}")?;
304        }
305    }
306    Ok(())
307}
308
309pub fn format_integer_vector(values: &[u64]) -> String {
310    values
311        .iter()
312        .map(|q| format!("{q}"))
313        .collect::<Vec<String>>()
314        .join(" ")
315}
316
317/// Write a list of qubits, with each prefixed by a space (including the first)
318fn write_qubits(
319    f: &mut impl fmt::Write,
320    fall_back_to_debug: bool,
321    qubits: &[Qubit],
322) -> crate::quil::ToQuilResult<()> {
323    for qubit in qubits {
324        write!(f, " ")?;
325        qubit.write(f, fall_back_to_debug)?;
326    }
327    Ok(())
328}
329
330/// Write qubits as a Quil parameter list, where all are prefixed with ` `.
331fn write_qubit_parameters(
332    f: &mut impl fmt::Write,
333    fall_back_to_debug: bool,
334    qubits: &[Qubit],
335) -> ToQuilResult<()> {
336    for qubit in qubits.iter() {
337        write!(f, " ")?;
338        qubit.write(f, fall_back_to_debug)?;
339    }
340    Ok(())
341}
342
343fn write_expression_parameter_string(
344    f: &mut impl fmt::Write,
345    fall_back_to_debug: bool,
346    parameters: &[Expression],
347) -> crate::quil::ToQuilResult<()> {
348    if parameters.is_empty() {
349        return Ok(());
350    }
351
352    write!(f, "(")?;
353    write_join_quil(f, fall_back_to_debug, parameters, ", ", "")?;
354    write!(f, ")")?;
355    Ok(())
356}
357
358fn write_parameter_string<T: AsRef<str>>(f: &mut impl fmt::Write, parameters: &[T]) -> fmt::Result {
359    if parameters.is_empty() {
360        return Ok(());
361    }
362
363    write!(f, "(")?;
364    write_join(
365        f,
366        parameters
367            .iter()
368            .map(AsRef::as_ref)
369            .collect::<Vec<_>>()
370            .as_slice(),
371        ", ",
372        "%",
373    )?;
374    write!(f, ")")
375}
376
377impl Quil for Instruction {
378    fn write(
379        &self,
380        f: &mut impl fmt::Write,
381        fall_back_to_debug: bool,
382    ) -> Result<(), crate::quil::ToQuilError> {
383        match self {
384            Instruction::Arithmetic(arithmetic) => arithmetic.write(f, fall_back_to_debug),
385            Instruction::CalibrationDefinition(calibration) => {
386                calibration.write(f, fall_back_to_debug)
387            }
388            Instruction::Call(call) => call.write(f, fall_back_to_debug),
389            Instruction::Capture(capture) => capture.write(f, fall_back_to_debug),
390            Instruction::CircuitDefinition(circuit) => circuit.write(f, fall_back_to_debug),
391            Instruction::Convert(convert) => convert.write(f, fall_back_to_debug),
392            Instruction::Declaration(declaration) => declaration.write(f, fall_back_to_debug),
393            Instruction::Delay(delay) => delay.write(f, fall_back_to_debug),
394            Instruction::Fence(fence) => fence.write(f, fall_back_to_debug),
395            Instruction::FrameDefinition(frame_definition) => {
396                frame_definition.write(f, fall_back_to_debug)
397            }
398            Instruction::Gate(gate) => gate.write(f, fall_back_to_debug),
399            Instruction::GateDefinition(gate_definition) => {
400                gate_definition.write(f, fall_back_to_debug)
401            }
402            Instruction::Include(include) => include.write(f, fall_back_to_debug),
403            Instruction::MeasureCalibrationDefinition(measure_calibration) => {
404                measure_calibration.write(f, fall_back_to_debug)
405            }
406            Instruction::Measurement(measurement) => measurement.write(f, fall_back_to_debug),
407            Instruction::Move(r#move) => r#move.write(f, fall_back_to_debug),
408            Instruction::Exchange(exchange) => exchange.write(f, fall_back_to_debug),
409            Instruction::Load(load) => load.write(f, fall_back_to_debug),
410            Instruction::Store(store) => store.write(f, fall_back_to_debug),
411            Instruction::Pulse(pulse) => pulse.write(f, fall_back_to_debug),
412            Instruction::Pragma(pragma) => pragma.write(f, fall_back_to_debug),
413            Instruction::RawCapture(raw_capture) => raw_capture.write(f, fall_back_to_debug),
414            Instruction::Reset(reset) => reset.write(f, fall_back_to_debug),
415            Instruction::SetFrequency(set_frequency) => set_frequency.write(f, fall_back_to_debug),
416            Instruction::SetPhase(set_phase) => set_phase.write(f, fall_back_to_debug),
417            Instruction::SetScale(set_scale) => set_scale.write(f, fall_back_to_debug),
418            Instruction::ShiftFrequency(shift_frequency) => {
419                shift_frequency.write(f, fall_back_to_debug)
420            }
421            Instruction::ShiftPhase(shift_phase) => shift_phase.write(f, fall_back_to_debug),
422            Instruction::SwapPhases(swap_phases) => swap_phases.write(f, fall_back_to_debug),
423            Instruction::WaveformDefinition(waveform_definition) => {
424                waveform_definition.write(f, fall_back_to_debug)
425            }
426            Instruction::Halt() => write!(f, "HALT").map_err(Into::into),
427            Instruction::Nop() => write!(f, "NOP").map_err(Into::into),
428            Instruction::Wait() => write!(f, "WAIT").map_err(Into::into),
429            Instruction::Jump(jump) => jump.write(f, fall_back_to_debug),
430            Instruction::JumpUnless(jump) => jump.write(f, fall_back_to_debug),
431            Instruction::JumpWhen(jump) => jump.write(f, fall_back_to_debug),
432            Instruction::Label(label) => label.write(f, fall_back_to_debug),
433            Instruction::Comparison(comparison) => comparison.write(f, fall_back_to_debug),
434            Instruction::BinaryLogic(binary_logic) => binary_logic.write(f, fall_back_to_debug),
435            Instruction::UnaryLogic(unary_logic) => unary_logic.write(f, fall_back_to_debug),
436        }
437    }
438}
439
440pub(crate) struct QuotedString<S>(pub(crate) S);
441
442impl<S> fmt::Display for QuotedString<S>
443where
444    S: AsRef<str>,
445{
446    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447        write!(f, "\"")?;
448        for c in self.0.as_ref().chars() {
449            match c {
450                '"' => write!(f, "\\\"")?,
451                '\\' => write!(f, "\\\\")?,
452                c => write!(f, "{c}")?,
453            }
454        }
455        write!(f, "\"")
456    }
457}
458
459#[cfg(test)]
460mod test_instruction_display {
461    use crate::{instruction::PragmaArgument, quil::Quil};
462
463    use super::{Instruction, Pragma};
464
465    #[test]
466    fn pragma() {
467        assert_eq!(
468            Instruction::Pragma(Pragma {
469                name: String::from("INITIAL_REWIRING"),
470                arguments: vec![],
471                data: Some(String::from("PARTIAL")),
472            })
473            .to_quil()
474            .unwrap(),
475            "PRAGMA INITIAL_REWIRING \"PARTIAL\""
476        );
477        assert_eq!(
478            Instruction::Pragma(Pragma {
479                name: String::from("LOAD-MEMORY"),
480                arguments: vec![PragmaArgument::Identifier("q0".to_string())],
481                data: Some(String::from("addr")),
482            })
483            .to_quil()
484            .unwrap(),
485            "PRAGMA LOAD-MEMORY q0 \"addr\""
486        );
487        assert_eq!(
488            Instruction::Pragma(Pragma {
489                name: String::from("PRESERVE_BLOCK"),
490                arguments: vec![],
491                data: None,
492            })
493            .to_quil()
494            .unwrap(),
495            "PRAGMA PRESERVE_BLOCK"
496        );
497    }
498}
499
500impl Instruction {
501    /// Apply the provided closure to this instruction, mutating any `Expression`s within.
502    /// Does not affect instructions without `Expression`s within.
503    /// Does not traverse or mutate instructions nested within blocks (such as
504    /// within `DEFCAL`).
505    ///
506    /// # Example
507    ///
508    /// ```rust
509    /// use std::mem::replace;
510    /// use std::str::FromStr;
511    /// use quil_rs::{expression::Expression, Program, quil::Quil};
512    ///
513    ///
514    /// let program = Program::from_str("SHIFT-PHASE 0 \"rf\" 2*2").unwrap();
515    /// let mut instructions = program.to_instructions();
516    /// instructions.iter_mut().for_each(|inst| inst.apply_to_expressions(Expression::simplify));
517    ///
518    /// assert_eq!(instructions[0].to_quil().unwrap(), String::from("SHIFT-PHASE 0 \"rf\" 4"))
519    ///
520    /// ```
521    pub fn apply_to_expressions(&mut self, mut closure: impl FnMut(&mut Expression)) {
522        match self {
523            Instruction::CalibrationDefinition(CalibrationDefinition {
524                identifier: CalibrationIdentifier { parameters, .. },
525                ..
526            })
527            | Instruction::Gate(Gate { parameters, .. }) => {
528                parameters.iter_mut().for_each(closure);
529            }
530            Instruction::Capture(Capture { waveform, .. })
531            | Instruction::Pulse(Pulse { waveform, .. }) => {
532                waveform.parameters.values_mut().for_each(closure);
533            }
534            Instruction::Delay(Delay { duration, .. })
535            | Instruction::RawCapture(RawCapture { duration, .. }) => {
536                closure(duration);
537            }
538            Instruction::FrameDefinition(FrameDefinition { attributes, .. }) => {
539                for value in attributes.values_mut() {
540                    if let AttributeValue::Expression(expression) = value {
541                        closure(expression);
542                    }
543                }
544            }
545            Instruction::SetFrequency(SetFrequency {
546                frequency: expression,
547                ..
548            })
549            | Instruction::SetPhase(SetPhase {
550                phase: expression, ..
551            })
552            | Instruction::SetScale(SetScale {
553                scale: expression, ..
554            })
555            | Instruction::ShiftFrequency(ShiftFrequency {
556                frequency: expression,
557                ..
558            })
559            | Instruction::ShiftPhase(ShiftPhase {
560                phase: expression, ..
561            }) => {
562                closure(expression);
563            }
564            Instruction::WaveformDefinition(WaveformDefinition { definition, .. }) => {
565                definition.matrix.iter_mut().for_each(closure);
566            }
567            Instruction::GateDefinition(GateDefinition {
568                specification: GateSpecification::Matrix(matrix),
569                ..
570            }) => {
571                for row in matrix {
572                    for cell in row {
573                        closure(cell);
574                    }
575                }
576            }
577            _ => {}
578        }
579    }
580
581    pub(crate) fn default_frame_match_condition<'a>(
582        &'a self,
583        qubits_available: &'a HashSet<Qubit>,
584    ) -> Option<FrameMatchConditions<'a>> {
585        match self {
586            Instruction::Pulse(Pulse {
587                blocking, frame, ..
588            })
589            | Instruction::Capture(Capture {
590                blocking, frame, ..
591            })
592            | Instruction::RawCapture(RawCapture {
593                blocking, frame, ..
594            }) => Some(FrameMatchConditions {
595                blocked: blocking
596                    .then(|| FrameMatchCondition::AnyOfQubits(frame.qubits.iter().collect())),
597                used: Some(FrameMatchCondition::Specific(frame)),
598            }),
599            Instruction::Delay(Delay {
600                frame_names,
601                qubits,
602                ..
603            }) => Some(FrameMatchConditions {
604                used: Some(if frame_names.is_empty() {
605                    FrameMatchCondition::ExactQubits(qubits.iter().collect())
606                } else {
607                    FrameMatchCondition::And(vec![
608                        FrameMatchCondition::ExactQubits(qubits.iter().collect()),
609                        FrameMatchCondition::AnyOfNames(
610                            frame_names.iter().map(String::as_str).collect(),
611                        ),
612                    ])
613                }),
614                blocked: None,
615            }),
616            Instruction::Fence(Fence { qubits }) => Some(FrameMatchConditions {
617                used: None,
618                blocked: Some(if qubits.is_empty() {
619                    FrameMatchCondition::All
620                } else {
621                    FrameMatchCondition::AnyOfQubits(qubits.iter().collect())
622                }),
623            }),
624            Instruction::Reset(Reset { qubit }) => {
625                let qubits = match qubit {
626                    Some(qubit) => {
627                        let mut set = HashSet::new();
628                        set.insert(qubit);
629                        set
630                    }
631                    None => qubits_available.iter().collect(),
632                };
633
634                Some(FrameMatchConditions {
635                    used: Some(FrameMatchCondition::ExactQubits(qubits.clone())),
636                    blocked: Some(FrameMatchCondition::AnyOfQubits(qubits)),
637                })
638            }
639            Instruction::SetFrequency(SetFrequency { frame, .. })
640            | Instruction::SetPhase(SetPhase { frame, .. })
641            | Instruction::SetScale(SetScale { frame, .. })
642            | Instruction::ShiftFrequency(ShiftFrequency { frame, .. })
643            | Instruction::ShiftPhase(ShiftPhase { frame, .. }) => Some(FrameMatchConditions {
644                used: Some(FrameMatchCondition::Specific(frame)),
645                blocked: None,
646            }),
647            Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => {
648                Some(FrameMatchConditions {
649                    used: Some(FrameMatchCondition::Or(vec![
650                        FrameMatchCondition::Specific(frame_1),
651                        FrameMatchCondition::Specific(frame_2),
652                    ])),
653                    blocked: None,
654                })
655            }
656            Instruction::Arithmetic(_)
657            | Instruction::BinaryLogic(_)
658            | Instruction::CalibrationDefinition(_)
659            | Instruction::Call(_)
660            | Instruction::CircuitDefinition(_)
661            | Instruction::Comparison(_)
662            | Instruction::Convert(_)
663            | Instruction::Declaration(_)
664            | Instruction::Exchange(_)
665            | Instruction::FrameDefinition(_)
666            | Instruction::Gate(_)
667            | Instruction::GateDefinition(_)
668            | Instruction::Halt()
669            | Instruction::Include(_)
670            | Instruction::Jump(_)
671            | Instruction::JumpUnless(_)
672            | Instruction::JumpWhen(_)
673            | Instruction::Label(_)
674            | Instruction::Load(_)
675            | Instruction::MeasureCalibrationDefinition(_)
676            | Instruction::Measurement(_)
677            | Instruction::Move(_)
678            | Instruction::Nop()
679            | Instruction::Pragma(_)
680            | Instruction::Store(_)
681            | Instruction::UnaryLogic(_)
682            | Instruction::WaveformDefinition(_)
683            | Instruction::Wait() => None,
684        }
685    }
686
687    /// Return immutable references to the [`Qubit`]s contained within an instruction
688    #[allow(dead_code)]
689    pub fn get_qubits(&self) -> Vec<&Qubit> {
690        match self {
691            Instruction::Gate(gate) => gate.qubits.iter().collect(),
692            Instruction::CalibrationDefinition(calibration) => calibration
693                .identifier
694                .qubits
695                .iter()
696                .chain(
697                    calibration
698                        .instructions
699                        .iter()
700                        .flat_map(|inst| inst.get_qubits()),
701                )
702                .collect(),
703            Instruction::MeasureCalibrationDefinition(measurement) => {
704                iter::once(&measurement.identifier.qubit)
705                    .chain(
706                        measurement
707                            .instructions
708                            .iter()
709                            .flat_map(|inst| inst.get_qubits()),
710                    )
711                    .collect()
712            }
713            Instruction::Measurement(measurement) => vec![&measurement.qubit],
714            Instruction::Reset(reset) => match &reset.qubit {
715                Some(qubit) => vec![qubit],
716                None => vec![],
717            },
718            Instruction::Delay(delay) => delay.qubits.iter().collect(),
719            Instruction::Fence(fence) => fence.qubits.iter().collect(),
720            Instruction::Capture(capture) => capture.frame.qubits.iter().collect(),
721            Instruction::Pulse(pulse) => pulse.frame.qubits.iter().collect(),
722            Instruction::RawCapture(raw_capture) => raw_capture.frame.qubits.iter().collect(),
723            _ => vec![],
724        }
725    }
726
727    /// Return mutable references to the [`Qubit`]s contained within an instruction
728    pub fn get_qubits_mut(&mut self) -> Vec<&mut Qubit> {
729        match self {
730            Instruction::Gate(gate) => gate.qubits.iter_mut().collect(),
731            Instruction::CalibrationDefinition(calibration) => calibration
732                .identifier
733                .qubits
734                .iter_mut()
735                .chain(
736                    calibration
737                        .instructions
738                        .iter_mut()
739                        .flat_map(|inst| inst.get_qubits_mut()),
740                )
741                .collect(),
742            Instruction::MeasureCalibrationDefinition(measurement) => {
743                iter::once(&mut measurement.identifier.qubit)
744                    .chain(
745                        measurement
746                            .instructions
747                            .iter_mut()
748                            .flat_map(|inst| inst.get_qubits_mut()),
749                    )
750                    .collect()
751            }
752            Instruction::Measurement(measurement) => vec![&mut measurement.qubit],
753            Instruction::Reset(reset) => match &mut reset.qubit {
754                Some(qubit) => vec![qubit],
755                None => vec![],
756            },
757            Instruction::Delay(delay) => delay.qubits.iter_mut().collect(),
758            Instruction::Fence(fence) => fence.qubits.iter_mut().collect(),
759            Instruction::Capture(capture) => capture.frame.qubits.iter_mut().collect(),
760            Instruction::Pulse(pulse) => pulse.frame.qubits.iter_mut().collect(),
761            Instruction::RawCapture(raw_capture) => raw_capture.frame.qubits.iter_mut().collect(),
762            _ => vec![],
763        }
764    }
765
766    /// Return the waveform _directly_ invoked by the instruction, if any.
767    ///
768    /// Note: this does not expand calibrations or other instructions which may
769    /// indirectly cause a waveform to be invoked.
770    pub(crate) fn get_waveform_invocation(&self) -> Option<&WaveformInvocation> {
771        match self {
772            Instruction::Capture(Capture { waveform, .. }) => Some(waveform),
773            Instruction::Pulse(Pulse { waveform, .. }) => Some(waveform),
774            _ => None,
775        }
776    }
777
778    /// Parse a single instruction from an input string. Returns an error if the input fails to parse,
779    /// or if there is input left over after parsing.
780    #[cfg(test)]
781    pub(crate) fn parse_in_test(input: &str) -> Result<Self, String> {
782        use crate::parser::instruction::parse_instruction;
783
784        let input = LocatedSpan::new(input);
785        let lexed = lex(input).map_err(|err| err.to_string())?;
786        let (_, instruction) =
787            nom::combinator::all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?;
788        Ok(instruction)
789    }
790
791    pub(crate) fn resolve_placeholders<TR, QR>(&mut self, target_resolver: TR, qubit_resolver: QR)
792    where
793        TR: Fn(&TargetPlaceholder) -> Option<String>,
794        QR: Fn(&QubitPlaceholder) -> Option<u64>,
795    {
796        match self {
797            Instruction::Label(label) => {
798                label.target.resolve_placeholder(target_resolver);
799            }
800            Instruction::Jump(jump) => {
801                jump.target.resolve_placeholder(target_resolver);
802            }
803            Instruction::JumpWhen(jump_when) => {
804                jump_when.target.resolve_placeholder(target_resolver);
805            }
806            Instruction::JumpUnless(jump_unless) => {
807                jump_unless.target.resolve_placeholder(target_resolver);
808            }
809            other => {
810                for qubit in other.get_qubits_mut() {
811                    qubit.resolve_placeholder(&qubit_resolver);
812                }
813            }
814        }
815    }
816}
817
818#[derive(Debug, thiserror::Error)]
819pub enum ParseInstructionError {
820    #[error("Failed to parse instruction: {0}")]
821    Parse(String),
822    #[error("Expected to parse exactly one instruction but got {0}")]
823    ZeroOrMany(usize),
824}
825
826impl FromStr for Instruction {
827    type Err = ParseInstructionError;
828
829    fn from_str(s: &str) -> Result<Self, Self::Err> {
830        let input = LocatedSpan::new(s);
831        let lexed = lex(input).map_err(|e| ParseInstructionError::Parse(e.to_string()))?;
832        let instructions =
833            parse_instructions(&lexed).map_err(|e| ParseInstructionError::Parse(e.to_string()))?;
834        if instructions.1.len() != 1 {
835            return Err(ParseInstructionError::ZeroOrMany(instructions.1.len()));
836        }
837        Ok(instructions.1[0].to_owned())
838    }
839}
840
841pub trait InstructionHandler {
842    /// Whether this instruction's timing within the pulse program must be precisely controlled so
843    /// as to begin exactly on the end of the latest preceding timed instruction.
844    ///
845    /// See [the Quil-T portion of the Quil specification (Annex T)][Quil-T] for more information.
846    ///
847    /// [Quil-T]: https://quil-lang.github.io/#12Annex-T--Pulse-Level-Control
848    #[inline]
849    fn is_scheduled(&self, instruction: &Instruction) -> bool {
850        DefaultHandler.is_scheduled(instruction)
851    }
852
853    /// Return this instruction's [role][InstructionRole].
854    #[inline]
855    fn role(&self, instruction: &Instruction) -> InstructionRole {
856        DefaultHandler.role(instruction)
857    }
858
859    /// Return the [frames][FrameIdentifier] which are either *used* or *blocked* by the given
860    /// instruction.
861    ///
862    /// - An instruction `I` *uses* a frame `F` if the execution of `I` plays on `F`.
863    ///
864    /// - An instruction `I` *blocks* a frame `F` if `I` does not play on `F` but, even so, other
865    ///   instructions may not play on `F` while `I` is executing.
866    ///
867    /// Only one instruction may play on a given frame at a time, so using a frame is a stronger
868    /// condition than blocking a frame.
869    ///
870    /// `None` is returned if the instruction does not execute in the context of a frame; this is
871    /// the case for purely classical instructions such as [`ADD`][Instruction::Add], for instance.
872    ///
873    /// See [the Quil-T portion of the Quil specification (Annex T)][Quil-T] for more information.
874    ///
875    /// [Quil-T]: https://quil-lang.github.io/#12Annex-T--Pulse-Level-Control
876    #[inline]
877    fn matching_frames<'p>(
878        &self,
879        program: &'p Program,
880        instruction: &Instruction,
881    ) -> Option<MatchedFrames<'p>> {
882        DefaultHandler.matching_frames(program, instruction)
883    }
884
885    /// Return all memory accesses by the instruction.
886    ///
887    /// Memory accesses may be performed by pure memory manipulation instructions (such as
888    /// [`MOVE`][Instruction::Move]), by instructions that perform memory accesses as part of their
889    /// semantics (such as [`CAPTURE`][Instruction::Capture]), by variable reads in expressions –
890    /// anywhere that memory is read.
891    ///
892    /// # Errors
893    ///
894    /// This function is always permitted to fail if the program contains
895    /// [`CALL`][Instruction::Call] instructions that cannot be resolved against a signature in the
896    /// provided [`ExternSignatureMap`], either because they attempt to call unknown functions or
897    /// because they call known functions with incorrect types.  Specific implementations may impose
898    /// other failure conditions, and are encouraged to call them out if so.
899    #[inline]
900    fn memory_accesses(
901        &self,
902        extern_signature_map: &ExternSignatureMap,
903        instruction: &Instruction,
904    ) -> Result<MemoryAccesses, MemoryAccessesError> {
905        DefaultHandler.memory_accesses(extern_signature_map, instruction)
906    }
907}
908
909/// The default instruction-handling behavior.
910#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
911pub struct DefaultHandler;
912
913impl fmt::Display for DefaultHandler {
914    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
915        write!(f, "default instruction handler")
916    }
917}
918
919impl InstructionHandler for DefaultHandler {
920    fn is_scheduled(&self, instruction: &Instruction) -> bool {
921        match instruction {
922            Instruction::Reset(_) => false,
923            Instruction::Wait() => true,
924            _ => self.role(instruction) == InstructionRole::RFControl,
925        }
926    }
927
928    fn role(&self, instruction: &Instruction) -> InstructionRole {
929        match instruction {
930            Instruction::CalibrationDefinition(_)
931            | Instruction::CircuitDefinition(_)
932            | Instruction::Declaration(_)
933            | Instruction::FrameDefinition(_)
934            | Instruction::Gate(_)
935            | Instruction::GateDefinition(_)
936            | Instruction::Include(_)
937            | Instruction::Label(_)
938            | Instruction::MeasureCalibrationDefinition(_)
939            | Instruction::Measurement(_)
940            | Instruction::WaveformDefinition(_) => InstructionRole::ProgramComposition,
941
942            Instruction::Reset(_)
943            | Instruction::Capture(_)
944            | Instruction::Delay(_)
945            | Instruction::Fence(_)
946            | Instruction::Pulse(_)
947            | Instruction::RawCapture(_)
948            | Instruction::SetFrequency(_)
949            | Instruction::SetPhase(_)
950            | Instruction::SetScale(_)
951            | Instruction::ShiftFrequency(_)
952            | Instruction::ShiftPhase(_)
953            | Instruction::SwapPhases(_) => InstructionRole::RFControl,
954
955            Instruction::Arithmetic(_)
956            | Instruction::Call(_)
957            | Instruction::Comparison(_)
958            | Instruction::Convert(_)
959            | Instruction::BinaryLogic(_)
960            | Instruction::UnaryLogic(_)
961            | Instruction::Move(_)
962            | Instruction::Exchange(_)
963            | Instruction::Load(_)
964            | Instruction::Nop()
965            | Instruction::Pragma(_)
966            | Instruction::Store(_) => InstructionRole::ClassicalCompute,
967
968            Instruction::Halt()
969            | Instruction::Jump(_)
970            | Instruction::JumpWhen(_)
971            | Instruction::JumpUnless(_)
972            | Instruction::Wait() => InstructionRole::ControlFlow,
973        }
974    }
975
976    fn matching_frames<'p>(
977        &self,
978        program: &'p Program,
979        instruction: &Instruction,
980    ) -> Option<MatchedFrames<'p>> {
981        instruction
982            .default_frame_match_condition(program.get_used_qubits())
983            .map(|condition| program.frames.filter(condition))
984    }
985
986    fn memory_accesses(
987        &self,
988        extern_signature_map: &ExternSignatureMap,
989        instruction: &Instruction,
990    ) -> Result<MemoryAccesses, MemoryAccessesError> {
991        // Building individual access sets
992
993        #[inline]
994        fn none() -> HashSet<String> {
995            HashSet::new()
996        }
997
998        #[inline]
999        fn access(reference: &MemoryReference) -> HashSet<String> {
1000            [reference.name.clone()].into()
1001        }
1002
1003        #[inline]
1004        fn access_dynamic(region: &str) -> HashSet<String> {
1005            [region.to_owned()].into()
1006        }
1007
1008        #[inline]
1009        fn accesses(reference1: &MemoryReference, reference2: &MemoryReference) -> HashSet<String> {
1010            [reference1.name.clone(), reference2.name.clone()].into()
1011        }
1012
1013        #[inline]
1014        fn accesses_dynamic_index(region: &str, index: &MemoryReference) -> HashSet<String> {
1015            [region.to_owned(), index.name.clone()].into()
1016        }
1017
1018        #[inline]
1019        fn access_opt(opt_reference: Option<&MemoryReference>) -> HashSet<String> {
1020            opt_reference.map_or_else(HashSet::new, access)
1021        }
1022
1023        #[inline]
1024        fn access_operand(operand: &impl ClassicalOperand) -> HashSet<String> {
1025            access_opt(operand.memory_reference())
1026        }
1027
1028        #[inline]
1029        fn accesses_with_operand(
1030            reference: &MemoryReference,
1031            operand: &impl ClassicalOperand,
1032        ) -> HashSet<String> {
1033            if let Some(other) = operand.memory_reference() {
1034                accesses(reference, other)
1035            } else {
1036                access(reference)
1037            }
1038        }
1039
1040        // Building complete access patterns
1041
1042        // Move-like operations: those that read from at most one place and write to another
1043        fn like_move(
1044            destination: &MemoryReference,
1045            source_accesses: HashSet<String>,
1046        ) -> MemoryAccesses {
1047            MemoryAccesses {
1048                reads: source_accesses,
1049                writes: access(destination),
1050                captures: none(),
1051            }
1052        }
1053
1054        // Updating binary operators: read from a possible source, read and write to the
1055        // destination.
1056        fn binary(destination: &MemoryReference, source: &impl ClassicalOperand) -> MemoryAccesses {
1057            MemoryAccesses {
1058                reads: accesses_with_operand(destination, source),
1059                writes: access(destination),
1060                captures: none(),
1061            }
1062        }
1063
1064        // Read-write operations, whose inputs are the same as their outputs.
1065        fn read_write(places: HashSet<String>) -> MemoryAccesses {
1066            MemoryAccesses {
1067                reads: places.clone(),
1068                writes: places,
1069                captures: none(),
1070            }
1071        }
1072
1073        // Classical instructions that read a single memory reference.
1074        fn read_one(place: &MemoryReference) -> MemoryAccesses {
1075            MemoryAccesses {
1076                reads: access(place),
1077                writes: none(),
1078                captures: none(),
1079            }
1080        }
1081
1082        // Instructions that read from many memory references; for instance, those that take an
1083        // expression as an argument.
1084        fn read_all<'a>(places: impl IntoIterator<Item = &'a MemoryReference>) -> MemoryAccesses {
1085            MemoryAccesses {
1086                reads: places.into_iter().map(|r| r.name.clone()).collect(),
1087                writes: none(),
1088                captures: none(),
1089            }
1090        }
1091
1092        // Memory accesses done by gate applications
1093        fn gate_application(Gate { parameters, .. }: &Gate) -> MemoryAccesses {
1094            read_all(parameters.iter().flat_map(Expression::memory_references))
1095        }
1096
1097        // The match
1098
1099        Ok(match instruction {
1100            // Operations with simple memory access patterns as captured (heh) above
1101            Instruction::Convert(Convert {
1102                destination,
1103                source,
1104            }) => like_move(destination, access(source)),
1105            Instruction::Move(Move {
1106                destination,
1107                source,
1108            }) => like_move(destination, access_operand(source)),
1109            Instruction::BinaryLogic(BinaryLogic {
1110                destination,
1111                source,
1112                operator: _,
1113            }) => binary(destination, source),
1114            Instruction::Arithmetic(Arithmetic {
1115                destination,
1116                source,
1117                ..
1118            }) => binary(destination, source),
1119            Instruction::UnaryLogic(UnaryLogic { operand, .. }) => read_write(access(operand)),
1120            Instruction::Exchange(Exchange { left, right }) => read_write(accesses(left, right)),
1121            Instruction::JumpWhen(JumpWhen {
1122                target: _,
1123                condition,
1124            })
1125            | Instruction::JumpUnless(JumpUnless {
1126                target: _,
1127                condition,
1128            }) => read_one(condition),
1129
1130            // Our sole ternary operator: read from the operands, write to the destination.
1131            Instruction::Comparison(Comparison {
1132                destination,
1133                lhs,
1134                rhs,
1135                operator: _,
1136            }) => MemoryAccesses {
1137                reads: accesses_with_operand(lhs, rhs),
1138                writes: access(destination),
1139                captures: none(),
1140            },
1141
1142            // Quil-T instructions that read from a single expression.
1143            Instruction::Delay(Delay { duration: expr, .. })
1144            | Instruction::SetPhase(SetPhase { phase: expr, .. })
1145            | Instruction::SetScale(SetScale { scale: expr, .. })
1146            | Instruction::ShiftPhase(ShiftPhase { phase: expr, .. })
1147            | Instruction::SetFrequency(SetFrequency {
1148                frequency: expr, ..
1149            })
1150            | Instruction::ShiftFrequency(ShiftFrequency {
1151                frequency: expr, ..
1152            }) => read_all(expr.memory_references()),
1153
1154            // Operations that read from memory and nothing else because they interact with the
1155            // quantum components of the system.
1156            Instruction::Pulse(Pulse {
1157                waveform,
1158                blocking: _,
1159                frame: _,
1160            }) => read_all(waveform.memory_references()),
1161            Instruction::Gate(gate) => gate_application(gate),
1162
1163            // Capturing operations; the Quil-T variants may also read from memory.
1164            Instruction::Capture(Capture {
1165                memory_reference,
1166                waveform,
1167                blocking: _,
1168                frame: _,
1169            }) => MemoryAccesses {
1170                reads: waveform
1171                    .memory_references()
1172                    .map(|r| r.name.clone())
1173                    .collect(),
1174                captures: access(memory_reference),
1175                writes: none(),
1176            },
1177            Instruction::Measurement(Measurement { target, .. }) => MemoryAccesses {
1178                captures: access_opt(target.as_ref()),
1179                reads: none(),
1180                writes: none(),
1181            },
1182            Instruction::RawCapture(RawCapture {
1183                duration,
1184                memory_reference,
1185                blocking: _,
1186                frame: _,
1187            }) => MemoryAccesses {
1188                reads: duration
1189                    .memory_references()
1190                    .map(|r| r.name.clone())
1191                    .collect(),
1192                captures: access(memory_reference),
1193                writes: none(),
1194            },
1195
1196            // Calls to external functions, which handle their own logic by looking at their
1197            // signature.
1198            Instruction::Call(call) => call.default_memory_accesses(extern_signature_map)?,
1199
1200            // Parameterized definitions whose parameters can also themselves reference memory
1201            Instruction::CalibrationDefinition(CalibrationDefinition {
1202                identifier:
1203                    CalibrationIdentifier {
1204                        parameters,
1205                        modifiers: _,
1206                        name: _,
1207                        qubits: _,
1208                    },
1209                instructions,
1210            }) => {
1211                let parameter_reads = MemoryAccesses {
1212                    reads: parameters
1213                        .iter()
1214                        .flat_map(Expression::memory_references)
1215                        .map(|r| r.name.clone())
1216                        .collect(),
1217                    writes: none(),
1218                    captures: none(),
1219                };
1220                instructions
1221                    .iter()
1222                    .map(|instr| self.memory_accesses(extern_signature_map, instr))
1223                    .fold_ok(parameter_reads, MemoryAccesses::union)?
1224            }
1225
1226            // Parameterized definitions whose parameters cannot themselves reference memory.  Note
1227            // that their memory accesses may refer to parameter names instead of global
1228            // declarations.
1229            Instruction::GateDefinition(GateDefinition {
1230                specification,
1231                name: _,
1232                parameters: _,
1233            }) => match specification {
1234                GateSpecification::Matrix(matrix) => read_all(
1235                    matrix
1236                        .iter()
1237                        .flat_map(|row| row.iter().flat_map(Expression::memory_references)),
1238                ),
1239                GateSpecification::Permutation(_) | GateSpecification::PauliSum(_) => {
1240                    MemoryAccesses::none()
1241                }
1242                GateSpecification::Sequence(DefGateSequence { gates, qubits: _ }) => gates
1243                    .iter()
1244                    .map(gate_application)
1245                    .fold(MemoryAccesses::none(), MemoryAccesses::union),
1246            },
1247            Instruction::CircuitDefinition(CircuitDefinition {
1248                instructions,
1249                name: _,
1250                parameters: _,
1251                qubit_variables: _,
1252            })
1253            | Instruction::MeasureCalibrationDefinition(MeasureCalibrationDefinition {
1254                instructions,
1255                identifier: _,
1256            }) => instructions
1257                .iter()
1258                .map(|instr| self.memory_accesses(extern_signature_map, instr))
1259                .fold_ok(MemoryAccesses::none(), MemoryAccesses::union)?,
1260            Instruction::WaveformDefinition(WaveformDefinition {
1261                definition:
1262                    Waveform {
1263                        matrix,
1264                        parameters: _,
1265                    },
1266                name: _,
1267            }) => read_all(matrix.iter().flat_map(Expression::memory_references)),
1268
1269            // Dynamic memory accesses.  If we ever track region indices precisely, these will
1270            // require conservatively marking accesses (read for load, write for store) as blocking
1271            // the whole region.
1272            Instruction::Load(Load {
1273                destination,
1274                source,
1275                offset,
1276            }) => MemoryAccesses {
1277                reads: accesses_dynamic_index(source, offset),
1278                writes: access(destination),
1279                captures: none(),
1280            },
1281            Instruction::Store(Store {
1282                destination,
1283                offset,
1284                source,
1285            }) => MemoryAccesses {
1286                reads: accesses_with_operand(offset, source),
1287                writes: access_dynamic(destination),
1288                captures: none(),
1289            },
1290
1291            // Instructions that can't contain any memory references.  Conservatively includes
1292            // `INCLUDE`, which we don't handle here, and `PRAGMA`, which we can't.
1293            Instruction::Declaration(_)
1294            | Instruction::Fence(_)
1295            | Instruction::FrameDefinition(_)
1296            | Instruction::Halt()
1297            | Instruction::Wait()
1298            | Instruction::Include(_)
1299            | Instruction::Jump(_)
1300            | Instruction::Label(_)
1301            | Instruction::Nop()
1302            | Instruction::Pragma(_)
1303            | Instruction::Reset(_)
1304            | Instruction::SwapPhases(_) => MemoryAccesses::none(),
1305        })
1306    }
1307}
1308
1309#[cfg(test)]
1310mod tests {
1311    use rstest::*;
1312    use std::str::FromStr as _;
1313
1314    use crate::{expression::Expression, Program};
1315
1316    use super::MemoryReference;
1317
1318    #[test]
1319    fn apply_to_expressions() {
1320        let mut program = Program::from_str(
1321            "DECLARE ro BIT
1322SET-PHASE 0 \"rf\" pi/2
1323RX(2) 0",
1324        )
1325        .unwrap();
1326        let closure = |expr: &mut Expression| *expr = Expression::Variable(String::from("a"));
1327        program.for_each_body_instruction(|instruction| {
1328            instruction.apply_to_expressions(closure);
1329        });
1330
1331        let expected_program = Program::from_str(
1332            "DECLARE ro BIT
1333SET-PHASE 0 \"rf\" %a
1334RX(%a) 0",
1335        )
1336        .unwrap();
1337
1338        assert_eq!(expected_program, program);
1339    }
1340
1341    #[rstest(input, expected,
1342        case("_", MemoryReference { name: "_".to_string(), index: 0 }),
1343        case("a", MemoryReference { name: "a".to_string(), index: 0 }),
1344        case("a---b", MemoryReference { name: "a---b".to_string(), index: 0 }),
1345        case("_a_b_", MemoryReference { name: "_a_b_".to_string(), index: 0 }),
1346        case("a-2_b-2", MemoryReference { name: "a-2_b-2".to_string(), index: 0 }),
1347        case("_[0]", MemoryReference { name: "_".to_string(), index: 0 }),
1348        case("a[1]", MemoryReference { name: "a".to_string(), index: 1 }),
1349        case("a---b[2]", MemoryReference { name: "a---b".to_string(), index: 2 }),
1350        case("_a_b_[3]", MemoryReference { name: "_a_b_".to_string(), index: 3 }),
1351        case("a-2_b-2[4]", MemoryReference { name: "a-2_b-2".to_string(), index: 4 }),
1352    )]
1353    fn it_parses_memory_reference_from_str(input: &str, expected: MemoryReference) {
1354        assert_eq!(MemoryReference::from_str(input), Ok(expected));
1355    }
1356
1357    #[rstest(
1358        input,
1359        case(""),
1360        case("[0]"),
1361        case("a[-1]"),
1362        case("2a[2]"),
1363        case("-a"),
1364        case("NOT[3]"),
1365        case("a a"),
1366        case("a[5] a[5]"),
1367        case("DECLARE a[6]")
1368    )]
1369    fn it_fails_to_parse_memory_reference_from_str(input: &str) {
1370        assert!(MemoryReference::from_str(input).is_err());
1371    }
1372
1373    mod placeholders {
1374        use std::collections::HashMap;
1375
1376        use crate::instruction::{Label, Qubit, QubitPlaceholder, Target, TargetPlaceholder};
1377
1378        #[allow(clippy::redundant_clone)]
1379        #[test]
1380        fn target() {
1381            let placeholder_1 = TargetPlaceholder::new(String::from("label"));
1382            let placeholder_2 = TargetPlaceholder::new(String::from("label"));
1383            let placeholder_3 = TargetPlaceholder::new(String::from("other"));
1384
1385            assert_eq!(placeholder_1, placeholder_1);
1386            assert_eq!(placeholder_1, placeholder_1.clone());
1387            assert_eq!(placeholder_1.clone(), placeholder_1.clone());
1388            assert_ne!(placeholder_1, placeholder_2);
1389            assert_ne!(placeholder_2, placeholder_3);
1390            assert_ne!(placeholder_1, placeholder_3);
1391        }
1392
1393        #[test]
1394        fn target_resolution() {
1395            let placeholder_1 = TargetPlaceholder::new(String::from("label"));
1396            let placeholder_2 = TargetPlaceholder::new(String::from("label"));
1397
1398            let resolver = HashMap::from([(placeholder_1.clone(), String::from("label_1"))]);
1399
1400            let mut label_1 = Label {
1401                target: Target::Placeholder(placeholder_1),
1402            };
1403            label_1
1404                .target
1405                .resolve_placeholder(|k| resolver.get(k).cloned());
1406            assert_eq!(label_1.target, Target::Fixed(String::from("label_1")));
1407
1408            let mut label_2 = Label {
1409                target: Target::Placeholder(placeholder_2.clone()),
1410            };
1411            label_2
1412                .target
1413                .resolve_placeholder(|k| resolver.get(k).cloned());
1414            assert_eq!(label_2.target, Target::Placeholder(placeholder_2));
1415        }
1416
1417        #[allow(clippy::redundant_clone)]
1418        #[test]
1419        fn qubit() {
1420            let placeholder_1 = QubitPlaceholder::default();
1421            let placeholder_2 = QubitPlaceholder::default();
1422
1423            assert_eq!(placeholder_1, placeholder_1);
1424            assert_eq!(placeholder_1, placeholder_1.clone());
1425            assert_eq!(placeholder_1.clone(), placeholder_1.clone());
1426            assert_ne!(placeholder_1, placeholder_2);
1427        }
1428
1429        #[test]
1430        fn qubit_resolution() {
1431            let placeholder_1 = QubitPlaceholder::default();
1432            let placeholder_2 = QubitPlaceholder::default();
1433
1434            let resolver = HashMap::from([(placeholder_1.clone(), 1)]);
1435
1436            let mut qubit_1 = Qubit::Placeholder(placeholder_1);
1437            qubit_1.resolve_placeholder(|k| resolver.get(k).copied());
1438            assert_eq!(qubit_1, Qubit::Fixed(1));
1439
1440            let mut qubit_2 = Qubit::Placeholder(placeholder_2.clone());
1441            qubit_2.resolve_placeholder(|k| resolver.get(k).copied());
1442            assert_eq!(qubit_2, Qubit::Placeholder(placeholder_2));
1443        }
1444    }
1445
1446    mod instruction_handler {
1447        use super::super::*;
1448
1449        struct CustomFrameHandler;
1450
1451        impl InstructionHandler for CustomFrameHandler {
1452            fn matching_frames<'p>(
1453                &self,
1454                program: &'p Program,
1455                instruction: &Instruction,
1456            ) -> Option<MatchedFrames<'p>> {
1457                if let Instruction::Pragma(_) = instruction {
1458                    Some(MatchedFrames {
1459                        used: program.frames.get_keys().into_iter().collect(),
1460                        blocked: HashSet::new(),
1461                    })
1462                } else {
1463                    DefaultHandler.matching_frames(program, instruction)
1464                }
1465            }
1466        }
1467
1468        #[test]
1469        fn it_considers_custom_instruction_frames() {
1470            let program = r#"DEFFRAME 0 "rf":
1471    CENTER-FREQUENCY: 3e9
1472
1473PRAGMA USES-ALL-FRAMES
1474"#
1475            .parse::<Program>()
1476            .unwrap();
1477
1478            // This test assumes that the default simplification behavior will not assign frames to
1479            // `PRAGMA` instructions. This is verified below.
1480            assert!(program.simplify(&DefaultHandler).unwrap().frames.is_empty());
1481
1482            assert_eq!(
1483                program.simplify(&CustomFrameHandler).unwrap().frames.len(),
1484                1
1485            );
1486        }
1487    }
1488}