Source code for pennylane.templates.layer

# Copyright 2018-2020 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


# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# See the License for the specific language governing permissions and
# limitations under the License.
Contains the ``layer`` template constructor.
# pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
from pennylane.templates.decorator import template as temp


[docs]@temp def layer(template, depth, *args, **kwargs): r"""Repeatedly applies a unitary a given number of times. Args: template (callable): The sequence of quantum gates that is being repeated. This could be a single gate, a function of gates, or a "registered" PennyLane template. depth (int): The number of times the unitary is repeatedly applied. *args: Dynamic parameters that are passed into the unitary each time it is repeated. Each dynamic argument must be a list of first dimension equal to ``depth``. The :math:`j`-th element of the list is the value of the argument the :math:`j`-th time the unitary is applied. **kwargs: Static parameters that are passed into the unitary each time it is repeated. See usage details for more information. .. UsageDetails:: **Layering Gates** The layering function can be used to repeatedly apply a function containing quantum operations, a template, or a quantum gate. For example, we can define the following subroutine: .. code-block:: python3 import pennylane as qml import numpy as np def subroutine(): qml.Hadamard(wires=[0]) qml.CNOT(wires=[0, 1]) qml.PauliX(wires=[1]) and then pass it into the ``qml.layer`` function. In this instance, we repeat ``subroutine`` three times: .. code-block:: python3 dev = qml.device('default.qubit', wires=3) @qml.qnode(dev) def circuit(): qml.layer(subroutine, 3) return [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] This creates the following circuit: >>> circuit() >>> print(circuit.draw()) 0: ──H──╭C──X──H──╭C──X──H──╭C──X──┤ ⟨Z⟩ 1: ─────╰X────────╰X────────╰X─────┤ ⟨Z⟩ **Static Arguments** Static arguments are arguments passed into ``template`` that don't change with each repetition. Static parameters are always passed as keyword arguments into ``qml.layer``. For example, consider the following subroutine: .. code-block:: python3 def subroutine(wires): qml.Hadamard(wires=wires[0]) qml.CNOT(wires=wires) qml.PauliX(wires=wires[1]) We wish to repeat this gate sequence three times on wires ``1`` and ``2``. Since the wires on which the subroutine acts don't change with each repetition, the ``wires`` parameter is passed as a keyword argument. Therefore, we define a circuit as: .. code-block:: python3 @qml.qnode(dev) def circuit(): qml.layer(subroutine, 3, wires=[1, 2]) return [qml.expval(qml.PauliZ(1)), qml.expval(qml.PauliZ(2))] which yields the following circuit: >>> circuit() >>> print(circuit.draw()) 1: ──H──╭C──X──H──╭C──X──H──╭C──X──┤ ⟨Z⟩ 2: ─────╰X────────╰X────────╰X─────┤ ⟨Z⟩ **Dynamic Arguments** In addition to passing static arguments to ``template``, we can also pass dynamic arguments. These are arguments that change with each repetition of the unitary. They are passed as non-keyword arguments to ``qml.layer``, after ``template`` and ``depth``. Each dynamic parameter must be a list of length equal to ``depth``. The :math:`j`-th element of the list represents the value of the argument used for the :math:`j`-th repetition. For example, let us define the following variational ansatz: .. code-block:: python3 def ansatz(params): qml.RX(params[0], wires=[0]) qml.MultiRZ(params[1], wires=[0, 1]) qml.RY(params[2], wires=[1]) We wish to repeat this ansatz two times, with each layer having different ``params``: .. code-block:: python3 @qml.qnode(dev) def circuit(params): qml.layer(ansatz, 2, params) return [qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))] Since we only have one dynamic argument, ``params``, we pass an array of first-dimension two, for the two layers of the repeated ansatz. We can also see that the ``params`` argument supplies three different parameters to three different gates. We therefore supply an array of size (2, 3) as an argument to ``qml.layer``: .. code-block:: python3 params = np.array([[0.5, 0.5, 0.5], [0.4, 0.4, 0.4]]) which yields the following circuit: >>> circuit(params) >>> print(circuit.draw()) 0: ──RX(0.5)──╭RZ(0.5)──RX(0.4)──╭RZ(0.4)──RX(0.3)──╭RZ(0.3)───────────┤ ⟨Z⟩ 1: ───────────╰RZ(0.5)──RY(0.5)──╰RZ(0.4)──RY(0.4)──╰RZ(0.3)──RY(0.3)──┤ ⟨Z⟩ **Passing Multiple Static and Dynamic Arguments** It is also possible to pass multiple static and dynamic arguments into the same unitary. Dynamic arguments must be ordered in ``qml.layer`` in the same order in which they are passed into the ``template``. Consider the following ansatz: .. code-block:: python3 def ansatz(param1, param2, wires, var): qml.RX(param1, wires=wires[0]) qml.MultiRZ(param2, wires=wires) if var: qml.Hadamard(wires=wires[1]) This circuit can be repeated as: .. code-block:: python3 @qml.qnode(dev) def circuit(param1, param2): qml.layer(ansatz, 2, param1, param2, wires=[1, 2], var=True) return [qml.expval(qml.PauliZ(1)), qml.expval(qml.PauliZ(2))] We can then run the circuit with a given set of parameters (note that the parameters are of size (2, 1), as the circuit is repeated twice, and for each repetition, both ``param1`` and ``param2`` are simply real numbers): .. code-block:: python3 param1 = np.array([0.1, 0.2]) param2 = np.array([0.3, 0.4]) This gives us the following circuit: >>> circuit(parm1, param2) >>> print(circuit.draw()) 1: ──RX(0.1)──╭RZ(0.3)──RX(0.2)──╭RZ(0.4)─────┤ ⟨Z⟩ 2: ───────────╰RZ(0.3)──H────────╰RZ(0.4)──H──┤ ⟨Z⟩ """ ############## # Input checks if not isinstance(depth, int): raise ValueError("'depth' must be of type int, got {}".format(type(depth).__name__)) for arg in args: if len(arg) != depth: raise ValueError( "Each positional argument must have length matching 'depth'; expected {} got {}".format( depth, len(arg) ) ) ############## for i in range(0, depth): arg_params = [k[i] for k in args] template(*arg_params, **kwargs)