Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use rust gates for Optimize1QGatesDecomposition #12650

Merged
merged 21 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4ac7782
Use rust gates for Optimize1QGatesDecomposition
mtreinish May 28, 2024
43f9dd2
Cache target decompositions for each qubit
mtreinish Jun 24, 2024
ac38a21
Optimize rust implementation slightly
mtreinish Jun 25, 2024
cab69de
Avoid extra allocations by inlining matrix multiplication
mtreinish Jun 25, 2024
411c300
Merge branch 'main' into optimize-1q-more-rust
mtreinish Jun 25, 2024
1dd4ceb
Remove unnecessary comment
mtreinish Jun 25, 2024
515c3ff
Remove stray code block
mtreinish Jun 25, 2024
130969a
Add import path for rust gate
mtreinish Jun 25, 2024
c325ffe
Use rust gate in circuit constructor
mtreinish Jun 26, 2024
e40a69f
Merge remote-tracking branch 'origin/main' into optimize-1q-more-rust
mtreinish Jun 27, 2024
09be2b6
Remove duplicated op_name getter and just use existing name getter
mtreinish Jun 27, 2024
8db2c4e
Merge remote-tracking branch 'origin/main' into optimize-1q-more-rust
mtreinish Jun 27, 2024
ec20ccd
Merge remote-tracking branch 'origin/main' into optimize-1q-more-rust
mtreinish Jun 27, 2024
c37e0b3
Merge remote-tracking branch 'origin/main' into optimize-1q-more-rust
mtreinish Jul 1, 2024
60d21df
Apply suggestions from code review
mtreinish Jul 1, 2024
0e5d1ce
Merge remote-tracking branch 'origin/main' into optimize-1q-more-rust
mtreinish Jul 2, 2024
68af440
Simplify construction of target_basis_vec
jlapeyre Jul 2, 2024
c744f6e
Fix rebase issue
mtreinish Jul 2, 2024
c1eb501
Update crates/accelerate/src/euler_one_qubit_decomposer.rs
mtreinish Jul 2, 2024
6b3b3c1
Merge branch 'main' into optimize-1q-more-rust
mtreinish Jul 2, 2024
1b65d73
Update crates/accelerate/src/euler_one_qubit_decomposer.rs
jlapeyre Jul 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
297 changes: 261 additions & 36 deletions crates/accelerate/src/euler_one_qubit_decomposer.rs

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions crates/accelerate/src/two_qubit_decompose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use rand_distr::StandardNormal;
use rand_pcg::Pcg64Mcg;

use qiskit_circuit::gate_matrix::{CX_GATE, H_GATE, ONE_QUBIT_IDENTITY, SX_GATE, X_GATE};
use qiskit_circuit::operations::Operation;
use qiskit_circuit::SliceOrInt;

const PI2: f64 = PI / 2.0;
Expand Down Expand Up @@ -1097,7 +1098,7 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c2r.gates {
gate_sequence.push((gate.0, gate.1, smallvec![0]))
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![0]))
}
global_phase += c2r.global_phase;
let c2l = unitary_to_gate_sequence_inner(
Expand All @@ -1110,7 +1111,7 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c2l.gates {
gate_sequence.push((gate.0, gate.1, smallvec![1]))
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![1]))
}
global_phase += c2l.global_phase;
self.weyl_gate(
Expand All @@ -1129,7 +1130,7 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c1r.gates {
gate_sequence.push((gate.0, gate.1, smallvec![0]))
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![0]))
}
global_phase += c2r.global_phase;
let c1l = unitary_to_gate_sequence_inner(
Expand All @@ -1142,7 +1143,7 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c1l.gates {
gate_sequence.push((gate.0, gate.1, smallvec![1]))
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![1]))
}
Ok(TwoQubitGateSequence {
gates: gate_sequence,
Expand Down Expand Up @@ -1542,7 +1543,7 @@ impl TwoQubitBasisDecomposer {
if let Some(sequence) = sequence {
*global_phase += sequence.global_phase;
for gate in sequence.gates {
gates.push((gate.0, gate.1, smallvec![qubit]));
gates.push((gate.0.name().to_string(), gate.1, smallvec![qubit]));
}
}
}
Expand Down Expand Up @@ -1955,27 +1956,27 @@ impl TwoQubitBasisDecomposer {
for i in 0..best_nbasis as usize {
if let Some(euler_decomp) = &euler_decompositions[2 * i] {
for gate in &euler_decomp.gates {
gates.push((gate.0.clone(), gate.1.clone(), smallvec![0]));
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![0]));
}
global_phase += euler_decomp.global_phase
}
if let Some(euler_decomp) = &euler_decompositions[2 * i + 1] {
for gate in &euler_decomp.gates {
gates.push((gate.0.clone(), gate.1.clone(), smallvec![1]));
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![1]));
}
global_phase += euler_decomp.global_phase
}
gates.push((self.gate.clone(), smallvec![], smallvec![0, 1]));
}
if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize] {
for gate in &euler_decomp.gates {
gates.push((gate.0.clone(), gate.1.clone(), smallvec![0]));
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![0]));
}
global_phase += euler_decomp.global_phase
}
if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize + 1] {
for gate in &euler_decomp.gates {
gates.push((gate.0.clone(), gate.1.clone(), smallvec![1]));
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![1]));
}
global_phase += euler_decomp.global_phase
}
Expand Down
51 changes: 50 additions & 1 deletion crates/circuit/src/dag_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::circuit_instruction::{
use crate::operations::Operation;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList, PySequence, PyString, PyTuple};
use pyo3::{intern, PyObject, PyResult};
use pyo3::{intern, PyObject, PyResult, ToPyObject};

/// Parent class for DAGOpNode, DAGInNode, and DAGOutNode.
#[pyclass(module = "qiskit._accelerate.circuit", subclass)]
Expand Down Expand Up @@ -144,6 +144,50 @@ impl DAGOpNode {
))
}

#[staticmethod]
fn from_instruction(
py: Python,
instruction: CircuitInstruction,
dag: Option<&Bound<PyAny>>,
) -> PyResult<PyObject> {
let qargs = instruction.qubits.clone_ref(py).into_bound(py);
let cargs = instruction.clbits.clone_ref(py).into_bound(py);

let sort_key = match dag {
Some(dag) => {
let cache = dag
.getattr(intern!(py, "_key_cache"))?
.downcast_into_exact::<PyDict>()?;
let cache_key = PyTuple::new_bound(py, [&qargs, &cargs]);
match cache.get_item(&cache_key)? {
Some(key) => key,
None => {
let indices: PyResult<Vec<_>> = qargs
.iter()
.chain(cargs.iter())
.map(|bit| {
dag.call_method1(intern!(py, "find_bit"), (bit,))?
.getattr(intern!(py, "index"))
})
.collect();
let index_strs: Vec<_> =
indices?.into_iter().map(|i| format!("{:04}", i)).collect();
let key = PyString::new_bound(py, index_strs.join(",").as_str());
cache.set_item(&cache_key, &key)?;
key.into_any()
}
}
}
None => qargs.str()?.into_any(),
};
let base = PyClassInitializer::from(DAGNode { _node_id: -1 });
let sub = base.add_subclass(DAGOpNode {
instruction,
sort_key: sort_key.unbind(),
});
Ok(Py::new(py, sub)?.to_object(py))
}

fn __reduce__(slf: PyRef<Self>, py: Python) -> PyResult<PyObject> {
let state = (slf.as_ref()._node_id, &slf.sort_key);
Ok((
Expand Down Expand Up @@ -229,6 +273,11 @@ impl DAGOpNode {
Ok(())
}

#[getter]
fn op_name(&self) -> &str {
self.instruction.operation.name()
}

/// Returns a representation of the DAGOpNode
fn __repr__(&self, py: Python) -> PyResult<String> {
Ok(format!(
Expand Down
11 changes: 11 additions & 0 deletions crates/circuit/src/gate_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,3 +324,14 @@ pub fn xx_plus_yy_gate(theta: f64, beta: f64) -> [[Complex64; 4]; 4] {
[c64(0., 0.), c64(0., 0.), c64(0., 0.), c64(1., 0.)],
]
}

pub fn r_gate(theta: f64, phi: f64) -> [[Complex64; 2]; 2] {
let cos = (theta / 2.).cos();
let sin = (theta / 2.).sin();
let exp_m = c64(0., -phi).exp();
let exp_p = c64(0., phi).exp();
[
[c64(cos, 0.), c64(0., -1.) * exp_m * sin],
[c64(0., -1.) * exp_p * sin, c64(cos, 0.)],
]
}
2 changes: 1 addition & 1 deletion crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ static STDGATE_IMPORT_PATHS: [[&str; 2]; STANDARD_GATE_SIZE] = [
// CRZGate = 31
["placeholder", "placeholder"],
// RGate 32
["placeholder", "placeholder"],
["qiskit.circuit.library.standard_gates.r", "RGate"],
// CHGate = 33
["qiskit.circuit.library.standard_gates.h", "CHGate"],
// CPhaseGate = 34
Expand Down
63 changes: 58 additions & 5 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,18 @@ pub enum StandardGate {
RZXGate = 52,
}

impl ToPyObject for StandardGate {
fn to_object(&self, py: Python) -> PyObject {
self.into_py(py)
}
}

// TODO: replace all 34s (placeholders) with actual number
static STANDARD_GATE_NUM_QUBITS: [u32; STANDARD_GATE_SIZE] = [
1, 1, 1, 2, 2, 2, 3, 1, 1, 1, // 0-9
2, 2, 1, 0, 1, 1, 1, 1, 1, 1, // 10-19
1, 1, 1, 2, 2, 2, 1, 1, 1, 34, // 20-29
34, 34, 34, 2, 2, 2, 2, 2, 3, 2, // 30-39
34, 34, 1, 2, 2, 2, 2, 2, 3, 2, // 30-39
2, 2, 34, 34, 34, 34, 34, 34, 34, 34, // 40-49
34, 34, 34, // 50-52
];
Expand All @@ -249,7 +255,7 @@ static STANDARD_GATE_NUM_PARAMS: [u32; STANDARD_GATE_SIZE] = [
0, 0, 0, 0, 0, 0, 0, 1, 1, 1, // 0-9
0, 0, 0, 1, 0, 0, 1, 3, 0, 0, // 10-19
0, 0, 0, 0, 2, 2, 1, 2, 3, 34, // 20-29
34, 34, 34, 0, 1, 0, 0, 0, 0, 3, // 30-39
34, 34, 2, 0, 1, 0, 0, 0, 0, 3, // 30-39
1, 3, 34, 34, 34, 34, 34, 34, 34, 34, // 40-49
34, 34, 34, // 50-52
];
Expand Down Expand Up @@ -514,7 +520,12 @@ impl Operation for StandardGate {
_ => None,
},
Self::CRXGate | Self::CRYGate | Self::CRZGate => todo!(),
Self::RGate => todo!(),
Self::RGate => match params {
[Param::Float(theta), Param::Float(phi)] => {
Some(aview2(&gate_matrix::r_gate(*theta, *phi)).to_owned())
}
_ => None,
},
Self::CHGate => todo!(),
Self::CPhaseGate => todo!(),
Self::CSGate => todo!(),
Expand Down Expand Up @@ -954,7 +965,23 @@ impl Operation for StandardGate {
)
}),
Self::CRXGate | Self::CRYGate | Self::CRZGate => todo!(),
Self::RGate => todo!(),
Self::RGate => Python::with_gil(|py| -> Option<CircuitData> {
let out_phi = subtract_param(&params[1], PI2, py);
let out_lam = add_param(&multiply_param(&params[1], -1., py), PI2, py);
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::UGate,
smallvec![params[0].clone(), out_phi, out_lam],
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::CHGate => todo!(),
Self::CPhaseGate => todo!(),
Self::CSGate => todo!(),
Expand Down Expand Up @@ -987,7 +1014,33 @@ fn multiply_param(param: &Param, mult: f64, py: Python) -> Param {
theta
.clone_ref(py)
.call_method1(py, intern!(py, "__rmul__"), (mult,))
.expect("Parameter expression for global phase failed"),
.expect("Parameter expression for multiplication failed"),
),
Param::Obj(_) => unreachable!(),
}
}

fn subtract_param(param: &Param, other: f64, py: Python) -> Param {
match param {
Param::Float(theta) => Param::Float(*theta - other),
Param::ParameterExpression(theta) => Param::ParameterExpression(
theta
.clone_ref(py)
.call_method1(py, intern!(py, "__sub__"), (other,))
.expect("Parameter expression for subtraction failed"),
),
Param::Obj(_) => unreachable!(),
}
}

fn add_param(param: &Param, other: f64, py: Python) -> Param {
match param {
Param::Float(theta) => Param::Float(*theta + other),
Param::ParameterExpression(theta) => Param::ParameterExpression(
theta
.clone_ref(py)
.call_method1(py, intern!(py, "__add__"), (other,))
.expect("Parameter expression for addition failed"),
),
Param::Obj(_) => unreachable!(),
}
Expand Down
3 changes: 3 additions & 0 deletions qiskit/circuit/library/standard_gates/r.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +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):
Expand Down Expand Up @@ -49,6 +50,8 @@ class RGate(Gate):
\end{pmatrix}
"""

_standard_gate = StandardGate.RGate

def __init__(
self,
theta: ParameterValueType,
Expand Down
4 changes: 1 addition & 3 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4649,9 +4649,7 @@ def r(
Returns:
A handle to the instructions created.
"""
from .library.standard_gates.r import RGate

return self.append(RGate(theta, phi), [qubit], [], copy=False)
return self._append_standard_gate(StandardGate.RGate, [theta, phi], [qubit])

def rv(
self,
Expand Down
Loading
Loading