Source code for pennylane.templates.subroutines.grover
# 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.
"""
Contains the Grover Operation template.
"""
import itertools
import functools
import numpy as np
from pennylane.operation import AnyWires, Operation
from pennylane.ops import Hadamard, PauliZ, MultiControlledX
[docs]class GroverOperator(Operation):
r"""Performs the Grover Diffusion Operator.
.. math::
G = 2 |s \rangle \langle s | - I
= H^{\bigotimes n} \left( 2 |0\rangle \langle 0| - I \right) H^{\bigotimes n}
where :math:`n` is the number of wires, and :math:`|s\rangle` is the uniform superposition:
.. math::
|s\rangle = H^{\bigotimes n} |0\rangle = \frac{1}{\sqrt{2^n}} \sum_{i=0}^{2^n-1} | i \rangle.
For this template, the operator is implemented with a layer of Hadamards, a layer of :math:`X`,
followed by a multi-controlled :math:`Z` gate, then another layer of :math:`X` and Hadamards.
This is expressed in a compact form by the circuit below:
.. figure:: ../../_static/templates/subroutines/grover.svg
:align: center
:width: 60%
:target: javascript:void(0);
The open circles on the controlled gate indicate control on 0 instead of 1.
The ``Z`` gates on the last wire result from leveraging the circuit identity :math:`HXH = Z`,
where the last ``H`` gate converts the multi-controlled :math:`Z` gate into a
multi-controlled :math:`X` gate.
Args:
wires (Union[Wires, Sequence[int], or int]): the wires to apply to
work_wires (Union[Wires, Sequence[int], or int]): optional auxiliary wires to assist
in the decomposition of :class:`~.MultiControlledX`.
**Example**
The Grover Diffusion Operator amplifies the magnitude of the basis state with
a negative phase. For example, if the solution to the search problem is the :math:`|111\rangle`
state, we require an oracle that flips its phase; this could be implemented using a `CCZ` gate:
.. code-block:: python
n_wires = 3
wires = list(range(n_wires))
def oracle():
qml.Hadamard(wires[-1])
qml.Toffoli(wires=wires)
qml.Hadamard(wires[-1])
We can then implement the entire Grover Search Algorithm for ``num_iterations`` iterations by alternating calls to the oracle and the diffusion operator:
.. code-block:: python
dev = qml.device('default.qubit', wires=wires)
@qml.qnode(dev)
def GroverSearch(num_iterations=1):
for wire in wires:
qml.Hadamard(wire)
for _ in range(num_iterations):
oracle()
qml.templates.GroverOperator(wires=wires)
return qml.probs(wires)
>>> GroverSearch(num_iterations=1)
tensor([0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125, 0.03125,
0.78125], requires_grad=True)
>>> GroverSearch(num_iterations=2)
tensor([0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125, 0.0078125,
0.0078125, 0.9453125], requires_grad=True)
We can see that the marked :math:`|111\rangle` state has the greatest probability amplitude.
Optimally, the oracle-operator pairing should be repeated :math:`\lceil \frac{\pi}{4}\sqrt{2^{n}} \rceil` times.
"""
num_wires = AnyWires
grad_method = None
def __init__(self, wires=None, work_wires=None, do_queue=True, id=None):
if (not hasattr(wires, "__len__")) or (len(wires) < 2):
raise ValueError("GroverOperator must have at least two wires provided.")
self._hyperparameters = {"n_wires": len(wires), "work_wires": work_wires}
super().__init__(wires=wires, do_queue=do_queue, id=id)
@property
def num_params(self):
return 0
[docs] @staticmethod
def compute_decomposition(
wires, work_wires, **kwargs
): # pylint: disable=arguments-differ,unused-argument
r"""Representation of the operator as a product of other operators.
.. math:: O = O_1 O_2 \dots O_n.
.. seealso:: :meth:`~.GroverOperator.decomposition`.
Args:
wires (Any or Iterable[Any]): wires that the operator acts on
work_wires (Any or Iterable[Any]): optional auxiliary wires to assist
in the decomposition of :class:`~.MultiControlledX`.
Returns:
list[.Operator]: decomposition of the operator
"""
ctrl_str = "0" * (len(wires) - 1)
op_list = []
for wire in wires[:-1]:
op_list.append(Hadamard(wire))
op_list.append(PauliZ(wires[-1]))
op_list.append(
MultiControlledX(
control_values=ctrl_str,
wires=wires,
work_wires=work_wires,
)
)
op_list.append(PauliZ(wires[-1]))
for wire in wires[:-1]:
op_list.append(Hadamard(wire))
return op_list
[docs] @staticmethod
@functools.lru_cache()
def compute_matrix(n_wires, work_wires): # pylint: disable=arguments-differ,unused-argument
# s1 = H|0>, Hadamard on a single qubit in the ground state
s1 = np.array([1, 1]) / np.sqrt(2)
# uniform superposition state |s>
s = functools.reduce(np.kron, list(itertools.repeat(s1, n_wires)))
# Grover diffusion operator
G = 2 * np.outer(s, s) - np.identity(2**n_wires)
return G
_modules/pennylane/templates/subroutines/grover
Download Python script
Download Notebook
View on GitHub