Skip to content

Commit

Permalink
Oxidize TwoQubitWeylDecomposition
Browse files Browse the repository at this point in the history
This commit is part 1 of migrating the default 2q unitary synthesis to leverage
parallel rust described in Qiskit#8774, the eventual goal is to be able to
run unitary synthesis in parallel for all the unitary matrices in a
circuit in a single call from the `UnitarySynthesis` pass. This commit
lays the initial groundwork for doing this by starting with the largest
piece of the default 2q unitary synthesis code, the
TwoQubitWeylDecomposition class. It migrates the entire class
to be a pyclass in rust. There is still a Python subclass for it that
handles the actual QuantumCircuit generation and also the string
representations which are dependent on circuit generation. The goal of
this is to keep the same basic algorithm in place but re-implement
as-is in Rust as a common starting point for eventual improvements to
the underlying algorithm as well as parallelizing the synthesis of
multiple 2q unitary matrices.
  • Loading branch information
mtreinish committed Mar 4, 2024
1 parent 85411bb commit dae02aa
Show file tree
Hide file tree
Showing 7 changed files with 982 additions and 736 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion crates/accelerate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ features = ["hashbrown", "indexmap", "num-complex", "num-bigint"]

[dependencies.ndarray]
version = "^0.15.6"
features = ["rayon"]
features = ["rayon", "approx-0_5"]

[dependencies.approx]
version = "0.5"
features = ["num-complex"]

[dependencies.hashbrown]
workspace = true
Expand Down
39 changes: 31 additions & 8 deletions crates/accelerate/src/euler_one_qubit_decomposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use numpy::PyReadonlyArray2;

use crate::utils::SliceOrInt;

const DEFAULT_ATOL: f64 = 1e-12;
pub const DEFAULT_ATOL: f64 = 1e-12;

#[pyclass(module = "qiskit._accelerate.euler_one_qubit_decomposer")]
pub struct OneQubitGateErrorMap {
Expand Down Expand Up @@ -61,9 +61,9 @@ impl OneQubitGateErrorMap {

#[pyclass(sequence)]
pub struct OneQubitGateSequence {
gates: Vec<(String, Vec<f64>)>,
pub gates: Vec<(String, Vec<f64>)>,
#[pyo3(get)]
global_phase: f64,
pub global_phase: f64,
}

#[pymethods]
Expand Down Expand Up @@ -553,7 +553,7 @@ pub fn generate_circuit(
}

#[inline]
fn angles_from_unitary(unitary: ArrayView2<Complex64>, target_basis: &str) -> [f64; 4] {
pub fn angles_from_unitary(unitary: ArrayView2<Complex64>, target_basis: &str) -> [f64; 4] {
match target_basis {
"U321" => params_u3_inner(unitary),
"U3" => params_u3_inner(unitary),
Expand Down Expand Up @@ -609,6 +609,7 @@ fn compute_error(
}
}

#[inline]
#[pyfunction]
pub fn compute_error_one_qubit_sequence(
circuit: &OneQubitGateSequence,
Expand All @@ -618,6 +619,7 @@ pub fn compute_error_one_qubit_sequence(
compute_error(&circuit.gates, error_map, qubit)
}

#[inline]
#[pyfunction]
pub fn compute_error_list(
circuit: Vec<(String, Vec<f64>)>,
Expand Down Expand Up @@ -648,7 +650,26 @@ pub fn unitary_to_gate_sequence(
}
}
let unitary_mat = unitary.as_array();
let best_result = target_basis_list
Ok(unitary_to_gate_sequence_inner(
unitary_mat,
&target_basis_list,
qubit,
error_map,
simplify,
atol,
))
}

#[inline]
pub fn unitary_to_gate_sequence_inner(
unitary_mat: ArrayView2<Complex64>,
target_basis_list: &[&str],
qubit: usize,
error_map: Option<&OneQubitGateErrorMap>,
simplify: bool,
atol: Option<f64>,
) -> Option<OneQubitGateSequence> {
target_basis_list
.iter()
.map(|target_basis| {
let [theta, phi, lam, phase] = angles_from_unitary(unitary_mat, target_basis);
Expand All @@ -658,12 +679,11 @@ pub fn unitary_to_gate_sequence(
let error_a = compare_error_fn(a, &error_map, qubit);
let error_b = compare_error_fn(b, &error_map, qubit);
error_a.partial_cmp(&error_b).unwrap_or(Ordering::Equal)
});
Ok(best_result)
})
}

#[inline]
fn det_one_qubit(mat: ArrayView2<Complex64>) -> Complex64 {
pub fn det_one_qubit(mat: ArrayView2<Complex64>) -> Complex64 {
mat[[0, 0]] * mat[[1, 1]] - mat[[0, 1]] * mat[[1, 0]]
}

Expand Down Expand Up @@ -697,6 +717,7 @@ fn params_zxz_inner(mat: ArrayView2<Complex64>) -> [f64; 4] {
[theta, phi + PI / 2., lam - PI / 2., phase]
}

#[inline]
#[pyfunction]
pub fn params_zyz(unitary: PyReadonlyArray2<Complex64>) -> [f64; 4] {
let mat = unitary.as_array();
Expand All @@ -712,6 +733,7 @@ fn params_u3_inner(mat: ArrayView2<Complex64>) -> [f64; 4] {
[theta, phi, lam, phase - 0.5 * (phi + lam)]
}

#[inline]
#[pyfunction]
pub fn params_u3(unitary: PyReadonlyArray2<Complex64>) -> [f64; 4] {
let mat = unitary.as_array();
Expand All @@ -726,6 +748,7 @@ fn params_u1x_inner(mat: ArrayView2<Complex64>) -> [f64; 4] {
[theta, phi, lam, phase - 0.5 * (theta + phi + lam)]
}

#[inline]
#[pyfunction]
pub fn params_u1x(unitary: PyReadonlyArray2<Complex64>) -> [f64; 4] {
let mat = unitary.as_array();
Expand Down
Loading

0 comments on commit dae02aa

Please sign in to comment.