From 5f6453f8de4ebf8d1bdafaf809fb6ad33136ef03 Mon Sep 17 00:00:00 2001 From: John Lapeyre Date: Mon, 3 Jun 2024 16:42:08 -0400 Subject: [PATCH] Implement RGate in Rust according to established pattern All the things are done to implement R(theta, phi) in Rust to the according to the pattern set in the gates-in-rust PR. The "definition" is left as a todo again following the same pattern. Implementing this is rather clumsy and laborious. Doing the further gates would of course be a bit easier. Some of this is due to interfaces beyond our control. We could probably add a few conveniences that would make a difference. For example implementing some conveniences for complex numbers. --- crates/circuit/src/gate_matrix.rs | 13 ++++++++ crates/circuit/src/imports.rs | 6 ++-- crates/circuit/src/operations.rs | 31 +++++++++++++++++++- qiskit/circuit/library/standard_gates/r.py | 8 +++-- test/python/circuit/test_rust_equivalence.py | 2 +- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/crates/circuit/src/gate_matrix.rs b/crates/circuit/src/gate_matrix.rs index d53c0269b9ff..2ad6d720b4f0 100644 --- a/crates/circuit/src/gate_matrix.rs +++ b/crates/circuit/src/gate_matrix.rs @@ -43,6 +43,19 @@ pub fn rz_gate(theta: f64) -> [[Complex64; 2]; 2] { ] } +#[inline] +pub fn r_gate(theta: f64, phi: f64) -> [[Complex64; 2]; 2] { + let half_theta = theta / 2.; + let cost = Complex64::new(half_theta.cos(), 0.); + let sint = half_theta.sin(); + let cosphi = phi.cos(); + let sinphi = phi.sin(); + [ + [cost, Complex64::new(sint * sinphi, -sint * cosphi)], + [Complex64::new(-sint * sinphi, -sint * cosphi), cost], + ] +} + pub static HGATE: [[Complex64; 2]; 2] = [ [ Complex64::new(FRAC_1_SQRT_2, 0.), diff --git a/crates/circuit/src/imports.rs b/crates/circuit/src/imports.rs index e7aa6c2972fb..2bbc51e23d98 100644 --- a/crates/circuit/src/imports.rs +++ b/crates/circuit/src/imports.rs @@ -87,6 +87,8 @@ static STDGATE_IMPORT_PATHS: [[&str; 2]; STANDARD_GATE_SIZE] = [ ["qiskit.circuit.library.standard_gates.p", "PhaseGate"], // UGate = 17 ["qiskit.circuit.library.standard_gates.u", "UGate"], + // RGate = 18 + ["qiskit.circuit.library.standard_gates.r", "RGate"], ]; /// A mapping from the enum variant in crate::operations::StandardGate to the python object for the @@ -109,7 +111,7 @@ pub fn populate_std_gate_map(py: Python, rs_gate: StandardGate, py_gate: PyObjec // as T isn't Copy. To avoid that we just list out None STANDARD_GATE_SIZE times let array: [Option; STANDARD_GATE_SIZE] = [ None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, None, + None, None, None, None, None, None, ]; STDGATE_PYTHON_GATES.set(py, array).unwrap(); STDGATE_PYTHON_GATES.get_mut().unwrap() @@ -131,7 +133,7 @@ pub fn get_std_gate_class(py: Python, rs_gate: StandardGate) -> PyResult; STANDARD_GATE_SIZE] = [ None, None, None, None, None, None, None, None, None, None, None, None, None, None, - None, None, None, None, + None, None, None, None, None, ]; array }) diff --git a/crates/circuit/src/operations.rs b/crates/circuit/src/operations.rs index a74da2783945..e19792f155f1 100644 --- a/crates/circuit/src/operations.rs +++ b/crates/circuit/src/operations.rs @@ -207,6 +207,7 @@ pub enum StandardGate { HGate = 15, PhaseGate = 16, UGate = 17, + RGate = 18, } #[pymethods] @@ -249,7 +250,7 @@ impl StandardGate { // // Remove this when std::mem::variant_count() is stabilized (see // https://github.com/rust-lang/rust/issues/73662 ) -pub const STANDARD_GATE_SIZE: usize = 18; +pub const STANDARD_GATE_SIZE: usize = 19; impl Operation for StandardGate { fn name(&self) -> &str { @@ -272,6 +273,7 @@ impl Operation for StandardGate { Self::HGate => "h", Self::PhaseGate => "p", Self::UGate => "u", + Self::RGate => "r", } } @@ -295,6 +297,7 @@ impl Operation for StandardGate { Self::HGate => 1, Self::PhaseGate => 1, Self::UGate => 1, + Self::RGate => 1, } } @@ -318,6 +321,7 @@ impl Operation for StandardGate { Self::HGate => 0, Self::PhaseGate => 1, Self::UGate => 3, + Self::RGate => 2, } } @@ -420,6 +424,30 @@ impl Operation for StandardGate { ) } } + Self::RGate => { + let params = params.unwrap(); + let theta: Option = match params[0] { + Param::Float(val) => Some(val), + Param::ParameterExpression(_) => None, + Param::Obj(_) => None, + }; + let phi: Option = match params[1] { + Param::Float(val) => Some(val), + Param::ParameterExpression(_) => None, + Param::Obj(_) => None, + }; + if theta.is_none() || phi.is_none() { + None + } else { + Some( + aview2(&gate_matrix::r_gate( + theta.unwrap(), + phi.unwrap(), + )) + .to_owned(), + ) + } + } } } @@ -602,6 +630,7 @@ impl Operation for StandardGate { ) }), Self::UGate => None, + Self::RGate => todo!("Add when we have U3"), } } diff --git a/qiskit/circuit/library/standard_gates/r.py b/qiskit/circuit/library/standard_gates/r.py index 9d4905e27866..31c570bceffd 100644 --- a/qiskit/circuit/library/standard_gates/r.py +++ b/qiskit/circuit/library/standard_gates/r.py @@ -20,7 +20,7 @@ from qiskit.circuit.gate import Gate from qiskit.circuit.quantumregister import QuantumRegister from qiskit.circuit.parameterexpression import ParameterValueType - +from qiskit._accelerate.circuit import StandardGate class RGate(Gate): r"""Rotation θ around the cos(φ)x + sin(φ)y axis. @@ -49,6 +49,8 @@ class RGate(Gate): \end{pmatrix} """ + _standard_gate = StandardGate.RGate + def __init__( self, theta: ParameterValueType, @@ -57,9 +59,11 @@ def __init__( *, duration=None, unit="dt", + _skip_validation=False, ): """Create new r single-qubit gate.""" - super().__init__("r", 1, [theta, phi], label=label, duration=duration, unit=unit) + super().__init__("r", 1, [theta, phi], label=label, duration=duration, unit=unit, + _skip_validation=_skip_validation,) def _define(self): """ diff --git a/test/python/circuit/test_rust_equivalence.py b/test/python/circuit/test_rust_equivalence.py index b657ed327fa3..16cecd342b15 100644 --- a/test/python/circuit/test_rust_equivalence.py +++ b/test/python/circuit/test_rust_equivalence.py @@ -21,7 +21,7 @@ from qiskit.circuit import QuantumCircuit from qiskit.circuit.library.standard_gates import get_standard_gate_name_mapping -SKIP_LIST = {"cy", "ccx", "rx", "ry", "ecr", "sx"} +SKIP_LIST = {"cy", "ccx", "rx", "ry", "ecr", "sx", "r"} CUSTOM_MAPPING = {"x", "rz"}