# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2020.
#
# 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.
"""Quantum Fourier Transform Circuit."""
from typing import Optional
import warnings
import numpy as np
from qiskit.circuit import QuantumCircuit, QuantumRegister, CircuitInstruction
from ..blueprintcircuit import BlueprintCircuit
[문서]class QFT(BlueprintCircuit):
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
The circuit that implements this transformation can be implemented using Hadamard gates
on each qubit, a series of controlled-U1 (or Z, depending on the phase) gates and a
layer of Swap gates. The layer of Swap gates can in principle be dropped if the QFT appears
at the end of the circuit, since then the re-ordering can be done classically. They
can be turned off using the ``do_swaps`` attribute.
For 4 qubits, the circuit that implements this transformation is:
.. plot::
from qiskit.circuit.library import QFT
from qiskit.tools.jupyter.library import _generate_circuit_library_visualization
circuit = QFT(4)
_generate_circuit_library_visualization(circuit)
The inverse QFT can be obtained by calling the ``inverse`` method on this class.
The respective circuit diagram is:
.. plot::
from qiskit.circuit.library import QFT
from qiskit.tools.jupyter.library import _generate_circuit_library_visualization
circuit = QFT(4).inverse()
_generate_circuit_library_visualization(circuit)
One method to reduce circuit depth is to implement the QFT approximately by ignoring
controlled-phase rotations where the angle is 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.
Here, this can be adjusted using the ``approximation_degree`` attribute: the smallest
``approximation_degree`` rotation angles are dropped from the QFT. For instance, a QFT
on 5 qubits with approximation degree 2 yields (the barriers are dropped in this example):
.. plot::
from qiskit.circuit.library import QFT
from qiskit.tools.jupyter.library import _generate_circuit_library_visualization
circuit = QFT(5, approximation_degree=2)
_generate_circuit_library_visualization(circuit)
"""
def __init__(
self,
num_qubits: Optional[int] = None,
approximation_degree: int = 0,
do_swaps: bool = True,
inverse: bool = False,
insert_barriers: bool = False,
name: Optional[str] = None,
) -> None:
"""Construct a new QFT circuit.
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.
inverse: If True, the inverse Fourier transform is constructed.
insert_barriers: If True, barriers are inserted as visualization improvement.
name: The name of the circuit.
"""
if name is None:
name = "IQFT" if inverse else "QFT"
super().__init__(name=name)
self._approximation_degree = approximation_degree
self._do_swaps = do_swaps
self._insert_barriers = insert_barriers
self._inverse = inverse
self.num_qubits = num_qubits
@property
def num_qubits(self) -> int:
"""The number of qubits in the QFT circuit.
Returns:
The number of qubits in the circuit.
"""
# This method needs to be overwritten to allow adding the setter for num_qubits while still
# complying to pylint.
return super().num_qubits
@num_qubits.setter
def num_qubits(self, num_qubits: int) -> None:
"""Set the number of qubits.
Note that this changes the registers of the circuit.
Args:
num_qubits: The new number of qubits.
"""
if num_qubits != self.num_qubits:
self._invalidate()
self.qregs = []
if num_qubits is not None and num_qubits > 0:
self.qregs = [QuantumRegister(num_qubits, name="q")]
@property
def approximation_degree(self) -> int:
"""The approximation degree of the QFT.
Returns:
The currently set approximation degree.
"""
return self._approximation_degree
@approximation_degree.setter
def approximation_degree(self, approximation_degree: int) -> None:
"""Set the approximation degree of the QFT.
Args:
approximation_degree: The new approximation degree.
Raises:
ValueError: If the approximation degree is smaller than 0.
"""
if approximation_degree < 0:
raise ValueError("Approximation degree cannot be smaller than 0.")
if approximation_degree != self._approximation_degree:
self._invalidate()
self._approximation_degree = approximation_degree
@property
def insert_barriers(self) -> bool:
"""Whether barriers are inserted for better visualization or not.
Returns:
True, if barriers are inserted, False if not.
"""
return self._insert_barriers
@insert_barriers.setter
def insert_barriers(self, insert_barriers: bool) -> None:
"""Specify whether barriers are inserted for better visualization or not.
Args:
insert_barriers: If True, barriers are inserted, if False not.
"""
if insert_barriers != self._insert_barriers:
self._invalidate()
self._insert_barriers = insert_barriers
@property
def do_swaps(self) -> bool:
"""Whether the final swaps of the QFT are applied or not.
Returns:
True, if the final swaps are applied, False if not.
"""
return self._do_swaps
@do_swaps.setter
def do_swaps(self, do_swaps: bool) -> None:
"""Specify whether to do the final swaps of the QFT circuit or not.
Args:
do_swaps: If True, the final swaps are applied, if False not.
"""
if do_swaps != self._do_swaps:
self._invalidate()
self._do_swaps = do_swaps
[문서] def is_inverse(self) -> bool:
"""Whether the inverse Fourier transform is implemented.
Returns:
True, if the inverse Fourier transform is implemented, False otherwise.
"""
return self._inverse
[문서] def inverse(self) -> "QFT":
"""Invert this circuit.
Returns:
The inverted circuit.
"""
if self.name in ("QFT", "IQFT"):
name = "QFT" if self._inverse else "IQFT"
else:
name = self.name + "_dg"
inverted = self.copy(name=name)
# data consists of the QFT gate only
iqft = self.data[0].operation.inverse()
iqft.name = name
inverted.data.clear()
inverted._append(CircuitInstruction(iqft, inverted.qubits, []))
inverted._inverse = not self._inverse
return inverted
def _warn_if_precision_loss(self):
"""Issue a warning if constructing the circuit will lose precision.
If we need an angle smaller than ``pi * 2**-1022``, we start to lose precision by going into
the subnormal numbers. We won't lose _all_ precision until an exponent of about 1075, but
beyond 1022 we're using fractional bits to represent leading zeros."""
max_num_entanglements = self.num_qubits - self.approximation_degree - 1
if max_num_entanglements > -np.finfo(float).minexp: # > 1022 for doubles.
warnings.warn(
"precision loss in QFT."
f" The rotation needed to represent {max_num_entanglements} entanglements"
" is smaller than the smallest normal floating-point number.",
category=RuntimeWarning,
stacklevel=3,
)
def _check_configuration(self, raise_on_failure: bool = True) -> bool:
"""Check if the current configuration is valid."""
valid = True
if self.num_qubits is None:
valid = False
if raise_on_failure:
raise AttributeError("The number of qubits has not been set.")
self._warn_if_precision_loss()
return valid
def _build(self) -> None:
"""If not already built, build the circuit."""
if self._is_built:
return
super()._build()
num_qubits = self.num_qubits
if num_qubits == 0:
return
circuit = QuantumCircuit(*self.qregs, name=self.name)
for j in reversed(range(num_qubits)):
circuit.h(j)
num_entanglements = max(0, j - max(0, self.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 self.insert_barriers:
circuit.barrier()
if self._do_swaps:
for i in range(num_qubits // 2):
circuit.swap(i, num_qubits - i - 1)
if self._inverse:
circuit = circuit.inverse()
wrapped = circuit.to_instruction() if self.insert_barriers else circuit.to_gate()
self.compose(wrapped, qubits=self.qubits, inplace=True)