diff --git a/crates/circuit/src/operations.rs b/crates/circuit/src/operations.rs index a73d81ad7180..199ecb16b8b8 100644 --- a/crates/circuit/src/operations.rs +++ b/crates/circuit/src/operations.rs @@ -22,7 +22,7 @@ use crate::{gate_matrix, Qubit}; use ndarray::{aview2, Array2}; use num_complex::Complex64; -use smallvec::smallvec; +use smallvec::{smallvec, SmallVec}; use numpy::IntoPyArray; use numpy::PyReadonlyArray2; @@ -470,6 +470,198 @@ impl StandardGate { pub fn num_ctrl_qubits(&self) -> u32 { STANDARD_GATE_NUM_CTRL_QUBITS[*self as usize] } + + pub fn inverse(&self, params: &[Param]) -> Option<(StandardGate, SmallVec<[Param; 3]>)> { + match self { + Self::GlobalPhaseGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::GlobalPhaseGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::HGate => Some((Self::HGate, smallvec![])), + Self::IGate => Some((Self::IGate, smallvec![])), + Self::XGate => Some((Self::XGate, smallvec![])), + Self::YGate => Some((Self::YGate, smallvec![])), + Self::ZGate => Some((Self::ZGate, smallvec![])), + Self::PhaseGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::PhaseGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::RGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RGate, + smallvec![multiply_param(¶ms[0], -1.0, py), params[1].clone()], + ) + })), + Self::RXGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RXGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::RYGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RYGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::RZGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RZGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::SGate => Some((Self::SdgGate, smallvec![])), + Self::SdgGate => Some((Self::SGate, smallvec![])), + Self::SXGate => Some((Self::SXdgGate, smallvec![])), + Self::SXdgGate => Some((Self::SXGate, smallvec![])), + Self::TGate => Some((Self::TdgGate, smallvec![])), + Self::TdgGate => Some((Self::TGate, smallvec![])), + Self::UGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::UGate, + smallvec![ + multiply_param(¶ms[0], -1.0, py), + multiply_param(¶ms[2], -1.0, py), + multiply_param(¶ms[1], -1.0, py), + ], + ) + })), + Self::U1Gate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::U1Gate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::U2Gate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::U2Gate, + smallvec![ + add_param(&multiply_param(¶ms[1], -1.0, py), -PI, py), + add_param(&multiply_param(¶ms[0], -1.0, py), PI, py), + ], + ) + })), + Self::U3Gate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::U3Gate, + smallvec![ + multiply_param(¶ms[0], -1.0, py), + multiply_param(¶ms[2], -1.0, py), + multiply_param(¶ms[1], -1.0, py), + ], + ) + })), + Self::CHGate => Some((Self::CHGate, smallvec![])), + Self::CXGate => Some((Self::CXGate, smallvec![])), + Self::CYGate => Some((Self::CYGate, smallvec![])), + Self::CZGate => Some((Self::CZGate, smallvec![])), + Self::DCXGate => None, // the inverse in not a StandardGate + Self::ECRGate => Some((Self::ECRGate, smallvec![])), + Self::SwapGate => Some((Self::SwapGate, smallvec![])), + Self::ISwapGate => None, // the inverse in not a StandardGate + Self::CPhaseGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::CPhaseGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::CRXGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::CRXGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::CRYGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::CRYGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::CRZGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::CRZGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::CSGate => Some((Self::CSdgGate, smallvec![])), + Self::CSdgGate => Some((Self::CSGate, smallvec![])), + Self::CSXGate => None, // the inverse in not a StandardGate + Self::CUGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::CUGate, + smallvec![ + multiply_param(¶ms[0], -1.0, py), + multiply_param(¶ms[2], -1.0, py), + multiply_param(¶ms[1], -1.0, py), + multiply_param(¶ms[3], -1.0, py), + ], + ) + })), + Self::CU1Gate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::CU1Gate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::CU3Gate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::CU3Gate, + smallvec![ + multiply_param(¶ms[0], -1.0, py), + multiply_param(¶ms[2], -1.0, py), + multiply_param(¶ms[1], -1.0, py), + ], + ) + })), + Self::RXXGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RXXGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::RYYGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RYYGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::RZZGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RZZGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::RZXGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::RZXGate, + smallvec![multiply_param(¶ms[0], -1.0, py)], + ) + })), + Self::XXMinusYYGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::XXMinusYYGate, + smallvec![multiply_param(¶ms[0], -1.0, py), params[1].clone()], + ) + })), + Self::XXPlusYYGate => Some(Python::with_gil(|py| -> (Self, SmallVec<[Param; 3]>) { + ( + Self::XXPlusYYGate, + smallvec![multiply_param(¶ms[0], -1.0, py), params[1].clone()], + ) + })), + Self::CCXGate => Some((Self::CCXGate, smallvec![])), + Self::CCZGate => Some((Self::CCZGate, smallvec![])), + Self::CSwapGate => Some((Self::CSwapGate, smallvec![])), + Self::RCCXGate => None, // the inverse in not a StandardGate + Self::C3XGate => Some((Self::C3XGate, smallvec![])), + Self::C3SXGate => None, // the inverse in not a StandardGate + Self::RC3XGate => None, // the inverse in not a StandardGate + } + } } #[pymethods] @@ -492,6 +684,10 @@ impl StandardGate { self.definition(¶ms) } + pub fn _inverse(&self, params: Vec) -> Option<(StandardGate, SmallVec<[Param; 3]>)> { + self.inverse(¶ms) + } + #[getter] pub fn get_num_qubits(&self) -> u32 { self.num_qubits() diff --git a/test/python/circuit/test_rust_equivalence.py b/test/python/circuit/test_rust_equivalence.py index db4f1441bea3..8e17efcfb098 100644 --- a/test/python/circuit/test_rust_equivalence.py +++ b/test/python/circuit/test_rust_equivalence.py @@ -146,6 +146,22 @@ def test_matrix(self): rs_def = standard_gate._to_matrix(params) np.testing.assert_allclose(rs_def, py_def) + def test_inverse(self): + """Test that the inverse is the same in rust space.""" + for name, gate_class in self.standard_gates.items(): + standard_gate = getattr(gate_class, "_standard_gate", None) + if standard_gate is None: + # gate is not in rust yet + continue + + with self.subTest(name=name): + params = [0.1 * (i + 1) for i in range(standard_gate._num_params())] + py_def = gate_class.base_class(*params).inverse() + rs_def = standard_gate._inverse(params) + if rs_def is not None: + self.assertEqual(py_def.name, rs_def[0].name) + np.testing.assert_allclose(py_def.params, rs_def[1]) + def test_name(self): """Test that the gate name properties match in rust space.""" for name, gate_class in self.standard_gates.items():