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

virtual padding for invalid section_size in synth_cnot_count_full_pmh #12712

Closed
Closed
Changes from 2 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
99 changes: 31 additions & 68 deletions qiskit/synthesis/linear/cnot_synth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
# 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.
Expand All @@ -26,102 +25,65 @@
def synth_cnot_count_full_pmh(
state: list[list[bool]] | np.ndarray[bool], section_size: int = 2
) -> QuantumCircuit:
"""
Abdalla01001 marked this conversation as resolved.
Show resolved Hide resolved
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 :math:`n \\times n` matrix.

Args:
state: :math:`n \\times n` boolean invertible matrix, describing
the state of the input circuit
section_size: The size of each section in the Patel–Markov–Hayes algorithm [1].

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

Raises:
QiskitError: when variable ``state`` isn't of type ``numpy.ndarray``
"""Synthesize linear reversible circuits using the Patel-Markov-Hayes algorithm with virtual padding."""

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(
f"state should be of type list or numpy.ndarray, but was of the type {type(state)}"
"state should be of type list or numpy.ndarray, "
"but was of the type {}".format(type(state))
)
state = np.array(state)
num_qubits = state.shape[0]

# Virtual Padding
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussing with @alexanderivrii and @ShellyGarion we came to the conclusion that the section size being larger than the number of qubits is an invalid input. It would be better to raise an error if section_size > num_qubits, as originally done in #12166. Could you do that and remove the virtual padding?

We'll go forward with your PR then, as it still fixes the bad section_size values due to your fixes on L131 and L148.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, I did it

if section_size > num_qubits:
padding_size = section_size - (num_qubits % section_size)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this be simplified to padding_size = section_size - num_qubits?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexanderivrii the goal of padding is to extend the matrix to a size divisible by the section_size. Just subtracting the number of qubits from the section size does not guarantee divisibility.

state = np.hstack((state, np.zeros((num_qubits, padding_size), dtype=bool)))

# Synthesize lower triangular part
[state, circuit_l] = _lwr_cnot_synth(state, section_size)
[state, circuit_l] = _lwr_cnot_synth(state, section_size, num_qubits)
state = np.transpose(state)

# Synthesize upper triangular part
[state, circuit_u] = _lwr_cnot_synth(state, section_size)
[state, circuit_u] = _lwr_cnot_synth(state, section_size, num_qubits)
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):
"""
Abdalla01001 marked this conversation as resolved.
Show resolved Hide resolved
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.
# Convert the list into a circuit of C-NOT gates (removing virtual padding)
circ = QuantumCircuit(num_qubits) # Circuit size is the original num_qubits
for i in circuit_u + circuit_l:
# Only add gates if both control and target are within the original matrix
if i[0] < num_qubits and i[1] < num_qubits:
circ.cx(i[0], i[1])

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.
return circ

Args:
state (ndarray): n x n matrix, describing a linear quantum circuit
section_size (int): the section size the matrix columns are divided into
def _lwr_cnot_synth(state, section_size, num_qubits):
"""Helper function for the Patel-Markov-Hayes algorithm with virtual padding."""

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)):
# Iterate over column sections (including padded sections)
for sec in range(1, int(np.ceil(state.shape[1] / 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])
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):
# Modified loop range
for col in range((sec - 1) * section_size, min(sec * section_size, num_qubits)):
# Check if 1 on diagonal
diag_one = 1
if state[col, col] == 0:
Expand All @@ -139,4 +101,5 @@ def _lwr_cnot_synth(state, section_size):
if sum(state[col, :] & state[row, :]) > cutoff:
state[col, :] ^= state[row, :]
circuit.append([row, col])

return [state, circuit]
Loading