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;
16use std::fmt;
17use std::iter;
18use std::str::FromStr;
19
20use nom_locate::LocatedSpan;
21
22#[cfg(feature = "stubs")]
23use pyo3_stub_gen::derive::{gen_stub_pyclass_complex_enum, gen_stub_pymethods};
24
25use crate::expression::Expression;
26use crate::parser::lex;
27use crate::parser::parse_instructions;
28use crate::program::frame::{FrameMatchCondition, FrameMatchConditions};
29use crate::program::ProgramError;
30use crate::program::{MatchedFrames, MemoryAccesses};
31use crate::quil::{write_join_quil, Quil, ToQuilResult};
32use crate::Program;
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 measurement;
46mod pragma;
47mod qubit;
48mod reset;
49mod timing;
50mod waveform;
51
52pub use self::calibration::{
53    CalibrationDefinition, CalibrationIdentifier, CalibrationSignature,
54    MeasureCalibrationDefinition, MeasureCalibrationIdentifier,
55};
56pub use self::circuit::CircuitDefinition;
57pub use self::classical::{
58    Arithmetic, ArithmeticOperand, ArithmeticOperator, BinaryLogic, BinaryOperand, BinaryOperator,
59    Comparison, ComparisonOperand, ComparisonOperator, Convert, Exchange, Move, UnaryLogic,
60    UnaryOperator,
61};
62pub use self::control_flow::{Jump, JumpUnless, JumpWhen, Label, Target, TargetPlaceholder};
63pub use self::declaration::{
64    Declaration, Load, MemoryReference, Offset, ScalarType, Sharing, Store, Vector,
65};
66pub use self::extern_call::*;
67pub use self::frame::{
68    AttributeValue, Capture, FrameAttributes, FrameDefinition, FrameIdentifier, Pulse, RawCapture,
69    SetFrequency, SetPhase, SetScale, ShiftFrequency, ShiftPhase, SwapPhases,
70};
71pub use self::gate::{
72    Gate, GateDefinition, GateError, GateModifier, GateSpecification, GateType, Matrix, PauliGate,
73    PauliSum, PauliTerm,
74};
75pub use self::measurement::Measurement;
76pub use self::pragma::{Include, Pragma, PragmaArgument, RESERVED_PRAGMA_EXTERN};
77pub use self::qubit::{Qubit, QubitPlaceholder};
78pub use self::reset::Reset;
79pub use self::timing::{Delay, Fence};
80pub use self::waveform::{Waveform, WaveformDefinition, WaveformInvocation, WaveformParameters};
81
82#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
83pub enum ValidationError {
84    #[error(transparent)]
85    GateError(#[from] GateError),
86}
87
88/// A Quil instruction.
89///
90/// Each variant (for Python users, each nested subclass)
91/// corresponds to a possible type of Quil instruction,
92/// which is accessible as a member within the variant.
93///
94/// # Python Users
95///
96/// The subclasses of this class are class attributes defined on it,
97/// and can be used to "wrap" instructions when they should be stored together.
98/// In particular, they are *NOT* the instruction classes you'd typically create,
99/// and instances of instruction classes are *NOT* subclasses of this class:
100///
101/// ```python
102/// >>> from quil.instructions import Instruction, Gate, Qubit
103/// >>> issubclass(Instruction.Gate, Instruction)
104/// True
105/// >>> issubclass(Gate, Instruction)
106/// False
107/// >>> g = Gate("X", (), (Qubit.Fixed(0),), ())
108/// >>> isinstance(g, Gate)
109/// True
110/// >>> isinstance(g, Instruction.Gate)
111/// False
112/// >>> g_instr = Instruction.Gate(g)
113/// >>> isinstance(g_instr, Gate)
114/// False
115/// >>> isinstance(g_instr, Instruction.Gate)
116/// True
117/// >>> isinstance(g_instr._0, Gate)
118/// True
119/// >>> g_instr._0 == g
120/// True
121/// ```
122///
123/// The point of this class is to wrap different kinds of instructions
124/// when stored together in a collection, all of which are of type `Instruction`.
125/// You can check for different instruction variants and destructure them using `match`:
126///
127/// ```python
128/// match g_instr:
129///     case Instruction.Gate(gate):
130///         assert isinstance(gate, Gate)
131///     case Instruction.Wait() | Instruction.Nop():
132///         # note the `()` -- these aren't like Python's enumerations!
133/// ```
134#[derive(Clone, Debug, PartialEq)]
135#[cfg_attr(feature = "stubs", gen_stub_pyclass_complex_enum)]
136#[cfg_attr(
137    feature = "python",
138    pyo3::pyclass(module = "quil.instructions", eq, frozen)
139)]
140pub enum Instruction {
141    Arithmetic(Arithmetic),
142    BinaryLogic(BinaryLogic),
143    CalibrationDefinition(CalibrationDefinition),
144    Call(Call),
145    Capture(Capture),
146    CircuitDefinition(CircuitDefinition),
147    Convert(Convert),
148    Comparison(Comparison),
149    Declaration(Declaration),
150    Delay(Delay),
151    Exchange(Exchange),
152    Fence(Fence),
153    FrameDefinition(FrameDefinition),
154    Gate(Gate),
155    GateDefinition(GateDefinition),
156    // Developer note: In Rust, this could be just `Halt`,
157    // but to be compatible with PyO3's "complex enums",
158    // it has to be an empty tuple variant.
159    // The same restriction applies `Nop` and `Wait`,
160    // as well as those in the `Expression` enumeration.
161    Halt(),
162    Include(Include),
163    Jump(Jump),
164    JumpUnless(JumpUnless),
165    JumpWhen(JumpWhen),
166    Label(Label),
167    Load(Load),
168    MeasureCalibrationDefinition(MeasureCalibrationDefinition),
169    Measurement(Measurement),
170    Move(Move),
171    Nop(),
172    Pragma(Pragma),
173    Pulse(Pulse),
174    RawCapture(RawCapture),
175    Reset(Reset),
176    SetFrequency(SetFrequency),
177    SetPhase(SetPhase),
178    SetScale(SetScale),
179    ShiftFrequency(ShiftFrequency),
180    ShiftPhase(ShiftPhase),
181    Store(Store),
182    SwapPhases(SwapPhases),
183    UnaryLogic(UnaryLogic),
184    WaveformDefinition(WaveformDefinition),
185    Wait(),
186}
187
188#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
189#[cfg_attr(feature = "python", pyo3::pymethods)]
190impl Instruction {
191    /// Returns true if the instruction is a Quil-T instruction.
192    pub fn is_quil_t(&self) -> bool {
193        match self {
194            Instruction::Capture(_)
195            | Instruction::CalibrationDefinition(_)
196            | Instruction::Delay(_)
197            | Instruction::Fence(_)
198            | Instruction::FrameDefinition(_)
199            | Instruction::MeasureCalibrationDefinition(_)
200            | Instruction::Pulse(_)
201            | Instruction::RawCapture(_)
202            | Instruction::SetFrequency(_)
203            | Instruction::SetPhase(_)
204            | Instruction::SetScale(_)
205            | Instruction::ShiftFrequency(_)
206            | Instruction::ShiftPhase(_)
207            | Instruction::SwapPhases(_)
208            | Instruction::WaveformDefinition(_) => true,
209
210            Instruction::Arithmetic(_)
211            | Instruction::BinaryLogic(_)
212            | Instruction::Call(_)
213            | Instruction::CircuitDefinition(_)
214            | Instruction::Convert(_)
215            | Instruction::Comparison(_)
216            | Instruction::Declaration(_)
217            | Instruction::Exchange(_)
218            | Instruction::Gate(_)
219            | Instruction::GateDefinition(_)
220            | Instruction::Halt()
221            | Instruction::Include(_)
222            | Instruction::Jump(_)
223            | Instruction::JumpUnless(_)
224            | Instruction::JumpWhen(_)
225            | Instruction::Label(_)
226            | Instruction::Load(_)
227            | Instruction::Measurement(_)
228            | Instruction::Move(_)
229            | Instruction::Nop()
230            | Instruction::Pragma(_)
231            | Instruction::Reset(_)
232            | Instruction::Store(_)
233            | Instruction::Wait()
234            | Instruction::UnaryLogic(_) => false,
235        }
236    }
237}
238
239#[derive(Clone, Copy, Debug)]
240pub enum InstructionRole {
241    ClassicalCompute,
242    ControlFlow,
243    ProgramComposition,
244    RFControl,
245}
246
247impl From<&Instruction> for InstructionRole {
248    fn from(instruction: &Instruction) -> Self {
249        match instruction {
250            Instruction::CalibrationDefinition(_)
251            | Instruction::CircuitDefinition(_)
252            | Instruction::Declaration(_)
253            | Instruction::FrameDefinition(_)
254            | Instruction::Gate(_)
255            | Instruction::GateDefinition(_)
256            | Instruction::Include(_)
257            | Instruction::Label(_)
258            | Instruction::MeasureCalibrationDefinition(_)
259            | Instruction::Measurement(_)
260            | Instruction::WaveformDefinition(_) => InstructionRole::ProgramComposition,
261            Instruction::Reset(_)
262            | Instruction::Capture(_)
263            | Instruction::Delay(_)
264            | Instruction::Fence(_)
265            | Instruction::Pulse(_)
266            | Instruction::RawCapture(_)
267            | Instruction::SetFrequency(_)
268            | Instruction::SetPhase(_)
269            | Instruction::SetScale(_)
270            | Instruction::ShiftFrequency(_)
271            | Instruction::ShiftPhase(_)
272            | Instruction::SwapPhases(_) => InstructionRole::RFControl,
273            Instruction::Arithmetic(_)
274            | Instruction::Call(_)
275            | Instruction::Comparison(_)
276            | Instruction::Convert(_)
277            | Instruction::BinaryLogic(_)
278            | Instruction::UnaryLogic(_)
279            | Instruction::Move(_)
280            | Instruction::Exchange(_)
281            | Instruction::Load(_)
282            | Instruction::Nop()
283            | Instruction::Pragma(_)
284            | Instruction::Store(_) => InstructionRole::ClassicalCompute,
285            Instruction::Halt()
286            | Instruction::Jump(_)
287            | Instruction::JumpWhen(_)
288            | Instruction::JumpUnless(_)
289            | Instruction::Wait() => InstructionRole::ControlFlow,
290        }
291    }
292}
293
294pub fn write_instruction_block<'i, I, Q>(
295    f: &mut impl std::fmt::Write,
296    fall_back_to_debug: bool,
297    values: I,
298) -> crate::quil::ToQuilResult<()>
299where
300    I: IntoIterator<Item = &'i Q>,
301    Q: Quil + 'i,
302{
303    write_join_quil(f, fall_back_to_debug, values, "\n", "\t")
304}
305
306pub(crate) fn write_join(
307    f: &mut impl std::fmt::Write,
308    values: &[impl std::fmt::Display],
309    separator: &str,
310    prefix: &str,
311) -> std::fmt::Result {
312    let mut iter = values.iter();
313    if let Some(first) = iter.next() {
314        write!(f, "{prefix}{first}")?;
315
316        for value in iter {
317            write!(f, "{separator}{prefix}{value}")?;
318        }
319    }
320    Ok(())
321}
322
323pub fn format_integer_vector(values: &[u64]) -> String {
324    values
325        .iter()
326        .map(|q| format!("{q}"))
327        .collect::<Vec<String>>()
328        .join(" ")
329}
330
331/// Write a list of qubits, with each prefixed by a space (including the first)
332fn write_qubits(
333    f: &mut impl std::fmt::Write,
334    fall_back_to_debug: bool,
335    qubits: &[Qubit],
336) -> crate::quil::ToQuilResult<()> {
337    for qubit in qubits {
338        write!(f, " ")?;
339        qubit.write(f, fall_back_to_debug)?;
340    }
341    Ok(())
342}
343
344/// Write qubits as a Quil parameter list, where all are prefixed with ` `.
345fn write_qubit_parameters(
346    f: &mut impl std::fmt::Write,
347    fall_back_to_debug: bool,
348    qubits: &[Qubit],
349) -> ToQuilResult<()> {
350    for qubit in qubits.iter() {
351        write!(f, " ")?;
352        qubit.write(f, fall_back_to_debug)?;
353    }
354    Ok(())
355}
356
357fn write_expression_parameter_string(
358    f: &mut impl std::fmt::Write,
359    fall_back_to_debug: bool,
360    parameters: &[Expression],
361) -> crate::quil::ToQuilResult<()> {
362    if parameters.is_empty() {
363        return Ok(());
364    }
365
366    write!(f, "(")?;
367    write_join_quil(f, fall_back_to_debug, parameters, ", ", "")?;
368    write!(f, ")")?;
369    Ok(())
370}
371
372fn write_parameter_string(f: &mut impl std::fmt::Write, parameters: &[String]) -> fmt::Result {
373    if parameters.is_empty() {
374        return Ok(());
375    }
376
377    write!(f, "(")?;
378    write_join(f, parameters, ", ", "%")?;
379    write!(f, ")")
380}
381
382impl Quil for Instruction {
383    fn write(
384        &self,
385        f: &mut impl std::fmt::Write,
386        fall_back_to_debug: bool,
387    ) -> Result<(), crate::quil::ToQuilError> {
388        match self {
389            Instruction::Arithmetic(arithmetic) => arithmetic.write(f, fall_back_to_debug),
390            Instruction::CalibrationDefinition(calibration) => {
391                calibration.write(f, fall_back_to_debug)
392            }
393            Instruction::Call(call) => call.write(f, fall_back_to_debug),
394            Instruction::Capture(capture) => capture.write(f, fall_back_to_debug),
395            Instruction::CircuitDefinition(circuit) => circuit.write(f, fall_back_to_debug),
396            Instruction::Convert(convert) => convert.write(f, fall_back_to_debug),
397            Instruction::Declaration(declaration) => declaration.write(f, fall_back_to_debug),
398            Instruction::Delay(delay) => delay.write(f, fall_back_to_debug),
399            Instruction::Fence(fence) => fence.write(f, fall_back_to_debug),
400            Instruction::FrameDefinition(frame_definition) => {
401                frame_definition.write(f, fall_back_to_debug)
402            }
403            Instruction::Gate(gate) => gate.write(f, fall_back_to_debug),
404            Instruction::GateDefinition(gate_definition) => {
405                gate_definition.write(f, fall_back_to_debug)
406            }
407            Instruction::Include(include) => include.write(f, fall_back_to_debug),
408            Instruction::MeasureCalibrationDefinition(measure_calibration) => {
409                measure_calibration.write(f, fall_back_to_debug)
410            }
411            Instruction::Measurement(measurement) => measurement.write(f, fall_back_to_debug),
412            Instruction::Move(r#move) => r#move.write(f, fall_back_to_debug),
413            Instruction::Exchange(exchange) => exchange.write(f, fall_back_to_debug),
414            Instruction::Load(load) => load.write(f, fall_back_to_debug),
415            Instruction::Store(store) => store.write(f, fall_back_to_debug),
416            Instruction::Pulse(pulse) => pulse.write(f, fall_back_to_debug),
417            Instruction::Pragma(pragma) => pragma.write(f, fall_back_to_debug),
418            Instruction::RawCapture(raw_capture) => raw_capture.write(f, fall_back_to_debug),
419            Instruction::Reset(reset) => reset.write(f, fall_back_to_debug),
420            Instruction::SetFrequency(set_frequency) => set_frequency.write(f, fall_back_to_debug),
421            Instruction::SetPhase(set_phase) => set_phase.write(f, fall_back_to_debug),
422            Instruction::SetScale(set_scale) => set_scale.write(f, fall_back_to_debug),
423            Instruction::ShiftFrequency(shift_frequency) => {
424                shift_frequency.write(f, fall_back_to_debug)
425            }
426            Instruction::ShiftPhase(shift_phase) => shift_phase.write(f, fall_back_to_debug),
427            Instruction::SwapPhases(swap_phases) => swap_phases.write(f, fall_back_to_debug),
428            Instruction::WaveformDefinition(waveform_definition) => {
429                waveform_definition.write(f, fall_back_to_debug)
430            }
431            Instruction::Halt() => write!(f, "HALT").map_err(Into::into),
432            Instruction::Nop() => write!(f, "NOP").map_err(Into::into),
433            Instruction::Wait() => write!(f, "WAIT").map_err(Into::into),
434            Instruction::Jump(jump) => jump.write(f, fall_back_to_debug),
435            Instruction::JumpUnless(jump) => jump.write(f, fall_back_to_debug),
436            Instruction::JumpWhen(jump) => jump.write(f, fall_back_to_debug),
437            Instruction::Label(label) => label.write(f, fall_back_to_debug),
438            Instruction::Comparison(comparison) => comparison.write(f, fall_back_to_debug),
439            Instruction::BinaryLogic(binary_logic) => binary_logic.write(f, fall_back_to_debug),
440            Instruction::UnaryLogic(unary_logic) => unary_logic.write(f, fall_back_to_debug),
441        }
442    }
443}
444
445pub(crate) struct QuotedString<S>(pub(crate) S);
446
447impl<S> fmt::Display for QuotedString<S>
448where
449    S: AsRef<str>,
450{
451    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452        write!(f, "\"")?;
453        for c in self.0.as_ref().chars() {
454            match c {
455                '"' => write!(f, "\\\"")?,
456                '\\' => write!(f, "\\\\")?,
457                c => write!(f, "{c}")?,
458            }
459        }
460        write!(f, "\"")
461    }
462}
463
464#[cfg(test)]
465mod test_instruction_display {
466    use crate::{instruction::PragmaArgument, quil::Quil};
467
468    use super::{Instruction, Pragma};
469
470    #[test]
471    fn pragma() {
472        assert_eq!(
473            Instruction::Pragma(Pragma {
474                name: String::from("INITIAL_REWIRING"),
475                arguments: vec![],
476                data: Some(String::from("PARTIAL")),
477            })
478            .to_quil()
479            .unwrap(),
480            "PRAGMA INITIAL_REWIRING \"PARTIAL\""
481        );
482        assert_eq!(
483            Instruction::Pragma(Pragma {
484                name: String::from("LOAD-MEMORY"),
485                arguments: vec![PragmaArgument::Identifier("q0".to_string())],
486                data: Some(String::from("addr")),
487            })
488            .to_quil()
489            .unwrap(),
490            "PRAGMA LOAD-MEMORY q0 \"addr\""
491        );
492        assert_eq!(
493            Instruction::Pragma(Pragma {
494                name: String::from("PRESERVE_BLOCK"),
495                arguments: vec![],
496                data: None,
497            })
498            .to_quil()
499            .unwrap(),
500            "PRAGMA PRESERVE_BLOCK"
501        );
502    }
503}
504
505impl Instruction {
506    /// Apply the provided closure to this instruction, mutating any `Expression`s within.
507    /// Does not affect instructions without `Expression`s within.
508    /// Does not traverse or mutate instructions nested within blocks (such as
509    /// within `DEFCAL`).
510    ///
511    /// # Example
512    ///
513    /// ```rust
514    /// use std::mem::replace;
515    /// use std::str::FromStr;
516    /// use quil_rs::{expression::Expression, Program, quil::Quil};
517    ///
518    ///
519    /// let program = Program::from_str("SHIFT-PHASE 0 \"rf\" 2*2").unwrap();
520    /// let mut instructions = program.to_instructions();
521    /// instructions.iter_mut().for_each(|inst| inst.apply_to_expressions(Expression::simplify));
522    ///
523    /// assert_eq!(instructions[0].to_quil().unwrap(), String::from("SHIFT-PHASE 0 \"rf\" 4"))
524    ///
525    /// ```
526    pub fn apply_to_expressions(&mut self, mut closure: impl FnMut(&mut Expression)) {
527        match self {
528            Instruction::CalibrationDefinition(CalibrationDefinition {
529                identifier: CalibrationIdentifier { parameters, .. },
530                ..
531            })
532            | Instruction::Gate(Gate { parameters, .. }) => {
533                parameters.iter_mut().for_each(closure);
534            }
535            Instruction::Capture(Capture { waveform, .. })
536            | Instruction::Pulse(Pulse { waveform, .. }) => {
537                waveform.parameters.values_mut().for_each(closure);
538            }
539            Instruction::Delay(Delay { duration, .. })
540            | Instruction::RawCapture(RawCapture { duration, .. }) => {
541                closure(duration);
542            }
543            Instruction::FrameDefinition(FrameDefinition { attributes, .. }) => {
544                for value in attributes.values_mut() {
545                    if let AttributeValue::Expression(expression) = value {
546                        closure(expression);
547                    }
548                }
549            }
550            Instruction::SetFrequency(SetFrequency {
551                frequency: expression,
552                ..
553            })
554            | Instruction::SetPhase(SetPhase {
555                phase: expression, ..
556            })
557            | Instruction::SetScale(SetScale {
558                scale: expression, ..
559            })
560            | Instruction::ShiftFrequency(ShiftFrequency {
561                frequency: expression,
562                ..
563            })
564            | Instruction::ShiftPhase(ShiftPhase {
565                phase: expression, ..
566            }) => {
567                closure(expression);
568            }
569            Instruction::WaveformDefinition(WaveformDefinition { definition, .. }) => {
570                definition.matrix.iter_mut().for_each(closure);
571            }
572            Instruction::GateDefinition(GateDefinition {
573                specification: GateSpecification::Matrix(matrix),
574                ..
575            }) => {
576                for row in matrix {
577                    for cell in row {
578                        closure(cell);
579                    }
580                }
581            }
582            _ => {}
583        }
584    }
585
586    pub(crate) fn get_frame_match_condition<'a>(
587        &'a self,
588        qubits_available: &'a HashSet<Qubit>,
589    ) -> Option<FrameMatchConditions<'a>> {
590        match self {
591            Instruction::Pulse(Pulse {
592                blocking, frame, ..
593            })
594            | Instruction::Capture(Capture {
595                blocking, frame, ..
596            })
597            | Instruction::RawCapture(RawCapture {
598                blocking, frame, ..
599            }) => Some(FrameMatchConditions {
600                blocked: blocking
601                    .then(|| FrameMatchCondition::AnyOfQubits(frame.qubits.iter().collect())),
602                used: Some(FrameMatchCondition::Specific(frame)),
603            }),
604            Instruction::Delay(Delay {
605                frame_names,
606                qubits,
607                ..
608            }) => Some(FrameMatchConditions {
609                used: Some(if frame_names.is_empty() {
610                    FrameMatchCondition::ExactQubits(qubits.iter().collect())
611                } else {
612                    FrameMatchCondition::And(vec![
613                        FrameMatchCondition::ExactQubits(qubits.iter().collect()),
614                        FrameMatchCondition::AnyOfNames(
615                            frame_names.iter().map(String::as_str).collect(),
616                        ),
617                    ])
618                }),
619                blocked: None,
620            }),
621            Instruction::Fence(Fence { qubits }) => Some(FrameMatchConditions {
622                used: None,
623                blocked: Some(if qubits.is_empty() {
624                    FrameMatchCondition::All
625                } else {
626                    FrameMatchCondition::AnyOfQubits(qubits.iter().collect())
627                }),
628            }),
629            Instruction::Reset(Reset { qubit }) => {
630                let qubits = match qubit {
631                    Some(qubit) => {
632                        let mut set = HashSet::new();
633                        set.insert(qubit);
634                        set
635                    }
636                    None => qubits_available.iter().collect(),
637                };
638
639                Some(FrameMatchConditions {
640                    used: Some(FrameMatchCondition::ExactQubits(qubits.clone())),
641                    blocked: Some(FrameMatchCondition::AnyOfQubits(qubits)),
642                })
643            }
644            Instruction::SetFrequency(SetFrequency { frame, .. })
645            | Instruction::SetPhase(SetPhase { frame, .. })
646            | Instruction::SetScale(SetScale { frame, .. })
647            | Instruction::ShiftFrequency(ShiftFrequency { frame, .. })
648            | Instruction::ShiftPhase(ShiftPhase { frame, .. }) => Some(FrameMatchConditions {
649                used: Some(FrameMatchCondition::Specific(frame)),
650                blocked: None,
651            }),
652            Instruction::SwapPhases(SwapPhases { frame_1, frame_2 }) => {
653                Some(FrameMatchConditions {
654                    used: Some(FrameMatchCondition::Or(vec![
655                        FrameMatchCondition::Specific(frame_1),
656                        FrameMatchCondition::Specific(frame_2),
657                    ])),
658                    blocked: None,
659                })
660            }
661            Instruction::Arithmetic(_)
662            | Instruction::BinaryLogic(_)
663            | Instruction::CalibrationDefinition(_)
664            | Instruction::Call(_)
665            | Instruction::CircuitDefinition(_)
666            | Instruction::Comparison(_)
667            | Instruction::Convert(_)
668            | Instruction::Declaration(_)
669            | Instruction::Exchange(_)
670            | Instruction::FrameDefinition(_)
671            | Instruction::Gate(_)
672            | Instruction::GateDefinition(_)
673            | Instruction::Halt()
674            | Instruction::Include(_)
675            | Instruction::Jump(_)
676            | Instruction::JumpUnless(_)
677            | Instruction::JumpWhen(_)
678            | Instruction::Label(_)
679            | Instruction::Load(_)
680            | Instruction::MeasureCalibrationDefinition(_)
681            | Instruction::Measurement(_)
682            | Instruction::Move(_)
683            | Instruction::Nop()
684            | Instruction::Pragma(_)
685            | Instruction::Store(_)
686            | Instruction::UnaryLogic(_)
687            | Instruction::WaveformDefinition(_)
688            | Instruction::Wait() => None,
689        }
690    }
691
692    /// Return immutable references to the [`Qubit`]s contained within an instruction
693    #[allow(dead_code)]
694    pub fn get_qubits(&self) -> Vec<&Qubit> {
695        match self {
696            Instruction::Gate(gate) => gate.qubits.iter().collect(),
697            Instruction::CalibrationDefinition(calibration) => calibration
698                .identifier
699                .qubits
700                .iter()
701                .chain(
702                    calibration
703                        .instructions
704                        .iter()
705                        .flat_map(|inst| inst.get_qubits()),
706                )
707                .collect(),
708            Instruction::MeasureCalibrationDefinition(measurement) => {
709                iter::once(&measurement.identifier.qubit)
710                    .chain(
711                        measurement
712                            .instructions
713                            .iter()
714                            .flat_map(|inst| inst.get_qubits()),
715                    )
716                    .collect()
717            }
718            Instruction::Measurement(measurement) => vec![&measurement.qubit],
719            Instruction::Reset(reset) => match &reset.qubit {
720                Some(qubit) => vec![qubit],
721                None => vec![],
722            },
723            Instruction::Delay(delay) => delay.qubits.iter().collect(),
724            Instruction::Fence(fence) => fence.qubits.iter().collect(),
725            Instruction::Capture(capture) => capture.frame.qubits.iter().collect(),
726            Instruction::Pulse(pulse) => pulse.frame.qubits.iter().collect(),
727            Instruction::RawCapture(raw_capture) => raw_capture.frame.qubits.iter().collect(),
728            _ => vec![],
729        }
730    }
731
732    /// Return mutable references to the [`Qubit`]s contained within an instruction
733    pub fn get_qubits_mut(&mut self) -> Vec<&mut Qubit> {
734        match self {
735            Instruction::Gate(gate) => gate.qubits.iter_mut().collect(),
736            Instruction::CalibrationDefinition(calibration) => calibration
737                .identifier
738                .qubits
739                .iter_mut()
740                .chain(
741                    calibration
742                        .instructions
743                        .iter_mut()
744                        .flat_map(|inst| inst.get_qubits_mut()),
745                )
746                .collect(),
747            Instruction::MeasureCalibrationDefinition(measurement) => {
748                iter::once(&mut measurement.identifier.qubit)
749                    .chain(
750                        measurement
751                            .instructions
752                            .iter_mut()
753                            .flat_map(|inst| inst.get_qubits_mut()),
754                    )
755                    .collect()
756            }
757            Instruction::Measurement(measurement) => vec![&mut measurement.qubit],
758            Instruction::Reset(reset) => match &mut reset.qubit {
759                Some(qubit) => vec![qubit],
760                None => vec![],
761            },
762            Instruction::Delay(delay) => delay.qubits.iter_mut().collect(),
763            Instruction::Fence(fence) => fence.qubits.iter_mut().collect(),
764            Instruction::Capture(capture) => capture.frame.qubits.iter_mut().collect(),
765            Instruction::Pulse(pulse) => pulse.frame.qubits.iter_mut().collect(),
766            Instruction::RawCapture(raw_capture) => raw_capture.frame.qubits.iter_mut().collect(),
767            _ => vec![],
768        }
769    }
770
771    /// Return the waveform _directly_ invoked by the instruction, if any.
772    ///
773    /// Note: this does not expand calibrations or other instructions which may
774    /// indirectly cause a waveform to be invoked.
775    pub(crate) fn get_waveform_invocation(&self) -> Option<&WaveformInvocation> {
776        match self {
777            Instruction::Capture(Capture { waveform, .. }) => Some(waveform),
778            Instruction::Pulse(Pulse { waveform, .. }) => Some(waveform),
779            _ => None,
780        }
781    }
782
783    /// Parse a single instruction from an input string. Returns an error if the input fails to parse,
784    /// or if there is input left over after parsing.
785    #[cfg(test)]
786    pub(crate) fn parse_in_test(input: &str) -> Result<Self, String> {
787        use crate::parser::instruction::parse_instruction;
788
789        let input = LocatedSpan::new(input);
790        let lexed = lex(input).map_err(|err| err.to_string())?;
791        let (_, instruction) =
792            nom::combinator::all_consuming(parse_instruction)(&lexed).map_err(|e| e.to_string())?;
793        Ok(instruction)
794    }
795
796    /// Per the Quil-T spec, whether this instruction's timing within the pulse
797    /// program must be precisely controlled so as to begin exactly on the end of
798    /// the latest preceding timed instruction
799    pub fn is_scheduled(&self) -> bool {
800        match self {
801            Instruction::Capture(_)
802            | Instruction::Delay(_)
803            | Instruction::Fence(_)
804            | Instruction::Pulse(_)
805            | Instruction::RawCapture(_)
806            | Instruction::SetFrequency(_)
807            | Instruction::SetPhase(_)
808            | Instruction::SetScale(_)
809            | Instruction::ShiftFrequency(_)
810            | Instruction::ShiftPhase(_)
811            | Instruction::SwapPhases(_)
812            | Instruction::Wait() => true,
813            Instruction::Arithmetic(_)
814            | Instruction::BinaryLogic(_)
815            | Instruction::CalibrationDefinition(_)
816            | Instruction::Call(_)
817            | Instruction::CircuitDefinition(_)
818            | Instruction::Convert(_)
819            | Instruction::Comparison(_)
820            | Instruction::Declaration(_)
821            | Instruction::Exchange(_)
822            | Instruction::FrameDefinition(_)
823            | Instruction::Gate(_)
824            | Instruction::GateDefinition(_)
825            | Instruction::Halt()
826            | Instruction::Include(_)
827            | Instruction::Jump(_)
828            | Instruction::JumpUnless(_)
829            | Instruction::JumpWhen(_)
830            | Instruction::Label(_)
831            | Instruction::Load(_)
832            | Instruction::MeasureCalibrationDefinition(_)
833            | Instruction::Measurement(_)
834            | Instruction::Move(_)
835            | Instruction::Nop()
836            | Instruction::Pragma(_)
837            | Instruction::Reset(_)
838            | Instruction::Store(_)
839            | Instruction::UnaryLogic(_)
840            | Instruction::WaveformDefinition(_) => false,
841        }
842    }
843
844    pub(crate) fn resolve_placeholders<TR, QR>(&mut self, target_resolver: TR, qubit_resolver: QR)
845    where
846        TR: Fn(&TargetPlaceholder) -> Option<String>,
847        QR: Fn(&QubitPlaceholder) -> Option<u64>,
848    {
849        match self {
850            Instruction::Label(label) => {
851                label.target.resolve_placeholder(target_resolver);
852            }
853            Instruction::Jump(jump) => {
854                jump.target.resolve_placeholder(target_resolver);
855            }
856            Instruction::JumpWhen(jump_when) => {
857                jump_when.target.resolve_placeholder(target_resolver);
858            }
859            Instruction::JumpUnless(jump_unless) => {
860                jump_unless.target.resolve_placeholder(target_resolver);
861            }
862            other => {
863                for qubit in other.get_qubits_mut() {
864                    qubit.resolve_placeholder(&qubit_resolver);
865                }
866            }
867        }
868    }
869}
870
871#[derive(Debug, thiserror::Error)]
872pub enum ParseInstructionError {
873    #[error("Failed to parse instruction: {0}")]
874    Parse(String),
875    #[error("Expected to parse exactly one instruction but got {0}")]
876    ZeroOrMany(usize),
877}
878
879impl FromStr for Instruction {
880    type Err = ParseInstructionError;
881
882    fn from_str(s: &str) -> Result<Self, Self::Err> {
883        let input = LocatedSpan::new(s);
884        let lexed = lex(input).map_err(|e| ParseInstructionError::Parse(e.to_string()))?;
885        let instructions =
886            parse_instructions(&lexed).map_err(|e| ParseInstructionError::Parse(e.to_string()))?;
887        if instructions.1.len() != 1 {
888            return Err(ParseInstructionError::ZeroOrMany(instructions.1.len()));
889        }
890        Ok(instructions.1[0].to_owned())
891    }
892}
893
894/// Trait signature for a function or closure that returns an optional override for whether
895/// an instruction should be scheduled.
896pub trait GetIsScheduledFnMut: FnMut(&Instruction) -> Option<bool> {}
897impl<F> GetIsScheduledFnMut for F where F: FnMut(&Instruction) -> Option<bool> {}
898
899/// Trait signature for a function or closure that returns an optional override for an
900/// instruction's [`InstructionRole`].
901pub trait GetRoleForInstructionFnMut: FnMut(&Instruction) -> Option<InstructionRole> {}
902impl<F> GetRoleForInstructionFnMut for F where F: FnMut(&Instruction) -> Option<InstructionRole> {}
903
904/// Trait signature for a function or closure that returns an optional override for an
905/// instruction's [`MatchedFrames`].
906pub trait GetMatchingFramesFnMut:
907    for<'p> FnMut(&Instruction, &'p Program) -> Option<Option<MatchedFrames<'p>>>
908{
909}
910impl<F> GetMatchingFramesFnMut for F where
911    F: for<'p> FnMut(&Instruction, &'p Program) -> Option<Option<MatchedFrames<'p>>>
912{
913}
914
915/// Trait signature for a function or closure that returns an optional override for an
916/// instruction's [`MemoryAccesses`].
917pub trait GetMemoryAccessesFnMut: FnMut(&Instruction) -> Option<MemoryAccesses> {}
918impl<F> GetMemoryAccessesFnMut for F where F: FnMut(&Instruction) -> Option<MemoryAccesses> {}
919
920/// A struct that allows setting optional overrides for key [`Instruction`] methods.
921///
922/// A common use case for this is to support custom `PRAGMA` instructions, which are treated as
923/// classical style no-ops by default.
924#[derive(Default)]
925pub struct InstructionHandler {
926    get_is_scheduled: Option<Box<dyn GetIsScheduledFnMut>>,
927    get_role_for_instruction: Option<Box<dyn GetRoleForInstructionFnMut>>,
928    get_matching_frames: Option<Box<dyn GetMatchingFramesFnMut>>,
929    get_memory_accesses: Option<Box<dyn GetMemoryAccessesFnMut>>,
930}
931
932impl InstructionHandler {
933    /// Set an override function for whether an instruction is scheduled.
934    ///
935    /// If the provided function returns `None`, a default will be used.
936    /// See also [`InstructionHandler::is_scheduled`].
937    pub fn set_is_scheduled<F>(mut self, f: F) -> Self
938    where
939        F: GetIsScheduledFnMut + 'static,
940    {
941        self.get_is_scheduled = Some(Box::new(f));
942        self
943    }
944
945    /// Set an override function for determining an instruction's [`InstructionRole`].
946    ///
947    /// If the provided function returns `None`, a default will be used.
948    /// See also [`InstructionHandler::role_for_instruction`].
949    pub fn set_role_for_instruction<F>(mut self, f: F) -> Self
950    where
951        F: GetRoleForInstructionFnMut + 'static,
952    {
953        self.get_role_for_instruction = Some(Box::new(f));
954        self
955    }
956
957    /// Set an override function for determining an instruction's [`MatchedFrames`].
958    ///
959    /// If the provided function returns `None`, a default will be used.
960    /// See also [`InstructionHandler::get_matching_frames`].
961    pub fn set_matching_frames<F>(mut self, f: F) -> Self
962    where
963        F: GetMatchingFramesFnMut + 'static,
964    {
965        self.get_matching_frames = Some(Box::new(f));
966        self
967    }
968
969    /// Set an override function for determining an instruction's [`MemoryAccesses`].
970    ///
971    /// If the provided function returns `None`, a default will be used.
972    /// See also [`InstructionHandler::get_memory_accesses`].
973    pub fn set_memory_accesses<F>(mut self, f: F) -> Self
974    where
975        F: GetMemoryAccessesFnMut + 'static,
976    {
977        self.get_memory_accesses = Some(Box::new(f));
978        self
979    }
980
981    /// Determine whether the given instruction is scheduled.
982    ///
983    /// This uses the return value of the override function, if set and returns `Some`. If not set
984    /// or the function returns `None`, defaults to the return value of
985    /// [`Instruction::is_scheduled`].
986    pub fn is_scheduled(&mut self, instruction: &Instruction) -> bool {
987        self.get_is_scheduled
988            .as_mut()
989            .and_then(|f| f(instruction))
990            .unwrap_or_else(|| instruction.is_scheduled())
991    }
992
993    /// Determine the [`InstructionRole`] for the given instruction.
994    ///
995    /// This uses the return value of the override function, if set and returns `Some`. If not set
996    /// or the function returns `None`, defaults to the return value of
997    /// [`InstructionRole::from`].
998    pub fn role_for_instruction(&mut self, instruction: &Instruction) -> InstructionRole {
999        self.get_role_for_instruction
1000            .as_mut()
1001            .and_then(|f| f(instruction))
1002            .unwrap_or_else(|| InstructionRole::from(instruction))
1003    }
1004
1005    /// Determine the [`MatchedFrames`] for the given instruction.
1006    ///
1007    /// This uses the return value of the override function, if set and returns `Some`. If not set
1008    /// or the function returns `None`, defaults to the return value of
1009    /// [`Program::get_frames_for_instruction`].
1010    pub fn matching_frames<'p>(
1011        &mut self,
1012        instruction: &Instruction,
1013        program: &'p Program,
1014    ) -> Option<MatchedFrames<'p>> {
1015        self.get_matching_frames
1016            .as_mut()
1017            .and_then(|f| f(instruction, program))
1018            .unwrap_or_else(|| program.get_frames_for_instruction(instruction))
1019    }
1020
1021    /// Determine the [`MemoryAccesses`] for the given instruction.
1022    ///
1023    /// This uses the return value of the override function, if set and returns `Some`. If not set
1024    /// or the function returns `None`, defaults to the return value of
1025    /// [`Instruction::get_memory_accesses`].
1026    pub fn memory_accesses(
1027        &mut self,
1028        instruction: &Instruction,
1029        extern_signature_map: &ExternSignatureMap,
1030    ) -> crate::program::MemoryAccessesResult {
1031        self.get_memory_accesses
1032            .as_mut()
1033            .and_then(|f| f(instruction))
1034            .map(Ok)
1035            .unwrap_or_else(|| instruction.get_memory_accesses(extern_signature_map))
1036    }
1037
1038    /// Like [`Program::into_simplified`], but using custom instruction handling.
1039    // TODO (#453): Address large error types.
1040    #[allow(clippy::result_large_err)]
1041    pub fn simplify_program(&mut self, program: &Program) -> Result<Program, ProgramError> {
1042        program.simplify_with_handler(self)
1043    }
1044}
1045
1046#[cfg(test)]
1047mod tests {
1048    use rstest::*;
1049    use std::str::FromStr;
1050
1051    use crate::{expression::Expression, Program};
1052
1053    use super::MemoryReference;
1054
1055    #[test]
1056    fn apply_to_expressions() {
1057        let mut program = Program::from_str(
1058            "DECLARE ro BIT
1059SET-PHASE 0 \"rf\" pi/2
1060RX(2) 0",
1061        )
1062        .unwrap();
1063        let closure = |expr: &mut Expression| *expr = Expression::Variable(String::from("a"));
1064        program.for_each_body_instruction(|instruction| {
1065            instruction.apply_to_expressions(closure);
1066        });
1067
1068        let expected_program = Program::from_str(
1069            "DECLARE ro BIT
1070SET-PHASE 0 \"rf\" %a
1071RX(%a) 0",
1072        )
1073        .unwrap();
1074
1075        assert_eq!(expected_program, program);
1076    }
1077
1078    #[rstest(input, expected,
1079        case("_", MemoryReference { name: "_".to_string(), index: 0 }),
1080        case("a", MemoryReference { name: "a".to_string(), index: 0 }),
1081        case("a---b", MemoryReference { name: "a---b".to_string(), index: 0 }),
1082        case("_a_b_", MemoryReference { name: "_a_b_".to_string(), index: 0 }),
1083        case("a-2_b-2", MemoryReference { name: "a-2_b-2".to_string(), index: 0 }),
1084        case("_[0]", MemoryReference { name: "_".to_string(), index: 0 }),
1085        case("a[1]", MemoryReference { name: "a".to_string(), index: 1 }),
1086        case("a---b[2]", MemoryReference { name: "a---b".to_string(), index: 2 }),
1087        case("_a_b_[3]", MemoryReference { name: "_a_b_".to_string(), index: 3 }),
1088        case("a-2_b-2[4]", MemoryReference { name: "a-2_b-2".to_string(), index: 4 }),
1089    )]
1090    fn it_parses_memory_reference_from_str(input: &str, expected: MemoryReference) {
1091        assert_eq!(MemoryReference::from_str(input), Ok(expected));
1092    }
1093
1094    #[rstest(
1095        input,
1096        case(""),
1097        case("[0]"),
1098        case("a[-1]"),
1099        case("2a[2]"),
1100        case("-a"),
1101        case("NOT[3]"),
1102        case("a a"),
1103        case("a[5] a[5]"),
1104        case("DECLARE a[6]")
1105    )]
1106    fn it_fails_to_parse_memory_reference_from_str(input: &str) {
1107        assert!(MemoryReference::from_str(input).is_err());
1108    }
1109
1110    mod placeholders {
1111        use std::collections::HashMap;
1112
1113        use crate::instruction::{Label, Qubit, QubitPlaceholder, Target, TargetPlaceholder};
1114
1115        #[allow(clippy::redundant_clone)]
1116        #[test]
1117        fn target() {
1118            let placeholder_1 = TargetPlaceholder::new(String::from("label"));
1119            let placeholder_2 = TargetPlaceholder::new(String::from("label"));
1120            let placeholder_3 = TargetPlaceholder::new(String::from("other"));
1121
1122            assert_eq!(placeholder_1, placeholder_1);
1123            assert_eq!(placeholder_1, placeholder_1.clone());
1124            assert_eq!(placeholder_1.clone(), placeholder_1.clone());
1125            assert_ne!(placeholder_1, placeholder_2);
1126            assert_ne!(placeholder_2, placeholder_3);
1127            assert_ne!(placeholder_1, placeholder_3);
1128        }
1129
1130        #[test]
1131        fn target_resolution() {
1132            let placeholder_1 = TargetPlaceholder::new(String::from("label"));
1133            let placeholder_2 = TargetPlaceholder::new(String::from("label"));
1134
1135            let resolver = HashMap::from([(placeholder_1.clone(), String::from("label_1"))]);
1136
1137            let mut label_1 = Label {
1138                target: Target::Placeholder(placeholder_1),
1139            };
1140            label_1
1141                .target
1142                .resolve_placeholder(|k| resolver.get(k).cloned());
1143            assert_eq!(label_1.target, Target::Fixed(String::from("label_1")));
1144
1145            let mut label_2 = Label {
1146                target: Target::Placeholder(placeholder_2.clone()),
1147            };
1148            label_2
1149                .target
1150                .resolve_placeholder(|k| resolver.get(k).cloned());
1151            assert_eq!(label_2.target, Target::Placeholder(placeholder_2));
1152        }
1153
1154        #[allow(clippy::redundant_clone)]
1155        #[test]
1156        fn qubit() {
1157            let placeholder_1 = QubitPlaceholder::default();
1158            let placeholder_2 = QubitPlaceholder::default();
1159
1160            assert_eq!(placeholder_1, placeholder_1);
1161            assert_eq!(placeholder_1, placeholder_1.clone());
1162            assert_eq!(placeholder_1.clone(), placeholder_1.clone());
1163            assert_ne!(placeholder_1, placeholder_2);
1164        }
1165
1166        #[test]
1167        fn qubit_resolution() {
1168            let placeholder_1 = QubitPlaceholder::default();
1169            let placeholder_2 = QubitPlaceholder::default();
1170
1171            let resolver = HashMap::from([(placeholder_1.clone(), 1)]);
1172
1173            let mut qubit_1 = Qubit::Placeholder(placeholder_1);
1174            qubit_1.resolve_placeholder(|k| resolver.get(k).copied());
1175            assert_eq!(qubit_1, Qubit::Fixed(1));
1176
1177            let mut qubit_2 = Qubit::Placeholder(placeholder_2.clone());
1178            qubit_2.resolve_placeholder(|k| resolver.get(k).copied());
1179            assert_eq!(qubit_2, Qubit::Placeholder(placeholder_2));
1180        }
1181    }
1182
1183    mod instruction_handler {
1184        use super::super::*;
1185
1186        #[test]
1187        fn it_considers_custom_instruction_frames() {
1188            let program = r#"DEFFRAME 0 "rf":
1189    CENTER-FREQUENCY: 3e9
1190
1191PRAGMA USES-ALL-FRAMES
1192"#
1193            .parse::<Program>()
1194            .unwrap();
1195
1196            // This test assumes that the default simplification behavior will not assign frames to
1197            // `PRAGMA` instructions. This is verified below.
1198            assert!(program.into_simplified().unwrap().frames.is_empty());
1199
1200            let mut handler =
1201                InstructionHandler::default().set_matching_frames(|instruction, program| {
1202                    if let Instruction::Pragma(_) = instruction {
1203                        Some(Some(MatchedFrames {
1204                            used: program.frames.get_keys().into_iter().collect(),
1205                            blocked: HashSet::new(),
1206                        }))
1207                    } else {
1208                        None
1209                    }
1210                });
1211
1212            assert_eq!(handler.simplify_program(&program).unwrap().frames.len(), 1);
1213        }
1214    }
1215}