Skip to content

Commit

Permalink
Implement RGate in Rust according to established pattern
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jlapeyre committed Jun 3, 2024
1 parent 2f81bde commit 5f6453f
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 6 deletions.
13 changes: 13 additions & 0 deletions crates/circuit/src/gate_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.),
Expand Down
6 changes: 4 additions & 2 deletions crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<PyObject>; 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()
Expand All @@ -131,7 +133,7 @@ pub fn get_std_gate_class(py: Python, rs_gate: StandardGate) -> PyResult<PyObjec
// as T isn't Copy. To avoid that we just list out None STANDARD_GATE_SIZE times
let array: [Option<PyObject>; 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
})
Expand Down
31 changes: 30 additions & 1 deletion crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ pub enum StandardGate {
HGate = 15,
PhaseGate = 16,
UGate = 17,
RGate = 18,
}

#[pymethods]
Expand Down Expand Up @@ -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 {
Expand All @@ -272,6 +273,7 @@ impl Operation for StandardGate {
Self::HGate => "h",
Self::PhaseGate => "p",
Self::UGate => "u",
Self::RGate => "r",
}
}

Expand All @@ -295,6 +297,7 @@ impl Operation for StandardGate {
Self::HGate => 1,
Self::PhaseGate => 1,
Self::UGate => 1,
Self::RGate => 1,
}
}

Expand All @@ -318,6 +321,7 @@ impl Operation for StandardGate {
Self::HGate => 0,
Self::PhaseGate => 1,
Self::UGate => 3,
Self::RGate => 2,
}
}

Expand Down Expand Up @@ -420,6 +424,30 @@ impl Operation for StandardGate {
)
}
}
Self::RGate => {
let params = params.unwrap();
let theta: Option<f64> = match params[0] {
Param::Float(val) => Some(val),
Param::ParameterExpression(_) => None,
Param::Obj(_) => None,
};
let phi: Option<f64> = 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(),
)
}
}
}
}

Expand Down Expand Up @@ -602,6 +630,7 @@ impl Operation for StandardGate {
)
}),
Self::UGate => None,
Self::RGate => todo!("Add when we have U3"),
}
}

Expand Down
8 changes: 6 additions & 2 deletions qiskit/circuit/library/standard_gates/r.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -49,6 +49,8 @@ class RGate(Gate):
\end{pmatrix}
"""

_standard_gate = StandardGate.RGate

def __init__(
self,
theta: ParameterValueType,
Expand All @@ -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):
"""
Expand Down
2 changes: 1 addition & 1 deletion test/python/circuit/test_rust_equivalence.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"}


Expand Down

0 comments on commit 5f6453f

Please sign in to comment.