Skip to content

Commit

Permalink
Add rust representation for the u1, u2, and u3 gates
Browse files Browse the repository at this point in the history
This commit adds the rust representation of the U1Gate, U2Gate, and
U3Gate to the `StandardGates` enum in rust.

Part of Qiskit#12566
  • Loading branch information
mtreinish committed Jun 13, 2024
1 parent f304a4b commit afad78d
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 11 deletions.
38 changes: 38 additions & 0 deletions crates/circuit/src/gate_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,41 @@ pub fn u_gate(theta: f64, phi: f64, lam: f64) -> [[Complex64; 2]; 2] {
[c64(0., phi).exp() * sin, c64(0., phi + lam).exp() * cos],
]
}

#[inline]
pub fn u1_gate(lam: f64) -> [[Complex64; 2]; 2] {
[
[Complex64::new(1., 0.), Complex64::new(0., 0.)],
[Complex64::new(0., 0.), Complex64::new(0., lam).exp()],
]
}

#[inline]
pub fn u2_gate(phi: f64, lam: f64) -> [[Complex64; 2]; 2] {
[
[
Complex64::new(FRAC_1_SQRT_2, 0.),
(-Complex64::new(0., lam).exp()) * FRAC_1_SQRT_2,
],
[
Complex64::new(0., phi).exp() * FRAC_1_SQRT_2,
Complex64::new(0., phi + lam).exp() * FRAC_1_SQRT_2,
],
]
}

#[inline]
pub fn u3_gate(theta: f64, phi: f64, lam: f64) -> [[Complex64; 2]; 2] {
let cos = (theta / 2.).cos();
let sin = (theta / 2.).sin();
[
[
Complex64::new(cos, 0.),
(-Complex64::new(0., lam).exp()) * sin,
],
[
Complex64::new(0., phi).exp() * sin,
Complex64::new(0., phi + lam).exp() * cos,
],
]
}
6 changes: 6 additions & 0 deletions crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ 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"],
// U1Gate = 18
["qiskit.circuit.library.standard_gates.u1", "U1Gate"],
// U1Gate = 19
["qiskit.circuit.library.standard_gates.u2", "U2Gate"],
// U1Gate = 20
["qiskit.circuit.library.standard_gates.u3", "U3Gate"],
];

/// A mapping from the enum variant in crate::operations::StandardGate to the python object for the
Expand Down
80 changes: 75 additions & 5 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,18 @@ pub enum StandardGate {
HGate = 15,
PhaseGate = 16,
UGate = 17,
U1Gate = 18,
U2Gate = 19,
U3Gate = 20,
}

static STANDARD_GATE_NUM_QUBITS: [u32; STANDARD_GATE_SIZE] =
[1, 1, 1, 2, 2, 2, 3, 1, 1, 1, 2, 2, 1, 0, 1, 1, 1, 1];
static STANDARD_GATE_NUM_QUBITS: [u32; STANDARD_GATE_SIZE] = [
1, 1, 1, 2, 2, 2, 3, 1, 1, 1, 2, 2, 1, 0, 1, 1, 1, 1, 1, 1, 1,
];

static STANDARD_GATE_NUM_PARAMS: [u32; STANDARD_GATE_SIZE] =
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 3];
static STANDARD_GATE_NUM_PARAMS: [u32; STANDARD_GATE_SIZE] = [
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 3, 1, 2, 3,
];

static STANDARD_GATE_NAME: [&str; STANDARD_GATE_SIZE] = [
"z",
Expand All @@ -221,6 +226,9 @@ static STANDARD_GATE_NAME: [&str; STANDARD_GATE_SIZE] = [
"h",
"p",
"u",
"u1",
"u2",
"u3",
];

#[pymethods]
Expand Down Expand Up @@ -269,7 +277,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 = 21;

impl Operation for StandardGate {
fn name(&self) -> &str {
Expand Down Expand Up @@ -374,6 +382,23 @@ impl Operation for StandardGate {
}
_ => None,
},
Self::U1Gate => match params[0] {
Param::Float(val) => Some(aview2(&gate_matrix::u1_gate(val)).to_owned()),
Param::ParameterExpression(_) => None,
Param::Obj(_) => None,
},
Self::U2Gate => match params {
[Param::Float(phi), Param::Float(lam)] => {
Some(aview2(&gate_matrix::u2_gate(*phi, *lam)).to_owned())
}
_ => None,
},
Self::U3Gate => match params {
[Param::Float(theta), Param::Float(phi), Param::Float(lam)] => {
Some(aview2(&gate_matrix::u3_gate(*theta, *phi, *lam)).to_owned())
}
_ => None,
},
}
}

Expand Down Expand Up @@ -540,6 +565,51 @@ impl Operation for StandardGate {
)
}),
Self::UGate => None,
Self::U1Gate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::PhaseGate,
params.iter().cloned().collect(),
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::U2Gate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::UGate,
smallvec![Param::Float(PI / 2.), params[0].clone(), params[1].clone()],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::U3Gate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::PhaseGate,
params.iter().cloned().collect(),
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
}
}

Expand Down
3 changes: 3 additions & 0 deletions qiskit/circuit/library/standard_gates/u1.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from qiskit.circuit.parameterexpression import ParameterValueType
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit._utils import _ctrl_state_to_int
from qiskit._accelerate.circuit import StandardGate


class U1Gate(Gate):
Expand Down Expand Up @@ -92,6 +93,8 @@ class U1Gate(Gate):
`1612.00858 <https://arxiv.org/abs/1612.00858>`_
"""

_standard_gate = StandardGate.U1Gate

def __init__(
self, theta: ParameterValueType, label: str | None = None, *, duration=None, unit="dt"
):
Expand Down
3 changes: 3 additions & 0 deletions qiskit/circuit/library/standard_gates/u2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from qiskit.circuit.gate import Gate
from qiskit.circuit.parameterexpression import ParameterValueType
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit._accelerate.circuit import StandardGate


class U2Gate(Gate):
Expand Down Expand Up @@ -86,6 +87,8 @@ class U2Gate(Gate):
using two X90 pulses.
"""

_standard_gate = StandardGate.U2Gate

def __init__(
self,
phi: ParameterValueType,
Expand Down
3 changes: 3 additions & 0 deletions qiskit/circuit/library/standard_gates/u3.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from qiskit.circuit.gate import Gate
from qiskit.circuit.parameterexpression import ParameterValueType
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit._accelerate.circuit import StandardGate


class U3Gate(Gate):
Expand Down Expand Up @@ -80,6 +81,8 @@ class U3Gate(Gate):
U3(\theta, 0, 0) = RY(\theta)
"""

_standard_gate = StandardGate.U3Gate

def __init__(
self,
theta: ParameterValueType,
Expand Down
23 changes: 17 additions & 6 deletions test/python/circuit/test_rust_equivalence.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,23 @@ def test_definitions(self):
)
# Rust uses P but python still uses u1
elif rs_inst.operation.name == "p":
self.assertEqual(py_inst.operation.name, "u1")
self.assertEqual(rs_inst.operation.params, py_inst.operation.params)
self.assertEqual(
[py_def.find_bit(x).index for x in py_inst.qubits],
[rs_def.find_bit(x).index for x in rs_inst.qubits],
)
if py_inst.operation.name == "u1":
self.assertEqual(py_inst.operation.name, "u1")
self.assertEqual(rs_inst.operation.params, py_inst.operation.params)
self.assertEqual(
[py_def.find_bit(x).index for x in py_inst.qubits],
[rs_def.find_bit(x).index for x in rs_inst.qubits],
)
else:
self.assertEqual(py_inst.operation.name, "u3")
self.assertEqual(
rs_inst.operation.params[0], py_inst.operation.params[2]
)
self.assertEqual(
[py_def.find_bit(x).index for x in py_inst.qubits],
[rs_def.find_bit(x).index for x in rs_inst.qubits],
)

else:
self.assertEqual(py_inst.operation.name, rs_inst.operation.name)
self.assertEqual(rs_inst.operation.params, py_inst.operation.params)
Expand Down

0 comments on commit afad78d

Please sign in to comment.