Source code for pennylane.templates.subroutines.qft
# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This submodule contains the template for QFT.
"""
import functools
import numpy as np
from pennylane import math
from pennylane.capture import enabled
from pennylane.control_flow import for_loop
from pennylane.ops import SWAP, ControlledPhaseShift, Hadamard
from pennylane.templates import Subroutine
from pennylane.wires import Wires, WiresLike
def setup_qft(wires: WiresLike):
"""Run pre-validation on the wires provided to QFT."""
wires = Wires(wires)
return (wires,), {}
def qft_decomp_resources(wires: WiresLike):
"""Calculate the resources for QFT."""
num_wires = len(wires)
return {
Hadamard: num_wires,
SWAP: num_wires // 2,
ControlledPhaseShift: num_wires * (num_wires - 1) // 2,
}
[docs]
@functools.partial(
Subroutine,
static_argnames=[],
setup_inputs=setup_qft,
compute_resources=qft_decomp_resources,
)
def QFT(wires: WiresLike):
r"""QFT(wires)
Apply a quantum Fourier transform (QFT).
For the :math:`N`-qubit computational basis state :math:`|m\rangle`, the QFT performs the
transformation
.. math::
|m\rangle \rightarrow \frac{1}{\sqrt{2^{N}}}\sum_{n=0}^{2^{N} - 1}\omega_{N}^{mn} |n\rangle,
where :math:`\omega_{N} = e^{\frac{2 \pi i}{2^{N}}}` is the :math:`2^{N}`-th root of unity.
**Details:**
* Number of wires: Any (the operation can act on any number of wires)
* Number of parameters: 0
* Gradient recipe: None
Args:
wires (int or Iterable[Number, str]]): the wire(s) the operation acts on
**Example**
The quantum Fourier transform is applied by specifying the corresponding wires:
.. code-block:: python
wires = 3
dev = qml.device('default.qubit',wires=wires)
@qml.qnode(dev)
def circuit_qft(basis_state):
qml.BasisState(basis_state, wires=range(wires))
qml.QFT(wires=range(wires))
return qml.state()
>>> circuit_qft(np.array([1.0, 0.0, 0.0])) # doctest: +SKIP
array([ 0.3536+0.j, -0.3536+0.j, 0.3536+0.j, -0.3536+0.j, 0.3536+0.j,
-0.3536+0.j, 0.3536+0.j, -0.3536+0.j])
.. details::
:title: Semiclassical Quantum Fourier transform
If the QFT is the last subroutine applied within a circuit, it can be
replaced by a
`semiclassical Fourier transform <https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.76.3228>`_.
It makes use of mid-circuit measurements and dynamic circuit control based
on the measurement values, allowing to reduce the number of two-qubit gates.
As an example, consider the following circuit implementing addition between two
numbers with ``n_wires`` bits (modulo ``2**n_wires``):
.. code-block:: python
dev = qml.device("default.qubit")
@qml.qnode(dev, shots=1)
def qft_add(m, k, n_wires):
qml.BasisEmbedding(m, wires=range(n_wires))
qml.adjoint(qml.QFT)(wires=range(n_wires))
for j in range(n_wires):
qml.RZ(-k * np.pi / (2**j), wires=j)
qml.QFT(wires=range(n_wires))
return qml.sample()
>>> qft_add(7, 3, n_wires=4)
array([[1, 0, 1, 0]])
The last building block of this circuit is a QFT, so we may replace it by its
semiclassical counterpart:
.. code-block:: python
def scFT(n_wires):
'''semiclassical Fourier transform'''
for w in range(n_wires-1):
qml.Hadamard(w)
mcm = qml.measure(w)
for m in range(w + 1, n_wires):
qml.cond(mcm, qml.PhaseShift)(np.pi / 2 ** (m + 1), wires=m)
qml.Hadamard(n_wires-1)
@qml.qnode(dev)
def scFT_add(m, k, n_wires):
qml.BasisEmbedding(m, wires=range(n_wires))
qml.adjoint(qml.QFT)(wires=range(n_wires))
for j in range(n_wires):
qml.RZ(-k * np.pi / (2**j), wires=j)
scFT(n_wires)
# Revert wire order because of PL's QFT convention
return qml.sample(wires=list(range(n_wires-1, -1, -1)))
>>> qml.set_shots(scFT_add, 1)(7, 3, n_wires=4) # doctest: +SKIP
array([[1, 1, 1, 0]])
"""
n_wires = len(wires)
shifts = [2 * np.pi * 2**-i for i in range(2, n_wires + 1)]
if enabled():
shifts = math.array(shifts, like="jax")
wires = math.array(wires, like="jax")
shift_len = len(shifts)
@for_loop(n_wires)
def outer_loop(i):
Hadamard(wires[i])
if n_wires > 1:
@for_loop(shift_len - i)
def cphaseshift_loop(j):
ControlledPhaseShift(shifts[j], wires=[wires[i + j + 1], wires[i]])
cphaseshift_loop() # pylint: disable=no-value-for-parameter
outer_loop() # pylint: disable=no-value-for-parameter
@for_loop(n_wires // 2)
def swaps(i):
SWAP(wires=[wires[i], wires[n_wires - i - 1]])
swaps() # pylint: disable=no-value-for-parameter
_modules/pennylane/templates/subroutines/qft
Download Python script
Download Notebook
View on GitHub