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

Synthesis of CZ and Clifford circuits for linear nearest neighbor connectivity #9450

Merged
merged 25 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d124098
add cz synthesis for LNN
ShellyGarion Jan 25, 2023
ca8bd61
basic cz synthesis test
ShellyGarion Jan 25, 2023
3cc7ec0
adjust clifford_decompose_layers to cz synthesis for LNN
ShellyGarion Jan 25, 2023
d054b20
add a basic test for layered clifford synthesis for LNN
ShellyGarion Jan 25, 2023
4811701
enhance the tests
ShellyGarion Jan 25, 2023
2e80042
fix lint, check function name
ShellyGarion Jan 25, 2023
83b0ba3
Add utils needed for LNN tests
ShellyGarion Jan 29, 2023
065f63b
add tests for LNN and depth
ShellyGarion Jan 29, 2023
25e094a
Add documentation for cz synthesis
ShellyGarion Jan 29, 2023
96884dd
fix misprint
ShellyGarion Jan 29, 2023
6654529
add synth_clifford_depth_lnn
ShellyGarion Jan 29, 2023
e6e955c
updates following review
ShellyGarion Jan 31, 2023
f3949d5
Merge branch 'main' into synth_cz_LNN
ShellyGarion Feb 8, 2023
a5570f1
rename synth_clifford_depth_lnn
ShellyGarion Feb 8, 2023
ea28360
update references
ShellyGarion Feb 9, 2023
71a2416
remove a util function that is not needed
ShellyGarion Feb 9, 2023
fe0e0a3
update docstring
ShellyGarion Feb 9, 2023
9ed42e1
add release notes
ShellyGarion Feb 9, 2023
d00cf99
replace int(n/2) by n//2
ShellyGarion Feb 10, 2023
134d3f4
reduce the number of 1-qubit gates
ShellyGarion Feb 13, 2023
7924796
fix test
ShellyGarion Feb 13, 2023
8e30f2e
refactor and minor changes following review
ShellyGarion Feb 16, 2023
29e90b3
update docs
ShellyGarion Feb 19, 2023
dfb4d38
update init file following review
ShellyGarion Feb 23, 2023
b918685
Merge branch 'main' into synth_cz_LNN
mergify[bot] Feb 23, 2023
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
13 changes: 10 additions & 3 deletions qiskit/synthesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@
MatrixExponential
QDrift

Linear Function Synthesis
=========================
Linear Function and Linear-Phase Synthesis
==========================================
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
.. autosummary::
:toctree: ../stubs/

synth_cnot_count_full_pmh
synth_cnot_depth_line_kms
synth_cz_depth_line_mr

Permutation Synthesis
=====================
Expand All @@ -59,6 +60,7 @@
synth_clifford_bm
synth_clifford_greedy
synth_clifford_layers
synth_clifford_depth_lnn

CNOTDihedral Synthesis
======================
Expand Down Expand Up @@ -94,13 +96,18 @@
synth_permutation_basic,
synth_permutation_acg,
)
from .linear import synth_cnot_count_full_pmh, synth_cnot_depth_line_kms
from .linear import (
synth_cnot_count_full_pmh,
synth_cnot_depth_line_kms,
synth_cz_depth_line_mr,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have a strong opinion on this either. Does it make sense that code for CZ synthesis appears under linear? Or would it be better to put it in the synthesis/cz subdirectory?

Copy link
Member Author

@ShellyGarion ShellyGarion Feb 16, 2023

Choose a reason for hiding this comment

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

I think the best solution is to have another folder of synthesis/linear_phase since the cz synthesis is into cx and phase gates, and relies on the theory of phase polynomials. It's similar to graysynth that should be moved this folder too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Moving and renaming graysynth, as well as deprecating https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/transpiler/synthesis/graysynth.py will be done in a separate PR

from .clifford import (
synth_clifford_full,
synth_clifford_ag,
synth_clifford_bm,
synth_clifford_greedy,
synth_clifford_layers,
synth_clifford_depth_lnn,
)
from .cnotdihedral import (
synth_cnotdihedral_full,
Expand Down
2 changes: 1 addition & 1 deletion qiskit/synthesis/clifford/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
from .clifford_decompose_ag import synth_clifford_ag
from .clifford_decompose_bm import synth_clifford_bm, _decompose_clifford_1q
from .clifford_decompose_greedy import synth_clifford_greedy
from .clifford_decompose_layers import synth_clifford_layers
from .clifford_decompose_layers import synth_clifford_layers, synth_clifford_depth_lnn
85 changes: 58 additions & 27 deletions qiskit/synthesis/clifford/clifford_decompose_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@
# pylint: disable=invalid-name

import numpy as np

from qiskit.circuit import QuantumCircuit
from qiskit.exceptions import QiskitError
from qiskit.synthesis.linear import synth_cnot_count_full_pmh
from qiskit.synthesis.linear import (
synth_cnot_count_full_pmh,
synth_cnot_depth_line_kms,
synth_cz_depth_line_mr,
)
from qiskit.synthesis.linear.linear_matrix_utils import (
calc_inverse_matrix,
check_invertible_binary_matrix,
_compute_rank,
_gauss_elimination,
_gauss_elimination_with_perm,
Expand All @@ -32,24 +36,17 @@
)


def _default_cx_synth_func(mat, validate):
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
def _default_cx_synth_func(mat):
"""
Construct the layer of CX gates from a boolean invertible matrix mat.
"""
if validate:
if not check_invertible_binary_matrix(mat):
raise QiskitError("The matrix for CX circuit is not invertible.")

CX_circ = synth_cnot_count_full_pmh(mat)
CX_circ.name = "CX"

if validate:
_check_gates(CX_circ, ("cx", "swap"))

return CX_circ


def _default_cz_synth_func(symmetric_mat, validate):
def _default_cz_synth_func(symmetric_mat):
"""
Construct the layer of CZ gates from a symmetric matrix.
"""
Expand All @@ -60,9 +57,6 @@ def _default_cz_synth_func(symmetric_mat, validate):
for i in range(0, j):
if symmetric_mat[i][j]:
qc.cz(i, j)

if validate:
_check_gates(qc, "cz")
return qc


Expand All @@ -71,10 +65,11 @@ def synth_clifford_layers(
cx_synth_func=_default_cx_synth_func,
cz_synth_func=_default_cz_synth_func,
cx_cz_synth_func=None,
reverse_qubits=False,
validate=False,
):
"""Synthesis of a Clifford into layers, it provides a similar decomposition to the synthesis
described in Lemma 8 of [1].
described in Lemma 8 of Bravyi and Maslov.
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved

For example, a 5-qubit Clifford circuit is decomposed into the following layers:

ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -97,6 +92,8 @@ def synth_clifford_layers(
cz_synth_func (Callable): a function to decompose the CZ sub-circuit.
cx_cz_synth_func (Callable): optional, a function to decompose both sub-circuits CZ and CX.
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
validate (Boolean): if True, validates the synthesis process.
reverse_qubits (Boolean): True only if cz_synth_func is synth_cz_depth_line_mr,
since this function returns a circuit that inverts the order of qubits.
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved

Return:
QuantumCircuit: a circuit implementation of the Clifford.
Expand All @@ -108,11 +105,15 @@ def synth_clifford_layers(
"""

num_qubits = cliff.num_qubits
if reverse_qubits:
cliff0 = _reverse_clifford(cliff)
else:
cliff0 = cliff

qubit_list = list(range(num_qubits))
layeredCircuit = QuantumCircuit(num_qubits)

H1_circ, cliff1 = _create_graph_state(cliff, validate=validate)
H1_circ, cliff1 = _create_graph_state(cliff0, validate=validate)

H2_circ, CZ1_circ, S1_circ, cliff2 = _decompose_graph_state(
cliff1, validate=validate, cz_synth_func=cz_synth_func
Expand All @@ -124,6 +125,7 @@ def synth_clifford_layers(
cz_synth_func=cz_synth_func,
cx_synth_func=cx_synth_func,
cx_cz_synth_func=cx_cz_synth_func,
reverse_qubits=reverse_qubits,
)

layeredCircuit.append(S2_circ, qubit_list)
Expand All @@ -136,6 +138,8 @@ def synth_clifford_layers(
layeredCircuit.append(S1_circ, qubit_list)
layeredCircuit.append(CZ1_circ, qubit_list)

if reverse_qubits:
H1_circ = H1_circ.reverse_bits()
layeredCircuit.append(H1_circ, qubit_list)

# Add Pauli layer to fix the Clifford phase signs
Expand Down Expand Up @@ -269,7 +273,7 @@ def _decompose_graph_state(cliff, validate, cz_synth_func):
"The multiplication of stabx_inv and stab_z is not a symmetric matrix."
)

CZ1_circ = cz_synth_func(stabz_update, validate=validate)
CZ1_circ = cz_synth_func(stabz_update)

for j in range(num_qubits):
for i in range(0, j):
Expand All @@ -288,7 +292,9 @@ def _decompose_graph_state(cliff, validate, cz_synth_func):
return H2_circ, CZ1_circ, S1_circ, cliff_cpy


def _decompose_hadamard_free(cliff, validate, cz_synth_func, cx_synth_func, cx_cz_synth_func):
def _decompose_hadamard_free(
cliff, validate, cz_synth_func, cx_synth_func, cx_cz_synth_func, reverse_qubits
):
"""Assumes that the Clifford cliff is Hadamard free.
Decompose it into the layers S2 - CZ2 - CX, where
S2_circ is a circuit that can contain only S gates,
Expand All @@ -301,6 +307,7 @@ def _decompose_hadamard_free(cliff, validate, cz_synth_func, cx_synth_func, cx_c
cz_synth_func (Callable): a function to decompose the CZ sub-circuit.
cx_synth_func (Callable): a function to decompose the CX sub-circuit.
cx_cz_synth_func (Callable): optional, a function to decompose both sub-circuits CZ and CX.
reverse_qubits (Boolean): True only if cz_synth_func is synth_cz_depth_line_mr.

Return:
S2_circ: a circuit that can contain only S gates.
Expand Down Expand Up @@ -339,10 +346,12 @@ def _decompose_hadamard_free(cliff, validate, cz_synth_func, cx_synth_func, cx_c
)
return S2_circ, CZ2_circ, CX_circ

CZ2_circ = cz_synth_func(destabz_update, validate=validate)
CZ2_circ = cz_synth_func(destabz_update)

mat = destabx.transpose()
CX_circ = cx_synth_func(mat, validate=validate)
if reverse_qubits:
mat = np.flip(mat, axis=0)
CX_circ = cx_synth_func(mat)

return S2_circ, CZ2_circ, CX_circ

Expand Down Expand Up @@ -381,11 +390,33 @@ def _calc_pauli_diff(cliff, cliff_target):
return pauli_circ


def _check_gates(qc, allowed_gates):
"""Check that quantum circuit qc consists only of allowed_gates.
qc - a QuantumCircuit
allowed_gates - list of strings
def synth_clifford_depth_lnn(cliff):
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
"""Synthesis of a Clifford into layers for linear-nearest neighbour connectivity.

The depth of the synthesized n-qubit circuit is bounded by 9*n+4, which is not optimal.
It should be replaced by a better algorithm that provides a depth bounded by 7*n-4 [3].
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved

Args:
cliff (Clifford): a clifford operator.

Return:
QuantumCircuit: a circuit implementation of the Clifford.

Reference:
1. S. Bravyi, D. Maslov, *Hadamard-free circuits expose the
structure of the Clifford group*,
`arXiv:2003.09412 [quant-ph] <https://arxiv.org/abs/2003.09412>`_
2. Dmitri Maslov, Martin Roetteler,
*Shorter stabilizer circuits via Bruhat decomposition and quantum circuit transformations*,
`arXiv:1705.09176 <https://arxiv.org/abs/1705.09176>`_.
3. Dmitri Maslov, Willers Yang, *CNOT circuits need little help to implement arbitrary
Hadamard-free Clifford transformations they generate*,
`arXiv:2210.16195 <https://arxiv.org/abs/2210.16195>`_.
"""
for inst, _, _ in qc.data:
if not inst.name in allowed_gates:
raise QiskitError("The gate name is not in the allowed_gates list.")
circ = synth_clifford_layers(
cliff,
cx_synth_func=synth_cnot_depth_line_kms,
cz_synth_func=synth_cz_depth_line_mr,
reverse_qubits=True,
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved
)
return circ
1 change: 1 addition & 0 deletions qiskit/synthesis/linear/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@


from .graysynth import graysynth, synth_cnot_count_full_pmh
from .cz_depth_lnn import synth_cz_depth_line_mr
from .linear_depth_lnn import synth_cnot_depth_line_kms
from .linear_matrix_utils import (
random_invertible_binary_matrix,
Expand Down
Loading