Skip to main content

quil/instruction/
mod.rs

1use std::str::FromStr;
2
3use pyo3::exceptions::PyValueError;
4use quil_rs::instruction::Instruction;
5use rigetti_pyo3::{
6    create_init_submodule, impl_repr, py_wrap_union_enum,
7    pyo3::{pymethods, types::PyDict, PyResult, Python},
8    PyWrapper,
9};
10
11use crate::{impl_eq, impl_to_quil};
12
13pub use self::{
14    calibration::{
15        PyCalibration, PyCalibrationIdentifier, PyMeasureCalibrationDefinition,
16        PyMeasureCalibrationIdentifier,
17    },
18    circuit::PyCircuitDefinition,
19    classical::{
20        PyArithmetic, PyArithmeticOperand, PyArithmeticOperator, PyBinaryLogic, PyBinaryOperand,
21        PyBinaryOperator, PyComparison, PyComparisonOperand, PyComparisonOperator, PyConvert,
22        PyExchange, PyMove, PyUnaryLogic, PyUnaryOperator,
23    },
24    control_flow::{PyJump, PyJumpUnless, PyJumpWhen, PyLabel, PyTarget, PyTargetPlaceholder},
25    declaration::{
26        ParseMemoryReferenceError, PyDeclaration, PyLoad, PyMemoryReference, PyOffset,
27        PyScalarType, PySharing, PyStore, PyVector,
28    },
29    extern_call::{
30        CallError, ExternError, PyCall, PyCallArgument, PyExternParameter, PyExternParameterType,
31        PyExternSignature,
32    },
33    frame::{
34        PyAttributeValue, PyCapture, PyFrameAttributes, PyFrameDefinition, PyFrameIdentifier,
35        PyPulse, PyRawCapture, PySetFrequency, PySetPhase, PySetScale, PyShiftFrequency,
36        PyShiftPhase, PySwapPhases,
37    },
38    gate::{
39        GateError, PyGate, PyGateDefinition, PyGateModifier, PyGateSpecification, PyPauliGate,
40        PyPauliSum, PyPauliTerm,
41    },
42    measurement::PyMeasurement,
43    pragma::{PyInclude, PyPragma, PyPragmaArgument},
44    qubit::{PyQubit, PyQubitPlaceholder},
45    reset::PyReset,
46    timing::{PyDelay, PyFence},
47    waveform::{PyWaveform, PyWaveformDefinition, PyWaveformInvocation},
48};
49
50mod calibration;
51mod circuit;
52mod classical;
53mod control_flow;
54mod declaration;
55mod extern_call;
56mod frame;
57mod gate;
58mod measurement;
59mod pragma;
60mod qubit;
61mod reset;
62mod timing;
63mod waveform;
64
65py_wrap_union_enum! {
66    #[derive(Debug, PartialEq)]
67    PyInstruction(Instruction) as "Instruction" {
68        arithmetic: Arithmetic => PyArithmetic,
69        binary_logic: BinaryLogic => PyBinaryLogic,
70        calibration_definition: CalibrationDefinition => PyCalibration,
71        call: Call => PyCall,
72        capture: Capture => PyCapture,
73        circuit_definition: CircuitDefinition => PyCircuitDefinition,
74        convert: Convert => PyConvert,
75        comparison: Comparison => PyComparison,
76        declaration: Declaration => PyDeclaration,
77        delay: Delay => PyDelay,
78        exchange: Exchange => PyExchange,
79        fence: Fence => PyFence,
80        frame_definition: FrameDefinition => PyFrameDefinition,
81        gate: Gate => PyGate,
82        gate_definition: GateDefinition => PyGateDefinition,
83        halt: Halt,
84        include: Include => PyInclude,
85        jump: Jump => PyJump,
86        jump_when: JumpWhen => PyJumpWhen,
87        jump_unless: JumpUnless => PyJumpUnless,
88        label: Label => PyLabel,
89        load: Load => PyLoad,
90        measure_calibration_definition: MeasureCalibrationDefinition => PyMeasureCalibrationDefinition,
91        measurement: Measurement => PyMeasurement,
92        move: Move => PyMove,
93        nop: Nop,
94        pragma: Pragma => PyPragma,
95        pulse: Pulse => PyPulse,
96        raw_capture: RawCapture => PyRawCapture,
97        reset: Reset => PyReset,
98        set_frequency: SetFrequency => PySetFrequency,
99        set_phase: SetPhase => PySetPhase,
100        set_scale: SetScale => PySetScale,
101        shift_frequency: ShiftFrequency => PyShiftFrequency,
102        shift_phase: ShiftPhase => PyShiftPhase,
103        store: Store => PyStore,
104        swap_phases: SwapPhases => PySwapPhases,
105        unary_logic: UnaryLogic => PyUnaryLogic,
106        waveform_definition: WaveformDefinition => PyWaveformDefinition,
107        wait: Wait
108    }
109}
110impl_repr!(PyInstruction);
111impl_to_quil!(PyInstruction);
112impl_eq!(PyInstruction);
113
114#[pymethods]
115impl PyInstruction {
116    pub fn is_quil_t(&self) -> bool {
117        self.as_inner().is_quil_t()
118    }
119
120    #[staticmethod]
121    pub fn parse(string: &str) -> PyResult<Self> {
122        match Instruction::from_str(string) {
123            Ok(instruction) => Ok(Self(instruction)),
124            Err(err) => Err(PyValueError::new_err(err.to_string())),
125        }
126    }
127
128    // Implement the __copy__ and __deepcopy__ dunder methods, which are used by Python's
129    // `copy` module.
130    //
131    // If the instruction contains some inner data, then the implementation for __deepcopy__
132    // is delegated to that inner type so that each type can define its own copy behavior.
133    // This comes with the caveat that this implementation will error if the inner type doesn't
134    // implement __deepcopy__ itself. See [`impl_copy_for_instruction!`] for an easy way to
135    // implement these methods on any variant of [`PyInstruction`].
136    pub fn __copy__(&self) -> Self {
137        self.clone()
138    }
139
140    pub fn __deepcopy__(&self, py: Python<'_>, memo: &PyDict) -> PyResult<Self> {
141        match self.inner(py) {
142            Ok(inner) => Ok(PyInstruction::new(
143                py,
144                inner.call_method1(py, "__deepcopy__", (memo,))?.as_ref(py),
145            )?),
146            Err(_) => Ok(self.clone()), // No inner data implies this is a simple instruction, safe to
147                                        // just clone.
148        }
149    }
150}
151
152create_init_submodule! {
153    classes: [
154        PyInstruction,
155        PyArithmetic,
156        PyArithmeticOperand,
157        PyArithmeticOperator,
158        PyBinaryLogic,
159        PyBinaryOperand,
160        PyBinaryOperator,
161        PyCall,
162        PyCallArgument,
163        PyComparison,
164        PyComparisonOperand,
165        PyComparisonOperator,
166        PyConvert,
167        PyExchange,
168        PyExternParameter,
169        PyExternParameterType,
170        PyExternSignature,
171        PyMove,
172        PyUnaryLogic,
173        PyUnaryOperator,
174        PyCalibration,
175        PyCalibrationIdentifier,
176        PyCircuitDefinition,
177        PyMeasureCalibrationDefinition,
178        PyMeasureCalibrationIdentifier,
179        PyDeclaration,
180        PyLoad,
181        PyOffset,
182        PySharing,
183        PyStore,
184        PyScalarType,
185        PyVector,
186        PyMeasurement,
187        PyInclude,
188        PyPragma,
189        PyPragmaArgument,
190        PyAttributeValue,
191        PyCapture,
192        PyFrameDefinition,
193        PyFrameIdentifier,
194        PyPulse,
195        PyRawCapture,
196        PySetFrequency,
197        PySetPhase,
198        PySetScale,
199        PyShiftFrequency,
200        PyShiftPhase,
201        PySwapPhases,
202        PyGate,
203        PyGateDefinition,
204        PyGateModifier,
205        PyGateSpecification,
206        PyPauliGate,
207        PyPauliTerm,
208        PyPauliSum,
209        PyJump,
210        PyJumpWhen,
211        PyJumpUnless,
212        PyLabel,
213        PyTarget,
214        PyTargetPlaceholder,
215        PyMeasurement,
216        PyMemoryReference,
217        PyQubit,
218        PyQubitPlaceholder,
219        PyReset,
220        PyDelay,
221        PyFence,
222        PyWaveform,
223        PyWaveformDefinition,
224        PyWaveformInvocation
225    ],
226    errors: [ CallError, ExternError, GateError, ParseMemoryReferenceError ],
227}
228
229/// Implements __copy__ and __deepcopy__ on any variant of the [`PyInstruction`] class, making
230/// them compatible with Python's `copy` module.
231///
232/// The `__copy__` method returns a reference to the instruction, making it shallow: any changes
233/// to the copy will update the original.
234///
235/// The `__deepcopy__` method creates a deep copy by cloning the inner instruction, querying its
236/// qubits, and replacing any [`quil_rs::instruction::QubitPlaceholder`]s with new instances so
237/// that resolving them in one copy doesn't affect the other. Duplicates of the same instruction in
238/// the original instruction will be replaced with the same copy in the new instruction.
239#[macro_export]
240macro_rules! impl_copy_for_instruction {
241    ($py_name: ident) => {
242        #[pyo3::pymethods]
243        impl $py_name {
244            pub fn __deepcopy__(
245                &self,
246                py: Python<'_>,
247                _memo: &pyo3::types::PyDict,
248            ) -> pyo3::PyResult<Self> {
249                let mut instruction = $crate::instruction::PyInstruction::new(
250                    py,
251                    pyo3::ToPyObject::to_object(&self, py).as_ref(py),
252                )?;
253
254                use quil_rs::instruction::{Qubit, QubitPlaceholder};
255                use std::collections::HashMap;
256                let mut placeholders: HashMap<QubitPlaceholder, QubitPlaceholder> = HashMap::new();
257
258                for qubit in
259                    rigetti_pyo3::PyWrapperMut::as_inner_mut(&mut instruction).get_qubits_mut()
260                {
261                    match qubit {
262                        Qubit::Fixed(_) | Qubit::Variable(_) => *qubit = qubit.clone(),
263                        Qubit::Placeholder(placeholder) => {
264                            *qubit = Qubit::Placeholder(
265                                placeholders.entry(placeholder.clone()).or_default().clone(),
266                            )
267                        }
268                    }
269                }
270
271                Ok(instruction
272                    .inner(py)
273                    .unwrap()
274                    .extract::<$py_name>(py)
275                    .expect("a copy of a type should extract to the same type"))
276            }
277
278            pub fn __copy__(&self) -> Self {
279                self.clone()
280            }
281        }
282    };
283}