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

Port synth_cz_depth_line_mr to Rust #12949

Merged
merged 22 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
172 changes: 172 additions & 0 deletions crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// 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 std::iter::once;

use hashbrown::HashMap;
use itertools::Itertools;
use ndarray::{Array1, ArrayView2};

use qiskit_circuit::{
operations::{Param, StandardGate},
Qubit,
};
use smallvec::{smallvec, SmallVec};

use crate::synthesis::permutation::{_append_cx_stage1, _append_cx_stage2};

// A sequence of Lnn gates
// Represents the return type for Lnn Synthesis algorithms
pub(crate) type LnnGatesVec = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>;

// A pattern denoted by Pj in [1] for odd number of qubits:
// [n-2, n-4, n-4, ..., 3, 3, 1, 1, 0, 0, 2, 2, ..., n-3, n-3]
fn _odd_pattern1(n: usize) -> Vec<usize> {
once(n - 2)
.chain((0..((n - 3) / 2)).flat_map(|i| [(n - 2 * i - 4); 2]))
.chain((0..((n - 1) / 2)).flat_map(|i| [2 * i; 2]))
.collect()
}

// A pattern denoted by Pk in [1] for odd number of qubits:
// [2, 2, 4, 4, ..., n-1, n-1, n-2, n-2, n-4, n-4, ..., 5, 5, 3, 3, 1]
fn _odd_pattern2(n: usize) -> Vec<usize> {
(0..((n - 1) / 2))
.flat_map(|i| [(2 * i + 2); 2])
.chain((0..((n - 3) / 2)).flat_map(|i| [n - 2 * i - 2; 2]))
.chain(once(1))
.collect()
}

// A pattern denoted by Pj in [1] for even number of qubits:
// [n-1, n-3, n-3, n-5, n-5, ..., 1, 1, 0, 0, 2, 2, ..., n-4, n-4, n-2]
fn _even_pattern1(n: usize) -> Vec<usize> {
once(n - 1)
.chain((0..((n - 2) / 2)).flat_map(|i| [n - 2 * i - 3; 2]))
.chain((0..((n - 2) / 2)).flat_map(|i| [2 * i; 2]))
.chain(once(n - 2))
.collect()
}

// A pattern denoted by Pk in [1] for even number of qubits:
// [2, 2, 4, 4, ..., n-2, n-2, n-1, n-1, ..., 3, 3, 1, 1]
fn _even_pattern2(n: usize) -> Vec<usize> {
(0..((n - 2) / 2))
.flat_map(|i| [2 * (i + 1); 2])
.chain((0..(n / 2)).flat_map(|i| [(n - 2 * i - 1); 2]))
.collect()
}

// Creating the patterns for the phase layers.
fn _create_patterns(n: usize) -> HashMap<(usize, usize), (usize, usize)> {
let (pat1, pat2) = if n % 2 == 0 {
(_even_pattern1(n), _even_pattern2(n))
} else {
(_odd_pattern1(n), _odd_pattern2(n))
};

let ind = if n % 2 == 0 {
(2 * n - 4) / 2
} else {
(2 * n - 4) / 2 - 1
};

HashMap::from_iter((0..n).map(|i| ((0, i), (i, i))).chain(
(0..(n / 2)).cartesian_product(0..n).map(|(layer, i)| {
(
(layer + 1, i),
(pat1[ind - (2 * layer) + i], pat2[(2 * layer) + i]),
)
}),
))
}

// Appends correct phase gate during CZ synthesis
fn _append_phase_gate(pat_val: usize, gates: &mut LnnGatesVec, qubit: usize) {
// Add phase gates: s, sdg or z
let gate_id = pat_val % 4;
if gate_id != 0 {
let gate = match gate_id {
1 => StandardGate::SdgGate,
2 => StandardGate::ZGate,
3 => StandardGate::SGate,
// cover 2 and 3
jlofti marked this conversation as resolved.
Show resolved Hide resolved
_ => unreachable!(), // unreachable as we have modulo 4
};
gates.push((gate, smallvec![], smallvec![Qubit(qubit as u32)]));
}
}

// Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity,
// based on Maslov and Roetteler.
jlofti marked this conversation as resolved.
Show resolved Hide resolved
pub(super) fn synth_cz_depth_line_mr_inner(matrix: ArrayView2<bool>) -> (usize, LnnGatesVec) {
let num_qubits = matrix.raw_dim()[0];
let pats = _create_patterns(num_qubits);

// s_gates[i] = 0, 1, 2 or 3 for a gate id, sdg, z or s on qubit i respectively
let mut s_gates = Array1::<usize>::zeros(num_qubits);

let mut patlist: Vec<(usize, usize)> = Vec::new();

let mut gates = LnnGatesVec::new();

for i in 0..num_qubits {
for j in (i + 1)..num_qubits {
if matrix[[i, j]] {
// CZ(i,j) gate
s_gates[[i]] += 2; // qc.z[i]
s_gates[[j]] += 2; // qc.z[j]
patlist.push((i, j - 1));
patlist.push((i, j));
patlist.push((i + 1, j - 1));
patlist.push((i + 1, j));
}
}
}

for i in 0..((num_qubits + 1) / 2) {
for j in 0..num_qubits {
let pat_val = pats[&(i, j)];
if patlist.contains(&pat_val) {
// patcnt should be 0 or 1, which checks if a Sdg gate should be added
let patcnt = patlist.iter().filter(|val| **val == pat_val).count();
s_gates[[j]] += patcnt; // qc.sdg[j]
}

_append_phase_gate(s_gates[[j]], &mut gates, j)
}

_append_cx_stage1(&mut gates, num_qubits);
_append_cx_stage2(&mut gates, num_qubits);
s_gates = Array1::<usize>::zeros(num_qubits);
}

if num_qubits % 2 == 0 {
let i = num_qubits / 2;

for j in 0..num_qubits {
let pat_val = pats[&(i, j)];
if patlist.contains(&pat_val) && pat_val.0 != pat_val.1 {
// patcnt should be 0 or 1, which checks if a Sdg gate should be added
let patcnt = patlist.iter().filter(|val| **val == pat_val).count();

s_gates[[j]] += patcnt; // qc.sdg[j]
}

_append_phase_gate(s_gates[[j]], &mut gates, j)
}

_append_cx_stage1(&mut gates, num_qubits);
}

(num_qubits, gates)
}
35 changes: 35 additions & 0 deletions crates/accelerate/src/synthesis/linear_phase/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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::PyReadonlyArray2;
use pyo3::{
prelude::*,
pyfunction,
types::{PyModule, PyModuleMethods},
wrap_pyfunction, Bound, PyResult,
};
use qiskit_circuit::{circuit_data::CircuitData, operations::Param};

pub(crate) mod cz_depth_lnn;

#[pyfunction]
#[pyo3(signature = (mat))]
fn synth_cz_depth_line_mr(py: Python, mat: PyReadonlyArray2<bool>) -> PyResult<CircuitData> {
jlofti marked this conversation as resolved.
Show resolved Hide resolved
let view = mat.as_array();
let (num_qubits, lnn_gates) = cz_depth_lnn::synth_cz_depth_line_mr_inner(view);
CircuitData::from_standard_gates(py, num_qubits as u32, lnn_gates, Param::Float(0.0))
}

pub fn linear_phase(m: &Bound<PyModule>) -> PyResult<()> {
m.add_wrapped(wrap_pyfunction!(synth_cz_depth_line_mr))?;
Ok(())
}
5 changes: 5 additions & 0 deletions crates/accelerate/src/synthesis/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

mod clifford;
pub mod linear;
pub mod linear_phase;
mod permutation;

use pyo3::prelude::*;
Expand All @@ -21,6 +22,10 @@ pub fn synthesis(m: &Bound<PyModule>) -> PyResult<()> {
linear::linear(&linear_mod)?;
m.add_submodule(&linear_mod)?;

let linear_phase_mod = PyModule::new_bound(m.py(), "linear_phase")?;
linear_phase::linear_phase(&linear_phase_mod)?;
m.add_submodule(&linear_phase_mod)?;

let permutation_mod = PyModule::new_bound(m.py(), "permutation")?;
permutation::permutation(&permutation_mod)?;
m.add_submodule(&permutation_mod)?;
Expand Down
53 changes: 53 additions & 0 deletions crates/accelerate/src/synthesis/permutation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use qiskit_circuit::circuit_data::CircuitData;
use qiskit_circuit::operations::{Param, StandardGate};
use qiskit_circuit::Qubit;

use super::linear_phase::cz_depth_lnn::LnnGatesVec;

mod utils;

/// Checks whether an array of size N is a permutation of 0, 1, ..., N - 1.
Expand Down Expand Up @@ -114,6 +116,57 @@ pub fn _synth_permutation_depth_lnn_kms(
)
}

// A single layer of CX gates.
pub(crate) fn _append_cx_stage1(gates: &mut LnnGatesVec, n: usize) {
for i in 0..(n / 2) {
gates.push((
StandardGate::CXGate,
smallvec![],
smallvec![Qubit((2 * i) as u32), Qubit((2 * i + 1) as u32)],
))
}

for i in 0..((n + 1) / 2 - 1) {
gates.push((
StandardGate::CXGate,
smallvec![],
smallvec![Qubit((2 * i + 2) as u32), Qubit((2 * i + 1) as u32)],
))
}
}

// A single layer of CX gates.
pub(crate) fn _append_cx_stage2(gates: &mut LnnGatesVec, n: usize) {
for i in 0..(n / 2) {
gates.push((
StandardGate::CXGate,
smallvec![],
smallvec![Qubit((2 * i + 1) as u32), Qubit((2 * i) as u32)],
))
}

for i in 0..((n + 1) / 2 - 1) {
gates.push((
StandardGate::CXGate,
smallvec![],
smallvec![Qubit((2 * i + 1) as u32), Qubit((2 * i + 2) as u32)],
))
}
}

// Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures
// using Kutin, Moulton, Smithline method.
fn _append_reverse_permutation_lnn_kms(gates: &mut LnnGatesVec, num_qubits: usize) {
(0..(num_qubits + 1) / 2).for_each(|_| {
_append_cx_stage1(gates, num_qubits);
_append_cx_stage2(gates, num_qubits);
});

if num_qubits % 2 == 0 {
_append_cx_stage1(gates, num_qubits);
}
}

pub fn permutation(m: &Bound<PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(_validate_permutation, m)?)?;
m.add_function(wrap_pyfunction!(_inverse_pattern, m)?)?;
Expand Down
1 change: 1 addition & 0 deletions qiskit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
sys.modules["qiskit._accelerate.synthesis.permutation"] = _accelerate.synthesis.permutation
sys.modules["qiskit._accelerate.synthesis.linear"] = _accelerate.synthesis.linear
sys.modules["qiskit._accelerate.synthesis.clifford"] = _accelerate.synthesis.clifford
sys.modules["qiskit._accelerate.synthesis.linear_phase"] = _accelerate.synthesis.linear_phase

from qiskit.exceptions import QiskitError, MissingOptionalLibraryError

Expand Down
Loading
Loading