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

Adding QFT gate to natively reason about Quantum Fourier Transforms #11463

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
71efe1c
initial commit
alexanderivrii Dec 28, 2023
3705ba4
release notes
alexanderivrii Dec 28, 2023
68c2ccf
fixing synthesis plugin options
alexanderivrii Dec 28, 2023
c835e20
Merge branch 'main' into qft_gate
alexanderivrii Feb 4, 2024
425a6d9
finalize merge conflicts
alexanderivrii Feb 4, 2024
ef45120
fixing default option values for qft plugins'
alexanderivrii Feb 6, 2024
09086e6
additional tests for qft plugins
alexanderivrii Feb 6, 2024
f1d4215
Merge branch 'main' into qft_gate
alexanderivrii Feb 6, 2024
5d8be05
Merge branch 'main' into qft_gate
alexanderivrii May 2, 2024
eac4ed6
renaming QftGate to QFTGate
alexanderivrii May 2, 2024
cac843b
Merge branch 'main' into qft_gate
alexanderivrii Jul 4, 2024
5acfc3f
Also renaming Qft to QFT in synthesis method names
alexanderivrii Jul 4, 2024
0b605bc
Merge branch 'qft_gate' of github.com:alexanderivrii/qiskit-terra int…
alexanderivrii Jul 4, 2024
6106c4a
appplying Jake's suggestion from code review
alexanderivrii Jul 4, 2024
a76fbe7
inlining _basic_definition into _define
alexanderivrii Jul 5, 2024
0d06898
docstring improvements
alexanderivrii Jul 5, 2024
71860bf
more docstring improvements
alexanderivrii Jul 5, 2024
d496f08
renaming do_swaps to reverse_qubits in the new code
alexanderivrii Jul 5, 2024
2822d5c
typos
alexanderivrii Jul 5, 2024
6feb4fc
adding synth_qft_full to __init__
alexanderivrii Jul 7, 2024
a0ea5ba
round of suggestions from code review
alexanderivrii Jul 9, 2024
b230c58
another round of code review suggestions
alexanderivrii Jul 9, 2024
8806c27
fixes
alexanderivrii Jul 9, 2024
4ae4c9e
also adding QFTGate plugins to the docs
alexanderivrii Jul 9, 2024
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
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ sk = "qiskit.transpiler.passes.synthesis.solovay_kitaev_synthesis:SolovayKitaevS
"permutation.kms" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:KMSSynthesisPermutation"
"permutation.basic" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:BasicSynthesisPermutation"
"permutation.acg" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:ACGSynthesisPermutation"
"qft.full" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisFull"
"qft.line" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisLine"
"qft.default" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:QFTSynthesisFull"
"permutation.token_swapper" = "qiskit.transpiler.passes.synthesis.high_level_synthesis:TokenSwapperSynthesisPermutation"

[project.entry-points."qiskit.transpiler.init"]
Expand Down
2 changes: 2 additions & 0 deletions qiskit/circuit/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
UCRXGate
UCRYGate
UCRZGate
QFTGate
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved

Boolean Logic Circuits
======================
Expand Down Expand Up @@ -514,6 +515,7 @@
UCRXGate,
UCRYGate,
UCRZGate,
QFTGate,
)
from .pauli_evolution import PauliEvolutionGate
from .hamiltonian_gate import HamiltonianGate
Expand Down
1 change: 1 addition & 0 deletions qiskit/circuit/library/generalized_gates/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@
from .ucrz import UCRZGate
from .unitary import UnitaryGate
from .mcg_up_to_diagonal import MCGupDiag
from .qft import QFTGate
53 changes: 53 additions & 0 deletions qiskit/circuit/library/generalized_gates/qft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# 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.

"""QFTGate: gate class for natively reasoning about Quantum Fourier Transforms."""

from __future__ import annotations
import numpy as np
from qiskit.circuit.quantumcircuit import Gate


class QFTGate(Gate):
Copy link
Member

Choose a reason for hiding this comment

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

This class appears in the Section "Generalized Gates", unlike QFT which appears in the section "Basis Change Circuits".
Is it deliberate?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes! It's a gate but not a "basic gate", so it's a "generalized gate". Are you proposing to restructure the library of generalized gates, grouping the generalized gates by "their purpose"?

Copy link
Member

Choose a reason for hiding this comment

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

I'm actually not sure why QFT appears separately than the other generalized gates

Copy link
Contributor

Choose a reason for hiding this comment

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

Generalized gates were thought to be "extensions" of the standard gates, such as multi-Pauli-gates or uniformly controlled gates. But by now there's a lot of other objects there that we don't yet have a better category for, like Isometry or Permutation. It also doesn't really matter as we allow (and encourage) import from qiskit.circuit.library, but I'd keep QFTGate it in basis_change for the time being.

We probably won't put all gates into generalized_gates since that wouldn't be any sorting really, hopefully we'll be able to clean it up a bit in the restructure.

Copy link
Member

Choose a reason for hiding this comment

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

I think that maybe all the (multi/uniformly) controlled gates deserve a special category (but that's not relevant to this PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I have moved the QFTGate to basis_change, the same file that contains the QFT circuit.

r"""Quantum Fourier Transform Circuit.
The Quantum Fourier Transform (QFT) on :math:`n` qubits is the operation
.. math::
|j\rangle \mapsto \frac{1}{2^{n/2}} \sum_{k=0}^{2^n - 1} e^{2\pi ijk / 2^n} |k\rangle
"""

def __init__(
self,
num_qubits: int,
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
):
"""Construct a new QFT gate.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think in most places we don't put this initial sentence anymore if it doesn't provide any info, since it looks nicer in the docs if we just start with the Args 🙂 but that's certainly true in the circuit lib 🙂

Suggested change
"""Construct a new QFT gate.
"""

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Args:
num_qubits: The number of qubits on which the QFT acts.
"""
super().__init__(name="qft", num_qubits=num_qubits, params=[])

def __array__(self, dtype=complex):
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
"""Return a numpy array for the QFTGate."""
n = self.num_qubits
nums = np.arange(2**n)
outer = np.outer(nums, nums)
return np.exp(2j * np.pi * outer * (0.5**n), dtype=dtype) * (0.5 ** (n / 2))

def _define(self):
"""Provide a specific decomposition of the QFTGate into a quantum circuit."""
from qiskit.synthesis.qft import synth_qft_full

self.definition = synth_qft_full(num_qubits=self.num_qubits)
2 changes: 2 additions & 0 deletions qiskit/qpy/binary_io/circuits.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ def _read_instruction(
"DiagonalGate",
}:
gate = gate_class(params)
elif gate_name == "QFTGate":
gate = gate_class(len(qargs), *params)
else:
if gate_name == "Barrier":
params = [len(qargs)]
Expand Down
1 change: 1 addition & 0 deletions qiskit/synthesis/qft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
"""Module containing stabilizer QFT circuit synthesis."""

from .qft_decompose_lnn import synth_qft_line
from .qft_decompose_full import synth_qft_full
alexanderivrii marked this conversation as resolved.
Show resolved Hide resolved
79 changes: 79 additions & 0 deletions qiskit/synthesis/qft/qft_decompose_full.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# 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.
"""
Circuit synthesis for a QFT circuit.
"""

from typing import Optional
import numpy as np
from qiskit.circuit.quantumcircuit import QuantumCircuit


def synth_qft_full(
num_qubits: int,
do_swaps: bool = True,
approximation_degree: int = 0,
insert_barriers: bool = False,
inverse: bool = False,
name: Optional[str] = None,
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you use the new style of

Suggested change
name: Optional[str] = None,
name: str | None = None,

instead? 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

) -> QuantumCircuit:
"""Construct a circuit for the Quantum Fourier Transform using all-to-all connectivity.
.. note::
With the default value of ``do_swaps = True``, this synthesis algorithm creates a
circuit that faithfully implements the QFT operation. This circuit contains a sequence
of swap gates at the end, corresponding to reversing the order of its output qubits.
In some applications this reversal permutation can be avoided. Setting ``do_swaps = False``
creates a circuit without this reversal permutation, at the expense that this circuit
implements the "QFT-with-reversal" instead of QFT. Alternatively, the
:class:`~.ElidePermutations` transpiler pass is able to remove these swap gates.
Args:
num_qubits: The number of qubits on which the Quantum Fourier Transform acts.
do_swaps: Whether to synthesize the "QFT" or the "QFT-with-reversal" operation.
approximation_degree: The degree of approximation (0 for no approximation).
It is possible to implement the QFT approximately by ignoring
controlled-phase rotations with the angle beneath a threshold. This is discussed
in more detail in https://arxiv.org/abs/quant-ph/9601018 or
https://arxiv.org/abs/quant-ph/0403071.
insert_barriers: If ``True``, barriers are inserted for improved visualization.
inverse: If ``True``, the inverse Quantum Fourier Transform is constructed.
name: The name of the circuit.
Returns:
A circuit implementing the QFT operation.
"""

circuit = QuantumCircuit(num_qubits, name=name)

for j in reversed(range(num_qubits)):
circuit.h(j)
num_entanglements = max(0, j - max(0, approximation_degree - (num_qubits - j - 1)))
for k in reversed(range(j - num_entanglements, j)):
# Use negative exponents so that the angle safely underflows to zero, rather than
# using a temporary variable that overflows to infinity in the worst case.
lam = np.pi * (2.0 ** (k - j))
circuit.cp(lam, j, k)

if insert_barriers:
circuit.barrier()

if do_swaps:
for i in range(num_qubits // 2):
circuit.swap(i, num_qubits - i - 1)

if inverse:
circuit = circuit.inverse()

return circuit
26 changes: 17 additions & 9 deletions qiskit/synthesis/qft/qft_decompose_lnn.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,29 @@
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].
"""Construct a circuit for the Quantum Fourier Transform using linear
neighbor connectivity.

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.
The construction is based on Fig 2.b in Fowler et al. [1].

.. note::

With the default value of ``do_swaps = True``, this synthesis algorithm creates a
circuit that faithfully implements the QFT operation. When ``do_swaps = False``,
this synthesis algorithm creates a circuit that corresponds to "QFT-with-reversal":
applying the QFT and reversing the order of its output qubits.

Args:
num_qubits: The number of qubits on which the QFT acts.
num_qubits: The number of qubits on which the Quantum Fourier Transform acts.
approximation_degree: The degree of approximation (0 for no approximation).
do_swaps: Whether to include the final swaps in the QFT.
It is possible to implement the QFT approximately by ignoring
controlled-phase rotations with the angle beneath a threshold. This is discussed
in more detail in https://arxiv.org/abs/quant-ph/9601018 or
https://arxiv.org/abs/quant-ph/0403071.
do_swaps: Whether to synthesize the "QFT" or the "QFT-with-reversal" operation.

Returns:
A circuit implementation of the QFT circuit.
A circuit implementing the QFT operation.

References:
1. A. G. Fowler, S. J. Devitt, and L. C. L. Hollenberg,
Expand Down
90 changes: 89 additions & 1 deletion qiskit/transpiler/passes/synthesis/high_level_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@
ControlModifier,
PowerModifier,
)
from qiskit.circuit.library import QFTGate
from qiskit.synthesis.clifford import (
synth_clifford_full,
synth_clifford_layers,
Expand All @@ -176,6 +177,10 @@
synth_permutation_acg,
synth_permutation_depth_lnn_kms,
)
from qiskit.synthesis.qft import (
synth_qft_full,
synth_qft_line,
)

from .plugin import HighLevelSynthesisPluginManager, HighLevelSynthesisPlugin

Expand Down Expand Up @@ -887,6 +892,90 @@ def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **
return decomposition


class QFTSynthesisFull(HighLevelSynthesisPlugin):
Copy link
Member

@ShellyGarion ShellyGarion Jul 9, 2024

Choose a reason for hiding this comment

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

do we see this class in the docs of the synthesis plugin? (also for QFTSynthesisLine)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this! I have extended the section on available plugins in 4ae4c9e

"""Synthesis plugin for QFT gates using all-to-all connectivity.
This plugin name is :``qft.full`` which can be used as the key on
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
The plugin supports the following additional options:
* reverse_qubits (bool): Whether to synthesize the "QFT" operation (default) or the
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure about the name of this argument, to me it sounds like reverse_qubits=True would give me a QFT with qubits in reversed order -- but it's the opposite. Could we flip the meaning, such that reverse_qubits=True returns the qubits in reversed order, meaning that do_swaps=not reverse_qubits?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, indeed, I had this wrong, fixed.

"QFT-with-reversal" operation (non-default). Some implementation of the ``QFTGate``
include a layer of swap gates at the end of the synthesized circuit, which can in
principle be dropped if the ``QFTGate`` itself is the last gate in the circuit.
* approximation_degree (int): The degree of approximation (0 for no approximation).
It is possible to implement the QFT approximately by ignoring
controlled-phase rotations with the angle beneath a threshold. This is discussed
in more detail in https://arxiv.org/abs/quant-ph/9601018 or
https://arxiv.org/abs/quant-ph/0403071.
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add them as references?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, I am slightly inclined to keep it a bit less formal: IMHO, a good reference section would first cite a paper defining a QFT operation, then cite papers that introduce different synthesis methods for QFT, and only then papers that discuss approximation.

Copy link
Contributor

Choose a reason for hiding this comment

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

Personally, I would also prefer the references written out, because of (1) consistency with other code and (2) being able to read authors & title w/o clicking the link (but maybe that's just me being lazy 😛 )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Due to popular demand, the references are now written out.

Copy link
Member

Choose a reason for hiding this comment

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

thanks, I think that you should still remove line 912

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, forgot to delete this line, done now.

* insert_barriers (bool): If True, barriers are inserted as visualization improvement.
* inverse (bool): If True, the inverse Fourier transform is constructed.
* name (str): The name of the circuit.
"""

def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given QFTGate."""
if not isinstance(high_level_object, QFTGate):
raise TranspilerError(
"The synthesis plugin 'qft.full` only applies to objects of type QFTGate."
)

reverse_qubits = options.get("reverse_qubits", True)
approximation_degree = options.get("approximation_degree", 0)
insert_barriers = options.get("insert_barriers", False)
inverse = options.get("inverse", False)
name = options.get("name", None)

decomposition = synth_qft_full(
num_qubits=high_level_object.num_qubits,
do_swaps=reverse_qubits,
approximation_degree=approximation_degree,
insert_barriers=insert_barriers,
inverse=inverse,
name=name,
)
return decomposition


class QFTSynthesisLine(HighLevelSynthesisPlugin):
"""Synthesis plugin for QFT gates using linear connectivity.
This plugin name is :``qft.line`` which can be used as the key on
an :class:`~.HLSConfig` object to use this method with :class:`~.HighLevelSynthesis`.
The plugin supports the following additional options:
* reverse_qubits (bool): Whether to synthesize the "QFT" operation (default) or the
"QFT-with-reversal" operation (non-default). Some implementation of the ``QFTGate``
include a layer of swap gates at the end of the synthesized circuit, which can in
principle be dropped if the ``QFTGate`` itself is the last gate in the circuit.
* approximation_degree (int): the degree of approximation (0 for no approximation).
It is possible to implement the QFT approximately by ignoring
controlled-phase rotations with the angle beneath a threshold. This is discussed
in more detail in https://arxiv.org/abs/quant-ph/9601018 or
https://arxiv.org/abs/quant-ph/0403071.
ShellyGarion marked this conversation as resolved.
Show resolved Hide resolved
"""

def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
"""Run synthesis for the given QFTGate."""
if not isinstance(high_level_object, QFTGate):
raise TranspilerError(
"The synthesis plugin 'qft.line` only applies to objects of type QFTGate."
)

reverse_qubits = options.get("reverse_qubits", True)
approximation_degree = options.get("approximation_degree", 0)

decomposition = synth_qft_line(
num_qubits=high_level_object.num_qubits,
do_swaps=reverse_qubits,
approximation_degree=approximation_degree,
)
return decomposition


class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin):
"""The permutation synthesis plugin based on the token swapper algorithm.
Expand Down Expand Up @@ -917,7 +1006,6 @@ class TokenSwapperSynthesisPermutation(HighLevelSynthesisPlugin):
For more details on the token swapper algorithm, see to the paper:
`arXiv:1902.09102 <https://arxiv.org/abs/1902.09102>`__.
"""

def run(self, high_level_object, coupling_map=None, target=None, qubits=None, **options):
Expand Down
22 changes: 22 additions & 0 deletions releasenotes/notes/add-qft-gate-fd4e08f6721a9da4.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
features:
- |
Added a new class :class:`~qiskit.circuit.library.QFTGate` for
natively representing Quantum Fourier Transforms (QFTs). The older way
of representing QFTs via quantum circuits, see
:class:`~qiskit.circuit.library.QFT`, remains for backward compatibility.
The new way of representing a QFT via a gate avoids synthesizing its
definition circuit when the gate is declared, delaying the actual synthesis to
the transpiler. It also allows to easily choose between several different
algorithms for synthesizing QFTs, which are available as high-level-synthesis
plugins.
- |
Added a synthesis method :func:`.synth_qft_full` for constructing a QFT circuit
assuming a fully-connected architecture.
- |
Added two high-level-synthesis plugins for synthesizing a
:class:`~qiskit.circuit.library.QFTGate`.
The class :class:`.QFTSynthesisFull` is based on :func:`.synth_qft_full` and synthesizes
a QFT gate assuming all-to-all connectivity.
The class :class:`.QFTSynthesisLine` is based on :func:`.synth_qft_line` and synthesizes
a QFT gate assuming linear nearest neighbor connectivity.
Loading
Loading