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

QFT circuit synthesis for linear nearest neighbor connectivity #11236

Merged
merged 11 commits into from
Dec 8, 2023
6 changes: 6 additions & 0 deletions qiskit/synthesis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@

.. autofunction:: generate_basic_approximations

Basis Change Synthesis
======================

.. autofunction:: synth_qft_line

"""

from .evolution import (
Expand Down Expand Up @@ -120,3 +125,4 @@
)
from .stabilizer import synth_stabilizer_layers, synth_stabilizer_depth_lnn
from .discrete_basis import SolovayKitaevDecomposition, generate_basic_approximations
from .qft import synth_qft_line
15 changes: 15 additions & 0 deletions qiskit/synthesis/qft/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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.

"""Module containing stabilizer QFT circuit synthesis."""

from .qft_decompose_lnn import synth_qft_line
74 changes: 74 additions & 0 deletions qiskit/synthesis/qft/qft_decompose_lnn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# 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.
"""
Circuit synthesis for a QFT circuit.
"""

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.synthesis.linear_phase.cz_depth_lnn import _append_cx_stage1, _append_cx_stage2


def synth_qft_line(
num_qubits: int, do_swaps: bool = True, approximation_degree: int = 0
) -> QuantumCircuit:
"""Synthesis of a QFT circuit for a linear nearest neighbor connectivity.
Based on Fig 2.b in Fowler et al. [1].

Note that this method *reverts* the order of qubits in the circuit,
compared to the original :class:`.QFT` code.
Hence, the default value of the ``do_swaps`` parameter is ``True``
since it produces a circuit with fewer CX gates.

Args:
num_qubits: The number of qubits on which the QFT acts.
approximation_degree: The degree of approximation (0 for no approximation).
do_swaps: Whether to include the final swaps in the QFT.

Return:
A circuit implementation of the QFT circuit.

Reference:
1. A. G. Fowler, S. J. Devitt, and L. C. L. Hollenberg,
*Implementation of Shor's algorithm on a linear nearest neighbour qubit array*,
Quantum Info. Comput. 4, 4 (July 2004), 237–251.
`arXiv:quant-ph/0402196 [quant-ph] <https://arxiv.org/abs/quant-ph/0402196>`_
"""

qc = QuantumCircuit(num_qubits)

for i in range(num_qubits):
qc.h(num_qubits - 1)

for j in range(i, num_qubits - 1):
if j - i + 2 < num_qubits - approximation_degree + 1:
qc.p(np.pi / 2 ** (j - i + 2), num_qubits - j + i - 1)
qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)
qc.p(-np.pi / 2 ** (j - i + 2), num_qubits - j + i - 2)
qc.cx(num_qubits - j + i - 2, num_qubits - j + i - 1)
qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)
qc.p(np.pi / 2 ** (j - i + 2), num_qubits - j + i - 1)
else:
qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)
qc.cx(num_qubits - j + i - 2, num_qubits - j + i - 1)
qc.cx(num_qubits - j + i - 1, num_qubits - j + i - 2)

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)
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved
if (num_qubits % 2) == 0:
qc = _append_cx_stage1(qc, num_qubits)

return qc
6 changes: 6 additions & 0 deletions releasenotes/notes/qft_lnn_synthesis-c917dc00c3a8cabc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
features:
- |
Add a new synthesis method :func:`.synth_qft_line` of a QFT circuit
for linear nearest neighbor connectivity, which significantly reduces
the number of SWAPs for large numbers of qubits compared to SABRE.
62 changes: 62 additions & 0 deletions test/python/synthesis/test_qft_synthesis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# 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.

"""Tests for QFT synthesis methods."""


import unittest
from test import combine
from ddt import ddt

from qiskit.test import QiskitTestCase
from qiskit.circuit.library import QFT
from qiskit.synthesis.qft import synth_qft_line
from qiskit.quantum_info import Operator

from qiskit.synthesis.linear.linear_circuits_utils import check_lnn_connectivity


@ddt
class TestQFTLNN(QiskitTestCase):
"""Tests for QFT synthesis functions."""

@combine(num_qubits=[2, 3, 4, 5, 6, 7, 8], do_swaps=[True, False])
def test_qft_lnn(self, num_qubits, do_swaps):
"""Assert that the original and synthesized QFT circuits are the same."""
qft_circ = QFT(num_qubits, do_swaps=do_swaps)
qft_lnn = synth_qft_line(num_qubits, do_swaps=do_swaps)

with self.subTest(msg="original and synthesized QFT circuits are not the same"):
self.assertEqual(Operator(qft_circ), Operator(qft_lnn))

# Check that the output circuit has LNN connectivity
with self.subTest(msg="synthesized QFT circuit do not have LNN connectivity"):
self.assertTrue(check_lnn_connectivity(qft_lnn))

@combine(num_qubits=[5, 6, 7, 8], do_swaps=[True, False], approximation_degree=[2, 3])
def test_qft_lnn_approximated(self, num_qubits, do_swaps, approximation_degree):
"""Assert that the original and synthesized QFT circuits are the same with approximation."""
qft_circ = QFT(num_qubits, do_swaps=do_swaps, approximation_degree=approximation_degree)
qft_lnn = synth_qft_line(
num_qubits, do_swaps=do_swaps, approximation_degree=approximation_degree
)

with self.subTest(msg="original and synthesized QFT circuits are not the same"):
self.assertEqual(Operator(qft_circ), Operator(qft_lnn))

# Check that the output circuit has LNN connectivity
with self.subTest(msg="synthesized QFT circuit do not have LNN connectivity"):
self.assertTrue(check_lnn_connectivity(qft_lnn))


if __name__ == "__main__":
unittest.main()