Skip to content

Commit

Permalink
Implements Shelly's suggestions
Browse files Browse the repository at this point in the history
  • Loading branch information
Cryoris committed Jul 1, 2024
1 parent 2bdaa79 commit 0743d23
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 54 deletions.
23 changes: 23 additions & 0 deletions crates/accelerate/src/synthesis/permutation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,33 @@ pub fn _synth_permutation_basic(py: Python, pattern: PyArrayLike1<i64>) -> PyRes
)
}

#[pyfunction]
#[pyo3(signature = (pattern))]
fn _synth_permutation_acg(py: Python, pattern: PyArrayLike1<i64>) -> PyResult<CircuitData> {
let view = pattern.as_array();
let num_qubits = view.len();
let cycles = utils::pattern_to_cycles(&view, &true);
let swaps = utils::decompose_cycles(&cycles);

CircuitData::from_standard_gates(
py,
num_qubits as u32,
swaps.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)?)?;
m.add_function(wrap_pyfunction!(_synth_permutation_acg, m)?)?;
Ok(())
}
58 changes: 11 additions & 47 deletions crates/accelerate/src/synthesis/permutation/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
// 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;
Expand Down Expand Up @@ -64,19 +63,19 @@ pub fn invert(pattern: &ArrayView1<i64>) -> Array1<usize> {
/// 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)> {
pub fn get_ordered_swap(pattern: &ArrayView1<i64>) -> Vec<(usize, usize)> {
let mut permutation: Vec<usize> = pattern.iter().map(|&x| x as usize).collect();
let mut index_map = invert(pattern);

let n = permutation.len();
let mut swaps: Vec<(i64, i64)> = Vec::with_capacity(n);
let mut swaps: Vec<(usize, usize)> = Vec::with_capacity(n);
for ii in 0..n {
let val = permutation[ii];
if val == ii {
continue;
}
let jj = index_map[ii];
swaps.push((ii as i64, jj as i64));
swaps.push((ii, jj));
(permutation[ii], permutation[jj]) = (permutation[jj], permutation[ii]);
index_map[val] = jj;
index_map[ii] = ii;
Expand All @@ -86,6 +85,10 @@ pub fn get_ordered_swap(pattern: &ArrayView1<i64>) -> Vec<(i64, i64)> {
swaps
}

/// Explore cycles in a permutation pattern. This is probably best explained in an
/// example: let a pattern be [1, 2, 3, 0, 4, 6, 5], then it contains the two
/// cycles [1, 2, 3, 0] and [6, 5]. The index [4] does not perform a permutation and does
/// therefore not create a cycle.
pub fn pattern_to_cycles(pattern: &ArrayView1<i64>, invert_order: &bool) -> Vec<Vec<usize>> {
// vector keeping track of which elements in the permutation pattern have been visited
let mut explored: Vec<bool> = vec![false; pattern.len()];
Expand Down Expand Up @@ -119,7 +122,10 @@ pub fn pattern_to_cycles(pattern: &ArrayView1<i64>, invert_order: &bool) -> Vec<
cycles
}

/// Given a disjoint cycle decomposition, decomposes every cycle into a SWAP
/// Given a disjoint cycle decomposition of a permutation pattern (see the function
/// ``pattern_to_cycles``), decomposes every cycle into a series of SWAPs to implement it.
/// In combination with ``pattern_to_cycle``, this function allows to implement a
/// full permutation pattern by applying SWAP gates on the returned index-pairs.
pub fn decompose_cycles(cycles: &Vec<Vec<usize>>) -> Vec<(usize, usize)> {
let mut swaps: Vec<(usize, usize)> = Vec::new();

Expand All @@ -143,45 +149,3 @@ pub fn decompose_cycles(cycles: &Vec<Vec<usize>>) -> Vec<(usize, usize)> {

swaps
}

/// 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))
}

/// Decompose a SWAP pattern into a series of SWAP gate indices to implement them.
/// For example, let the pattern be [1, 2, 3, 4, 0, 6, 5], which contains the two cycles
/// [1, 2, 3, 4, 0] and [6, 5]. These can then be implemented with the SWAPs
/// [(0, 3), (1, 2), (0, 4), (1, 3)], respectively [(6, 5)].
/// If ``invert`` is True, reverse the indices before computing the SWAPs.
#[pyfunction]
#[pyo3(signature = (pattern, invert_order=true))]
fn _pattern_to_swaps(
py: Python,
pattern: PyArrayLike1<i64>,
invert_order: bool,
) -> PyResult<PyObject> {
let view = pattern.as_array();
let cycles = pattern_to_cycles(&view, &invert_order);
let swaps = decompose_cycles(&cycles);
let swaps_i64: Vec<(i64, i64)> = swaps.iter().map(|&x| (x.0 as i64, x.1 as i64)).collect();
Ok(swaps_i64.to_object(py))
}

#[pymodule]
pub fn permutation(m: &Bound<PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(_get_ordered_swap, m)?)?;
m.add_function(wrap_pyfunction!(_pattern_to_swaps, m)?)?;
Ok(())
}
6 changes: 4 additions & 2 deletions qiskit/synthesis/permutation/permutation_full.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

import numpy as np
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit._accelerate.permutation import _synth_permutation_acg
from qiskit._accelerate.synthesis.permutation import _synth_permutation_basic
from qiskit._accelerate.synthesis.permutation import (
_synth_permutation_basic,
_synth_permutation_acg,
)


def synth_permutation_basic(pattern: list[int] | np.ndarray[int]) -> QuantumCircuit:
Expand Down
6 changes: 1 addition & 5 deletions qiskit/synthesis/permutation/permutation_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,4 @@
"""Utility functions for handling permutations."""

# pylint: disable=unused-import
from qiskit._accelerate.synthesis.permutation import (
_inverse_pattern,
_validate_permutation,
_pattern_to_swaps,
)
from qiskit._accelerate.synthesis.permutation import _inverse_pattern, _validate_permutation

0 comments on commit 0743d23

Please sign in to comment.