diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index c191eac97472..49e7885d5096 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -51,6 +51,7 @@ .. autofunction:: synth_permutation_depth_lnn_kms .. autofunction:: synth_permutation_basic .. autofunction:: synth_permutation_acg +.. autofunction:: synth_permutation_reverse_lnn_kms Clifford Synthesis ================== @@ -140,6 +141,7 @@ synth_permutation_depth_lnn_kms, synth_permutation_basic, synth_permutation_acg, + synth_permutation_reverse_lnn_kms, ) from .linear import ( synth_cnot_count_full_pmh, diff --git a/qiskit/synthesis/linear_phase/cz_depth_lnn.py b/qiskit/synthesis/linear_phase/cz_depth_lnn.py index b3931d078179..6dc7db5d619b 100644 --- a/qiskit/synthesis/linear_phase/cz_depth_lnn.py +++ b/qiskit/synthesis/linear_phase/cz_depth_lnn.py @@ -24,24 +24,10 @@ import numpy as np from qiskit.circuit import QuantumCircuit - - -def _append_cx_stage1(qc, n): - """A single layer of CX gates.""" - for i in range(n // 2): - qc.cx(2 * i, 2 * i + 1) - for i in range((n + 1) // 2 - 1): - qc.cx(2 * i + 2, 2 * i + 1) - return qc - - -def _append_cx_stage2(qc, n): - """A single layer of CX gates.""" - for i in range(n // 2): - qc.cx(2 * i + 1, 2 * i) - for i in range((n + 1) // 2 - 1): - qc.cx(2 * i + 1, 2 * i + 2) - return qc +from qiskit.synthesis.permutation.permutation_reverse_lnn import ( + _append_cx_stage1, + _append_cx_stage2, +) def _odd_pattern1(n): diff --git a/qiskit/synthesis/permutation/__init__.py b/qiskit/synthesis/permutation/__init__.py index 7cc8d0174d71..5a8b9a7a13f8 100644 --- a/qiskit/synthesis/permutation/__init__.py +++ b/qiskit/synthesis/permutation/__init__.py @@ -15,3 +15,4 @@ from .permutation_lnn import synth_permutation_depth_lnn_kms from .permutation_full import synth_permutation_basic, synth_permutation_acg +from .permutation_reverse_lnn import synth_permutation_reverse_lnn_kms diff --git a/qiskit/synthesis/permutation/permutation_reverse_lnn.py b/qiskit/synthesis/permutation/permutation_reverse_lnn.py new file mode 100644 index 000000000000..26287a06177e --- /dev/null +++ b/qiskit/synthesis/permutation/permutation_reverse_lnn.py @@ -0,0 +1,90 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024 +# +# 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. +""" +Synthesis of a reverse permutation for LNN connectivity. +""" + +from qiskit.circuit import QuantumCircuit + + +def _append_cx_stage1(qc, n): + """A single layer of CX gates.""" + for i in range(n // 2): + qc.cx(2 * i, 2 * i + 1) + for i in range((n + 1) // 2 - 1): + qc.cx(2 * i + 2, 2 * i + 1) + return qc + + +def _append_cx_stage2(qc, n): + """A single layer of CX gates.""" + for i in range(n // 2): + qc.cx(2 * i + 1, 2 * i) + for i in range((n + 1) // 2 - 1): + qc.cx(2 * i + 1, 2 * i + 2) + return qc + + +def _append_reverse_permutation_lnn_kms(qc: QuantumCircuit, num_qubits: int) -> None: + """ + Append reverse permutation to a QuantumCircuit for linear nearest-neighbor architectures + using Kutin, Moulton, Smithline method. + + Synthesis algorithm for reverse permutation from [1], section 5. + This algorithm synthesizes the reverse permutation on :math:`n` qubits over + a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. + + Args: + qc: The original quantum circuit. + num_qubits: The number of qubits. + + Returns: + The quantum circuit with appended reverse permutation. + + References: + 1. Kutin, S., Moulton, D. P., Smithline, L., + *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), + `arXiv:quant-ph/0701194 `_ + """ + + for _ in range((num_qubits + 1) // 2): + _append_cx_stage1(qc, num_qubits) + _append_cx_stage2(qc, num_qubits) + if (num_qubits % 2) == 0: + _append_cx_stage1(qc, num_qubits) + + +def synth_permutation_reverse_lnn_kms(num_qubits: int) -> QuantumCircuit: + """ + Synthesize reverse permutation for linear nearest-neighbor architectures using + Kutin, Moulton, Smithline method. + + Synthesis algorithm for reverse permutation from [1], section 5. + This algorithm synthesizes the reverse permutation on :math:`n` qubits over + a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. + + Args: + num_qubits: The number of qubits. + + Returns: + The synthesized quantum circuit. + + References: + 1. Kutin, S., Moulton, D. P., Smithline, L., + *Computation at a distance*, Chicago J. Theor. Comput. Sci., vol. 2007, (2007), + `arXiv:quant-ph/0701194 `_ + """ + + qc = QuantumCircuit(num_qubits) + _append_reverse_permutation_lnn_kms(qc, num_qubits) + + return qc diff --git a/qiskit/synthesis/qft/qft_decompose_lnn.py b/qiskit/synthesis/qft/qft_decompose_lnn.py index 4dd8d9d56d13..a54be481f51b 100644 --- a/qiskit/synthesis/qft/qft_decompose_lnn.py +++ b/qiskit/synthesis/qft/qft_decompose_lnn.py @@ -15,7 +15,7 @@ import numpy as np from qiskit.circuit import QuantumCircuit -from qiskit.synthesis.linear_phase.cz_depth_lnn import _append_cx_stage1, _append_cx_stage2 +from qiskit.synthesis.permutation.permutation_reverse_lnn import _append_reverse_permutation_lnn_kms def synth_qft_line( @@ -65,10 +65,6 @@ def synth_qft_line( if not do_swaps: # Add a reversal network for LNN connectivity in depth 2*n+2, # based on Kutin at al., https://arxiv.org/abs/quant-ph/0701194, Section 5. - for _ in range((num_qubits + 1) // 2): - qc = _append_cx_stage1(qc, num_qubits) - qc = _append_cx_stage2(qc, num_qubits) - if (num_qubits % 2) == 0: - qc = _append_cx_stage1(qc, num_qubits) + _append_reverse_permutation_lnn_kms(qc, num_qubits) return qc diff --git a/releasenotes/notes/reverse-permutation-lnn-409a07c7f6d0eed9.yaml b/releasenotes/notes/reverse-permutation-lnn-409a07c7f6d0eed9.yaml new file mode 100644 index 000000000000..357345adfa26 --- /dev/null +++ b/releasenotes/notes/reverse-permutation-lnn-409a07c7f6d0eed9.yaml @@ -0,0 +1,8 @@ +--- +features_synthesis: + - | + Add a new synthesis method :func:`.synth_permutation_reverse_lnn_kms` + of reverse permutations for linear nearest-neighbor architectures using + Kutin, Moulton, Smithline method. + This algorithm synthesizes the reverse permutation on :math:`n` qubits over + a linear nearest-neighbor architecture using CX gates with depth :math:`2 * n + 2`. diff --git a/test/python/synthesis/test_permutation_synthesis.py b/test/python/synthesis/test_permutation_synthesis.py index 7fc6f5e24ab8..5c4317ed58a3 100644 --- a/test/python/synthesis/test_permutation_synthesis.py +++ b/test/python/synthesis/test_permutation_synthesis.py @@ -19,8 +19,12 @@ from qiskit.quantum_info.operators import Operator from qiskit.circuit.library import LinearFunction, PermutationGate -from qiskit.synthesis import synth_permutation_acg -from qiskit.synthesis.permutation import synth_permutation_depth_lnn_kms, synth_permutation_basic +from qiskit.synthesis.permutation import ( + synth_permutation_acg, + synth_permutation_depth_lnn_kms, + synth_permutation_basic, + synth_permutation_reverse_lnn_kms, +) from qiskit.synthesis.permutation.permutation_utils import _get_ordered_swap from test import QiskitTestCase # pylint: disable=wrong-import-order @@ -108,6 +112,26 @@ def test_synth_permutation_depth_lnn_kms(self, width): synthesized_pattern = LinearFunction(qc).permutation_pattern() self.assertTrue(np.array_equal(synthesized_pattern, pattern)) + @data(1, 2, 3, 4, 5, 10, 15, 20) + def test_synth_permutation_reverse_lnn_kms(self, num_qubits): + """Test synth_permutation_reverse_lnn_kms function produces the correct + circuit.""" + pattern = list(reversed(range(num_qubits))) + qc = synth_permutation_reverse_lnn_kms(num_qubits) + self.assertListEqual((LinearFunction(qc).permutation_pattern()).tolist(), pattern) + + # Check that the CX depth of the circuit is at 2*n+2 + self.assertTrue(qc.depth() <= 2 * num_qubits + 2) + + # Check that the synthesized circuit consists of CX gates only, + # and that these CXs adhere to the LNN connectivity. + for instruction in qc.data: + self.assertEqual(instruction.operation.name, "cx") + q0 = qc.find_bit(instruction.qubits[0]).index + q1 = qc.find_bit(instruction.qubits[1]).index + dist = abs(q0 - q1) + self.assertEqual(dist, 1) + @data(4, 5, 6, 7) def test_permutation_matrix(self, width): """Test that the unitary matrix constructed from permutation pattern