layer(template, depth, *args, **kwargs)¶
Repeatedly applies a unitary a given number of times.
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 \(j\)-th element of the list is the value of the argument the \(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.
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:
import pennylane as qml import numpy as np def subroutine(): qml.Hadamard(wires=) qml.CNOT(wires=[0, 1]) qml.PauliX(wires=)
and then pass it into the
qml.layerfunction. In this instance, we repeat
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──H──╭C──H──╭C─────┤ ⟨Z⟩ 1: ─────╰X──X──╰X──X──╰X──X──┤ ⟨Z⟩
Static arguments are arguments passed into
templatethat don’t change with each repetition. Static parameters are always passed as keyword arguments into
qml.layer. For example, consider the following subroutine:
def subroutine(wires): qml.Hadamard(wires=wires) qml.CNOT(wires=wires) qml.PauliX(wires=wires)
We wish to repeat this gate sequence three times on wires
2. Since the wires on which the subroutine acts don’t change with each repetition, the
wiresparameter is passed as a keyword argument. Therefore, we define a circuit as:
@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──H──╭C──H──╭C─────┤ ⟨Z⟩ 2: ─────╰X──X──╰X──X──╰X──X──┤ ⟨Z⟩
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
depth. Each dynamic parameter must be a list of length equal to
depth. The \(j\)-th element of the list represents the value of the argument used for the \(j\)-th repetition.
For example, let us define the following variational ansatz:
def ansatz(params): qml.RX(params, wires=) qml.MultiRZ(params, wires=[0, 1]) qml.RY(params, wires=)
We wish to repeat this ansatz two times, with each layer having different
@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
paramsargument supplies three different parameters to three different gates. We therefore supply an array of size (2, 3) as an argument to
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)───────────┤ ⟨Z⟩ 1: ───────────╰RZ(0.5)──RY(0.5)──╰RZ(0.4)──RY(0.4)──┤ ⟨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.layerin the same order in which they are passed into the
Consider the following ansatz:
def ansatz(param1, param2, wires, var): qml.RX(param1, wires=wires) qml.MultiRZ(param2, wires=wires) if var: qml.Hadamard(wires=wires)
This circuit can be repeated as:
@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
param2are simply real numbers):
param1 = np.array([0.1, 0.2]) param2 = np.array([0.3, 0.4])
This gives us the following circuit:
>>> circuit(param1, 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⟩