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

Prepare qiskit/transpiler/graysynth.py for deprecation in next release #9795

Merged
merged 18 commits into from
Apr 17, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
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
2 changes: 1 addition & 1 deletion qiskit/synthesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@
synth_cnot_count_full_pmh,
synth_cnot_depth_line_kms,
)
from .linear_phase import synth_cz_depth_line_mr
from .linear_phase import synth_cz_depth_line_mr, synth_cnot_phase_aam
from .clifford import (
synth_clifford_full,
synth_clifford_ag,
Expand Down
2 changes: 1 addition & 1 deletion qiskit/synthesis/linear/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

"""Module containing cnot circuits"""

from .graysynth import graysynth, synth_cnot_count_full_pmh
from .cnot_synth import synth_cnot_count_full_pmh
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved
from .linear_depth_lnn import synth_cnot_depth_line_kms
from .linear_matrix_utils import (
random_invertible_binary_matrix,
Expand Down
141 changes: 141 additions & 0 deletions qiskit/synthesis/linear/cnot_synth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
#
# 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.

"""
Implementation of the GraySynth algorithm for synthesizing CNOT-Phase
circuits with efficient CNOT cost, and the Patel-Hayes-Markov algorithm
for optimal synthesis of linear (CNOT-only) reversible circuits.
"""

import copy
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.exceptions import QiskitError


def synth_cnot_count_full_pmh(state, section_size=2):
"""
Synthesize linear reversible circuits for all-to-all architecture
using Patel, Markov and Hayes method.
This function is an implementation of the Patel, Markov and Hayes algorithm from [1]
for optimal synthesis of linear reversible circuits for all-to-all architecture,
as specified by an n x n matrix.
Args:
state (list[list] or ndarray): n x n boolean invertible matrix, describing the state
of the input circuit
section_size (int): the size of each section, used in the
Patel–Markov–Hayes algorithm [1]. section_size must be a factor of num_qubits.
Returns:
QuantumCircuit: a CX-only circuit implementing the linear transformation.
Raises:
QiskitError: when variable "state" isn't of type numpy.ndarray
References:
1. Patel, Ketan N., Igor L. Markov, and John P. Hayes,
*Optimal synthesis of linear reversible circuits*,
Quantum Information & Computation 8.3 (2008): 282-294.
`arXiv:quant-ph/0302002 [quant-ph] <https://arxiv.org/abs/quant-ph/0302002>`_
"""
if not isinstance(state, (list, np.ndarray)):
raise QiskitError(
"state should be of type list or numpy.ndarray, "
"but was of the type {}".format(type(state))
)
state = np.array(state)
# Synthesize lower triangular part
[state, circuit_l] = _lwr_cnot_synth(state, section_size)
state = np.transpose(state)
# Synthesize upper triangular part
[state, circuit_u] = _lwr_cnot_synth(state, section_size)
circuit_l.reverse()
for i in circuit_u:
i.reverse()
# Convert the list into a circuit of C-NOT gates
circ = QuantumCircuit(state.shape[0])
for i in circuit_u + circuit_l:
circ.cx(i[0], i[1])
return circ


def _lwr_cnot_synth(state, section_size):
"""
This function is a helper function of the algorithm for optimal synthesis
of linear reversible circuits (the Patel–Markov–Hayes algorithm). It works
like gaussian elimination, except that it works a lot faster, and requires
fewer steps (and therefore fewer CNOTs). It takes the matrix "state" and
splits it into sections of size section_size. Then it eliminates all non-zero
sub-rows within each section, which are the same as a non-zero sub-row
above. Once this has been done, it continues with normal gaussian elimination.
The benefit is that with small section sizes (m), most of the sub-rows will
be cleared in the first step, resulting in a factor m fewer row row operations
during Gaussian elimination.
The algorithm is described in detail in the following paper
"Optimal synthesis of linear reversible circuits."
Patel, Ketan N., Igor L. Markov, and John P. Hayes.
Quantum Information & Computation 8.3 (2008): 282-294.
Note:
This implementation tweaks the Patel, Markov, and Hayes algorithm by adding
a "back reduce" which adds rows below the pivot row with a high degree of
overlap back to it. The intuition is to avoid a high-weight pivot row
increasing the weight of lower rows.
Args:
state (ndarray): n x n matrix, describing a linear quantum circuit
section_size (int): the section size the matrix columns are divided into
Returns:
numpy.matrix: n by n matrix, describing the state of the output circuit
list: a k by 2 list of C-NOT operations that need to be applied
"""
circuit = []
num_qubits = state.shape[0]
cutoff = 1

# Iterate over column sections
for sec in range(1, int(np.floor(num_qubits / section_size) + 1)):
# Remove duplicate sub-rows in section sec
patt = {}
for row in range((sec - 1) * section_size, num_qubits):
sub_row_patt = copy.deepcopy(state[row, (sec - 1) * section_size : sec * section_size])
if np.sum(sub_row_patt) == 0:
continue
if str(sub_row_patt) not in patt:
patt[str(sub_row_patt)] = row
else:
state[row, :] ^= state[patt[str(sub_row_patt)], :]
circuit.append([patt[str(sub_row_patt)], row])
# Use gaussian elimination for remaining entries in column section
for col in range((sec - 1) * section_size, sec * section_size):
# Check if 1 on diagonal
diag_one = 1
if state[col, col] == 0:
diag_one = 0
# Remove ones in rows below column col
for row in range(col + 1, num_qubits):
if state[row, col] == 1:
if diag_one == 0:
state[col, :] ^= state[row, :]
circuit.append([row, col])
diag_one = 1
state[row, :] ^= state[col, :]
circuit.append([col, row])
# Back reduce the pivot row using the current row
if sum(state[col, :] & state[row, :]) > cutoff:
state[col, :] ^= state[row, :]
circuit.append([row, col])
return [state, circuit]
1 change: 1 addition & 0 deletions qiskit/synthesis/linear_phase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
"""Module containing cnot-phase circuits"""

from .cz_depth_lnn import synth_cz_depth_line_mr
from .cnot_phase_synth import synth_cnot_phase_aam
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,21 @@
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.exceptions import QiskitError
from qiskit.synthesis.linear import synth_cnot_count_full_pmh


def graysynth(cnots, angles, section_size=2):
"""This function is an implementation of the GraySynth algorithm.
def synth_cnot_phase_aam(cnots, angles, section_size=2):
"""This function is an implementation of the GraySynth algorithm of
Amy, Azimadeh and Mosca.

GraySynth is a heuristic algorithm for synthesizing small parity networks.
GraySynth is a heuristic algorithm from [1] for synthesizing small parity networks.
It is inspired by Gray codes. Given a set of binary strings S
(called "cnots" bellow), the algorithm synthesizes a parity network for S by
repeatedly choosing an index i to expand and then effectively recursing on
the co-factors S_0 and S_1, consisting of the strings y in S,
with y_i = 0 or 1 respectively. As a subset S is recursively expanded,
CNOT gates are applied so that a designated target bit contains the
(partial) parity ksi_y(x) where y_i = 1 if and only if y'_i = 1 for for all
(partial) parity ksi_y(x) where y_i = 1 if and only if y'_i = 1 for all
y' in S. If S is a singleton {y'}, then y = y', hence the target bit contains
the value ksi_y'(x) as desired.

Expand All @@ -42,10 +44,7 @@ def graysynth(cnots, angles, section_size=2):
of bits. This allows the algorithm to avoid the 'backtracking' inherent in
uncomputing-based methods.

The algorithm is described in detail in the following paper in section 4:
"On the controlled-NOT complexity of controlled-NOT–phase circuits."
Amy, Matthew, Parsiad Azimzadeh, and Michele Mosca.
Quantum Science and Technology 4.1 (2018): 015002.
The algorithm is described in detail in section 4 of [1].

Args:
cnots (list[list]): a matrix whose columns are the parities to be synthesized
Expand All @@ -62,17 +61,23 @@ def graysynth(cnots, angles, section_size=2):

angles (list): a list containing all the phase-shift gates which are
to be applied, in the same order as in "cnots". A number is
interpreted as the angle of u1(angle), otherwise the elements
interpreted as the angle of p(angle), otherwise the elements
have to be 't', 'tdg', 's', 'sdg' or 'z'.

section_size (int): the size of every section, used in _lwr_cnot_synth(), in the
Patel–Markov–Hayes algorithm. section_size must be a factor of num_qubits.

Returns:
QuantumCircuit: the quantum circuit
QuantumCircuit: the decomposed quantum circuit.

Raises:
QiskitError: when dimensions of cnots and angles don't align
QiskitError: when dimensions of cnots and angles don't align.

References:
1. Amy, Matthew, Parsiad Azimzadeh, and Michele Mosca.
*On the controlled-NOT complexity of controlled-NOT–phase circuits.*,
Quantum Science and Technology 4.1 (2018): 015002.
`arXiv:1712.01859 <https://arxiv.org/abs/1712.01859>`_
"""
num_qubits = len(cnots)

Expand Down Expand Up @@ -180,125 +185,6 @@ def graysynth(cnots, angles, section_size=2):
return qcir


def synth_cnot_count_full_pmh(state, section_size=2):
"""
Synthesize linear reversible circuits for all-to-all architecture
using Patel, Markov and Hayes method.

This function is an implementation of the Patel, Markov and Hayes algorithm from [1]
for optimal synthesis of linear reversible circuits for all-to-all architecture,
as specified by an n x n matrix.

Args:
state (list[list] or ndarray): n x n boolean invertible matrix, describing the state
of the input circuit
section_size (int): the size of each section, used in the
Patel–Markov–Hayes algorithm [1]. section_size must be a factor of num_qubits.

Returns:
QuantumCircuit: a CX-only circuit implementing the linear transformation.

Raises:
QiskitError: when variable "state" isn't of type numpy.ndarray

References:
1. Patel, Ketan N., Igor L. Markov, and John P. Hayes,
*Optimal synthesis of linear reversible circuits*,
Quantum Information & Computation 8.3 (2008): 282-294.
`arXiv:quant-ph/0302002 [quant-ph] <https://arxiv.org/abs/quant-ph/0302002>`_
"""
if not isinstance(state, (list, np.ndarray)):
raise QiskitError(
"state should be of type list or numpy.ndarray, "
"but was of the type {}".format(type(state))
)
state = np.array(state)
# Synthesize lower triangular part
[state, circuit_l] = _lwr_cnot_synth(state, section_size)
state = np.transpose(state)
# Synthesize upper triangular part
[state, circuit_u] = _lwr_cnot_synth(state, section_size)
circuit_l.reverse()
for i in circuit_u:
i.reverse()
# Convert the list into a circuit of C-NOT gates
circ = QuantumCircuit(state.shape[0])
for i in circuit_u + circuit_l:
circ.cx(i[0], i[1])
return circ


def _lwr_cnot_synth(state, section_size):
"""
This function is a helper function of the algorithm for optimal synthesis
of linear reversible circuits (the Patel–Markov–Hayes algorithm). It works
like gaussian elimination, except that it works a lot faster, and requires
fewer steps (and therefore fewer CNOTs). It takes the matrix "state" and
splits it into sections of size section_size. Then it eliminates all non-zero
sub-rows within each section, which are the same as a non-zero sub-row
above. Once this has been done, it continues with normal gaussian elimination.
The benefit is that with small section sizes (m), most of the sub-rows will
be cleared in the first step, resulting in a factor m fewer row row operations
during Gaussian elimination.

The algorithm is described in detail in the following paper
"Optimal synthesis of linear reversible circuits."
Patel, Ketan N., Igor L. Markov, and John P. Hayes.
Quantum Information & Computation 8.3 (2008): 282-294.

Note:
This implementation tweaks the Patel, Markov, and Hayes algorithm by adding
a "back reduce" which adds rows below the pivot row with a high degree of
overlap back to it. The intuition is to avoid a high-weight pivot row
increasing the weight of lower rows.

Args:
state (ndarray): n x n matrix, describing a linear quantum circuit
section_size (int): the section size the matrix columns are divided into

Returns:
numpy.matrix: n by n matrix, describing the state of the output circuit
list: a k by 2 list of C-NOT operations that need to be applied
"""
circuit = []
num_qubits = state.shape[0]
cutoff = 1

# Iterate over column sections
for sec in range(1, int(np.floor(num_qubits / section_size) + 1)):
# Remove duplicate sub-rows in section sec
patt = {}
for row in range((sec - 1) * section_size, num_qubits):
sub_row_patt = copy.deepcopy(state[row, (sec - 1) * section_size : sec * section_size])
if np.sum(sub_row_patt) == 0:
continue
if str(sub_row_patt) not in patt:
patt[str(sub_row_patt)] = row
else:
state[row, :] ^= state[patt[str(sub_row_patt)], :]
circuit.append([patt[str(sub_row_patt)], row])
# Use gaussian elimination for remaining entries in column section
for col in range((sec - 1) * section_size, sec * section_size):
# Check if 1 on diagonal
diag_one = 1
if state[col, col] == 0:
diag_one = 0
# Remove ones in rows below column col
for row in range(col + 1, num_qubits):
if state[row, col] == 1:
if diag_one == 0:
state[col, :] ^= state[row, :]
circuit.append([row, col])
diag_one = 1
state[row, :] ^= state[col, :]
circuit.append([col, row])
# Back reduce the pivot row using the current row
if sum(state[col, :] & state[row, :]) > cutoff:
state[col, :] ^= state[row, :]
circuit.append([row, col])
return [state, circuit]


def _remove_duplicates(lists):
"""
Remove duplicates in list
Expand Down
11 changes: 1 addition & 10 deletions qiskit/transpiler/synthesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,7 @@
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=undefined-all-variable

"""Module containing transpiler synthesize."""

import importlib

__all__ = ["graysynth", "cnot_synth"]


def __getattr__(name):
if name in __all__:
return getattr(importlib.import_module(".graysynth", __name__), name)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
from .graysynth import graysynth, cnot_synth
Loading