Templates

PennyLane provides a growing library of pre-coded templates of common variational circuit architectures that can be used to easily build, evaluate, and train more complex models. In the literature, such architectures are commonly known as an ansatz.

Note

Templates are constructed out of structured combinations of the quantum operations provided by PennyLane. This means that template functions can only be used within a valid QNode.

PennyLane conceptually distinguishes different types of templates, such as Embeddings, Layers, State preparations and Subroutines.

Most templates are complemented by functions that provide an array of random initial parameters.

An example of how to use templates is the following:

import pennylane as qml
from pennylane.templates.embeddings import AngleEmbedding
from pennylane.templates.layers import StronglyEntanglingLayers
from pennylane.init import strong_ent_layers_uniform

dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev)
def circuit(weights, x=None):
    AngleEmbedding(x, [0,1])
    StronglyEntanglingLayers(weights, wires=[0,1])
    return qml.expval(qml.PauliZ(0))

init_weights = strong_ent_layers_uniform(n_layers=3, n_wires=2)
print(circuit(init_weights, x=[1., 2.]))

Here, we used the embedding template AngleEmbedding() together with the layer template StronglyEntanglingLayers(), and the uniform parameter initialization strategy strong_ent_layers_uniform().

Custom templates

In addition, custom templates can be created; simply decorate a Python function that applies quantum gates with the pennylane.template() decorator:

@qml.template
def bell_state_preparation(wires):
    qml.Hadamard(wires=wires[0])
    qml.CNOT(wires=wires)

This registers the template with PennyLane, making it compatible with functions that act on templates, such as inv():

dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev)
def circuit():
    qml.inv(bell_state_preparation(wires=[0, 1]))
    return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

The following is a gallery of built-in templates provided by PennyLane.

Embedding templates

Embeddings encode input features into the quantum state of the circuit. Hence, they take a feature vector as an argument. Embeddings can also depend on trainable parameters, and they may consist of repeated layers.

Layer templates

Layer architectures define sequences of trainable gates that are repeated like the layers in a neural network.

State Preparations

State preparation templates transform a given state into a sequence of gates preparing that state.

Subroutines

Subroutines are sequences of (possibly trainable) gates that do not fulfill the conditions of other templates.

Broadcasting function

PennyLane offers a broadcasting function to easily construct templates: broadcast() takes single quantum operations or other templates and applies them to wires in a specific pattern.

Parameter initializations

Each trainable template has dedicated functions in the pennylane.init module, which generate randomly initialized arrays for the trainable parameters. For example, random_layers_uniform() can be used together with the template RandomLayers():

import pennylane as qml
from pennylane.templates import RandomLayers
from pennylane.init import random_layers_uniform

dev = qml.device('default.qubit', wires=3)

@qml.qnode(dev)
def circuit(weights):
    RandomLayers(weights=weights, wires=[0, 2])
    return qml.expval(qml.PauliZ(0))

init_pars = random_layers_uniform(n_layers=3, n_wires=2)
circuit(init_pars)

Templates that take more than one parameter array require several initialization functions:

from pennylane.templates import Interferometer
from pennylane.init import (interferometer_theta_uniform,
                            interferometer_phi_uniform,
                            interferometer_varphi_normal)

dev = qml.device('default.gaussian', wires=3)

@qml.qnode(dev)
def circuit(theta, phi, varphi):
    Interferometer(theta=theta, phi=phi, varphi=varphi, wires=[0, 2])
    return qml.expval(qml.X(0))

init_theta = interferometer_theta_uniform(n_wires=2)
init_phi = interferometer_phi_uniform(n_wires=2)
init_varphi = interferometer_varphi_normal(n_wires=2)

circuit(init_theta, init_phi, init_varphi)

For templates with multiple parameters, initializations that return a list of all parameter arrays at once are provided, and can be conveniently used in conjunction with the unpacking operator *:

from pennylane.templates import Interferometer
from pennylane.init import interferometer_all

dev = qml.device('default.gaussian', wires=3)

@qml.qnode(dev)
def circuit(*pars):
    Interferometer(*pars, wires=[0, 2])
    return qml.expval(qml.X(0))

init_pars = interferometer_all(n_wires=2)

circuit(*init_pars)

Initial parameters can be converted to Torch or TensorFlow tensors, which can be used in the respective interfaces.

import torch
import tensorflow as tf
from pennylane.init import strong_ent_layers_normal

init_pars = strong_ent_layers_normal(n_layers=3, n_wires=2)
init_torch = torch.tensor(init_pars)
init_tf = tf.Variable(init_pars)

The initialization functions can be found in the init module.

Adding a new template

Consult the Contributing templates page to learn how to grow the templates library by adding your own template to PennyLane.