From 149baf2ddfdf45a948ee9a0e120d1bf4ae3e064a Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Wed, 5 Apr 2023 14:17:44 -0700 Subject: [PATCH 01/14] Adding depth-5n -CX-CZ- synthesis algorithm Adding the synthesis algorithm that jointly synthesize a CNOT circuit and CZ circuit in depth 5n by inserting P gates to the CNOT network. Reference: D. Maslove and W. Yang, "CNOT circuits need little help to implement arbitrary Hadamard-free Clifford transformations they generate," 2022. https://arxiv.org/abs/2210.16195 --- .../clifford/clifford_decompose_layers.py | 42 +++- .../synthesis/linear_phase/cx_cz_depth_lnn.py | 226 ++++++++++++++++++ 2 files changed, 261 insertions(+), 7 deletions(-) create mode 100644 qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py diff --git a/qiskit/synthesis/clifford/clifford_decompose_layers.py b/qiskit/synthesis/clifford/clifford_decompose_layers.py index f4f8a1f6c7b3..3b3d83a344f3 100644 --- a/qiskit/synthesis/clifford/clifford_decompose_layers.py +++ b/qiskit/synthesis/clifford/clifford_decompose_layers.py @@ -135,10 +135,23 @@ def synth_clifford_layers( ) layeredCircuit.append(S2_circ, qubit_list) - layeredCircuit.append(CZ2_circ, qubit_list) - CXinv = CX_circ.copy().inverse() - layeredCircuit.append(CXinv, qubit_list) +################################################################################ +#### Changed Part ############################################################## + + if cx_cz_synth_func is None: + layeredCircuit.append(CZ2_circ, qubit_list) + + CXinv = CX_circ.copy().inverse() + layeredCircuit.append(CXinv, qubit_list) + + else: + # note CZ2_circ is None and built into the CX_circ when + # cx_cz_synth_func is not None + layeredCircuit.append(CX_circ, qubit_list) + +################################################################################ + layeredCircuit.append(H2_circ, qubit_list) layeredCircuit.append(S1_circ, qubit_list) @@ -346,11 +359,26 @@ def _decompose_hadamard_free( if destabz_update[i][i]: S2_circ.s(i) +################################################################################ +#### Changed Part ############################################################## + if cx_cz_synth_func is not None: - CZ2_circ, CX_circ = cx_cz_synth_func( - destabz_update, cliff.destab_x.transpose(), num_qubits=num_qubits - ) - return S2_circ, CZ2_circ, CX_circ + + # The cx_cz_synth_func takes as input Mx/Mz representing a CX/CZ circuit + # and returns the circuit -CZ-CX- implementing them both + for i in range(num_qubits): + destabz_update[i][i] = 0 + + mat_z = destabz_update + mat_x = calc_inverse_matrix(destabx.transpose()) + + CXCZ_circ = cx_cz_synth_func(mat_x, mat_z) + + + return S2_circ, QuantumCircuit(num_qubits), CXCZ_circ + +################################################################################ + CZ2_circ = cz_synth_func(destabz_update) diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py new file mode 100644 index 000000000000..31668e8a07fc --- /dev/null +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -0,0 +1,226 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2022 +# +# 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. + +""" +Given -CZ-CX- transformation (a layer consisting only CNOT gates + followed by a layer consisting only CZ gates) +Return a depth-5n circuit implementation of the -CX-CZ- transformation over LNN. + +Input: + Mx: n*n invertable binary matrix representing a -CX- transformation + Mz: n*n symmetric binary matrix representing a -CZ- circuit + +Output: + qc: QuantumCircuit object containing a depth-5n circuit to implement -CZ-CX- + +References: + [1] S. A. Kutin, D. P. Moulton, and L. M. Smithline, "Computation at a distance," 2007. + [2] D. Maslove and W. Yang, "CNOT circuits need little help to implement arbitrary + Hadamard-free Clifford transformations they generate," 2022. +""" + +import numpy as np +from copy import deepcopy + +from qiskit.exceptions import QiskitError +from qiskit.circuit import QuantumCircuit +from qiskit.synthesis.linear.linear_matrix_utils import (calc_inverse_matrix) + +from qiskit.synthesis.linear.linear_depth_lnn import ( + _optimize_cx_circ_depth_5n_line + ) + +def _initializeS(Mz): + ''' + Given a CZ layer (represented as an n*n CZ matrix Mz) + Return a scheudle of phase gates implementing Mz in a SWAP-only netwrok + (Ref. [Alg 1, 2]) + ''' + n = len(Mz) + S = np.zeros((n,n),dtype = int) + for i, j in zip(*np.where(Mz)): + if i>j: + continue + + S[i,j] = 3 + S[i,i] += 1 + S[j,j] += 1 + + return S + +def _shuffle(l,odd): + ''' + Given a list of indices l and boolean odd indicating odd/even layers, + Shuffle the indices in l by swapping adjacent elements + (Ref. [Fig.2, 2]) + ''' + swapped = [v for p in zip(l[1::2],l[::2]) for v in p] + return swapped + l[-1:] if odd else swapped + +def _makeSeq(n): + ''' + Given the width of the circuit n, + Return the label of the boxes in order from left to right, top to bottom + (Ref. [Fig.2, 2]) + ''' + seq = [] + l = list(range(n-1,-1,-1)) + + for i in range(n): + r = _shuffle(l,n%2) if i%2==0 else l[0:1] + _shuffle(l[1:],(n+1)%2) + seq += list([(min(i),max(i)) for i in zip(l[::2],r[::2]) if i[0]!=i[1]]) + l = r + return seq + +def _swapPlus(instructions,seq): + ''' + Given CX instructions (Ref. [Thm 7.1, 1]) and the labels of all boxes, + Return a list of labels of the boxes that is SWAP+ in descending order + * Assumes the instruction gives gates in the order from top to bottom, + from left to right + ''' + instr = deepcopy(instructions) + swapPs = set() + for i,j in reversed(seq): + CNOT1 = instr.pop() + CNOT2 = instr.pop() + + if instr == [] or instr[-1]!= CNOT1: + #Only two CNOTs on same set of controls -> this box is SWAP+ + swapPs.add((i,j)) + else: + CNOT3 = instr.pop() + return swapPs + +def _updateS(n, S, swapPs): + ''' + Given S initialized to induce a CZ circuit in SWAP-only network and list of SWAP+ boxes + Update S for each SWAP+ according to Algorithm 2 [2] + ''' + l = list(range(n)) + Pn = l[-3::-2]+l[-2::-2][::-1] + orderComp = np.argsort(Pn[::-1]) + + # Go through each box by descending layer order + + for i in Pn: + for j in range(i+1,n): + if (i,j) not in swapPs: + continue + # we need to correct for the effected linear functions: + + # We first correct type 1 and type 2 by switching + # the phase applied to c_j and c_i+c_j + S[j,j] , S[i,j] = S[i,j], S[j,j] + + # Then, we go through all the boxes that permutes j BEFORE box(i,j) and update: + + for k in range(n): #all boxes that permutes j + if i==k or j==k: + continue + if orderComp[min(k,j)] < orderComp[i] and S[min(k,j),max(k,j)]%4!=0: + phase = S[min(k,j),max(k,j)] + S[min(k,j),max(k,j)] = 0 + + #Step 1, apply phase to c_i, c_j, c_k + for l in [i,j,k]: + S[l,l] = (S[l,l]+phase*3)%4 + + #Step 2, apply phase to c_i+ c_j, c_i+c_k, c_j+c_k: + for l1,l2 in [(i,j), (i,k),(j,k)]: + ls = min(l1,l2) + lb = max(l1,l2) + S[ls,lb] = (S[ls,lb]+phase*3)%4 + return S + +def _apply_S_to_NW_circuit(n,S,seq, swapPs): + ''' + Given + Width of the circuit (int n) + A CZ circuit, represented by the n*n phase schedule S + A CX circuit, represented by box-labels (seq) and whether the box is SWAP+ (swapPs) + * This circuit corresponds to the CX tranformation that tranforms a matrix to + a NW matrix (Ref. [Prop.7.4, 1]) + Return a QuantumCircuit that computes S and CX + ''' + cir = QuantumCircuit(n) + + wires = list(zip(range(n),range(1,n))) + wires = wires[::2]+wires[1::2] + + + for (i,(j,k)) in zip(range(len(seq)-1,-1,-1),reversed(seq)): + + w1, w2 = wires[i%(n-1)] + + p = S[j,k] + + if (j,k) not in swapPs: + cir.cnot(w1,w2) + + cir.cnot(w2,w1) + + if p%4 == 0: + pass + elif p%4 == 1: + cir.sdg(w2) + elif p%4 == 2: + cir.z(w2) + elif p%4 == 3: + cir.s(w2) + + cir.cnot(w1,w2) + + for i in range(n): + p = S[n-1-i,n-1-i] + if p%4 == 0: + continue + elif p%4 == 1: + cir.sdg(i) + elif p%4 == 2: + cir.z(i) + elif p%4 == 3: + cir.s(i) + + return cir + + + +def synth_cx_cz_line_my(Mx,Mz): + ''' + Given + -CX- circuit, represented by n*n binary, invertible matrix Mx + -CZ- circuit, repredented by n*n binary, symmetric matrix Mz + where n is the width of the circuit + Return a QuantumCircuit object, qc, that implements the -CZ-CX- in two-qubit depth at most 5n + ''' + + # Find circuits implementing Mx by Proposition 7.3 and Proposition 7.4 of [1] + + n = len(Mx) + Mx = calc_inverse_matrix(Mx) + + cx_instructions_rows_m2nw, cx_instructions_rows_nw2id = _optimize_cx_circ_depth_5n_line(Mx) + + #Meanwhile, also build the -CZ- circuit via Phase gate insertions as per Algorithm 2 [2] + S = _initializeS(Mz) + seq = _makeSeq(n) + swapPs = _swapPlus(cx_instructions_rows_nw2id, seq) + + _updateS(n, S, swapPs) + + qc = _apply_S_to_NW_circuit(n,S,seq, swapPs) + + for i,j in reversed(cx_instructions_rows_m2nw): + qc.cx(i,j) + + return qc From b8196555aff1b965eb1a08c233e48369973548ee Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:32:32 -0700 Subject: [PATCH 02/14] Adding some test cases --- test/python/synthesis/test_cx_cz_synthesis.py | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 test/python/synthesis/test_cx_cz_synthesis.py diff --git a/test/python/synthesis/test_cx_cz_synthesis.py b/test/python/synthesis/test_cx_cz_synthesis.py new file mode 100644 index 000000000000..b962b30a2a80 --- /dev/null +++ b/test/python/synthesis/test_cx_cz_synthesis.py @@ -0,0 +1,93 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2023. +# +# 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. + +"""Test -CZ-CX- joint synthesis function.""" + +import unittest +from test import combine +import numpy as np +from ddt import ddt +from qiskit import QuantumCircuit +from qiskit.synthesis.linear.linear_depth_lnn import synth_cnot_depth_line_kms +from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_line_my + +from qiskit.quantum_info import Clifford + +from qiskit.synthesis.linear import ( + synth_cnot_count_full_pmh, + synth_cnot_depth_line_kms, + random_invertible_binary_matrix, + check_invertible_binary_matrix, + calc_inverse_matrix, +) + +from qiskit.synthesis.linear.linear_circuits_utils import ( + transpose_cx_circ, + optimize_cx_4_options, + check_lnn_connectivity) + +from qiskit.test import QiskitTestCase + +@ddt +class TestCXCZSynth(QiskitTestCase): + """Test the linear reversible circuit synthesis functions.""" + + @combine(num_qubits=[3, 4, 5, 6, 7, 8, 9, 10]) + def test_cx_cz_synth_lnn(self, num_qubits): + """Test the CXCZ synthesis code for linear nearest neighbour connectivity.""" + seed = 1234 + rng = np.random.default_rng(seed) + num_gates = 10 + num_trials = 8 + + for _ in range(num_trials): + + + # Generate a random CZ circuit + mat_z = np.zeros((num_qubits, num_qubits)) + cir_z = QuantumCircuit(num_qubits) + for _ in range(num_gates): + i = rng.integers(num_qubits) + j = rng.integers(num_qubits) + if i != j: + cir_z.cz(i, j) + if j > i: + mat_z[i][j] = (mat_z[i][j] + 1) % 2 + else: + mat_z[j][i] = (mat_z[j][i] + 1) % 2 + + # Generate a random CX circuit + mat_x = random_invertible_binary_matrix(num_qubits, seed=rng) + mat_x = np.array(mat_x, dtype=bool) + cir_x = synth_cnot_depth_line_kms(mat_x) + + + # Joint Synthesis + + cirZX_test = QuantumCircuit.compose(cir_z, cir_x) + + cirZX = synth_cx_cz_line_my(mat_x,mat_z) + + # Check that the output circuit 2-qubit depth is at most 5n + + depth2q = cirZX.depth(filter_function=lambda x: x.operation.num_qubits == 2) + self.assertTrue(depth2q <= 5 * num_qubits ) + + # Check that the output circuit has LNN connectivity + self.assertTrue(check_lnn_connectivity(cirZX)) + + # Assert that we get the same elements as other methods + self.assertEqual(Clifford(cirZX), Clifford(cirZX_test)) + + +if __name__ == "__main__": + unittest.main() From 02a799f854d8fa215e8346ef577af34790adcf3d Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:42:44 +0200 Subject: [PATCH 03/14] Fixing formatting and docstrings. Updating synth_clifford_depth_lnn --- .../clifford/clifford_decompose_layers.py | 25 +- qiskit/synthesis/linear_phase/__init__.py | 1 + .../synthesis/linear_phase/cx_cz_depth_lnn.py | 251 ++++++++++-------- .../test_clifford_decompose_layers.py | 10 +- test/python/synthesis/test_cx_cz_synthesis.py | 13 +- 5 files changed, 156 insertions(+), 144 deletions(-) diff --git a/qiskit/synthesis/clifford/clifford_decompose_layers.py b/qiskit/synthesis/clifford/clifford_decompose_layers.py index 3b3d83a344f3..a0e5c3c66761 100644 --- a/qiskit/synthesis/clifford/clifford_decompose_layers.py +++ b/qiskit/synthesis/clifford/clifford_decompose_layers.py @@ -22,7 +22,11 @@ synth_cnot_count_full_pmh, synth_cnot_depth_line_kms, ) -from qiskit.synthesis.linear_phase import synth_cz_depth_line_mr +from qiskit.synthesis.linear_phase import ( + synth_cz_depth_line_mr, + synth_cx_cz_line_my, +) + from qiskit.synthesis.linear.linear_matrix_utils import ( calc_inverse_matrix, @@ -136,9 +140,6 @@ def synth_clifford_layers( layeredCircuit.append(S2_circ, qubit_list) -################################################################################ -#### Changed Part ############################################################## - if cx_cz_synth_func is None: layeredCircuit.append(CZ2_circ, qubit_list) @@ -146,12 +147,9 @@ def synth_clifford_layers( layeredCircuit.append(CXinv, qubit_list) else: - # note CZ2_circ is None and built into the CX_circ when + # note CZ2_circ is None and built into the CX_circ when # cx_cz_synth_func is not None layeredCircuit.append(CX_circ, qubit_list) - -################################################################################ - layeredCircuit.append(H2_circ, qubit_list) layeredCircuit.append(S1_circ, qubit_list) @@ -359,11 +357,7 @@ def _decompose_hadamard_free( if destabz_update[i][i]: S2_circ.s(i) -################################################################################ -#### Changed Part ############################################################## - if cx_cz_synth_func is not None: - # The cx_cz_synth_func takes as input Mx/Mz representing a CX/CZ circuit # and returns the circuit -CZ-CX- implementing them both for i in range(num_qubits): @@ -374,12 +368,8 @@ def _decompose_hadamard_free( CXCZ_circ = cx_cz_synth_func(mat_x, mat_z) - return S2_circ, QuantumCircuit(num_qubits), CXCZ_circ -################################################################################ - - CZ2_circ = cz_synth_func(destabz_update) mat = destabx.transpose() @@ -427,7 +417,7 @@ def _calc_pauli_diff(cliff, cliff_target): def synth_clifford_depth_lnn(cliff): """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. + The depth of the synthesized n-qubit circuit is bounded by 7*n+2, which is not optimal. It should be replaced by a better algorithm that provides depth bounded by 7*n-4 [3]. Args: @@ -451,6 +441,7 @@ def synth_clifford_depth_lnn(cliff): cliff, cx_synth_func=synth_cnot_depth_line_kms, cz_synth_func=synth_cz_depth_line_mr, + cx_cz_synth_func=synth_cx_cz_line_my, cz_func_reverse_qubits=True, ) return circ diff --git a/qiskit/synthesis/linear_phase/__init__.py b/qiskit/synthesis/linear_phase/__init__.py index 78ce4433603f..da2b6ec6393a 100644 --- a/qiskit/synthesis/linear_phase/__init__.py +++ b/qiskit/synthesis/linear_phase/__init__.py @@ -13,3 +13,4 @@ """Module containing cnot-phase circuits""" from .cz_depth_lnn import synth_cz_depth_line_mr +from .cx_cz_depth_lnn import synth_cx_cz_line_my diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py index 31668e8a07fc..a8d6754e9f58 100644 --- a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -13,12 +13,12 @@ """ Given -CZ-CX- transformation (a layer consisting only CNOT gates followed by a layer consisting only CZ gates) -Return a depth-5n circuit implementation of the -CX-CZ- transformation over LNN. +Return a depth-5n circuit implementation of the -CZ-CX- transformation over LNN. -Input: - Mx: n*n invertable binary matrix representing a -CX- transformation +Args: Mz: n*n symmetric binary matrix representing a -CZ- circuit - + Mx: n*n invertable binary matrix representing a -CX- transformation + Output: qc: QuantumCircuit object containing a depth-5n circuit to implement -CZ-CX- @@ -33,194 +33,209 @@ from qiskit.exceptions import QiskitError from qiskit.circuit import QuantumCircuit -from qiskit.synthesis.linear.linear_matrix_utils import (calc_inverse_matrix) +from qiskit.synthesis.linear.linear_matrix_utils import calc_inverse_matrix + +from qiskit.synthesis.linear.linear_depth_lnn import _optimize_cx_circ_depth_5n_line + -from qiskit.synthesis.linear.linear_depth_lnn import ( - _optimize_cx_circ_depth_5n_line - ) - def _initializeS(Mz): - ''' + """ Given a CZ layer (represented as an n*n CZ matrix Mz) - Return a scheudle of phase gates implementing Mz in a SWAP-only netwrok + Return a scheudle of phase gates implementing Mz in a SWAP-only netwrok (Ref. [Alg 1, 2]) - ''' + """ n = len(Mz) - S = np.zeros((n,n),dtype = int) + S = np.zeros((n, n), dtype=int) for i, j in zip(*np.where(Mz)): - if i>j: + if i >= j: continue - - S[i,j] = 3 - S[i,i] += 1 - S[j,j] += 1 - - return S - -def _shuffle(l,odd): - ''' + + S[i, j] = 3 + S[i, i] += 1 + S[j, j] += 1 + + return S + + +def _shuffle(l, odd): + """ Given a list of indices l and boolean odd indicating odd/even layers, Shuffle the indices in l by swapping adjacent elements (Ref. [Fig.2, 2]) - ''' - swapped = [v for p in zip(l[1::2],l[::2]) for v in p] + """ + swapped = [v for p in zip(l[1::2], l[::2]) for v in p] return swapped + l[-1:] if odd else swapped - + + def _makeSeq(n): - ''' - Given the width of the circuit n, + """ + Given the width of the circuit n, Return the label of the boxes in order from left to right, top to bottom (Ref. [Fig.2, 2]) - ''' + """ seq = [] - l = list(range(n-1,-1,-1)) + l = list(range(n - 1, -1, -1)) for i in range(n): - r = _shuffle(l,n%2) if i%2==0 else l[0:1] + _shuffle(l[1:],(n+1)%2) - seq += list([(min(i),max(i)) for i in zip(l[::2],r[::2]) if i[0]!=i[1]]) + r = _shuffle(l, n % 2) if i % 2 == 0 else l[0:1] + _shuffle(l[1:], (n + 1) % 2) + seq += list([(min(i), max(i)) for i in zip(l[::2], r[::2]) if i[0] != i[1]]) l = r return seq -def _swapPlus(instructions,seq): - ''' + +def _swapPlus(instructions, seq): + """ Given CX instructions (Ref. [Thm 7.1, 1]) and the labels of all boxes, - Return a list of labels of the boxes that is SWAP+ in descending order - * Assumes the instruction gives gates in the order from top to bottom, + Return a list of labels of the boxes that is SWAP+ in descending order + * Assumes the instruction gives gates in the order from top to bottom, from left to right - ''' + """ instr = deepcopy(instructions) swapPs = set() - for i,j in reversed(seq): + for i, j in reversed(seq): CNOT1 = instr.pop() CNOT2 = instr.pop() - if instr == [] or instr[-1]!= CNOT1: - #Only two CNOTs on same set of controls -> this box is SWAP+ - swapPs.add((i,j)) + if instr == [] or instr[-1] != CNOT1: + # Only two CNOTs on same set of controls -> this box is SWAP+ + swapPs.add((i, j)) else: CNOT3 = instr.pop() return swapPs + def _updateS(n, S, swapPs): - ''' + """ Given S initialized to induce a CZ circuit in SWAP-only network and list of SWAP+ boxes Update S for each SWAP+ according to Algorithm 2 [2] - ''' + """ l = list(range(n)) - Pn = l[-3::-2]+l[-2::-2][::-1] + Pn = l[-3::-2] + l[-2::-2][::-1] orderComp = np.argsort(Pn[::-1]) # Go through each box by descending layer order - + for i in Pn: - for j in range(i+1,n): - if (i,j) not in swapPs: + for j in range(i + 1, n): + if (i, j) not in swapPs: continue # we need to correct for the effected linear functions: - # We first correct type 1 and type 2 by switching + # We first correct type 1 and type 2 by switching # the phase applied to c_j and c_i+c_j - S[j,j] , S[i,j] = S[i,j], S[j,j] + S[j, j], S[i, j] = S[i, j], S[j, j] # Then, we go through all the boxes that permutes j BEFORE box(i,j) and update: - for k in range(n): #all boxes that permutes j - if i==k or j==k: + for k in range(n): # all boxes that permutes j + if i == k or j == k: continue - if orderComp[min(k,j)] < orderComp[i] and S[min(k,j),max(k,j)]%4!=0: - phase = S[min(k,j),max(k,j)] - S[min(k,j),max(k,j)] = 0 - - #Step 1, apply phase to c_i, c_j, c_k - for l in [i,j,k]: - S[l,l] = (S[l,l]+phase*3)%4 - - #Step 2, apply phase to c_i+ c_j, c_i+c_k, c_j+c_k: - for l1,l2 in [(i,j), (i,k),(j,k)]: - ls = min(l1,l2) - lb = max(l1,l2) - S[ls,lb] = (S[ls,lb]+phase*3)%4 + if orderComp[min(k, j)] < orderComp[i] and S[min(k, j), max(k, j)] % 4 != 0: + phase = S[min(k, j), max(k, j)] + S[min(k, j), max(k, j)] = 0 + + # Step 1, apply phase to c_i, c_j, c_k + for l in [i, j, k]: + S[l, l] = (S[l, l] + phase * 3) % 4 + + # Step 2, apply phase to c_i+ c_j, c_i+c_k, c_j+c_k: + for l1, l2 in [(i, j), (i, k), (j, k)]: + ls = min(l1, l2) + lb = max(l1, l2) + S[ls, lb] = (S[ls, lb] + phase * 3) % 4 return S - -def _apply_S_to_NW_circuit(n,S,seq, swapPs): - ''' - Given + + +def _apply_S_to_NW_circuit(n, S, seq, swapPs): + """ + Given Width of the circuit (int n) A CZ circuit, represented by the n*n phase schedule S A CX circuit, represented by box-labels (seq) and whether the box is SWAP+ (swapPs) * This circuit corresponds to the CX tranformation that tranforms a matrix to a NW matrix (Ref. [Prop.7.4, 1]) - Return a QuantumCircuit that computes S and CX - ''' + Return a QuantumCircuit that computes the phase scheudle S inside CX + """ cir = QuantumCircuit(n) - - wires = list(zip(range(n),range(1,n))) - wires = wires[::2]+wires[1::2] - - - for (i,(j,k)) in zip(range(len(seq)-1,-1,-1),reversed(seq)): - - w1, w2 = wires[i%(n-1)] - - p = S[j,k] - - if (j,k) not in swapPs: - cir.cnot(w1,w2) - - cir.cnot(w2,w1) - - if p%4 == 0: + + wires = list(zip(range(n), range(1, n))) + wires = wires[::2] + wires[1::2] + + for i, (j, k) in zip(range(len(seq) - 1, -1, -1), reversed(seq)): + w1, w2 = wires[i % (n - 1)] + + p = S[j, k] + + if (j, k) not in swapPs: + cir.cnot(w1, w2) + + cir.cnot(w2, w1) + + if p % 4 == 0: pass - elif p%4 == 1: + elif p % 4 == 1: cir.sdg(w2) - elif p%4 == 2: + elif p % 4 == 2: cir.z(w2) - elif p%4 == 3: + elif p % 4 == 3: cir.s(w2) - - cir.cnot(w1,w2) - + + cir.cnot(w1, w2) + for i in range(n): - p = S[n-1-i,n-1-i] - if p%4 == 0: + p = S[n - 1 - i, n - 1 - i] + if p % 4 == 0: continue - elif p%4 == 1: + elif p % 4 == 1: cir.sdg(i) - elif p%4 == 2: + elif p % 4 == 2: cir.z(i) - elif p%4 == 3: - cir.s(i) - + elif p % 4 == 3: + cir.s(i) + return cir +def synth_cx_cz_line_my(Mx, Mz): + """ + Joint synthesis of a -CZ-CX- circuit for linear nearest neighbour (LNN) connectivity, + with 2-qubit depth at most 5n, based on Maslov and Yang [2]. + This method computes the CZ circuit inside the CX circuit via phase gate insertions. + + Args: + Mz: a boolean symetric matrix representing a CZ circuit. + Mz[i][j]=1 represents a CZ(i,j) gate -def synth_cx_cz_line_my(Mx,Mz): - ''' - Given - -CX- circuit, represented by n*n binary, invertible matrix Mx - -CZ- circuit, repredented by n*n binary, symmetric matrix Mz - where n is the width of the circuit - Return a QuantumCircuit object, qc, that implements the -CZ-CX- in two-qubit depth at most 5n - ''' + Mx: a boolean invertible matrix representing a CX circuit. - # Find circuits implementing Mx by Proposition 7.3 and Proposition 7.4 of [1] + Return: + QuantumCircuit: a circuit implementation of a CX circuit following a CZ circuit, + denoted as a -CZ-CX- circuit,in two-qubit depth at most 5n, for LNN connectivity. + + Reference: + 1. S. A. Kutin, D. P. Moulton, and L. M. Smithline, "Computation at a distance," 2007. + + 2. D. Maslove and W. Yang, "CNOT circuits need little help to implement arbitrary + Hadamard-free Clifford transformations they generate," 2022. + """ + + # First, find circuits implementing Mx by Proposition 7.3 and Proposition 7.4 of [1] n = len(Mx) Mx = calc_inverse_matrix(Mx) cx_instructions_rows_m2nw, cx_instructions_rows_nw2id = _optimize_cx_circ_depth_5n_line(Mx) - - #Meanwhile, also build the -CZ- circuit via Phase gate insertions as per Algorithm 2 [2] - S = _initializeS(Mz) + + # Meanwhile, also build the -CZ- circuit via Phase gate insertions as per Algorithm 2 [2] + S = _initializeS(Mz) seq = _makeSeq(n) swapPs = _swapPlus(cx_instructions_rows_nw2id, seq) _updateS(n, S, swapPs) - - qc = _apply_S_to_NW_circuit(n,S,seq, swapPs) - - for i,j in reversed(cx_instructions_rows_m2nw): - qc.cx(i,j) - + + qc = _apply_S_to_NW_circuit(n, S, seq, swapPs) + + for i, j in reversed(cx_instructions_rows_m2nw): + qc.cx(i, j) + return qc diff --git a/test/python/synthesis/test_clifford_decompose_layers.py b/test/python/synthesis/test_clifford_decompose_layers.py index 2cd2a9ffb78c..f30d296ab0e9 100644 --- a/test/python/synthesis/test_clifford_decompose_layers.py +++ b/test/python/synthesis/test_clifford_decompose_layers.py @@ -11,6 +11,12 @@ # that they have been altered from the originals. """Tests for Clifford class.""" +import os, sys + +HOME = "/Users/willers/Documents/GitHub/qiskit-terra" +QISKIT_ROOT = HOME +root_dir = os.path.expanduser(QISKIT_ROOT) +sys.path = [os.path.expanduser(QISKIT_ROOT)] + sys.path import unittest from test import combine @@ -58,11 +64,11 @@ def test_decompose_lnn_depth(self, num_qubits): for _ in range(samples): cliff = random_clifford(num_qubits, seed=rng) circ = synth_clifford_depth_lnn(cliff) - # Check that the Clifford circuit 2-qubit depth is bounded by 9*n+4 + # Check that the Clifford circuit 2-qubit depth is bounded by 7*n+2 depth2q = (circ.decompose()).depth( filter_function=lambda x: x.operation.num_qubits == 2 ) - self.assertTrue(depth2q <= 9 * num_qubits + 4) + self.assertTrue(depth2q <= 7 * num_qubits + 2) # Check that the Clifford circuit has linear nearest neighbour connectivity self.assertTrue(check_lnn_connectivity(circ.decompose())) cliff_target = Clifford(circ) diff --git a/test/python/synthesis/test_cx_cz_synthesis.py b/test/python/synthesis/test_cx_cz_synthesis.py index b962b30a2a80..ccee5ef08c99 100644 --- a/test/python/synthesis/test_cx_cz_synthesis.py +++ b/test/python/synthesis/test_cx_cz_synthesis.py @@ -31,12 +31,14 @@ ) from qiskit.synthesis.linear.linear_circuits_utils import ( - transpose_cx_circ, + transpose_cx_circ, optimize_cx_4_options, - check_lnn_connectivity) + check_lnn_connectivity, +) from qiskit.test import QiskitTestCase + @ddt class TestCXCZSynth(QiskitTestCase): """Test the linear reversible circuit synthesis functions.""" @@ -50,8 +52,6 @@ def test_cx_cz_synth_lnn(self, num_qubits): num_trials = 8 for _ in range(num_trials): - - # Generate a random CZ circuit mat_z = np.zeros((num_qubits, num_qubits)) cir_z = QuantumCircuit(num_qubits) @@ -70,17 +70,16 @@ def test_cx_cz_synth_lnn(self, num_qubits): mat_x = np.array(mat_x, dtype=bool) cir_x = synth_cnot_depth_line_kms(mat_x) - # Joint Synthesis cirZX_test = QuantumCircuit.compose(cir_z, cir_x) - cirZX = synth_cx_cz_line_my(mat_x,mat_z) + cirZX = synth_cx_cz_line_my(mat_x, mat_z) # Check that the output circuit 2-qubit depth is at most 5n depth2q = cirZX.depth(filter_function=lambda x: x.operation.num_qubits == 2) - self.assertTrue(depth2q <= 5 * num_qubits ) + self.assertTrue(depth2q <= 5 * num_qubits) # Check that the output circuit has LNN connectivity self.assertTrue(check_lnn_connectivity(cirZX)) From 00ab7aacec0ff057ab7d4a7b80a948711147777c Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 24 Apr 2023 16:33:46 +0200 Subject: [PATCH 04/14] Update test_clifford_decompose_layers.py --- test/python/synthesis/test_clifford_decompose_layers.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/python/synthesis/test_clifford_decompose_layers.py b/test/python/synthesis/test_clifford_decompose_layers.py index f30d296ab0e9..137f6eed6c86 100644 --- a/test/python/synthesis/test_clifford_decompose_layers.py +++ b/test/python/synthesis/test_clifford_decompose_layers.py @@ -12,12 +12,6 @@ """Tests for Clifford class.""" import os, sys - -HOME = "/Users/willers/Documents/GitHub/qiskit-terra" -QISKIT_ROOT = HOME -root_dir = os.path.expanduser(QISKIT_ROOT) -sys.path = [os.path.expanduser(QISKIT_ROOT)] + sys.path - import unittest from test import combine from ddt import ddt From b2fe5d956f25093470e8594e65d297df1825ae77 Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 24 Apr 2023 16:46:03 +0200 Subject: [PATCH 05/14] Fixing some imports --- qiskit/synthesis/clifford/clifford_decompose_layers.py | 6 ++---- qiskit/synthesis/linear_phase/__init__.py | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/qiskit/synthesis/clifford/clifford_decompose_layers.py b/qiskit/synthesis/clifford/clifford_decompose_layers.py index a0e5c3c66761..54080126f5e0 100644 --- a/qiskit/synthesis/clifford/clifford_decompose_layers.py +++ b/qiskit/synthesis/clifford/clifford_decompose_layers.py @@ -22,10 +22,8 @@ synth_cnot_count_full_pmh, synth_cnot_depth_line_kms, ) -from qiskit.synthesis.linear_phase import ( - synth_cz_depth_line_mr, - synth_cx_cz_line_my, -) +from qiskit.synthesis.linear_phase import synth_cz_depth_line_mr +from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_line_my from qiskit.synthesis.linear.linear_matrix_utils import ( diff --git a/qiskit/synthesis/linear_phase/__init__.py b/qiskit/synthesis/linear_phase/__init__.py index da2b6ec6393a..78ce4433603f 100644 --- a/qiskit/synthesis/linear_phase/__init__.py +++ b/qiskit/synthesis/linear_phase/__init__.py @@ -13,4 +13,3 @@ """Module containing cnot-phase circuits""" from .cz_depth_lnn import synth_cz_depth_line_mr -from .cx_cz_depth_lnn import synth_cx_cz_line_my From 79489e57115b11cadb4fcedc26ca4bc646ab739c Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Tue, 25 Apr 2023 16:11:13 +0200 Subject: [PATCH 06/14] Fixing more formatting issues --- .../synthesis/linear_phase/cx_cz_depth_lnn.py | 149 ++++++++++-------- test/python/synthesis/test_cx_cz_synthesis.py | 22 +-- 2 files changed, 87 insertions(+), 84 deletions(-) diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py index a8d6754e9f58..99c6a3a189d7 100644 --- a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -16,8 +16,8 @@ Return a depth-5n circuit implementation of the -CZ-CX- transformation over LNN. Args: - Mz: n*n symmetric binary matrix representing a -CZ- circuit - Mx: n*n invertable binary matrix representing a -CX- transformation + mat_z: n*n symmetric binary matrix representing a -CZ- circuit + mat_x: n*n invertable binary matrix representing a -CX- transformation Output: qc: QuantumCircuit object containing a depth-5n circuit to implement -CZ-CX- @@ -28,62 +28,71 @@ Hadamard-free Clifford transformations they generate," 2022. """ -import numpy as np + from copy import deepcopy -from qiskit.exceptions import QiskitError +import numpy as np from qiskit.circuit import QuantumCircuit from qiskit.synthesis.linear.linear_matrix_utils import calc_inverse_matrix from qiskit.synthesis.linear.linear_depth_lnn import _optimize_cx_circ_depth_5n_line -def _initializeS(Mz): +def _initialize_phase_schedule(mat_z): """ Given a CZ layer (represented as an n*n CZ matrix Mz) Return a scheudle of phase gates implementing Mz in a SWAP-only netwrok (Ref. [Alg 1, 2]) """ - n = len(Mz) - S = np.zeros((n, n), dtype=int) - for i, j in zip(*np.where(Mz)): + n = len(mat_z) + phase_schedule = np.zeros((n, n), dtype=int) + for i, j in zip(*np.where(mat_z)): if i >= j: continue - S[i, j] = 3 - S[i, i] += 1 - S[j, j] += 1 + phase_schedule[i, j] = 3 + phase_schedule[i, i] += 1 + phase_schedule[j, j] += 1 - return S + return phase_schedule -def _shuffle(l, odd): +def _shuffle(labels, odd): """ - Given a list of indices l and boolean odd indicating odd/even layers, - Shuffle the indices in l by swapping adjacent elements + Args: + labels : a list of indices + odd : a boolean indicating whether this layer is odd or even, + Shuffle the indices in labels by swapping adjacent elements (Ref. [Fig.2, 2]) """ - swapped = [v for p in zip(l[1::2], l[::2]) for v in p] - return swapped + l[-1:] if odd else swapped + swapped = [v for p in zip(labels[1::2], labels[::2]) for v in p] + return swapped + labels[-1:] if odd else swapped -def _makeSeq(n): +def _make_seq(n): """ Given the width of the circuit n, - Return the label of the boxes in order from left to right, top to bottom + Return the labels of the boxes in order from left to right, top to bottom (Ref. [Fig.2, 2]) """ seq = [] - l = list(range(n - 1, -1, -1)) + wire_labels = list(range(n - 1, -1, -1)) for i in range(n): - r = _shuffle(l, n % 2) if i % 2 == 0 else l[0:1] + _shuffle(l[1:], (n + 1) % 2) - seq += list([(min(i), max(i)) for i in zip(l[::2], r[::2]) if i[0] != i[1]]) - l = r + wire_labels_new = ( + _shuffle(wire_labels, n % 2) + if i % 2 == 0 + else wire_labels[0:1] + _shuffle(wire_labels[1:], (n + 1) % 2) + ) + seq += [ + (min(i), max(i)) for i in zip(wire_labels[::2], wire_labels_new[::2]) if i[0] != i[1] + ] + wire_labels = wire_labels_new + return seq -def _swapPlus(instructions, seq): +def _swap_plus(instructions, seq): """ Given CX instructions (Ref. [Thm 7.1, 1]) and the labels of all boxes, Return a list of labels of the boxes that is SWAP+ in descending order @@ -91,67 +100,69 @@ def _swapPlus(instructions, seq): from left to right """ instr = deepcopy(instructions) - swapPs = set() + swap_plus = set() for i, j in reversed(seq): - CNOT1 = instr.pop() - CNOT2 = instr.pop() + cnot_1 = instr.pop() + instr.pop() - if instr == [] or instr[-1] != CNOT1: + if instr == [] or instr[-1] != cnot_1: # Only two CNOTs on same set of controls -> this box is SWAP+ - swapPs.add((i, j)) + swap_plus.add((i, j)) else: - CNOT3 = instr.pop() - return swapPs + instr.pop() + return swap_plus -def _updateS(n, S, swapPs): +def _update_phase_schedule(n, phase_schedule, swap_plus): """ - Given S initialized to induce a CZ circuit in SWAP-only network and list of SWAP+ boxes - Update S for each SWAP+ according to Algorithm 2 [2] + Given phase_schedule initialized to induce a CZ circuit in SWAP-only network and list of SWAP+ boxes + Update phase_schedule for each SWAP+ according to Algorithm 2 [2] """ - l = list(range(n)) - Pn = l[-3::-2] + l[-2::-2][::-1] - orderComp = np.argsort(Pn[::-1]) + layer_order = list(range(n))[-3::-2] + list(range(n))[-2::-2][::-1] + order_comp = np.argsort(layer_order[::-1]) # Go through each box by descending layer order - for i in Pn: + for i in layer_order: for j in range(i + 1, n): - if (i, j) not in swapPs: + if (i, j) not in swap_plus: continue # we need to correct for the effected linear functions: # We first correct type 1 and type 2 by switching # the phase applied to c_j and c_i+c_j - S[j, j], S[i, j] = S[i, j], S[j, j] + phase_schedule[j, j], phase_schedule[i, j] = phase_schedule[i, j], phase_schedule[j, j] # Then, we go through all the boxes that permutes j BEFORE box(i,j) and update: for k in range(n): # all boxes that permutes j - if i == k or j == k: + if k in (i, j): continue - if orderComp[min(k, j)] < orderComp[i] and S[min(k, j), max(k, j)] % 4 != 0: - phase = S[min(k, j), max(k, j)] - S[min(k, j), max(k, j)] = 0 + if ( + order_comp[min(k, j)] < order_comp[i] + and phase_schedule[min(k, j), max(k, j)] % 4 != 0 + ): + phase = phase_schedule[min(k, j), max(k, j)] + phase_schedule[min(k, j), max(k, j)] = 0 # Step 1, apply phase to c_i, c_j, c_k - for l in [i, j, k]: - S[l, l] = (S[l, l] + phase * 3) % 4 + for l in (i, j, k): + phase_schedule[l, l] = (phase_schedule[l, l] + phase * 3) % 4 # Step 2, apply phase to c_i+ c_j, c_i+c_k, c_j+c_k: for l1, l2 in [(i, j), (i, k), (j, k)]: ls = min(l1, l2) lb = max(l1, l2) - S[ls, lb] = (S[ls, lb] + phase * 3) % 4 - return S + phase_schedule[ls, lb] = (phase_schedule[ls, lb] + phase * 3) % 4 + return phase_schedule -def _apply_S_to_NW_circuit(n, S, seq, swapPs): +def _apply_phase_to_nw_circuit(n, phase_schedule, seq, swap_plus): """ Given Width of the circuit (int n) - A CZ circuit, represented by the n*n phase schedule S - A CX circuit, represented by box-labels (seq) and whether the box is SWAP+ (swapPs) + A CZ circuit, represented by the n*n phase schedule phase_schedule + A CX circuit, represented by box-labels (seq) and whether the box is SWAP+ (swap_plus) * This circuit corresponds to the CX tranformation that tranforms a matrix to a NW matrix (Ref. [Prop.7.4, 1]) Return a QuantumCircuit that computes the phase scheudle S inside CX @@ -164,9 +175,9 @@ def _apply_S_to_NW_circuit(n, S, seq, swapPs): for i, (j, k) in zip(range(len(seq) - 1, -1, -1), reversed(seq)): w1, w2 = wires[i % (n - 1)] - p = S[j, k] + p = phase_schedule[j, k] - if (j, k) not in swapPs: + if (j, k) not in swap_plus: cir.cnot(w1, w2) cir.cnot(w2, w1) @@ -177,36 +188,36 @@ def _apply_S_to_NW_circuit(n, S, seq, swapPs): cir.sdg(w2) elif p % 4 == 2: cir.z(w2) - elif p % 4 == 3: + else: cir.s(w2) cir.cnot(w1, w2) for i in range(n): - p = S[n - 1 - i, n - 1 - i] + p = phase_schedule[n - 1 - i, n - 1 - i] if p % 4 == 0: continue - elif p % 4 == 1: + if p % 4 == 1: cir.sdg(i) elif p % 4 == 2: cir.z(i) - elif p % 4 == 3: + else: cir.s(i) return cir -def synth_cx_cz_line_my(Mx, Mz): +def synth_cx_cz_line_my(mat_x, mat_z): """ Joint synthesis of a -CZ-CX- circuit for linear nearest neighbour (LNN) connectivity, with 2-qubit depth at most 5n, based on Maslov and Yang [2]. This method computes the CZ circuit inside the CX circuit via phase gate insertions. Args: - Mz: a boolean symetric matrix representing a CZ circuit. + mat_z : a boolean symetric matrix representing a CZ circuit. Mz[i][j]=1 represents a CZ(i,j) gate - Mx: a boolean invertible matrix representing a CX circuit. + mat_x : a boolean invertible matrix representing a CX circuit. Return: QuantumCircuit: a circuit implementation of a CX circuit following a CZ circuit, @@ -219,21 +230,21 @@ def synth_cx_cz_line_my(Mx, Mz): Hadamard-free Clifford transformations they generate," 2022. """ - # First, find circuits implementing Mx by Proposition 7.3 and Proposition 7.4 of [1] + # First, find circuits implementing mat_x by Proposition 7.3 and Proposition 7.4 of [1] - n = len(Mx) - Mx = calc_inverse_matrix(Mx) + n = len(mat_x) + mat_x = calc_inverse_matrix(mat_x) - cx_instructions_rows_m2nw, cx_instructions_rows_nw2id = _optimize_cx_circ_depth_5n_line(Mx) + cx_instructions_rows_m2nw, cx_instructions_rows_nw2id = _optimize_cx_circ_depth_5n_line(mat_x) # Meanwhile, also build the -CZ- circuit via Phase gate insertions as per Algorithm 2 [2] - S = _initializeS(Mz) - seq = _makeSeq(n) - swapPs = _swapPlus(cx_instructions_rows_nw2id, seq) + phase_schedule = _initialize_phase_schedule(mat_z) + seq = _make_seq(n) + swap_plus = _swap_plus(cx_instructions_rows_nw2id, seq) - _updateS(n, S, swapPs) + _update_phase_schedule(n, phase_schedule, swap_plus) - qc = _apply_S_to_NW_circuit(n, S, seq, swapPs) + qc = _apply_phase_to_nw_circuit(n, phase_schedule, seq, swap_plus) for i, j in reversed(cx_instructions_rows_m2nw): qc.cx(i, j) diff --git a/test/python/synthesis/test_cx_cz_synthesis.py b/test/python/synthesis/test_cx_cz_synthesis.py index ccee5ef08c99..1a751ed9cb9e 100644 --- a/test/python/synthesis/test_cx_cz_synthesis.py +++ b/test/python/synthesis/test_cx_cz_synthesis.py @@ -17,24 +17,16 @@ import numpy as np from ddt import ddt from qiskit import QuantumCircuit -from qiskit.synthesis.linear.linear_depth_lnn import synth_cnot_depth_line_kms -from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_line_my from qiskit.quantum_info import Clifford +from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_line_my from qiskit.synthesis.linear import ( - synth_cnot_count_full_pmh, synth_cnot_depth_line_kms, random_invertible_binary_matrix, - check_invertible_binary_matrix, - calc_inverse_matrix, ) -from qiskit.synthesis.linear.linear_circuits_utils import ( - transpose_cx_circ, - optimize_cx_4_options, - check_lnn_connectivity, -) +from qiskit.synthesis.linear.linear_circuits_utils import check_lnn_connectivity from qiskit.test import QiskitTestCase @@ -72,20 +64,20 @@ def test_cx_cz_synth_lnn(self, num_qubits): # Joint Synthesis - cirZX_test = QuantumCircuit.compose(cir_z, cir_x) + cir_zx_test = QuantumCircuit.compose(cir_z, cir_x) - cirZX = synth_cx_cz_line_my(mat_x, mat_z) + cir_zx= synth_cx_cz_line_my(mat_x, mat_z) # Check that the output circuit 2-qubit depth is at most 5n - depth2q = cirZX.depth(filter_function=lambda x: x.operation.num_qubits == 2) + depth2q = cir_zx.depth(filter_function=lambda x: x.operation.num_qubits == 2) self.assertTrue(depth2q <= 5 * num_qubits) # Check that the output circuit has LNN connectivity - self.assertTrue(check_lnn_connectivity(cirZX)) + self.assertTrue(check_lnn_connectivity(cir_zx)) # Assert that we get the same elements as other methods - self.assertEqual(Clifford(cirZX), Clifford(cirZX_test)) + self.assertEqual(Clifford(cir_zx), Clifford(cir_zx_test)) if __name__ == "__main__": From 3da1f9524dccae94a55d68f976da4734f5f6f758 Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Tue, 25 Apr 2023 16:29:42 +0200 Subject: [PATCH 07/14] Add parameter type documentation and minor changes --- qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py | 6 +++--- test/python/synthesis/test_cx_cz_synthesis.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py index 99c6a3a189d7..52f34a19fa84 100644 --- a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -146,8 +146,8 @@ def _update_phase_schedule(n, phase_schedule, swap_plus): phase_schedule[min(k, j), max(k, j)] = 0 # Step 1, apply phase to c_i, c_j, c_k - for l in (i, j, k): - phase_schedule[l, l] = (phase_schedule[l, l] + phase * 3) % 4 + for l_s in (i, j, k): + phase_schedule[l_s, l_s] = (phase_schedule[l_s, l_s] + phase * 3) % 4 # Step 2, apply phase to c_i+ c_j, c_i+c_k, c_j+c_k: for l1, l2 in [(i, j), (i, k), (j, k)]: @@ -207,7 +207,7 @@ def _apply_phase_to_nw_circuit(n, phase_schedule, seq, swap_plus): return cir -def synth_cx_cz_line_my(mat_x, mat_z): +def synth_cx_cz_line_my(mat_x: np.ndarray, mat_z: np.ndarray): """ Joint synthesis of a -CZ-CX- circuit for linear nearest neighbour (LNN) connectivity, with 2-qubit depth at most 5n, based on Maslov and Yang [2]. diff --git a/test/python/synthesis/test_cx_cz_synthesis.py b/test/python/synthesis/test_cx_cz_synthesis.py index 1a751ed9cb9e..cae93d639035 100644 --- a/test/python/synthesis/test_cx_cz_synthesis.py +++ b/test/python/synthesis/test_cx_cz_synthesis.py @@ -66,7 +66,7 @@ def test_cx_cz_synth_lnn(self, num_qubits): cir_zx_test = QuantumCircuit.compose(cir_z, cir_x) - cir_zx= synth_cx_cz_line_my(mat_x, mat_z) + cir_zx = synth_cx_cz_line_my(mat_x, mat_z) # Check that the output circuit 2-qubit depth is at most 5n From f74e96387be9c0b641ab0f9762064309d8ca41b6 Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 8 May 2023 14:54:56 +0200 Subject: [PATCH 08/14] adding release note --- qiskit/synthesis/__init__.py | 2 +- qiskit/synthesis/linear_phase/__init__.py | 1 + qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py | 2 +- .../notes/cx_cz_synthesis-3d5ec98372ce1608.yaml | 13 +++++++++++++ 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index 4d118ddb1fa3..d377be7b704d 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -118,7 +118,7 @@ synth_cnot_count_full_pmh, synth_cnot_depth_line_kms, ) -from .linear_phase import synth_cz_depth_line_mr, synth_cnot_phase_aam +from .linear_phase import synth_cz_depth_line_mr, synth_cx_cz_line_my, synth_cnot_phase_aam from .clifford import ( synth_clifford_full, synth_clifford_ag, diff --git a/qiskit/synthesis/linear_phase/__init__.py b/qiskit/synthesis/linear_phase/__init__.py index 0be6c9c91175..abffe0db06db 100644 --- a/qiskit/synthesis/linear_phase/__init__.py +++ b/qiskit/synthesis/linear_phase/__init__.py @@ -13,4 +13,5 @@ """Module containing cnot-phase circuits""" from .cz_depth_lnn import synth_cz_depth_line_mr +from .cx_cz_depth_lnn import synth_cx_cz_line_my from .cnot_phase_synth import synth_cnot_phase_aam diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py index 52f34a19fa84..15cc0dbd96e8 100644 --- a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -220,7 +220,7 @@ def synth_cx_cz_line_my(mat_x: np.ndarray, mat_z: np.ndarray): mat_x : a boolean invertible matrix representing a CX circuit. Return: - QuantumCircuit: a circuit implementation of a CX circuit following a CZ circuit, + qc : a circuit implementation of a CX circuit following a CZ circuit, denoted as a -CZ-CX- circuit,in two-qubit depth at most 5n, for LNN connectivity. Reference: diff --git a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml new file mode 100644 index 000000000000..80185b87b9fd --- /dev/null +++ b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml @@ -0,0 +1,13 @@ +--- +features: + - | + Add a new synthesis algorithm `.synth_cx_cz_depth_line_my` of a CX circuit + accompanied by a CZ circuit for linear nearest neighbor (LNN) connectivity in + 2-qubit depth of 5n using CX and phase gates (S, Sdg or Z). The synthesis algorithm + is based on the paper of Maslov and Yang (https://arxiv.org/abs/2210.16195). + For LNN connectivity in 2-qubit depth of 7n+2 (which is still not optimal), + using the layered Clifford synthesis (`.synth_clifford_layers`), + `.synth_cx_cz_depth_line_my` to synthesize the CX layer and the first CZ layer in + depth 5n, and `.synth_cz_depth_line_mr` to synthesize the second CZ layers in depth 2n+2. + This PR will be followed by another PR that performs further local optimizations between + two layers, and hence reduce the depth of a Clifford circuit to 7n-4 for LNN connectivity. From 84ee0ee153d2ea8df11f1e6f01b116a59070b3d9 Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 8 May 2023 17:28:54 +0200 Subject: [PATCH 09/14] Update __init__.py for API --- qiskit/synthesis/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index d377be7b704d..3853401f9c84 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -46,6 +46,7 @@ :toctree: ../stubs/ synth_cz_depth_line_mr + synth_cx_cz_line_my Permutation Synthesis ===================== From 05bf1edefce2f701e5a7454484cfe01c7afbca35 Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 8 May 2023 17:56:26 +0200 Subject: [PATCH 10/14] Updating function name from synth_cx_cz_line_my to synth_cx_cz_depth_line_my --- qiskit/synthesis/__init__.py | 4 ++-- qiskit/synthesis/clifford/clifford_decompose_layers.py | 4 ++-- qiskit/synthesis/linear_phase/__init__.py | 2 +- qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py | 2 +- releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml | 4 ++-- test/python/synthesis/test_cx_cz_synthesis.py | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index 3853401f9c84..ff7a3b025a8f 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -46,7 +46,7 @@ :toctree: ../stubs/ synth_cz_depth_line_mr - synth_cx_cz_line_my + synth_cx_cz_depth_line_my Permutation Synthesis ===================== @@ -119,7 +119,7 @@ synth_cnot_count_full_pmh, synth_cnot_depth_line_kms, ) -from .linear_phase import synth_cz_depth_line_mr, synth_cx_cz_line_my, synth_cnot_phase_aam +from .linear_phase import synth_cz_depth_line_mr, synth_cx_cz_depth_line_my, synth_cnot_phase_aam from .clifford import ( synth_clifford_full, synth_clifford_ag, diff --git a/qiskit/synthesis/clifford/clifford_decompose_layers.py b/qiskit/synthesis/clifford/clifford_decompose_layers.py index 54080126f5e0..6963e9abca5a 100644 --- a/qiskit/synthesis/clifford/clifford_decompose_layers.py +++ b/qiskit/synthesis/clifford/clifford_decompose_layers.py @@ -23,7 +23,7 @@ synth_cnot_depth_line_kms, ) from qiskit.synthesis.linear_phase import synth_cz_depth_line_mr -from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_line_my +from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_depth_line_my from qiskit.synthesis.linear.linear_matrix_utils import ( @@ -439,7 +439,7 @@ def synth_clifford_depth_lnn(cliff): cliff, cx_synth_func=synth_cnot_depth_line_kms, cz_synth_func=synth_cz_depth_line_mr, - cx_cz_synth_func=synth_cx_cz_line_my, + cx_cz_synth_func=synth_cx_cz_depth_line_my, cz_func_reverse_qubits=True, ) return circ diff --git a/qiskit/synthesis/linear_phase/__init__.py b/qiskit/synthesis/linear_phase/__init__.py index abffe0db06db..a73f956e5015 100644 --- a/qiskit/synthesis/linear_phase/__init__.py +++ b/qiskit/synthesis/linear_phase/__init__.py @@ -13,5 +13,5 @@ """Module containing cnot-phase circuits""" from .cz_depth_lnn import synth_cz_depth_line_mr -from .cx_cz_depth_lnn import synth_cx_cz_line_my +from .cx_cz_depth_lnn import synth_cx_cz_depth_line_my from .cnot_phase_synth import synth_cnot_phase_aam diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py index 15cc0dbd96e8..5264140e1af1 100644 --- a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -207,7 +207,7 @@ def _apply_phase_to_nw_circuit(n, phase_schedule, seq, swap_plus): return cir -def synth_cx_cz_line_my(mat_x: np.ndarray, mat_z: np.ndarray): +def synth_cx_cz_depth_line_my(mat_x: np.ndarray, mat_z: np.ndarray): """ Joint synthesis of a -CZ-CX- circuit for linear nearest neighbour (LNN) connectivity, with 2-qubit depth at most 5n, based on Maslov and Yang [2]. diff --git a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml index 80185b87b9fd..fea312f7c48e 100644 --- a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml +++ b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml @@ -1,13 +1,13 @@ --- features: - | - Add a new synthesis algorithm `.synth_cx_cz_depth_line_my` of a CX circuit + Add a new synthesis algorithm `.synth_cx_cz_line_my` of a CX circuit accompanied by a CZ circuit for linear nearest neighbor (LNN) connectivity in 2-qubit depth of 5n using CX and phase gates (S, Sdg or Z). The synthesis algorithm is based on the paper of Maslov and Yang (https://arxiv.org/abs/2210.16195). For LNN connectivity in 2-qubit depth of 7n+2 (which is still not optimal), using the layered Clifford synthesis (`.synth_clifford_layers`), - `.synth_cx_cz_depth_line_my` to synthesize the CX layer and the first CZ layer in + `.synth_cx_cz_line_my` to synthesize the CX layer and the first CZ layer in depth 5n, and `.synth_cz_depth_line_mr` to synthesize the second CZ layers in depth 2n+2. This PR will be followed by another PR that performs further local optimizations between two layers, and hence reduce the depth of a Clifford circuit to 7n-4 for LNN connectivity. diff --git a/test/python/synthesis/test_cx_cz_synthesis.py b/test/python/synthesis/test_cx_cz_synthesis.py index cae93d639035..eec3afe1dfff 100644 --- a/test/python/synthesis/test_cx_cz_synthesis.py +++ b/test/python/synthesis/test_cx_cz_synthesis.py @@ -20,7 +20,7 @@ from qiskit.quantum_info import Clifford -from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_line_my +from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_depth_line_my from qiskit.synthesis.linear import ( synth_cnot_depth_line_kms, random_invertible_binary_matrix, @@ -66,7 +66,7 @@ def test_cx_cz_synth_lnn(self, num_qubits): cir_zx_test = QuantumCircuit.compose(cir_z, cir_x) - cir_zx = synth_cx_cz_line_my(mat_x, mat_z) + cir_zx = synth_cx_cz_depth_line_my(mat_x, mat_z) # Check that the output circuit 2-qubit depth is at most 5n From c5d432fb0874c3e76b96ea0425d605f2117571ef Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 8 May 2023 17:58:31 +0200 Subject: [PATCH 11/14] Updating release note for function name change --- releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml index fea312f7c48e..80185b87b9fd 100644 --- a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml +++ b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml @@ -1,13 +1,13 @@ --- features: - | - Add a new synthesis algorithm `.synth_cx_cz_line_my` of a CX circuit + Add a new synthesis algorithm `.synth_cx_cz_depth_line_my` of a CX circuit accompanied by a CZ circuit for linear nearest neighbor (LNN) connectivity in 2-qubit depth of 5n using CX and phase gates (S, Sdg or Z). The synthesis algorithm is based on the paper of Maslov and Yang (https://arxiv.org/abs/2210.16195). For LNN connectivity in 2-qubit depth of 7n+2 (which is still not optimal), using the layered Clifford synthesis (`.synth_clifford_layers`), - `.synth_cx_cz_line_my` to synthesize the CX layer and the first CZ layer in + `.synth_cx_cz_depth_line_my` to synthesize the CX layer and the first CZ layer in depth 5n, and `.synth_cz_depth_line_mr` to synthesize the second CZ layers in depth 2n+2. This PR will be followed by another PR that performs further local optimizations between two layers, and hence reduce the depth of a Clifford circuit to 7n-4 for LNN connectivity. From 11700fc4fb58a6b14d187af254a900ae6bd1b52d Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 22 May 2023 12:03:07 -0700 Subject: [PATCH 12/14] Stylistic changes addressing comments --- .../clifford/clifford_decompose_layers.py | 4 +- .../synthesis/linear_phase/cx_cz_depth_lnn.py | 46 +++++++++++-------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/qiskit/synthesis/clifford/clifford_decompose_layers.py b/qiskit/synthesis/clifford/clifford_decompose_layers.py index 6963e9abca5a..e859219ba0f2 100644 --- a/qiskit/synthesis/clifford/clifford_decompose_layers.py +++ b/qiskit/synthesis/clifford/clifford_decompose_layers.py @@ -24,8 +24,6 @@ ) from qiskit.synthesis.linear_phase import synth_cz_depth_line_mr from qiskit.synthesis.linear_phase.cx_cz_depth_lnn import synth_cx_cz_depth_line_my - - from qiskit.synthesis.linear.linear_matrix_utils import ( calc_inverse_matrix, _compute_rank, @@ -145,7 +143,7 @@ def synth_clifford_layers( layeredCircuit.append(CXinv, qubit_list) else: - # note CZ2_circ is None and built into the CX_circ when + # note that CZ2_circ is None and built into the CX_circ when # cx_cz_synth_func is not None layeredCircuit.append(CX_circ, qubit_list) diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py index 5264140e1af1..87a629a034d6 100644 --- a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2022 +# (C) Copyright IBM 2017, 2023 # # 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 @@ -20,21 +20,19 @@ mat_x: n*n invertable binary matrix representing a -CX- transformation Output: - qc: QuantumCircuit object containing a depth-5n circuit to implement -CZ-CX- + QuantumCircuit: QuantumCircuit object containing a depth-5n circuit to implement -CZ-CX- References: [1] S. A. Kutin, D. P. Moulton, and L. M. Smithline, "Computation at a distance," 2007. - [2] D. Maslove and W. Yang, "CNOT circuits need little help to implement arbitrary + [2] D. Maslov and W. Yang, "CNOT circuits need little help to implement arbitrary Hadamard-free Clifford transformations they generate," 2022. """ - from copy import deepcopy - import numpy as np + from qiskit.circuit import QuantumCircuit from qiskit.synthesis.linear.linear_matrix_utils import calc_inverse_matrix - from qiskit.synthesis.linear.linear_depth_lnn import _optimize_cx_circ_depth_5n_line @@ -42,7 +40,7 @@ def _initialize_phase_schedule(mat_z): """ Given a CZ layer (represented as an n*n CZ matrix Mz) Return a scheudle of phase gates implementing Mz in a SWAP-only netwrok - (Ref. [Alg 1, 2]) + (c.f. Alg 1, [2]) """ n = len(mat_z) phase_schedule = np.zeros((n, n), dtype=int) @@ -63,7 +61,7 @@ def _shuffle(labels, odd): labels : a list of indices odd : a boolean indicating whether this layer is odd or even, Shuffle the indices in labels by swapping adjacent elements - (Ref. [Fig.2, 2]) + (c.f. Fig.2, [2]) """ swapped = [v for p in zip(labels[1::2], labels[::2]) for v in p] return swapped + labels[-1:] if odd else swapped @@ -73,7 +71,7 @@ def _make_seq(n): """ Given the width of the circuit n, Return the labels of the boxes in order from left to right, top to bottom - (Ref. [Fig.2, 2]) + (c.f. Fig.2, [2]) """ seq = [] wire_labels = list(range(n - 1, -1, -1)) @@ -94,10 +92,15 @@ def _make_seq(n): def _swap_plus(instructions, seq): """ - Given CX instructions (Ref. [Thm 7.1, 1]) and the labels of all boxes, + Given CX instructions (c.f. Thm 7.1, [1]) and the labels of all boxes, Return a list of labels of the boxes that is SWAP+ in descending order * Assumes the instruction gives gates in the order from top to bottom, from left to right + * SWAP+ is defined in section 3.A. of [2]. Note the northwest + diagonalization procedure of [1] consists exactly n layers of boxes, + each being either a SWAP or a SWAP+. That is, each northwest + diagonalization circuit can be uniquely represented by which of its + n(n-1)/2 boxes are SWAP+ and which are SWAP. """ instr = deepcopy(instructions) swap_plus = set() @@ -116,7 +119,7 @@ def _swap_plus(instructions, seq): def _update_phase_schedule(n, phase_schedule, swap_plus): """ Given phase_schedule initialized to induce a CZ circuit in SWAP-only network and list of SWAP+ boxes - Update phase_schedule for each SWAP+ according to Algorithm 2 [2] + Update phase_schedule for each SWAP+ according to Algorithm 2, [2] """ layer_order = list(range(n))[-3::-2] + list(range(n))[-2::-2][::-1] order_comp = np.argsort(layer_order[::-1]) @@ -164,7 +167,12 @@ def _apply_phase_to_nw_circuit(n, phase_schedule, seq, swap_plus): A CZ circuit, represented by the n*n phase schedule phase_schedule A CX circuit, represented by box-labels (seq) and whether the box is SWAP+ (swap_plus) * This circuit corresponds to the CX tranformation that tranforms a matrix to - a NW matrix (Ref. [Prop.7.4, 1]) + a NW matrix (c.f. Prop.7.4, [1]) + * SWAP+ is defined in section 3.A. of [2]. + * As previously noted, the northwest diagonalization procedure of [1] consists + of exactly n layers of boxes, each being either a SWAP or a SWAP+. That is, + each northwest diagonalization circuit can be uniquely represented by which + of its n(n-1)/2 boxes are SWAP+ and which are SWAP. Return a QuantumCircuit that computes the phase scheudle S inside CX """ cir = QuantumCircuit(n) @@ -214,20 +222,22 @@ def synth_cx_cz_depth_line_my(mat_x: np.ndarray, mat_z: np.ndarray): This method computes the CZ circuit inside the CX circuit via phase gate insertions. Args: - mat_z : a boolean symetric matrix representing a CZ circuit. + mat_z : a boolean symmetric matrix representing a CZ circuit. Mz[i][j]=1 represents a CZ(i,j) gate mat_x : a boolean invertible matrix representing a CX circuit. Return: - qc : a circuit implementation of a CX circuit following a CZ circuit, + QuantumCircuit : a circuit implementation of a CX circuit following a CZ circuit, denoted as a -CZ-CX- circuit,in two-qubit depth at most 5n, for LNN connectivity. Reference: - 1. S. A. Kutin, D. P. Moulton, and L. M. Smithline, "Computation at a distance," 2007. - - 2. D. Maslove and W. Yang, "CNOT circuits need little help to implement arbitrary - Hadamard-free Clifford transformations they generate," 2022. + 1. Kutin, S., Moulton, D. P., Smithline, L., + *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), + `arXiv:quant-ph/0701194 `_ + 2. Dmitri Maslov, Willers Yang, *CNOT circuits need little help to implement arbitrary + Hadamard-free Clifford transformations they generate*, + `arXiv:2210.16195 `_. """ # First, find circuits implementing mat_x by Proposition 7.3 and Proposition 7.4 of [1] From 8d035b7c822544c2ff6b8b80f30a49e2bb5b309b Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Mon, 10 Jul 2023 09:49:48 -0400 Subject: [PATCH 13/14] Fixing doc strings and adding example to release notes --- qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py | 4 ++-- releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py index 87a629a034d6..31795bf13aca 100644 --- a/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cx_cz_depth_lnn.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2017, 2023 +# (C) Copyright IBM 2023 # # 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 @@ -218,7 +218,7 @@ def _apply_phase_to_nw_circuit(n, phase_schedule, seq, swap_plus): def synth_cx_cz_depth_line_my(mat_x: np.ndarray, mat_z: np.ndarray): """ Joint synthesis of a -CZ-CX- circuit for linear nearest neighbour (LNN) connectivity, - with 2-qubit depth at most 5n, based on Maslov and Yang [2]. + with 2-qubit depth at most 5n, based on Maslov and Yang. This method computes the CZ circuit inside the CX circuit via phase gate insertions. Args: diff --git a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml index 80185b87b9fd..98a35ce63c82 100644 --- a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml +++ b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml @@ -3,8 +3,13 @@ features: - | Add a new synthesis algorithm `.synth_cx_cz_depth_line_my` of a CX circuit accompanied by a CZ circuit for linear nearest neighbor (LNN) connectivity in - 2-qubit depth of 5n using CX and phase gates (S, Sdg or Z). The synthesis algorithm - is based on the paper of Maslov and Yang (https://arxiv.org/abs/2210.16195). + 2-qubit depth of 5n using CX and phase gates (S, Sdg or Z). For example, let + mat_x be an invertable binary matrix representing a CX circuit, and let mat_z + be a binary symmetric matrix representing a CZ circuit. Calling + synth_cx_cz_depth_line_my(mat_x, mat_z) returns a quantum circuit object with + two-qubit-depth at most 5n computing the composition of the CX and CZ circuits. + The synthesis algorithm is based on the paper of Maslov and Yang + (https://arxiv.org/abs/2210.16195). For LNN connectivity in 2-qubit depth of 7n+2 (which is still not optimal), using the layered Clifford synthesis (`.synth_clifford_layers`), `.synth_cx_cz_depth_line_my` to synthesize the CX layer and the first CZ layer in From 9d85d9f07335158ec01f5501c3f3014d88071360 Mon Sep 17 00:00:00 2001 From: Willers Yang <112653929+Willers-Yang@users.noreply.github.com> Date: Tue, 11 Jul 2023 12:21:21 -0400 Subject: [PATCH 14/14] Update cx-cz release note with Alex's suggestions --- .../cx_cz_synthesis-3d5ec98372ce1608.yaml | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml index 98a35ce63c82..2f91f60cc002 100644 --- a/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml +++ b/releasenotes/notes/cx_cz_synthesis-3d5ec98372ce1608.yaml @@ -1,18 +1,32 @@ --- features: - | - Add a new synthesis algorithm `.synth_cx_cz_depth_line_my` of a CX circuit - accompanied by a CZ circuit for linear nearest neighbor (LNN) connectivity in - 2-qubit depth of 5n using CX and phase gates (S, Sdg or Z). For example, let - mat_x be an invertable binary matrix representing a CX circuit, and let mat_z - be a binary symmetric matrix representing a CZ circuit. Calling - synth_cx_cz_depth_line_my(mat_x, mat_z) returns a quantum circuit object with - two-qubit-depth at most 5n computing the composition of the CX and CZ circuits. - The synthesis algorithm is based on the paper of Maslov and Yang - (https://arxiv.org/abs/2210.16195). - For LNN connectivity in 2-qubit depth of 7n+2 (which is still not optimal), - using the layered Clifford synthesis (`.synth_clifford_layers`), - `.synth_cx_cz_depth_line_my` to synthesize the CX layer and the first CZ layer in - depth 5n, and `.synth_cz_depth_line_mr` to synthesize the second CZ layers in depth 2n+2. - This PR will be followed by another PR that performs further local optimizations between - two layers, and hence reduce the depth of a Clifford circuit to 7n-4 for LNN connectivity. + Added a new synthesis algorithm :func:`qiskit.synthesis.linear_phase.synth_cx_cz_depth_line_my` + of a CX circuit followed by a CZ circuit for linear nearest neighbor (LNN) connectivity in + 2-qubit depth of at most 5n using CX and phase gates (S, Sdg or Z). The synthesis algorithm is + based on the paper of Maslov and Yang (https://arxiv.org/abs/2210.16195). + The algorithm accepts a binary invertible matrix ``mat_x`` representing the CX-circuit, + a binary symmetric matrix ``mat_z`` representing the CZ-circuit, and returns a quantum circuit + with 2-qubit depth of at most 5n computing the composition of the CX and CZ circuits. + The following example illustrates the new functionality:: + + import numpy as np + from qiskit.synthesis.linear_phase import synth_cx_cz_depth_line_my + mat_x = np.array([[0, 1], [1, 1]]) + mat_z = np.array([[0, 1], [1, 0]]) + qc = synth_cx_cz_depth_line_my(mat_x, mat_z) + + This algorithm is now used by default in the Clifford synthesis algorithm + :func:`qiskit.synthesis.clifford.synth_clifford_depth_lnn` that optimizes 2-qubit depth + for LNN connectivity, improving the 2-qubit depth from 9n+4 to 7n+2. + The clifford synthesis algorithm can be used as follows:: + + from qiskit.quantum_info import random_clifford + from qiskit.synthesis import synth_clifford_depth_lnn + + cliff = random_clifford(3) + qc = synth_clifford_depth_lnn(cliff) + + The above synthesis can be further improved as described in the paper by Maslov and Yang, + using local optimization between 2-qubit layers. This improvement is left for follow-up + work. \ No newline at end of file