Skip to content

Commit

Permalink
Add rust representation for SGates, TGates, and iSwap gate (Qiskit#12598
Browse files Browse the repository at this point in the history
)

* Add SGate, SdgGate, iSWAP, TGate, and TdgGate to standard gates in rust.
Add missing gate definitions that depended on these gates (marked as todo).

* Add fast path to circuit methods, fix sneaky bugs, unskip cy and sx tests.

* Unskip ccx test too!

* Fix black
  • Loading branch information
ElePT authored Jun 18, 2024
1 parent 6d6dce3 commit d4e795b
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 34 deletions.
24 changes: 24 additions & 0 deletions crates/circuit/src/gate_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ pub static SX_GATE: [[Complex64; 2]; 2] = [
[c64(0.5, -0.5), c64(0.5, 0.5)],
];

pub static SXDG_GATE: [[Complex64; 2]; 2] = [
[c64(0.5, -0.5), c64(0.5, 0.5)],
[c64(0.5, 0.5), c64(0.5, -0.5)],
];

pub static X_GATE: [[Complex64; 2]; 2] = [[c64(0., 0.), c64(1., 0.)], [c64(1., 0.), c64(0., 0.)]];

pub static Z_GATE: [[Complex64; 2]; 2] = [[c64(1., 0.), c64(0., 0.)], [c64(0., 0.), c64(-1., 0.)]];
Expand Down Expand Up @@ -199,6 +204,25 @@ pub static SWAP_GATE: [[Complex64; 4]; 4] = [
[c64(0., 0.), c64(1., 0.), c64(0., 0.), c64(0., 0.)],
[c64(0., 0.), c64(0., 0.), c64(0., 0.), c64(1., 0.)],
];
pub static ISWAP_GATE: [[Complex64; 4]; 4] = [
[c64(1., 0.), c64(0., 0.), c64(0., 0.), c64(0., 0.)],
[c64(0., 0.), c64(0., 0.), c64(0., 1.), c64(0., 0.)],
[c64(0., 0.), c64(0., 1.), c64(0., 0.), c64(0., 0.)],
[c64(0., 0.), c64(0., 0.), c64(0., 0.), c64(1., 0.)],
];

pub static S_GATE: [[Complex64; 2]; 2] = [[c64(1., 0.), c64(0., 0.)], [c64(0., 0.), c64(0., 1.)]];
pub static SDG_GATE: [[Complex64; 2]; 2] =
[[c64(1., 0.), c64(0., 0.)], [c64(0., 0.), c64(0., -1.)]];

pub static T_GATE: [[Complex64; 2]; 2] = [
[c64(1., 0.), c64(0., 0.)],
[c64(0., 0.), c64(FRAC_1_SQRT_2, FRAC_1_SQRT_2)],
];
pub static TDG_GATE: [[Complex64; 2]; 2] = [
[c64(1., 0.), c64(0., 0.)],
[c64(0., 0.), c64(FRAC_1_SQRT_2, -FRAC_1_SQRT_2)],
];

#[inline]
pub fn global_phase_gate(theta: f64) -> [[Complex64; 1]; 1] {
Expand Down
14 changes: 13 additions & 1 deletion crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub static SINGLETON_CONTROLLED_GATE: ImportOnceCell =
/// when a gate is added directly via the StandardGate path and there isn't a Python object
/// to poll the _standard_gate attribute for.
///
/// NOTE: the order here is significant it must match the StandardGate variant's number must match
/// NOTE: the order here is significant, the StandardGate variant's number must match
/// index of it's entry in this table. This is all done statically for performance
static STDGATE_IMPORT_PATHS: [[&str; 2]; STANDARD_GATE_SIZE] = [
// ZGate = 0
Expand Down Expand Up @@ -119,6 +119,18 @@ 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"],
// SGate = 18
["qiskit.circuit.library.standard_gates.s", "SGate"],
// SdgGate = 19
["qiskit.circuit.library.standard_gates.s", "SdgGate"],
// TGate = 20
["qiskit.circuit.library.standard_gates.s", "TGate"],
// TdgGate = 21
["qiskit.circuit.library.standard_gates.s", "TdgGate"],
// SXdgGate = 22
["qiskit.circuit.library.standard_gates.sx", "SXdgGate"],
// iSWAPGate = 23
["qiskit.circuit.library.standard_gates.iswap", "iSwapGate"],
];

/// A mapping from the enum variant in crate::operations::StandardGate to the python object for the
Expand Down
220 changes: 206 additions & 14 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ use pyo3::prelude::*;
use pyo3::{intern, IntoPy, Python};
use smallvec::smallvec;

const PI2: f64 = PI / 2.0;
const PI4: f64 = PI / 4.0;

/// Valid types for an operation field in a CircuitInstruction
///
/// These are basically the types allowed in a QuantumCircuit
Expand Down Expand Up @@ -194,13 +197,21 @@ pub enum StandardGate {
HGate = 15,
PhaseGate = 16,
UGate = 17,
SGate = 18,
SdgGate = 19,
TGate = 20,
TdgGate = 21,
SXdgGate = 22,
ISwapGate = 23,
}

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, 1, 1, 2,
];

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, 0, 0, 0, 0, 0, 0,
];

static STANDARD_GATE_NAME: [&str; STANDARD_GATE_SIZE] = [
"z",
Expand All @@ -221,6 +232,12 @@ static STANDARD_GATE_NAME: [&str; STANDARD_GATE_SIZE] = [
"h",
"p",
"u",
"s",
"sdg",
"t",
"tdg",
"sxdg",
"iswap",
];

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

impl Operation for StandardGate {
fn name(&self) -> &str {
Expand Down Expand Up @@ -350,6 +368,10 @@ impl Operation for StandardGate {
[] => Some(aview2(&gate_matrix::SX_GATE).to_owned()),
_ => None,
},
Self::SXdgGate => match params {
[] => Some(aview2(&gate_matrix::SXDG_GATE).to_owned()),
_ => None,
},
Self::GlobalPhaseGate => match params {
[Param::Float(theta)] => {
Some(aview2(&gate_matrix::global_phase_gate(*theta)).to_owned())
Expand All @@ -374,6 +396,26 @@ impl Operation for StandardGate {
}
_ => None,
},
Self::SGate => match params {
[] => Some(aview2(&gate_matrix::S_GATE).to_owned()),
_ => None,
},
Self::SdgGate => match params {
[] => Some(aview2(&gate_matrix::SDG_GATE).to_owned()),
_ => None,
},
Self::TGate => match params {
[] => Some(aview2(&gate_matrix::T_GATE).to_owned()),
_ => None,
},
Self::TdgGate => match params {
[] => Some(aview2(&gate_matrix::TDG_GATE).to_owned()),
_ => None,
},
Self::ISwapGate => match params {
[] => Some(aview2(&gate_matrix::ISWAP_GATE).to_owned()),
_ => None,
},
}
}

Expand Down Expand Up @@ -401,11 +443,7 @@ impl Operation for StandardGate {
1,
[(
Self::UGate,
smallvec![
Param::Float(PI),
Param::Float(PI / 2.),
Param::Float(PI / 2.),
],
smallvec![Param::Float(PI), Param::Float(PI2), Param::Float(PI2),],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
Expand Down Expand Up @@ -445,9 +483,56 @@ impl Operation for StandardGate {
.expect("Unexpected Qiskit python bug"),
)
}),
Self::CYGate => todo!("Add when we have S and S dagger"),
Self::CYGate => Python::with_gil(|py| -> Option<CircuitData> {
let q1 = smallvec![Qubit(1)];
let q0_1 = smallvec![Qubit(0), Qubit(1)];
Some(
CircuitData::from_standard_gates(
py,
2,
[
(Self::SdgGate, smallvec![], q1.clone()),
(Self::CXGate, smallvec![], q0_1),
(Self::SGate, smallvec![], q1),
],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::CXGate => None,
Self::CCXGate => todo!("Add when we have T and TDagger"),
Self::CCXGate => Python::with_gil(|py| -> Option<CircuitData> {
let q1 = smallvec![Qubit(1)];
let q2 = smallvec![Qubit(2)];
let q0_1 = smallvec![Qubit(0), Qubit(1)];
let q0_2 = smallvec![Qubit(0), Qubit(2)];
let q1_2 = smallvec![Qubit(1), Qubit(2)];
Some(
CircuitData::from_standard_gates(
py,
3,
[
(Self::HGate, smallvec![], q2.clone()),
(Self::CXGate, smallvec![], q1_2.clone()),
(Self::TdgGate, smallvec![], q2.clone()),
(Self::CXGate, smallvec![], q0_2.clone()),
(Self::TGate, smallvec![], q2.clone()),
(Self::CXGate, smallvec![], q1_2),
(Self::TdgGate, smallvec![], q2.clone()),
(Self::CXGate, smallvec![], q0_2),
(Self::TGate, smallvec![], q1.clone()),
(Self::TGate, smallvec![], q2.clone()),
(Self::HGate, smallvec![], q2),
(Self::CXGate, smallvec![], q0_1.clone()),
(Self::TGate, smallvec![], smallvec![Qubit(0)]),
(Self::TdgGate, smallvec![], q1),
(Self::CXGate, smallvec![], q0_1),
],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::RXGate => todo!("Add when we have R"),
Self::RYGate => todo!("Add when we have R"),
Self::RZGate => Python::with_gil(|py| -> Option<CircuitData> {
Expand Down Expand Up @@ -501,7 +586,36 @@ impl Operation for StandardGate {
.expect("Unexpected Qiskit python bug"),
)
}),
Self::SXGate => todo!("Add when we have S dagger"),
Self::SXGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[
(Self::SdgGate, smallvec![], smallvec![Qubit(0)]),
(Self::HGate, smallvec![], smallvec![Qubit(0)]),
(Self::SdgGate, smallvec![], smallvec![Qubit(0)]),
],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::SXdgGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[
(Self::SGate, smallvec![], smallvec![Qubit(0)]),
(Self::HGate, smallvec![], smallvec![Qubit(0)]),
(Self::SGate, smallvec![], smallvec![Qubit(0)]),
],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::GlobalPhaseGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(py, 0, [], params[0].clone())
Expand All @@ -516,7 +630,7 @@ impl Operation for StandardGate {
1,
[(
Self::UGate,
smallvec![Param::Float(PI / 2.), Param::Float(0.), Param::Float(PI)],
smallvec![Param::Float(PI2), Param::Float(0.), Param::Float(PI)],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
Expand All @@ -540,6 +654,84 @@ impl Operation for StandardGate {
)
}),
Self::UGate => None,
Self::SGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::PhaseGate,
smallvec![Param::Float(PI2)],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::SdgGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::PhaseGate,
smallvec![Param::Float(-PI2)],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::TGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::PhaseGate,
smallvec![Param::Float(PI4)],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::TdgGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::PhaseGate,
smallvec![Param::Float(-PI4)],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::ISwapGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
2,
[
(Self::SGate, smallvec![], smallvec![Qubit(0)]),
(Self::SGate, smallvec![], smallvec![Qubit(1)]),
(Self::HGate, smallvec![], smallvec![Qubit(0)]),
(Self::CXGate, smallvec![], smallvec![Qubit(0), Qubit(1)]),
(Self::CXGate, smallvec![], smallvec![Qubit(1), Qubit(0)]),
(Self::HGate, smallvec![], smallvec![Qubit(1)]),
],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
}
}

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

from .xx_plus_yy import XXPlusYYGate

Expand Down Expand Up @@ -85,6 +86,8 @@ class iSwapGate(SingletonGate):
\end{pmatrix}
"""

_standard_gate = StandardGate.ISwapGate

def __init__(self, label: Optional[str] = None, *, duration=None, unit="dt"):
"""Create new iSwap gate."""
super().__init__("iswap", 2, [], label=label, duration=duration, unit=unit)
Expand Down
Loading

0 comments on commit d4e795b

Please sign in to comment.