Skip to content

Commit

Permalink
Merge branch 'Qiskit:main' into move-equivalence
Browse files Browse the repository at this point in the history
  • Loading branch information
raynelfss authored Jun 24, 2024
2 parents e16e57b + 35f6297 commit b8bd422
Show file tree
Hide file tree
Showing 22 changed files with 351 additions and 165 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion crates/accelerate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ num-traits = "0.2"
num-complex.workspace = true
num-bigint = "0.4"
rustworkx-core = "0.14"
faer = "0.19.0"
faer = "0.19.1"
itertools = "0.13.0"
qiskit-circuit.workspace = true

Expand Down
2 changes: 1 addition & 1 deletion crates/accelerate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ pub mod isometry;
pub mod nlayout;
pub mod optimize_1q_gates;
pub mod pauli_exp_val;
pub mod permutation;
pub mod results;
pub mod sabre;
pub mod sampled_exp_val;
pub mod sparse_pauli_op;
pub mod stochastic_swap;
pub mod synthesis;
pub mod two_qubit_decompose;
pub mod uc_gate;
pub mod utils;
Expand Down
22 changes: 22 additions & 0 deletions crates/accelerate/src/synthesis/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This code is part of Qiskit.
//
// (C) Copyright IBM 2024
//
// This code is licensed under the Apache License, Version 2.0. You may
// obtain a copy of this license in the LICENSE.txt file in the root directory
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
//
// Any modifications or derivative works of this code must retain this
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

mod permutation;

use pyo3::prelude::*;
use pyo3::wrap_pymodule;

#[pymodule]
pub fn synthesis(m: &Bound<PyModule>) -> PyResult<()> {
m.add_wrapped(wrap_pymodule!(permutation::permutation))?;
Ok(())
}
68 changes: 68 additions & 0 deletions crates/accelerate/src/synthesis/permutation/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// This code is part of Qiskit.
//
// (C) Copyright IBM 2024
//
// This code is licensed under the Apache License, Version 2.0. You may
// obtain a copy of this license in the LICENSE.txt file in the root directory
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
//
// Any modifications or derivative works of this code must retain this
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.

use numpy::PyArrayLike1;
use smallvec::smallvec;

use pyo3::prelude::*;
use pyo3::wrap_pyfunction;

use qiskit_circuit::circuit_data::CircuitData;
use qiskit_circuit::operations::{Param, StandardGate};
use qiskit_circuit::Qubit;

mod utils;

/// Checks whether an array of size N is a permutation of 0, 1, ..., N - 1.
#[pyfunction]
#[pyo3(signature = (pattern))]
pub fn _validate_permutation(py: Python, pattern: PyArrayLike1<i64>) -> PyResult<PyObject> {
let view = pattern.as_array();
utils::validate_permutation(&view)?;
Ok(py.None())
}

/// Finds inverse of a permutation pattern.
#[pyfunction]
#[pyo3(signature = (pattern))]
pub fn _inverse_pattern(py: Python, pattern: PyArrayLike1<i64>) -> PyResult<PyObject> {
let view = pattern.as_array();
let inverse_i64: Vec<i64> = utils::invert(&view).iter().map(|&x| x as i64).collect();
Ok(inverse_i64.to_object(py))
}

#[pyfunction]
#[pyo3(signature = (pattern))]
pub fn _synth_permutation_basic(py: Python, pattern: PyArrayLike1<i64>) -> PyResult<CircuitData> {
let view = pattern.as_array();
let num_qubits = view.len();
CircuitData::from_standard_gates(
py,
num_qubits as u32,
utils::get_ordered_swap(&view).iter().map(|(i, j)| {
(
StandardGate::SwapGate,
smallvec![],
smallvec![Qubit(*i as u32), Qubit(*j as u32)],
)
}),
Param::Float(0.0),
)
}

#[pymodule]
pub fn permutation(m: &Bound<PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(_validate_permutation, m)?)?;
m.add_function(wrap_pyfunction!(_inverse_pattern, m)?)?;
m.add_function(wrap_pyfunction!(_synth_permutation_basic, m)?)?;
Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@
// that they have been altered from the originals.

use ndarray::{Array1, ArrayView1};
use numpy::PyArrayLike1;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use std::vec::Vec;

fn validate_permutation(pattern: &ArrayView1<i64>) -> PyResult<()> {
pub fn validate_permutation(pattern: &ArrayView1<i64>) -> PyResult<()> {
let n = pattern.len();
let mut seen: Vec<bool> = vec![false; n];

Expand Down Expand Up @@ -47,15 +46,24 @@ fn validate_permutation(pattern: &ArrayView1<i64>) -> PyResult<()> {
Ok(())
}

fn invert(pattern: &ArrayView1<i64>) -> Array1<usize> {
pub fn invert(pattern: &ArrayView1<i64>) -> Array1<usize> {
let mut inverse: Array1<usize> = Array1::zeros(pattern.len());
pattern.iter().enumerate().for_each(|(ii, &jj)| {
inverse[jj as usize] = ii;
});
inverse
}

fn get_ordered_swap(pattern: &ArrayView1<i64>) -> Vec<(i64, i64)> {
/// Sorts the input permutation by iterating through the permutation list
/// and putting each element to its correct position via a SWAP (if it's not
/// at the correct position already). If ``n`` is the length of the input
/// permutation, this requires at most ``n`` SWAPs.
///
/// More precisely, if the input permutation is a cycle of length ``m``,
/// then this creates a quantum circuit with ``m-1`` SWAPs (and of depth ``m-1``);
/// if the input permutation consists of several disjoint cycles, then each cycle
/// is essentially treated independently.
pub fn get_ordered_swap(pattern: &ArrayView1<i64>) -> Vec<(i64, i64)> {
let mut permutation: Vec<usize> = pattern.iter().map(|&x| x as usize).collect();
let mut index_map = invert(pattern);

Expand All @@ -76,45 +84,3 @@ fn get_ordered_swap(pattern: &ArrayView1<i64>) -> Vec<(i64, i64)> {
swaps[..].reverse();
swaps
}

/// Checks whether an array of size N is a permutation of 0, 1, ..., N - 1.
#[pyfunction]
#[pyo3(signature = (pattern))]
fn _validate_permutation(py: Python, pattern: PyArrayLike1<i64>) -> PyResult<PyObject> {
let view = pattern.as_array();
validate_permutation(&view)?;
Ok(py.None())
}

/// Finds inverse of a permutation pattern.
#[pyfunction]
#[pyo3(signature = (pattern))]
fn _inverse_pattern(py: Python, pattern: PyArrayLike1<i64>) -> PyResult<PyObject> {
let view = pattern.as_array();
let inverse_i64: Vec<i64> = invert(&view).iter().map(|&x| x as i64).collect();
Ok(inverse_i64.to_object(py))
}

/// Sorts the input permutation by iterating through the permutation list
/// and putting each element to its correct position via a SWAP (if it's not
/// at the correct position already). If ``n`` is the length of the input
/// permutation, this requires at most ``n`` SWAPs.
///
/// More precisely, if the input permutation is a cycle of length ``m``,
/// then this creates a quantum circuit with ``m-1`` SWAPs (and of depth ``m-1``);
/// if the input permutation consists of several disjoint cycles, then each cycle
/// is essentially treated independently.
#[pyfunction]
#[pyo3(signature = (permutation_in))]
fn _get_ordered_swap(py: Python, permutation_in: PyArrayLike1<i64>) -> PyResult<PyObject> {
let view = permutation_in.as_array();
Ok(get_ordered_swap(&view).to_object(py))
}

#[pymodule]
pub fn permutation(m: &Bound<PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(_validate_permutation, m)?)?;
m.add_function(wrap_pyfunction!(_inverse_pattern, m)?)?;
m.add_function(wrap_pyfunction!(_get_ordered_swap, m)?)?;
Ok(())
}
32 changes: 32 additions & 0 deletions crates/circuit/src/gate_matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,38 @@ pub fn xx_minus_yy_gate(theta: f64, beta: f64) -> [[Complex64; 4]; 4] {
]
}

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

#[inline]
pub fn u2_gate(phi: f64, lam: f64) -> [[Complex64; 2]; 2] {
[
[
c64(FRAC_1_SQRT_2, 0.),
(-c64(0., lam).exp()) * FRAC_1_SQRT_2,
],
[
c64(0., phi).exp() * FRAC_1_SQRT_2,
c64(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();
[
[c64(cos, 0.), -(c64(0., lam).exp()) * sin],
[c64(0., phi).exp() * sin, c64(0., phi + lam).exp() * cos],
]
}

#[inline]
pub fn xx_plus_yy_gate(theta: f64, beta: f64) -> [[Complex64; 4]; 4] {
let cos = (theta / 2.).cos();
Expand Down
6 changes: 6 additions & 0 deletions crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@ static STDGATE_IMPORT_PATHS: [[&str; 2]; STANDARD_GATE_SIZE] = [
"qiskit.circuit.library.standard_gates.xx_plus_yy",
"XXPlusYYGate",
],
// U1Gate = 26
["qiskit.circuit.library.standard_gates.u1", "U1Gate"],
// U2Gate = 27
["qiskit.circuit.library.standard_gates.u2", "U2Gate"],
// U3Gate = 28
["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
74 changes: 70 additions & 4 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,14 +242,17 @@ pub enum StandardGate {
ISwapGate = 23,
XXMinusYYGate = 24,
XXPlusYYGate = 25,
U1Gate = 26,
U2Gate = 27,
U3Gate = 28,
}

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

static STANDARD_GATE_NAME: [&str; STANDARD_GATE_SIZE] = [
Expand Down Expand Up @@ -279,6 +282,9 @@ static STANDARD_GATE_NAME: [&str; STANDARD_GATE_SIZE] = [
"iswap",
"xx_minus_yy",
"xx_plus_yy",
"u1",
"u2",
"u3",
];

#[pymethods]
Expand Down Expand Up @@ -327,8 +333,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 = 26;
pub const STANDARD_GATE_SIZE: usize = 29;

impl Operation for StandardGate {
fn name(&self) -> &str {
Expand Down Expand Up @@ -469,6 +474,22 @@ impl Operation for StandardGate {
}
_ => None,
},
Self::U1Gate => match params[0] {
Param::Float(val) => Some(aview2(&gate_matrix::u1_gate(val)).to_owned()),
_ => 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 @@ -704,6 +725,21 @@ impl Operation for StandardGate {
.expect("Unexpected Qiskit python bug"),
)
}),
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::SdgGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
Expand All @@ -719,6 +755,21 @@ impl Operation for StandardGate {
.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::TGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
Expand All @@ -734,6 +785,21 @@ impl Operation for StandardGate {
.expect("Unexpected Qiskit python bug"),
)
}),
Self::U3Gate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
py,
1,
[(
Self::UGate,
params.iter().cloned().collect(),
smallvec![Qubit(0)],
)],
FLOAT_ZERO,
)
.expect("Unexpected Qiskit python bug"),
)
}),
Self::TdgGate => Python::with_gil(|py| -> Option<CircuitData> {
Some(
CircuitData::from_standard_gates(
Expand Down
Loading

0 comments on commit b8bd422

Please sign in to comment.