From b9e03d1b4fea34954c6b826c6ee4ad19e2c12edd Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Mon, 12 Aug 2024 19:45:50 -0500 Subject: [PATCH 01/18] First pass, pre cleanup Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/lnn.rs | 248 ++++++++++++++++++ crates/accelerate/src/synthesis/linear/mod.rs | 11 + qiskit/synthesis/linear_phase/cz_depth_lnn.py | 5 +- 3 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 crates/accelerate/src/synthesis/linear/lnn.rs diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear/lnn.rs new file mode 100644 index 000000000000..5a8acb87cf27 --- /dev/null +++ b/crates/accelerate/src/synthesis/linear/lnn.rs @@ -0,0 +1,248 @@ +// 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 hashbrown::HashMap; +use ndarray::{Array1, ArrayView2}; + +use qiskit_circuit::{ + operations::{Param, StandardGate}, + Qubit, +}; +use smallvec::{smallvec, SmallVec}; + +type LnnGatesVec = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>; + +fn _odd_pattern1(n: isize) -> Vec { + let mut pat = Vec::new(); + + pat.push(n - 2); + for i in 0..((n - 3) / 2) { + pat.push(n - 2 * i - 4); + pat.push(n - 2 * i - 4) + } + + for i in 0..((n - 1) / 2) { + pat.push(2 * i); + pat.push(2 * i); + } + + pat +} + +fn _odd_pattern2(n: isize) -> Vec { + let mut pat = Vec::new(); + + for i in 0..((n - 1) / 2) { + pat.push(2 * i + 2); + pat.push(2 * i + 2); + } + + for i in 0..((n - 3) / 2) { + pat.push(n - 2 * i - 2); + pat.push(n - 2 * i - 2); + } + + pat.push(1); + pat +} + +fn _even_pattern1(n: isize) -> Vec { + let mut pat = Vec::new(); + + pat.push(n - 1); + + for i in 0..((n - 2) / 2) { + pat.push(n - 2 * i - 3); + pat.push(n - 2 * i - 3); + } + + for i in 0..((n - 2) / 2) { + pat.push(2 * i); + pat.push(2 * i); + } + + pat.push(n - 2); + pat +} + +fn _even_pattern2(n: isize) -> Vec { + let mut pat = Vec::new(); + + for i in 0..((n - 2) / 2) { + pat.push(2 * (i + 1)); + pat.push(2 * (i + 1)); + } + for i in 0..(n / 2) { + pat.push(n - 2 * i - 1); + pat.push(n - 2 * i - 1); + } + + pat +} + +fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { + let (pat1, pat2) = if n % 2 == 0 { + (_even_pattern1(n), _even_pattern2(n)) + } else { + (_odd_pattern1(n), _odd_pattern2(n)) + }; + + let mut pats = HashMap::from_iter((0..n).map(|i| ((0, i), (i, i)))); + + let mut ind2 = 0; + + let mut ind1 = if n % 2 == 0 { + (2 * n - 4) / 2 + } else { + (2 * n - 4) / 2 - 1 + }; + + for layer in 0..(n / 2) { + for i in 0..n { + pats.insert( + (layer + 1, i), + (pat1[(ind1 + i) as usize], pat2[(ind2 + i) as usize]), + ); + } + ind1 -= 2; + ind2 += 2; + } + + pats +} + +fn _append_cx_stage1(gates: &mut LnnGatesVec, n: isize) { + 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)], + )) + } +} + +fn _append_cx_stage2(gates: &mut LnnGatesVec, n: isize) { + 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)], + )) + } +} +pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGatesVec) { + let num_qubits = matrix.raw_dim()[0]; + let pats = _create_patterns(num_qubits as isize); + + let mut s_gates = Array1::::zeros(num_qubits); + + let mut patlist: Vec<(isize, isize)> = Vec::new(); + + let mut gates = LnnGatesVec::new(); + + for i in 0..num_qubits { + for j in (i + 1)..num_qubits { + if matrix[[i, j]] { + s_gates[[i]] += 2; + s_gates[[j]] += 2; + patlist.push((i as isize, j as isize - 1)); + patlist.push((i as isize, j as isize)); + patlist.push((i as isize + 1, j as isize - 1)); + patlist.push((i as isize + 1, j as isize)); + } + } + } + + for i in 0..((num_qubits + 1) / 2) { + for j in 0..num_qubits { + if patlist.contains(&pats[&(i as isize, j as isize)]) { + let pacnt = patlist + .iter() + .filter(|val| **val == pats[&(i as isize, j as isize)]) + .count(); + for _ in 0..pacnt { + s_gates[[j]] += 1; + } + } + } + + for j in 0..num_qubits { + if s_gates[[j]] % 4 == 1 { + gates.push(( + StandardGate::SdgGate, + smallvec![], + smallvec![Qubit(j as u32)], + )) + } else if s_gates[[j]] % 4 == 2 { + gates.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(j as u32)])) + } else if s_gates[[j]] % 4 == 3 { + gates.push((StandardGate::SGate, smallvec![], smallvec![Qubit(j as u32)])) + } + } + + _append_cx_stage1(&mut gates, num_qubits as isize); + _append_cx_stage2(&mut gates, num_qubits as isize); + s_gates = Array1::::zeros(num_qubits); + } + + if num_qubits % 2 == 0 { + let i = num_qubits / 2; + + for j in 0..num_qubits { + if patlist.contains(&pats[&(i as isize, j as isize)]) + && pats[&(i as isize, j as isize)].0 != pats[&(i as isize, j as isize)].1 + { + let pacnt = patlist + .iter() + .filter(|val| **val == pats[&(i as isize, j as isize)]) + .count(); + for _ in 0..pacnt { + s_gates[[j]] += 1; + } + } + } + + for j in 0..num_qubits { + if s_gates[[j]] % 4 == 1 { + gates.push(( + StandardGate::SdgGate, + smallvec![], + smallvec![Qubit(j as u32)], + )) + } else if s_gates[[j]] % 4 == 2 { + gates.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(j as u32)])) + } else if s_gates[[j]] % 4 == 3 { + gates.push((StandardGate::SGate, smallvec![], smallvec![Qubit(j as u32)])) + } + } + + _append_cx_stage1(&mut gates, num_qubits as isize); + } + + (num_qubits as usize, gates) +} diff --git a/crates/accelerate/src/synthesis/linear/mod.rs b/crates/accelerate/src/synthesis/linear/mod.rs index 08a0b1e104b3..68fc47018d98 100644 --- a/crates/accelerate/src/synthesis/linear/mod.rs +++ b/crates/accelerate/src/synthesis/linear/mod.rs @@ -13,7 +13,9 @@ use crate::QiskitError; use numpy::{IntoPyArray, PyArray2, PyReadonlyArray2, PyReadwriteArray2}; use pyo3::prelude::*; +use qiskit_circuit::{circuit_data::CircuitData, operations::Param}; +mod lnn; mod pmh; pub mod utils; @@ -175,6 +177,14 @@ fn check_invertible_binary_matrix(py: Python, mat: PyReadonlyArray2) -> Py Ok(out.to_object(py)) } +#[pyfunction] +#[pyo3(signature = (mat))] +fn synth_cz_depth_line_mr(py: Python, mat: PyReadonlyArray2) -> PyResult { + let view = mat.as_array(); + let (num_qubits, lnn_gates) = lnn::synth_cz_depth_line_mr(view); + CircuitData::from_standard_gates(py, num_qubits as u32, lnn_gates, Param::Float(0.0)) +} + pub fn linear(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(gauss_elimination_with_perm))?; m.add_wrapped(wrap_pyfunction!(gauss_elimination))?; @@ -187,5 +197,6 @@ pub fn linear(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(random_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(check_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(pmh::synth_cnot_count_full_pmh))?; + m.add_wrapped(wrap_pyfunction!(synth_cz_depth_line_mr))?; Ok(()) } diff --git a/qiskit/synthesis/linear_phase/cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cz_depth_lnn.py index 7a195f0caf96..c5367f97083e 100644 --- a/qiskit/synthesis/linear_phase/cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cz_depth_lnn.py @@ -29,6 +29,8 @@ _append_cx_stage2, ) +from qiskit._accelerate.synthesis.linear import synth_cz_depth_line_mr as synth_cz_depth_line_mr_inner + def _odd_pattern1(n): """A pattern denoted by Pj in [1] for odd number of qubits: @@ -139,6 +141,7 @@ def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit: *Shorter stabilizer circuits via Bruhat decomposition and quantum circuit transformations*, `arXiv:1705.09176 `_. """ + return QuantumCircuit._from_circuit_data(synth_cz_depth_line_mr_inner(mat.astype(bool))) num_qubits = mat.shape[0] pats = _create_patterns(num_qubits) patlist = [] @@ -191,4 +194,4 @@ def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit: qc.s(j) qc = _append_cx_stage1(qc, num_qubits) - return qc + From b6a4bbf402a92fb772b07f4843ac98117e73426c Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Mon, 12 Aug 2024 20:46:17 -0500 Subject: [PATCH 02/18] optimizations and cleanup Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/lnn.rs | 130 +++++++--------- qiskit/synthesis/linear_phase/cz_depth_lnn.py | 147 +----------------- 2 files changed, 57 insertions(+), 220 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear/lnn.rs index 5a8acb87cf27..57787db1c089 100644 --- a/crates/accelerate/src/synthesis/linear/lnn.rs +++ b/crates/accelerate/src/synthesis/linear/lnn.rs @@ -10,7 +10,10 @@ // 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::{ @@ -19,76 +22,49 @@ use qiskit_circuit::{ }; use smallvec::{smallvec, SmallVec}; +// A sequence of Lnn gates +// Represents the return type for Lnn Synthesis algorithms 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: isize) -> Vec { - let mut pat = Vec::new(); - - pat.push(n - 2); - for i in 0..((n - 3) / 2) { - pat.push(n - 2 * i - 4); - pat.push(n - 2 * i - 4) - } - - for i in 0..((n - 1) / 2) { - pat.push(2 * i); - pat.push(2 * i); - } - - pat + 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: isize) -> Vec { - let mut pat = Vec::new(); - - for i in 0..((n - 1) / 2) { - pat.push(2 * i + 2); - pat.push(2 * i + 2); - } - - for i in 0..((n - 3) / 2) { - pat.push(n - 2 * i - 2); - pat.push(n - 2 * i - 2); - } - - pat.push(1); - pat + (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: isize) -> Vec { - let mut pat = Vec::new(); - - pat.push(n - 1); - - for i in 0..((n - 2) / 2) { - pat.push(n - 2 * i - 3); - pat.push(n - 2 * i - 3); - } - - for i in 0..((n - 2) / 2) { - pat.push(2 * i); - pat.push(2 * i); - } - - pat.push(n - 2); - pat + 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: isize) -> Vec { - let mut pat = Vec::new(); - - for i in 0..((n - 2) / 2) { - pat.push(2 * (i + 1)); - pat.push(2 * (i + 1)); - } - for i in 0..(n / 2) { - pat.push(n - 2 * i - 1); - pat.push(n - 2 * i - 1); - } - - pat + (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: isize) -> HashMap<(isize, isize), (isize, isize)> { let (pat1, pat2) = if n % 2 == 0 { (_even_pattern1(n), _even_pattern2(n)) @@ -96,28 +72,23 @@ fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { (_odd_pattern1(n), _odd_pattern2(n)) }; - let mut pats = HashMap::from_iter((0..n).map(|i| ((0, i), (i, i)))); - - let mut ind2 = 0; - - let mut ind1 = if n % 2 == 0 { + let ind = if n % 2 == 0 { (2 * n - 4) / 2 } else { (2 * n - 4) / 2 - 1 }; - for layer in 0..(n / 2) { - for i in 0..n { - pats.insert( + 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[(ind1 + i) as usize], pat2[(ind2 + i) as usize]), - ); - } - ind1 -= 2; - ind2 += 2; - } - - pats + ( + pat1[(ind - (2 * layer) + i) as usize], + pat2[((2 * layer) + i) as usize], + ), + ) + }), + )) } fn _append_cx_stage1(gates: &mut LnnGatesVec, n: isize) { @@ -155,10 +126,14 @@ fn _append_cx_stage2(gates: &mut LnnGatesVec, n: isize) { )) } } + +// Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, +// based on Maslov and Roetteler. pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGatesVec) { let num_qubits = matrix.raw_dim()[0]; let pats = _create_patterns(num_qubits as isize); + // 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::::zeros(num_qubits); let mut patlist: Vec<(isize, isize)> = Vec::new(); @@ -168,8 +143,9 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat for i in 0..num_qubits { for j in (i + 1)..num_qubits { if matrix[[i, j]] { - s_gates[[i]] += 2; - s_gates[[j]] += 2; + // CZ(i,j) gate + s_gates[[i]] += 2; // qc.z[i] + s_gates[[j]] += 2; // qc.z[j] patlist.push((i as isize, j as isize - 1)); patlist.push((i as isize, j as isize)); patlist.push((i as isize + 1, j as isize - 1)); @@ -186,11 +162,12 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat .filter(|val| **val == pats[&(i as isize, j as isize)]) .count(); for _ in 0..pacnt { - s_gates[[j]] += 1; + s_gates[[j]] += 1; // qc.sdg[j] } } } + // Add phase gates: s, sdg or z for j in 0..num_qubits { if s_gates[[j]] % 4 == 1 { gates.push(( @@ -222,11 +199,12 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat .filter(|val| **val == pats[&(i as isize, j as isize)]) .count(); for _ in 0..pacnt { - s_gates[[j]] += 1; + s_gates[[j]] += 1; // qc.sdg[j] } } } + // Add phase gates: s, sdg or z for j in 0..num_qubits { if s_gates[[j]] % 4 == 1 { gates.push(( diff --git a/qiskit/synthesis/linear_phase/cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cz_depth_lnn.py index c5367f97083e..b895299ab20b 100644 --- a/qiskit/synthesis/linear_phase/cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cz_depth_lnn.py @@ -24,102 +24,9 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.synthesis.permutation.permutation_reverse_lnn import ( - _append_cx_stage1, - _append_cx_stage2, -) from qiskit._accelerate.synthesis.linear import synth_cz_depth_line_mr as synth_cz_depth_line_mr_inner - -def _odd_pattern1(n): - """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] - """ - pat = [] - pat.append(n - 2) - for i in range((n - 3) // 2): - pat.append(n - 2 * i - 4) - pat.append(n - 2 * i - 4) - for i in range((n - 1) // 2): - pat.append(2 * i) - pat.append(2 * i) - return pat - - -def _odd_pattern2(n): - """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] - """ - pat = [] - for i in range((n - 1) // 2): - pat.append(2 * i + 2) - pat.append(2 * i + 2) - for i in range((n - 3) // 2): - pat.append(n - 2 * i - 2) - pat.append(n - 2 * i - 2) - pat.append(1) - return pat - - -def _even_pattern1(n): - """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] - """ - pat = [] - pat.append(n - 1) - for i in range((n - 2) // 2): - pat.append(n - 2 * i - 3) - pat.append(n - 2 * i - 3) - for i in range((n - 2) // 2): - pat.append(2 * i) - pat.append(2 * i) - pat.append(n - 2) - return pat - - -def _even_pattern2(n): - """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] - """ - pat = [] - for i in range((n - 2) // 2): - pat.append(2 * (i + 1)) - pat.append(2 * (i + 1)) - for i in range(n // 2): - pat.append(n - 2 * i - 1) - pat.append(n - 2 * i - 1) - return pat - - -def _create_patterns(n): - """Creating the patterns for the phase layers.""" - if (n % 2) == 0: - pat1 = _even_pattern1(n) - pat2 = _even_pattern2(n) - else: - pat1 = _odd_pattern1(n) - pat2 = _odd_pattern2(n) - pats = {} - - layer = 0 - for i in range(n): - pats[(0, i)] = (i, i) - - if (n % 2) == 0: - ind1 = (2 * n - 4) // 2 - else: - ind1 = (2 * n - 4) // 2 - 1 - ind2 = 0 - while layer < (n // 2): - for i in range(n): - pats[(layer + 1, i)] = (pat1[ind1 + i], pat2[ind2 + i]) - layer += 1 - ind1 -= 2 - ind2 += 2 - return pats - - def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit: r"""Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, based on Maslov and Roetteler. @@ -141,57 +48,9 @@ def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit: *Shorter stabilizer circuits via Bruhat decomposition and quantum circuit transformations*, `arXiv:1705.09176 `_. """ - return QuantumCircuit._from_circuit_data(synth_cz_depth_line_mr_inner(mat.astype(bool))) - num_qubits = mat.shape[0] - pats = _create_patterns(num_qubits) - patlist = [] - # s_gates[i] = 0, 1, 2 or 3 for a gate id, sdg, z or s on qubit i respectively - s_gates = np.zeros(num_qubits) - - qc = QuantumCircuit(num_qubits) - for i in range(num_qubits): - for j in range(i + 1, num_qubits): - if mat[i][j]: # CZ(i,j) gate - s_gates[i] += 2 # qc.z[i] - s_gates[j] += 2 # qc.z[j] - patlist.append((i, j - 1)) - patlist.append((i, j)) - patlist.append((i + 1, j - 1)) - patlist.append((i + 1, j)) - - for i in range((num_qubits + 1) // 2): - for j in range(num_qubits): - if pats[(i, j)] in patlist: - patcnt = patlist.count(pats[(i, j)]) - for _ in range(patcnt): - s_gates[j] += 1 # qc.sdg[j] - # Add phase gates: s, sdg or z - for j in range(num_qubits): - if s_gates[j] % 4 == 1: - qc.sdg(j) - elif s_gates[j] % 4 == 2: - qc.z(j) - elif s_gates[j] % 4 == 3: - qc.s(j) - qc = _append_cx_stage1(qc, num_qubits) - qc = _append_cx_stage2(qc, num_qubits) - s_gates = np.zeros(num_qubits) - if (num_qubits % 2) == 0: - i = num_qubits // 2 - for j in range(num_qubits): - if pats[(i, j)] in patlist and pats[(i, j)][0] != pats[(i, j)][1]: - patcnt = patlist.count(pats[(i, j)]) - for _ in range(patcnt): - s_gates[j] += 1 # qc.sdg[j] - # Add phase gates: s, sdg or z - for j in range(num_qubits): - if s_gates[j] % 4 == 1: - qc.sdg(j) - elif s_gates[j] % 4 == 2: - qc.z(j) - elif s_gates[j] % 4 == 3: - qc.s(j) - qc = _append_cx_stage1(qc, num_qubits) + # Call Rust implementaton + return QuantumCircuit._from_circuit_data(synth_cz_depth_line_mr_inner(mat.astype(bool))) + From 17f83249872070e8ad8d6299dd556f142538f49a Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:02:55 -0500 Subject: [PATCH 03/18] more cleanup Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/lnn.rs | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear/lnn.rs index 57787db1c089..e41342b37c7f 100644 --- a/crates/accelerate/src/synthesis/linear/lnn.rs +++ b/crates/accelerate/src/synthesis/linear/lnn.rs @@ -134,7 +134,7 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat let pats = _create_patterns(num_qubits as isize); // 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::::zeros(num_qubits); + let mut s_gates = Array1::::zeros(num_qubits); let mut patlist: Vec<(isize, isize)> = Vec::new(); @@ -157,18 +157,15 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat for i in 0..((num_qubits + 1) / 2) { for j in 0..num_qubits { if patlist.contains(&pats[&(i as isize, j as isize)]) { - let pacnt = patlist + let patcnt = patlist .iter() .filter(|val| **val == pats[&(i as isize, j as isize)]) .count(); - for _ in 0..pacnt { - s_gates[[j]] += 1; // qc.sdg[j] - } + + s_gates[[j]] += patcnt; // qc.sdg[j] } - } - // Add phase gates: s, sdg or z - for j in 0..num_qubits { + // Add phase gates: s, sdg or z if s_gates[[j]] % 4 == 1 { gates.push(( StandardGate::SdgGate, @@ -184,7 +181,7 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat _append_cx_stage1(&mut gates, num_qubits as isize); _append_cx_stage2(&mut gates, num_qubits as isize); - s_gates = Array1::::zeros(num_qubits); + s_gates = Array1::::zeros(num_qubits); } if num_qubits % 2 == 0 { @@ -194,18 +191,15 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat if patlist.contains(&pats[&(i as isize, j as isize)]) && pats[&(i as isize, j as isize)].0 != pats[&(i as isize, j as isize)].1 { - let pacnt = patlist + let patcnt = patlist .iter() .filter(|val| **val == pats[&(i as isize, j as isize)]) .count(); - for _ in 0..pacnt { - s_gates[[j]] += 1; // qc.sdg[j] - } + + s_gates[[j]] += patcnt; // qc.sdg[j] } - } - // Add phase gates: s, sdg or z - for j in 0..num_qubits { + // Add phase gates: s, sdg or z if s_gates[[j]] % 4 == 1 { gates.push(( StandardGate::SdgGate, From c7be163114b4e80808c431ede0144bf56851af1f Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:15:27 -0500 Subject: [PATCH 04/18] broke out function Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/lnn.rs | 69 +++++++++---------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear/lnn.rs index e41342b37c7f..12340620bfda 100644 --- a/crates/accelerate/src/synthesis/linear/lnn.rs +++ b/crates/accelerate/src/synthesis/linear/lnn.rs @@ -127,6 +127,30 @@ fn _append_cx_stage2(gates: &mut LnnGatesVec, n: isize) { } } +// 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 + if pat_val % 4 == 1 { + gates.push(( + StandardGate::SdgGate, + smallvec![], + smallvec![Qubit(qubit as u32)], + )) + } else if pat_val % 4 == 2 { + gates.push(( + StandardGate::ZGate, + smallvec![], + smallvec![Qubit(qubit as u32)], + )) + } else if pat_val % 4 == 3 { + gates.push(( + StandardGate::SGate, + smallvec![], + smallvec![Qubit(qubit as u32)], + )) + } +} + // Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, // based on Maslov and Roetteler. pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGatesVec) { @@ -156,27 +180,13 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat for i in 0..((num_qubits + 1) / 2) { for j in 0..num_qubits { - if patlist.contains(&pats[&(i as isize, j as isize)]) { - let patcnt = patlist - .iter() - .filter(|val| **val == pats[&(i as isize, j as isize)]) - .count(); - + let pat_val = pats[&(i as isize, j as isize)]; + if patlist.contains(&pat_val) { + let patcnt = patlist.iter().filter(|val| **val == pat_val).count(); s_gates[[j]] += patcnt; // qc.sdg[j] } - // Add phase gates: s, sdg or z - if s_gates[[j]] % 4 == 1 { - gates.push(( - StandardGate::SdgGate, - smallvec![], - smallvec![Qubit(j as u32)], - )) - } else if s_gates[[j]] % 4 == 2 { - gates.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(j as u32)])) - } else if s_gates[[j]] % 4 == 3 { - gates.push((StandardGate::SGate, smallvec![], smallvec![Qubit(j as u32)])) - } + _append_phase_gate(s_gates[[j]], &mut gates, j) } _append_cx_stage1(&mut gates, num_qubits as isize); @@ -188,29 +198,14 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat let i = num_qubits / 2; for j in 0..num_qubits { - if patlist.contains(&pats[&(i as isize, j as isize)]) - && pats[&(i as isize, j as isize)].0 != pats[&(i as isize, j as isize)].1 - { - let patcnt = patlist - .iter() - .filter(|val| **val == pats[&(i as isize, j as isize)]) - .count(); + let pat_val = pats[&(i as isize, j as isize)]; + if patlist.contains(&pat_val) && pat_val.0 != pat_val.1 { + let patcnt = patlist.iter().filter(|val| **val == pat_val).count(); s_gates[[j]] += patcnt; // qc.sdg[j] } - // Add phase gates: s, sdg or z - if s_gates[[j]] % 4 == 1 { - gates.push(( - StandardGate::SdgGate, - smallvec![], - smallvec![Qubit(j as u32)], - )) - } else if s_gates[[j]] % 4 == 2 { - gates.push((StandardGate::ZGate, smallvec![], smallvec![Qubit(j as u32)])) - } else if s_gates[[j]] % 4 == 3 { - gates.push((StandardGate::SGate, smallvec![], smallvec![Qubit(j as u32)])) - } + _append_phase_gate(s_gates[[j]], &mut gates, j) } _append_cx_stage1(&mut gates, num_qubits as isize); From cd205f8931a3188bd9325be182a7dc4f5163cf36 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:18:06 -0500 Subject: [PATCH 05/18] reformat Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- qiskit/synthesis/linear_phase/cz_depth_lnn.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/synthesis/linear_phase/cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cz_depth_lnn.py index b895299ab20b..d1458f76f39a 100644 --- a/qiskit/synthesis/linear_phase/cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cz_depth_lnn.py @@ -25,7 +25,10 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit._accelerate.synthesis.linear import synth_cz_depth_line_mr as synth_cz_depth_line_mr_inner +from qiskit._accelerate.synthesis.linear import ( + synth_cz_depth_line_mr as synth_cz_depth_line_mr_inner, +) + def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit: r"""Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, @@ -51,6 +54,3 @@ def synth_cz_depth_line_mr(mat: np.ndarray) -> QuantumCircuit: # Call Rust implementaton return QuantumCircuit._from_circuit_data(synth_cz_depth_line_mr_inner(mat.astype(bool))) - - - From 25da37215d83387fef1008f5be66db8ce7d5040a Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Mon, 12 Aug 2024 21:21:06 -0500 Subject: [PATCH 06/18] comments for append_cx Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/lnn.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear/lnn.rs index 12340620bfda..59eef933b96d 100644 --- a/crates/accelerate/src/synthesis/linear/lnn.rs +++ b/crates/accelerate/src/synthesis/linear/lnn.rs @@ -91,6 +91,7 @@ fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { )) } +// A single layer of CX gates. fn _append_cx_stage1(gates: &mut LnnGatesVec, n: isize) { for i in 0..(n / 2) { gates.push(( @@ -109,6 +110,7 @@ fn _append_cx_stage1(gates: &mut LnnGatesVec, n: isize) { } } +// A single layer of CX gates. fn _append_cx_stage2(gates: &mut LnnGatesVec, n: isize) { for i in 0..(n / 2) { gates.push(( From b42023c339c09547140c03610a8127c01d943968 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Mon, 12 Aug 2024 22:15:37 -0500 Subject: [PATCH 07/18] correct usize usage Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/lnn.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear/lnn.rs index 59eef933b96d..1922f3ef9020 100644 --- a/crates/accelerate/src/synthesis/linear/lnn.rs +++ b/crates/accelerate/src/synthesis/linear/lnn.rs @@ -92,7 +92,7 @@ fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { } // A single layer of CX gates. -fn _append_cx_stage1(gates: &mut LnnGatesVec, n: isize) { +fn _append_cx_stage1(gates: &mut LnnGatesVec, n: usize) { for i in 0..(n / 2) { gates.push(( StandardGate::CXGate, @@ -111,7 +111,7 @@ fn _append_cx_stage1(gates: &mut LnnGatesVec, n: isize) { } // A single layer of CX gates. -fn _append_cx_stage2(gates: &mut LnnGatesVec, n: isize) { +fn _append_cx_stage2(gates: &mut LnnGatesVec, n: usize) { for i in 0..(n / 2) { gates.push(( StandardGate::CXGate, @@ -191,8 +191,8 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat _append_phase_gate(s_gates[[j]], &mut gates, j) } - _append_cx_stage1(&mut gates, num_qubits as isize); - _append_cx_stage2(&mut gates, num_qubits as isize); + _append_cx_stage1(&mut gates, num_qubits); + _append_cx_stage2(&mut gates, num_qubits); s_gates = Array1::::zeros(num_qubits); } @@ -210,8 +210,8 @@ pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGat _append_phase_gate(s_gates[[j]], &mut gates, j) } - _append_cx_stage1(&mut gates, num_qubits as isize); + _append_cx_stage1(&mut gates, num_qubits); } - (num_qubits as usize, gates) + (num_qubits, gates) } From 7cee583651e2b9ce2427b8f5e4142656abd39a98 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Thu, 15 Aug 2024 21:47:02 +0200 Subject: [PATCH 08/18] added _inner to function name Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/lnn.rs | 2 +- crates/accelerate/src/synthesis/linear/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear/lnn.rs index 1922f3ef9020..17542064552a 100644 --- a/crates/accelerate/src/synthesis/linear/lnn.rs +++ b/crates/accelerate/src/synthesis/linear/lnn.rs @@ -155,7 +155,7 @@ fn _append_phase_gate(pat_val: usize, gates: &mut LnnGatesVec, qubit: usize) { // Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, // based on Maslov and Roetteler. -pub(super) fn synth_cz_depth_line_mr(matrix: ArrayView2) -> (usize, LnnGatesVec) { +pub(super) fn synth_cz_depth_line_mr_inner(matrix: ArrayView2) -> (usize, LnnGatesVec) { let num_qubits = matrix.raw_dim()[0]; let pats = _create_patterns(num_qubits as isize); diff --git a/crates/accelerate/src/synthesis/linear/mod.rs b/crates/accelerate/src/synthesis/linear/mod.rs index 68fc47018d98..062fa633316d 100644 --- a/crates/accelerate/src/synthesis/linear/mod.rs +++ b/crates/accelerate/src/synthesis/linear/mod.rs @@ -181,7 +181,7 @@ fn check_invertible_binary_matrix(py: Python, mat: PyReadonlyArray2) -> Py #[pyo3(signature = (mat))] fn synth_cz_depth_line_mr(py: Python, mat: PyReadonlyArray2) -> PyResult { let view = mat.as_array(); - let (num_qubits, lnn_gates) = lnn::synth_cz_depth_line_mr(view); + let (num_qubits, lnn_gates) = lnn::synth_cz_depth_line_mr_inner(view); CircuitData::from_standard_gates(py, num_qubits as u32, lnn_gates, Param::Float(0.0)) } From 2018812aeb9ec26b01f3bd1e5915d8f641879d81 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:31:50 +0200 Subject: [PATCH 09/18] changed directory structure Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/linear/mod.rs | 11 ----- .../lnn.rs => linear_phase/cz_depth_lnn.rs} | 42 ++----------------- .../src/synthesis/linear_phase/mod.rs | 35 ++++++++++++++++ crates/accelerate/src/synthesis/mod.rs | 5 +++ .../src/synthesis/permutation/mod.rs | 40 ++++++++++++++++++ qiskit/__init__.py | 1 + qiskit/synthesis/linear_phase/cz_depth_lnn.py | 2 +- 7 files changed, 85 insertions(+), 51 deletions(-) rename crates/accelerate/src/synthesis/{linear/lnn.rs => linear_phase/cz_depth_lnn.rs} (84%) create mode 100644 crates/accelerate/src/synthesis/linear_phase/mod.rs diff --git a/crates/accelerate/src/synthesis/linear/mod.rs b/crates/accelerate/src/synthesis/linear/mod.rs index 062fa633316d..08a0b1e104b3 100644 --- a/crates/accelerate/src/synthesis/linear/mod.rs +++ b/crates/accelerate/src/synthesis/linear/mod.rs @@ -13,9 +13,7 @@ use crate::QiskitError; use numpy::{IntoPyArray, PyArray2, PyReadonlyArray2, PyReadwriteArray2}; use pyo3::prelude::*; -use qiskit_circuit::{circuit_data::CircuitData, operations::Param}; -mod lnn; mod pmh; pub mod utils; @@ -177,14 +175,6 @@ fn check_invertible_binary_matrix(py: Python, mat: PyReadonlyArray2) -> Py Ok(out.to_object(py)) } -#[pyfunction] -#[pyo3(signature = (mat))] -fn synth_cz_depth_line_mr(py: Python, mat: PyReadonlyArray2) -> PyResult { - let view = mat.as_array(); - let (num_qubits, lnn_gates) = 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(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(gauss_elimination_with_perm))?; m.add_wrapped(wrap_pyfunction!(gauss_elimination))?; @@ -197,6 +187,5 @@ pub fn linear(m: &Bound) -> PyResult<()> { m.add_wrapped(wrap_pyfunction!(random_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(check_invertible_binary_matrix))?; m.add_wrapped(wrap_pyfunction!(pmh::synth_cnot_count_full_pmh))?; - m.add_wrapped(wrap_pyfunction!(synth_cz_depth_line_mr))?; Ok(()) } diff --git a/crates/accelerate/src/synthesis/linear/lnn.rs b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs similarity index 84% rename from crates/accelerate/src/synthesis/linear/lnn.rs rename to crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs index 17542064552a..d071d9771d8b 100644 --- a/crates/accelerate/src/synthesis/linear/lnn.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs @@ -22,9 +22,11 @@ use qiskit_circuit::{ }; 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 -type LnnGatesVec = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>; +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] @@ -91,44 +93,6 @@ fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { )) } -// A single layer of CX gates. -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. -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)], - )) - } -} - // 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 diff --git a/crates/accelerate/src/synthesis/linear_phase/mod.rs b/crates/accelerate/src/synthesis/linear_phase/mod.rs new file mode 100644 index 000000000000..b0763d04cc7c --- /dev/null +++ b/crates/accelerate/src/synthesis/linear_phase/mod.rs @@ -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) -> PyResult { + 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) -> PyResult<()> { + m.add_wrapped(wrap_pyfunction!(synth_cz_depth_line_mr))?; + Ok(()) +} diff --git a/crates/accelerate/src/synthesis/mod.rs b/crates/accelerate/src/synthesis/mod.rs index fae05c6739cc..6e22281e2250 100644 --- a/crates/accelerate/src/synthesis/mod.rs +++ b/crates/accelerate/src/synthesis/mod.rs @@ -12,6 +12,7 @@ mod clifford; pub mod linear; +pub mod linear_phase; mod permutation; use pyo3::prelude::*; @@ -21,6 +22,10 @@ pub fn synthesis(m: &Bound) -> 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)?; diff --git a/crates/accelerate/src/synthesis/permutation/mod.rs b/crates/accelerate/src/synthesis/permutation/mod.rs index 55dc3efe4a87..212b9fd75ea1 100644 --- a/crates/accelerate/src/synthesis/permutation/mod.rs +++ b/crates/accelerate/src/synthesis/permutation/mod.rs @@ -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. @@ -114,6 +116,44 @@ 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)], + )) + } +} + pub fn permutation(m: &Bound) -> PyResult<()> { m.add_function(wrap_pyfunction!(_validate_permutation, m)?)?; m.add_function(wrap_pyfunction!(_inverse_pattern, m)?)?; diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 6091bfa90346..6a8df393307e 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -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 diff --git a/qiskit/synthesis/linear_phase/cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cz_depth_lnn.py index d1458f76f39a..419aec806f2e 100644 --- a/qiskit/synthesis/linear_phase/cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cz_depth_lnn.py @@ -25,7 +25,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit._accelerate.synthesis.linear import ( +from qiskit._accelerate.synthesis.linear_phase import ( synth_cz_depth_line_mr as synth_cz_depth_line_mr_inner, ) From 34f46d693a5262ffb4049c400aa17753932fc8e6 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:36:53 +0200 Subject: [PATCH 10/18] port _append_reverse_permutation_lnn_kms Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- crates/accelerate/src/synthesis/permutation/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/accelerate/src/synthesis/permutation/mod.rs b/crates/accelerate/src/synthesis/permutation/mod.rs index 212b9fd75ea1..fad98fde8570 100644 --- a/crates/accelerate/src/synthesis/permutation/mod.rs +++ b/crates/accelerate/src/synthesis/permutation/mod.rs @@ -154,6 +154,19 @@ pub(crate) fn _append_cx_stage2(gates: &mut LnnGatesVec, n: usize) { } } +// 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) -> PyResult<()> { m.add_function(wrap_pyfunction!(_validate_permutation, m)?)?; m.add_function(wrap_pyfunction!(_inverse_pattern, m)?)?; From 21751bdaf7343cf1d689702ec445a539ac00bdbb Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Thu, 15 Aug 2024 22:46:55 +0200 Subject: [PATCH 11/18] concise function, added sdg comment, all to usize Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- .../synthesis/linear_phase/cz_depth_lnn.rs | 61 ++++++++----------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs index d071d9771d8b..7a941276b2c4 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs @@ -30,7 +30,7 @@ pub(crate) type LnnGatesVec = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec< // 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: isize) -> Vec { +fn _odd_pattern1(n: usize) -> Vec { 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])) @@ -39,7 +39,7 @@ fn _odd_pattern1(n: isize) -> Vec { // 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: isize) -> Vec { +fn _odd_pattern2(n: usize) -> Vec { (0..((n - 1) / 2)) .flat_map(|i| [(2 * i + 2); 2]) .chain((0..((n - 3) / 2)).flat_map(|i| [n - 2 * i - 2; 2])) @@ -49,7 +49,7 @@ fn _odd_pattern2(n: isize) -> Vec { // 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: isize) -> Vec { +fn _even_pattern1(n: usize) -> Vec { 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])) @@ -59,7 +59,7 @@ fn _even_pattern1(n: isize) -> Vec { // 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: isize) -> Vec { +fn _even_pattern2(n: usize) -> Vec { (0..((n - 2) / 2)) .flat_map(|i| [2 * (i + 1); 2]) .chain((0..(n / 2)).flat_map(|i| [(n - 2 * i - 1); 2])) @@ -67,7 +67,7 @@ fn _even_pattern2(n: isize) -> Vec { } // Creating the patterns for the phase layers. -fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { +fn _create_patterns(n: usize) -> HashMap<(usize, usize), (usize, usize)> { let (pat1, pat2) = if n % 2 == 0 { (_even_pattern1(n), _even_pattern2(n)) } else { @@ -84,10 +84,7 @@ fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { (0..(n / 2)).cartesian_product(0..n).map(|(layer, i)| { ( (layer + 1, i), - ( - pat1[(ind - (2 * layer) + i) as usize], - pat2[((2 * layer) + i) as usize], - ), + (pat1[ind - (2 * layer) + i], pat2[(2 * layer) + i]), ) }), )) @@ -96,24 +93,16 @@ fn _create_patterns(n: isize) -> HashMap<(isize, isize), (isize, isize)> { // 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 - if pat_val % 4 == 1 { - gates.push(( - StandardGate::SdgGate, - smallvec![], - smallvec![Qubit(qubit as u32)], - )) - } else if pat_val % 4 == 2 { - gates.push(( - StandardGate::ZGate, - smallvec![], - smallvec![Qubit(qubit as u32)], - )) - } else if pat_val % 4 == 3 { - gates.push(( - StandardGate::SGate, - smallvec![], - smallvec![Qubit(qubit as u32)], - )) + 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 + _ => unreachable!(), // unreachable as we have modulo 4 + }; + gates.push((gate, smallvec![], smallvec![Qubit(qubit as u32)])); } } @@ -121,12 +110,12 @@ fn _append_phase_gate(pat_val: usize, gates: &mut LnnGatesVec, qubit: usize) { // based on Maslov and Roetteler. pub(super) fn synth_cz_depth_line_mr_inner(matrix: ArrayView2) -> (usize, LnnGatesVec) { let num_qubits = matrix.raw_dim()[0]; - let pats = _create_patterns(num_qubits as isize); + 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::::zeros(num_qubits); - let mut patlist: Vec<(isize, isize)> = Vec::new(); + let mut patlist: Vec<(usize, usize)> = Vec::new(); let mut gates = LnnGatesVec::new(); @@ -136,18 +125,19 @@ pub(super) fn synth_cz_depth_line_mr_inner(matrix: ArrayView2) -> (usize, // CZ(i,j) gate s_gates[[i]] += 2; // qc.z[i] s_gates[[j]] += 2; // qc.z[j] - patlist.push((i as isize, j as isize - 1)); - patlist.push((i as isize, j as isize)); - patlist.push((i as isize + 1, j as isize - 1)); - patlist.push((i as isize + 1, j as isize)); + 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 as isize, j as isize)]; + 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] } @@ -164,8 +154,9 @@ pub(super) fn synth_cz_depth_line_mr_inner(matrix: ArrayView2) -> (usize, let i = num_qubits / 2; for j in 0..num_qubits { - let pat_val = pats[&(i as isize, j as isize)]; + 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] From 613c9be3e7643ca53443352205a377314a175f91 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Fri, 16 Aug 2024 23:44:23 +0200 Subject: [PATCH 12/18] port synth_permutation_reverse_lnn_kms to rust Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- .../src/synthesis/permutation/mod.rs | 26 ++++++++++++- .../permutation/permutation_reverse_lnn.py | 38 +++---------------- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/crates/accelerate/src/synthesis/permutation/mod.rs b/crates/accelerate/src/synthesis/permutation/mod.rs index fad98fde8570..9a3a4b53a689 100644 --- a/crates/accelerate/src/synthesis/permutation/mod.rs +++ b/crates/accelerate/src/synthesis/permutation/mod.rs @@ -154,8 +154,21 @@ pub(crate) fn _append_cx_stage2(gates: &mut LnnGatesVec, n: usize) { } } -// Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures -// using Kutin, Moulton, Smithline method. +/// Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures +/// using Kutin, Moulton, Smithline method. +// +/// Synthesis algorithm for reverse permutation from [1], section 5. +/// This algorithm synthesizes the reverse permutation on :math:`n` qubits over +/// a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. +/// +/// Args: +/// gates: Vector of gates representing the original quantum circuit to be modified. +/// num_qubits: The number of qubits. +/// +/// References: +/// 1. Kutin, S., Moulton, D. P., Smithline, L., +/// *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), +/// `arXiv:quant-ph/0701194 `_ 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); @@ -167,11 +180,20 @@ fn _append_reverse_permutation_lnn_kms(gates: &mut LnnGatesVec, num_qubits: usiz } } +#[pyfunction] +#[pyo3(signature = (num_qubits))] +fn synth_permutation_reverse_lnn_kms(py: Python, num_qubits: usize) -> PyResult { + let mut gates = LnnGatesVec::new(); + _append_reverse_permutation_lnn_kms(&mut gates, num_qubits); + CircuitData::from_standard_gates(py, num_qubits as u32, gates, Param::Float(0.0)) +} + pub fn permutation(m: &Bound) -> 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)?)?; m.add_function(wrap_pyfunction!(_synth_permutation_acg, m)?)?; m.add_function(wrap_pyfunction!(_synth_permutation_depth_lnn_kms, m)?)?; + m.add_function(wrap_pyfunction!(synth_permutation_reverse_lnn_kms, m)?)?; Ok(()) } diff --git a/qiskit/synthesis/permutation/permutation_reverse_lnn.py b/qiskit/synthesis/permutation/permutation_reverse_lnn.py index 26287a06177e..2ad56a1322c3 100644 --- a/qiskit/synthesis/permutation/permutation_reverse_lnn.py +++ b/qiskit/synthesis/permutation/permutation_reverse_lnn.py @@ -14,6 +14,9 @@ """ from qiskit.circuit import QuantumCircuit +from qiskit._accelerate.synthesis.permutation import ( + synth_permutation_reverse_lnn_kms as synth_permutation_reverse_lnn_kms_inner +) def _append_cx_stage1(qc, n): @@ -34,35 +37,6 @@ def _append_cx_stage2(qc, n): return qc -def _append_reverse_permutation_lnn_kms(qc: QuantumCircuit, num_qubits: int) -> None: - """ - Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures - using Kutin, Moulton, Smithline method. - - Synthesis algorithm for reverse permutation from [1], section 5. - This algorithm synthesizes the reverse permutation on :math:`n` qubits over - a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. - - Args: - qc: The original quantum circuit. - num_qubits: The number of qubits. - - Returns: - The quantum circuit with appended reverse permutation. - - References: - 1. Kutin, S., Moulton, D. P., Smithline, L., - *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), - `arXiv:quant-ph/0701194 `_ - """ - - for _ in range((num_qubits + 1) // 2): - _append_cx_stage1(qc, num_qubits) - _append_cx_stage2(qc, num_qubits) - if (num_qubits % 2) == 0: - _append_cx_stage1(qc, num_qubits) - - def synth_permutation_reverse_lnn_kms(num_qubits: int) -> QuantumCircuit: """ Synthesize reverse permutation for linear nearest-neighbor architectures using @@ -84,7 +58,5 @@ def synth_permutation_reverse_lnn_kms(num_qubits: int) -> QuantumCircuit: `arXiv:quant-ph/0701194 `_ """ - qc = QuantumCircuit(num_qubits) - _append_reverse_permutation_lnn_kms(qc, num_qubits) - - return qc + # Call Rust implementation + return QuantumCircuit._from_circuit_data(synth_permutation_reverse_lnn_kms_inner(num_qubits)) From 15a2303fe8eb87fa5f79a2cb2c27ffed4ad99c30 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Fri, 16 Aug 2024 23:46:18 +0200 Subject: [PATCH 13/18] cleanup Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- .../permutation/permutation_reverse_lnn.py | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/qiskit/synthesis/permutation/permutation_reverse_lnn.py b/qiskit/synthesis/permutation/permutation_reverse_lnn.py index 2ad56a1322c3..bc8a8c0023ba 100644 --- a/qiskit/synthesis/permutation/permutation_reverse_lnn.py +++ b/qiskit/synthesis/permutation/permutation_reverse_lnn.py @@ -15,28 +15,10 @@ from qiskit.circuit import QuantumCircuit from qiskit._accelerate.synthesis.permutation import ( - synth_permutation_reverse_lnn_kms as synth_permutation_reverse_lnn_kms_inner + synth_permutation_reverse_lnn_kms as synth_permutation_reverse_lnn_kms_inner, ) -def _append_cx_stage1(qc, n): - """A single layer of CX gates.""" - for i in range(n // 2): - qc.cx(2 * i, 2 * i + 1) - for i in range((n + 1) // 2 - 1): - qc.cx(2 * i + 2, 2 * i + 1) - return qc - - -def _append_cx_stage2(qc, n): - """A single layer of CX gates.""" - for i in range(n // 2): - qc.cx(2 * i + 1, 2 * i) - for i in range((n + 1) // 2 - 1): - qc.cx(2 * i + 1, 2 * i + 2) - return qc - - def synth_permutation_reverse_lnn_kms(num_qubits: int) -> QuantumCircuit: """ Synthesize reverse permutation for linear nearest-neighbor architectures using From f97ab51cb5b1a5c52a7c1a04db3d1ac6d7bbb0b4 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Fri, 16 Aug 2024 23:49:57 +0200 Subject: [PATCH 14/18] readded Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- .../permutation/permutation_reverse_lnn.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/qiskit/synthesis/permutation/permutation_reverse_lnn.py b/qiskit/synthesis/permutation/permutation_reverse_lnn.py index bc8a8c0023ba..f214fd7ce294 100644 --- a/qiskit/synthesis/permutation/permutation_reverse_lnn.py +++ b/qiskit/synthesis/permutation/permutation_reverse_lnn.py @@ -19,6 +19,53 @@ ) +def _append_cx_stage1(qc, n): + """A single layer of CX gates.""" + for i in range(n // 2): + qc.cx(2 * i, 2 * i + 1) + for i in range((n + 1) // 2 - 1): + qc.cx(2 * i + 2, 2 * i + 1) + return qc + + +def _append_cx_stage2(qc, n): + """A single layer of CX gates.""" + for i in range(n // 2): + qc.cx(2 * i + 1, 2 * i) + for i in range((n + 1) // 2 - 1): + qc.cx(2 * i + 1, 2 * i + 2) + return qc + + +def _append_reverse_permutation_lnn_kms(qc: QuantumCircuit, num_qubits: int) -> None: + """ + Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures + using Kutin, Moulton, Smithline method. + + Synthesis algorithm for reverse permutation from [1], section 5. + This algorithm synthesizes the reverse permutation on :math:`n` qubits over + a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. + + Args: + qc: The original quantum circuit. + num_qubits: The number of qubits. + + Returns: + The quantum circuit with appended reverse permutation. + + References: + 1. Kutin, S., Moulton, D. P., Smithline, L., + *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), + `arXiv:quant-ph/0701194 `_ + """ + + for _ in range((num_qubits + 1) // 2): + _append_cx_stage1(qc, num_qubits) + _append_cx_stage2(qc, num_qubits) + if (num_qubits % 2) == 0: + _append_cx_stage1(qc, num_qubits) + + def synth_permutation_reverse_lnn_kms(num_qubits: int) -> QuantumCircuit: """ Synthesize reverse permutation for linear nearest-neighbor architectures using From b10915dec659334ea70512741fa7d8fe8ed65588 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Sat, 17 Aug 2024 00:01:03 +0200 Subject: [PATCH 15/18] release notes and simplified comment Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- .../accelerate/src/synthesis/permutation/mod.rs | 17 ++--------------- ...-depth-line-mr-to-rust-1376d5a41948112a.yaml | 8 ++++++++ 2 files changed, 10 insertions(+), 15 deletions(-) create mode 100644 releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml diff --git a/crates/accelerate/src/synthesis/permutation/mod.rs b/crates/accelerate/src/synthesis/permutation/mod.rs index 9a3a4b53a689..199605141b12 100644 --- a/crates/accelerate/src/synthesis/permutation/mod.rs +++ b/crates/accelerate/src/synthesis/permutation/mod.rs @@ -154,21 +154,8 @@ pub(crate) fn _append_cx_stage2(gates: &mut LnnGatesVec, n: usize) { } } -/// Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures -/// using Kutin, Moulton, Smithline method. -// -/// Synthesis algorithm for reverse permutation from [1], section 5. -/// This algorithm synthesizes the reverse permutation on :math:`n` qubits over -/// a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. -/// -/// Args: -/// gates: Vector of gates representing the original quantum circuit to be modified. -/// num_qubits: The number of qubits. -/// -/// References: -/// 1. Kutin, S., Moulton, D. P., Smithline, L., -/// *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), -/// `arXiv:quant-ph/0701194 `_ +// 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); diff --git a/releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml b/releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml new file mode 100644 index 000000000000..7d8a166dbd69 --- /dev/null +++ b/releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml @@ -0,0 +1,8 @@ +--- +features_synthesis: + - | + Port :func: `.synth_cz_depth_line_mr` to Rust. This function synthesizes a CZ circuit for linear nearest neighbour (LNN) connectivity, + based on Maslov and Roetteler. On a 350x350 binary matrix, the Rust implementation yields a speedup of about 30 times. + + Additionally, port :func: `.synth_permutation_reverse_lnn_kms` to Rust, which synthesizes a reverse permutation for linear nearest-neighbor architectures using + Kutin, Moulton, Smithline method. From 8e2ae0cdfe601bff30b82ab3b090736c4afcfbe2 Mon Sep 17 00:00:00 2001 From: Joseph Loftin <65878716+jlofti@users.noreply.github.com> Date: Sun, 18 Aug 2024 18:35:16 +0200 Subject: [PATCH 16/18] Update releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml Co-authored-by: Alexander Ivrii --- ...t-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml b/releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml index 7d8a166dbd69..ee4c6933a269 100644 --- a/releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml +++ b/releasenotes/notes/port-synth-cz-depth-line-mr-to-rust-1376d5a41948112a.yaml @@ -1,8 +1,6 @@ --- features_synthesis: - | - Port :func: `.synth_cz_depth_line_mr` to Rust. This function synthesizes a CZ circuit for linear nearest neighbour (LNN) connectivity, - based on Maslov and Roetteler. On a 350x350 binary matrix, the Rust implementation yields a speedup of about 30 times. - - Additionally, port :func: `.synth_permutation_reverse_lnn_kms` to Rust, which synthesizes a reverse permutation for linear nearest-neighbor architectures using - Kutin, Moulton, Smithline method. + Port :func: `.synth_cz_depth_line_mr` to Rust. This function synthesizes a CZ circuit for linear nearest neighbor (LNN) connectivity, based on the Maslov and Roetteler method. On a 350x350 binary matrix, the Rust implementation yields a speedup of about 30 times. + - | + Port :func: `.synth_permutation_reverse_lnn_kms` to Rust, which synthesizes a reverse permutation for linear nearest-neighbor architecture using the Kutin, Moulton, Smithline method. From ec231c21d7e084cc33b8e80bd6ecf7f42a3d5fd2 Mon Sep 17 00:00:00 2001 From: jlofti <65878716+jlofti@users.noreply.github.com> Date: Sun, 18 Aug 2024 18:51:42 +0200 Subject: [PATCH 17/18] promoted to docstring and added new docstrings Signed-off-by: jlofti <65878716+jlofti@users.noreply.github.com> --- .../synthesis/linear_phase/cz_depth_lnn.rs | 24 +++++++++---------- .../src/synthesis/linear_phase/mod.rs | 11 +++++++++ .../src/synthesis/permutation/mod.rs | 19 +++++++++++---- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs index 7a941276b2c4..ac4a553f5388 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs @@ -28,8 +28,8 @@ use crate::synthesis::permutation::{_append_cx_stage1, _append_cx_stage2}; // 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] +/// 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 { once(n - 2) .chain((0..((n - 3) / 2)).flat_map(|i| [(n - 2 * i - 4); 2])) @@ -37,8 +37,8 @@ fn _odd_pattern1(n: usize) -> Vec { .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] +/// 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 { (0..((n - 1) / 2)) .flat_map(|i| [(2 * i + 2); 2]) @@ -47,8 +47,8 @@ fn _odd_pattern2(n: usize) -> Vec { .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] +/// 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 { once(n - 1) .chain((0..((n - 2) / 2)).flat_map(|i| [n - 2 * i - 3; 2])) @@ -57,8 +57,8 @@ fn _even_pattern1(n: usize) -> Vec { .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] +/// 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 { (0..((n - 2) / 2)) .flat_map(|i| [2 * (i + 1); 2]) @@ -66,7 +66,7 @@ fn _even_pattern2(n: usize) -> Vec { .collect() } -// Creating the patterns for the phase layers. +/// 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)) @@ -90,7 +90,7 @@ fn _create_patterns(n: usize) -> HashMap<(usize, usize), (usize, usize)> { )) } -// Appends correct phase gate during CZ synthesis +/// 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; @@ -106,8 +106,8 @@ fn _append_phase_gate(pat_val: usize, gates: &mut LnnGatesVec, qubit: usize) { } } -// Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, -// based on Maslov and Roetteler. +/// Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, +/// based on Maslov and Roetteler. pub(super) fn synth_cz_depth_line_mr_inner(matrix: ArrayView2) -> (usize, LnnGatesVec) { let num_qubits = matrix.raw_dim()[0]; let pats = _create_patterns(num_qubits); diff --git a/crates/accelerate/src/synthesis/linear_phase/mod.rs b/crates/accelerate/src/synthesis/linear_phase/mod.rs index b0763d04cc7c..fd95985e1025 100644 --- a/crates/accelerate/src/synthesis/linear_phase/mod.rs +++ b/crates/accelerate/src/synthesis/linear_phase/mod.rs @@ -21,6 +21,17 @@ use qiskit_circuit::{circuit_data::CircuitData, operations::Param}; pub(crate) mod cz_depth_lnn; +/// Synthesis of a CZ circuit for linear nearest neighbor (LNN) connectivity, +/// based on Maslov and Roetteler. +/// +/// Note that this method *reverts* the order of qubits in the circuit, +/// and returns a circuit containing :class:`.CXGate`\s and phase gates +/// (:class:`.SGate`, :class:`.SdgGate` or :class:`.ZGate`). +/// +/// References: +/// 1. Dmitri Maslov, Martin Roetteler, +/// *Shorter stabilizer circuits via Bruhat decomposition and quantum circuit transformations*, +/// `arXiv:1705.09176 `_. #[pyfunction] #[pyo3(signature = (mat))] fn synth_cz_depth_line_mr(py: Python, mat: PyReadonlyArray2) -> PyResult { diff --git a/crates/accelerate/src/synthesis/permutation/mod.rs b/crates/accelerate/src/synthesis/permutation/mod.rs index 199605141b12..2f84776cb5fd 100644 --- a/crates/accelerate/src/synthesis/permutation/mod.rs +++ b/crates/accelerate/src/synthesis/permutation/mod.rs @@ -116,7 +116,7 @@ pub fn _synth_permutation_depth_lnn_kms( ) } -// A single layer of CX gates. +/// 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(( @@ -135,7 +135,7 @@ pub(crate) fn _append_cx_stage1(gates: &mut LnnGatesVec, n: usize) { } } -// A single layer of CX gates. +/// 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(( @@ -154,8 +154,8 @@ pub(crate) fn _append_cx_stage2(gates: &mut LnnGatesVec, n: usize) { } } -// Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures -// using Kutin, Moulton, Smithline method. +/// 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); @@ -167,6 +167,17 @@ fn _append_reverse_permutation_lnn_kms(gates: &mut LnnGatesVec, num_qubits: usiz } } +/// Synthesize reverse permutation for linear nearest-neighbor architectures using +/// Kutin, Moulton, Smithline method. +/// +/// Synthesis algorithm for reverse permutation from [1], section 5. +/// This algorithm synthesizes the reverse permutation on :math:`n` qubits over +/// a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. +/// +/// References: +/// 1. Kutin, S., Moulton, D. P., Smithline, L., +/// *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), +/// `arXiv:quant-ph/0701194 `_ #[pyfunction] #[pyo3(signature = (num_qubits))] fn synth_permutation_reverse_lnn_kms(py: Python, num_qubits: usize) -> PyResult { From dd7a2c1560f0806b2abeb9267a0a057d22d6713f Mon Sep 17 00:00:00 2001 From: Joseph Loftin <65878716+jlofti@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:07:11 +0200 Subject: [PATCH 18/18] Update crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs Co-authored-by: Julien Gacon --- crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs index ac4a553f5388..df01c1ed6fa8 100644 --- a/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs +++ b/crates/accelerate/src/synthesis/linear_phase/cz_depth_lnn.rs @@ -99,7 +99,6 @@ fn _append_phase_gate(pat_val: usize, gates: &mut LnnGatesVec, qubit: usize) { 1 => StandardGate::SdgGate, 2 => StandardGate::ZGate, 3 => StandardGate::SGate, - // cover 2 and 3 _ => unreachable!(), // unreachable as we have modulo 4 }; gates.push((gate, smallvec![], smallvec![Qubit(qubit as u32)]));