Skip to main content

quil/instruction/
gate.rs

1use numpy::{PyArray2, ToPyArray};
2use quil_rs::{
3    expression::Expression,
4    instruction::{
5        Gate, GateDefinition, GateModifier, GateSpecification, PauliGate, PauliSum, PauliTerm,
6        Qubit,
7    },
8};
9use rigetti_pyo3::{
10    impl_from_str, impl_hash, impl_parse, impl_repr, impl_str,
11    num_complex::Complex64,
12    py_wrap_data_struct, py_wrap_error, py_wrap_simple_enum, py_wrap_type, py_wrap_union_enum,
13    pyo3::{
14        exceptions::PyValueError,
15        pymethods,
16        types::{PyInt, PyString},
17        Py, PyErr, PyResult, Python,
18    },
19    wrap_error, PyTryFrom, PyWrapper, PyWrapperMut, ToPython, ToPythonError,
20};
21use strum;
22
23use crate::{
24    expression::PyExpression, impl_copy_for_instruction, impl_eq, impl_pickle_for_instruction,
25    impl_to_quil, instruction::PyQubit,
26};
27
28wrap_error!(RustGateError(quil_rs::instruction::GateError));
29py_wrap_error!(quil, RustGateError, GateError, PyValueError);
30wrap_error!(RustParseEnumError(strum::ParseError));
31py_wrap_error!(quil, RustParseEnumError, EnumParseError, PyValueError);
32
33py_wrap_data_struct! {
34    #[derive(Debug, PartialEq, Eq)]
35    #[pyo3(subclass, module = "quil.instructions")]
36    PyGate(Gate) as "Gate" {
37        name: String => Py<PyString>,
38        parameters: Vec<Expression> => Vec<PyExpression>,
39        qubits: Vec<Qubit> => Vec<PyQubit>,
40        modifiers: Vec<GateModifier> => Vec<PyGateModifier>
41    }
42}
43impl_repr!(PyGate);
44impl_copy_for_instruction!(PyGate);
45impl_to_quil!(PyGate);
46impl_hash!(PyGate);
47impl_eq!(PyGate);
48impl_pickle_for_instruction!(PyGate);
49
50#[pymethods]
51impl PyGate {
52    #[new]
53    fn new(
54        py: Python<'_>,
55        name: String,
56        parameters: Vec<PyExpression>,
57        qubits: Vec<PyQubit>,
58        modifiers: Vec<PyGateModifier>,
59    ) -> PyResult<Self> {
60        Ok(Self(
61            Gate::new(
62                &name,
63                Vec::<Expression>::py_try_from(py, &parameters)?,
64                Vec::<Qubit>::py_try_from(py, &qubits)?,
65                Vec::<GateModifier>::py_try_from(py, &modifiers)?,
66            )
67            .map_err(RustGateError::from)
68            .map_err(RustGateError::to_py_err)?,
69        ))
70    }
71
72    fn dagger(&self, py: Python<'_>) -> PyResult<Self> {
73        self.as_inner().clone().dagger().to_python(py)
74    }
75
76    fn controlled(&self, py: Python<'_>, control_qubit: PyQubit) -> PyResult<Self> {
77        self.as_inner()
78            .clone()
79            .controlled(Qubit::py_try_from(py, &control_qubit)?)
80            .to_python(py)
81    }
82
83    fn forked(
84        &self,
85        py: Python<'_>,
86        fork_qubit: PyQubit,
87        alt_params: Vec<PyExpression>,
88    ) -> PyResult<Self> {
89        self.as_inner()
90            .clone()
91            .forked(
92                Qubit::py_try_from(py, &fork_qubit)?,
93                Vec::<Expression>::py_try_from(py, &alt_params)?,
94            )
95            .map_err(RustGateError::from)
96            .map_err(RustGateError::to_py_err)?
97            .to_python(py)
98    }
99
100    fn to_unitary_mut(
101        &mut self,
102        py: Python<'_>,
103        n_qubits: u64,
104    ) -> PyResult<Py<PyArray2<Complex64>>> {
105        Ok(self
106            .as_inner_mut()
107            .to_unitary(n_qubits)
108            .map_err(RustGateError::from)
109            .map_err(RustGateError::to_py_err)?
110            .to_pyarray(py)
111            .to_owned())
112    }
113}
114
115py_wrap_simple_enum! {
116    #[derive(Debug, PartialEq, Eq)]
117    PyGateModifier(GateModifier) as "GateModifier" {
118        Controlled,
119        Dagger,
120        Forked
121    }
122}
123impl_repr!(PyGateModifier);
124impl_to_quil!(PyGateModifier);
125impl_hash!(PyGateModifier);
126impl_eq!(PyGateModifier);
127
128py_wrap_simple_enum! {
129    #[derive(Debug, PartialEq, Eq)]
130    PyPauliGate(PauliGate) as "PauliGate" {
131        I,
132        X,
133        Y,
134        Z
135    }
136}
137impl_repr!(PyPauliGate);
138impl_str!(PyPauliGate);
139impl_hash!(PyPauliGate);
140impl_from_str!(PyPauliGate, RustParseEnumError);
141impl_parse!(PyPauliGate);
142
143// This is a helper type to help manage easy conversion of the inner tuple
144// with the macros. It should not be exposed directly.
145py_wrap_type! {
146    PyPauliPair((PauliGate, String))
147}
148
149impl PyPauliPair {
150    pub(crate) fn from_py_tuple(py: Python<'_>, tuple: (PyPauliGate, String)) -> PyResult<Self> {
151        Ok(Self((PauliGate::py_try_from(py, &tuple.0)?, tuple.1)))
152    }
153}
154
155py_wrap_data_struct! {
156    #[derive(Debug, PartialEq, Eq)]
157    #[pyo3(subclass)]
158    PyPauliTerm(PauliTerm) as "PauliTerm" {
159        arguments: Vec<(PauliGate, String)> => Vec<PyPauliPair>,
160        expression: Expression => PyExpression
161    }
162}
163
164#[pymethods]
165impl PyPauliTerm {
166    #[new]
167    pub fn new(
168        py: Python<'_>,
169        arguments: Vec<(PyPauliGate, String)>,
170        expression: PyExpression,
171    ) -> PyResult<Self> {
172        Ok(Self(PauliTerm::new(
173            Vec::<(PauliGate, String)>::py_try_from(
174                py,
175                &PyPauliTerm::py_pairs_from_tuples(py, arguments)?,
176            )?,
177            Expression::py_try_from(py, &expression)?,
178        )))
179    }
180
181    // Override the getters/setters generated by [`py_wrap_data_struct!`] so that they
182    // return/take tuples instead of the wrapping [`PyPauliPair`] type.
183    #[getter(arguments)]
184    fn get_arguments_as_tuple(&self, py: Python<'_>) -> PyResult<Vec<(PyPauliGate, String)>> {
185        let mut pairs: Vec<(PyPauliGate, String)> =
186            Vec::with_capacity(self.as_inner().arguments.len());
187        self.as_inner()
188            .arguments
189            .iter()
190            .try_for_each(|(gate, arg)| {
191                pairs.push((gate.to_python(py)?, arg.clone()));
192                Ok::<(), PyErr>(())
193            })?;
194        Ok(pairs)
195    }
196
197    #[setter(arguments)]
198    fn set_arguments_from_tuple(
199        &mut self,
200        py: Python<'_>,
201        arguments: Vec<(PyPauliGate, String)>,
202    ) -> PyResult<()> {
203        self.as_inner_mut().arguments = Vec::<(PauliGate, String)>::py_try_from(
204            py,
205            &PyPauliTerm::py_pairs_from_tuples(py, arguments)?,
206        )?;
207        Ok(())
208    }
209}
210
211impl PyPauliTerm {
212    pub(crate) fn py_pairs_from_tuples(
213        py: Python<'_>,
214        tuples: Vec<(PyPauliGate, String)>,
215    ) -> PyResult<Vec<PyPauliPair>> {
216        let mut pairs: Vec<PyPauliPair> = Vec::with_capacity(tuples.len());
217        tuples.into_iter().try_for_each(|tuple| {
218            pairs.push(PyPauliPair::from_py_tuple(py, tuple)?);
219            Ok::<(), PyErr>(())
220        })?;
221        Ok(pairs)
222    }
223}
224
225py_wrap_data_struct! {
226    #[derive(Debug, PartialEq, Eq)]
227    #[pyo3(subclass)]
228    PyPauliSum(PauliSum) as "PauliSum" {
229        arguments: Vec<String> => Vec<Py<PyString>>,
230        terms: Vec<PauliTerm> => Vec<PyPauliTerm>
231    }
232}
233impl_repr!(PyPauliSum);
234impl_eq!(PyPauliSum);
235
236#[pymethods]
237impl PyPauliSum {
238    #[new]
239    pub fn new(py: Python<'_>, arguments: Vec<String>, terms: Vec<PyPauliTerm>) -> PyResult<Self> {
240        Ok(Self(
241            PauliSum::new(arguments, Vec::<PauliTerm>::py_try_from(py, &terms)?)
242                .map_err(RustGateError::from)
243                .map_err(RustGateError::to_py_err)?,
244        ))
245    }
246}
247
248py_wrap_union_enum! {
249    #[derive(Debug, PartialEq, Eq)]
250    PyGateSpecification(GateSpecification) as "GateSpecification" {
251        matrix: Matrix => Vec<Vec<PyExpression>>,
252        permutation: Permutation => Vec<Py<PyInt>>,
253        pauli_sum: PauliSum => PyPauliSum
254    }
255}
256impl_repr!(PyGateSpecification);
257impl_to_quil!(PyGateSpecification);
258impl_hash!(PyGateSpecification);
259impl_eq!(PyGateSpecification);
260
261py_wrap_data_struct! {
262    #[derive(Debug, PartialEq, Eq)]
263    #[pyo3(subclass, module = "quil.instructions")]
264    PyGateDefinition(GateDefinition) as "GateDefinition" {
265        name: String => Py<PyString>,
266        parameters: Vec<String> => Vec<Py<PyString>>,
267        specification: GateSpecification => PyGateSpecification
268    }
269}
270impl_repr!(PyGateDefinition);
271impl_to_quil!(PyGateDefinition);
272impl_copy_for_instruction!(PyGateDefinition);
273impl_hash!(PyGateDefinition);
274impl_eq!(PyGateDefinition);
275impl_pickle_for_instruction!(PyGateDefinition);
276
277#[pymethods]
278impl PyGateDefinition {
279    #[new]
280    pub fn new(
281        py: Python<'_>,
282        name: String,
283        parameters: Vec<String>,
284        specification: PyGateSpecification,
285    ) -> PyResult<Self> {
286        Ok(Self(
287            GateDefinition::new(
288                name,
289                parameters,
290                GateSpecification::py_try_from(py, &specification)?,
291            )
292            .map_err(RustGateError::from)
293            .map_err(RustGateError::to_py_err)?,
294        ))
295    }
296}