Skip to content

Commit

Permalink
Merge branch 'master' into jaxitnorm
Browse files Browse the repository at this point in the history
  • Loading branch information
rmoyard committed Sep 28, 2021
2 parents 3754765 + 4df8dc5 commit 55ad9ef
Show file tree
Hide file tree
Showing 28 changed files with 1,198 additions and 858 deletions.
10 changes: 0 additions & 10 deletions doc/code/qml_init.rst

This file was deleted.

19 changes: 10 additions & 9 deletions doc/development/adding_templates.rst
Expand Up @@ -14,14 +14,15 @@ PennyLane's :mod:`template <pennylane.template>` library.
:mod:`subroutines <pennylane.templates.subroutines>`. Below you need to replace ``<templ_type>`` with the
correct template type.

Templates are just gates
~~~~~~~~~~~~~~~~~~~~~~~~
Templates are just operations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Conceptually, there is no difference in PennyLane between a template or ansatz and a :doc:`operation </introduction/operations>`.
Both inherit from the :class:`Operation <pennylane.operation.Operation>` class, which has an ``expand()`` function
that can be used to define a decomposition into other gates. If a device does not recognise the name of the operation,
it calls the ``expand()`` function which returns a :class:`tape <pennylane.tape.QuantumTape>` instance that
represents the queue of the decomposing gates.
Conceptually, there is no difference in PennyLane between a template or ansatz and a gate --- they are
both :doc:`operations </introduction/operations>` and inherit from the
:class:`Operation <pennylane.operation.Operation>` class. Unless a device knows how to implement this class on a
quantum computer, it queries the operation's ``expand()`` method, which expresses the operation as a
decomposition of other operations. More precisely, this method returns a :class:`tape <pennylane.tape.QuantumTape>`
instance that represents the queue of the decomposition.

For example, the following shows a simple template for a layer of of Pauli-X rotations:

Expand All @@ -32,8 +33,8 @@ For example, the following shows a simple template for a layer of of Pauli-X rot
class MyNewTemplate(Operation):
num_params = 1
num_wires = AnyWires
num_params = 1 # how many trainable parameters does this operation expect
num_wires = AnyWires # what wires does this operation act on
par_domain = "A" # note: this attribute will be deprecated soon
def expand(self):
Expand Down
1 change: 0 additions & 1 deletion doc/index.rst
Expand Up @@ -202,7 +202,6 @@ PennyLane is **free** and **open source**, released under the Apache License, Ve
:hidden:

code/qml
code/qml_init
code/qml_interfaces
code/qml_operation
code/qml_devices
Expand Down
167 changes: 57 additions & 110 deletions doc/introduction/templates.rst
Expand Up @@ -8,21 +8,10 @@ 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** :class:`QNode <pennylane.QNode>`.

PennyLane conceptually distinguishes different types of templates, such as :ref:`Embeddings <intro_ref_temp_emb>`,
:ref:`Layers <intro_ref_temp_lay>`, :ref:`State preparations <intro_ref_temp_stateprep>` and
:ref:`Subroutines <intro_ref_temp_subroutines>`.


Most templates are complemented by functions that provide an array of
random :ref:`initial parameters <intro_ref_temp_init>`.
literature, such architectures are commonly known as an *ansatz*. Templates can be used to
:ref:`embed data <intro_ref_temp_emb>` into quantum states, to define trainable :ref:`layers <intro_ref_temp_lay>`
of quantum gates, to :ref:`prepare quantum states <intro_ref_temp_stateprep>` as the first operation in a circuit,
or simply as general :ref:`subroutines <intro_ref_temp_subroutines>` that a circuit is built from.

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

Expand All @@ -32,8 +21,8 @@ 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.
Hence, they usually take a data sample such as a feature vector as an argument. Embeddings can also depend on
trainable parameters, and they may be constructed from repeated layers.

.. customgalleryitem::
:link: ../code/api/pennylane.templates.embeddings.AmplitudeEmbedding.html
Expand Down Expand Up @@ -82,7 +71,7 @@ Layer templates
.. currentmodule:: pennylane.templates.layers

Layer architectures define sequences of trainable gates that are repeated like the layers in a
neural network. Note arbitrary templates or operations can also be layered using the
neural network. Note that arbitrary templates or operations can also be repeated using the
:func:`~pennylane.layer` function.

.. customgalleryitem::
Expand Down Expand Up @@ -129,7 +118,9 @@ neural network. Note arbitrary templates or operations can also be layered using
State Preparations
------------------

State preparation templates transform a given state into a sequence of gates preparing that state.
State preparation templates transform the zero state :math:`|0\dots 0 \rangle` to another initial
state. In contrast to embeddings that can in principle be used anywhere in a circuit,
state preparation is typically used as the first operation.

.. customgalleryitem::
:link: ../code/api/pennylane.templates.state_preparations.BasisStatePreparation.html
Expand All @@ -152,11 +143,10 @@ State preparation templates transform a given state into a sequence of gates pre

.. _intro_ref_temp_subroutines:

Subroutines
-----------
Other subroutines
-----------------

Subroutines are sequences of (possibly trainable) gates that do not fulfill the conditions
of other templates.
Other useful templates which do not belong to the previous categories can be found here.

.. customgalleryitem::
:link: ../code/api/pennylane.templates.subroutines.GroverOperator.html
Expand Down Expand Up @@ -228,7 +218,12 @@ Broadcasting function
---------------------

PennyLane offers a broadcasting function to easily construct templates: :func:`~.broadcast`
takes single quantum operations or other templates and applies them to wires in a specific pattern.
takes either quantum gates or templates and applies them to wires in a specific pattern.

.. warning::

While the broadcasting function can make template construction very convenient, it
adds an overhead and is therefore not recommended when speed is a major concern.

.. customgalleryitem::
:link: ../code/api/pennylane.broadcast.html
Expand Down Expand Up @@ -279,113 +274,65 @@ takes single quantum operations or other templates and applies them to wires in
Parameter initializations
-------------------------

Each trainable template has dedicated functions in the :mod:`pennylane.init` module, which generate
randomly initialized arrays for the trainable parameters. For example, :func:`random_layers_uniform` can
be used together with the template :func:`RandomLayers`:
Templates that take a weight parameter tensor usually provide methods that return the shape of this tensor.
The shape can for example be used to construct random weights at the beginning of training.

.. code-block:: python
import pennylane as qml
from pennylane.templates import RandomLayers
from pennylane.init import random_layers_uniform
from pennylane.templates import BasicEntanglerLayers
from pennylane import numpy as np
dev = qml.device('default.qubit', wires=3)
n_wires = 3
dev = qml.device('default.qubit', wires=n_wires)
@qml.qnode(dev)
def circuit(weights):
RandomLayers(weights=weights, wires=[0, 2])
BasicEntanglerLayers(weights=weights, wires=range(n_wires))
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:

.. code-block:: python
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 ``*``:
shape = BasicEntanglerLayers.shape(n_layers=2, n_wires=n_wires)
np.random.seed(42) # to make the result reproducable
weights = np.random.random(size=shape)
.. code-block:: python
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.

.. code-block:: python
>>> circuit(weights)
0.7258859204630561

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 :mod:`~.pennylane.init` module.
If a template takes more than one weight tensor, the ``shape`` method returns a list of shape tuples.

Custom templates
----------------

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

.. code-block:: python3
Creating a custom template can be as simple as defining a function that creates operations and does not have a return
statement:

@qml.template
def bell_state_preparation(wires):
qml.Hadamard(wires=wires[0])
qml.CNOT(wires=wires)
.. code-block:: python
This registers the template with PennyLane, making it compatible with
functions that act on templates, such as :func:`~.pennylane.inv`:
from pennylane import numpy as np
.. code-block:: python3
def MyTemplate(a, b, wires):
c = np.sin(a) + b
qml.RX(c, wires=wires[0])
dev = qml.device('default.qubit', wires=2)
n_wires = 3
dev = qml.device('default.qubit', wires=n_wires)
@qml.qnode(dev)
def circuit():
qml.inv(bell_state_preparation(wires=[0, 1]))
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
def circuit(a, b):
MyTemplate(a, b, wires=range(n_wires))
return qml.expval(qml.PauliZ(0))
Adding a new template
---------------------
>>> circuit(2, 3)
-0.7195065654396784

.. note::

Make sure that classical processing is compatible with the autodifferentiation library you are using. For example,
if ``MyTemplate`` is to be used with the torch framework, we would have to change ``np.sin`` to ``torch.sin``.
PennyLane's :mod:`math <pennylane.math>` library contains some advanced functionality for
framework-agnostic processing.

Consult the :ref:`Contributing templates<contributing_templates>` page to learn how to grow the templates library by
adding your own template to PennyLane.
As suggested by the camel-case naming, built-in templates in PennyLane are classes. Classes are more complex
data structures than functions, since they can define properties and methods of templates (such as gradient
recipes or matrix representations). Consult the :ref:`Contributing templates<contributing_templates>`
page to learn how to code up your own template class, and how to add it to the PennyLane template library.
30 changes: 28 additions & 2 deletions doc/releases/changelog-dev.md
Expand Up @@ -263,6 +263,23 @@
* ``qml.circuit_drawer.CircuitDrawer`` can accept a string for the ``charset`` keyword, instead of a ``CharSet`` object.
[(#1640)](https://github.com/PennyLaneAI/pennylane/pull/1640)

* Operations can now have gradient recipes that depend on the state of the operation.
[(#1674)](https://github.com/PennyLaneAI/pennylane/pull/1674)

For example, this allows for gradient recipes that are parameter dependent:

```python
class RX(qml.RX):

@property
def grad_recipe(self):
# The gradient is given by [f(2x) - f(0)] / (2 sin(x)), by subsituting
# shift = x into the two term parameter-shift rule.
x = self.data[0]
c = 0.5 / np.sin(x)
return ([[c, 0.0, 2 * x], [-c, 0.0, 0.0]],)
```

<h3>Breaking changes</h3>

- The `QNode.metric_tensor` method has been deprecated, and will be removed in an upcoming release.
Expand All @@ -275,6 +292,14 @@
`requires_grad=False` was explicitly set.
[(#1638)](https://github.com/PennyLaneAI/pennylane/pull/1638)

<h3>Deprecations</h3>

* The `init` module, which contains functions to generate random parameter tensors for
templates, is flagged for deprecation and will be removed in the next release cycle.
Instead, the templates' `shape` method can be used to get the desired shape of the tensor,
which can then be generated manually.
[(#1689)](https://github.com/PennyLaneAI/pennylane/pull/1689)

<h3>Bug fixes</h3>

* Fix a bug where it was not possible to use `jax.jit` on a `QNode` when using `QubitStateVector`.
Expand All @@ -299,5 +324,6 @@

This release contains contributions from (in alphabetical order):

Utkarsh Azad, Olivia Di Matteo, Andrew Gardhouse, Josh Izaac, Christina Lee, Romain Moyard
Ingrid Strandberg, Antal Száva, David Wierichs.

Utkarsh Azad, Olivia Di Matteo, Andrew Gardhouse, Josh Izaac, Christina Lee, Romain Moyard,
Maria Schuld, Ingrid Strandberg, Antal Száva, David Wierichs.
5 changes: 3 additions & 2 deletions pennylane/collections/apply.py
Expand Up @@ -37,12 +37,13 @@ def apply(func, qnode_collection):
As we are using the ``'torch'`` interface, we now apply ``torch.sum``
to the QNodeCollection:
>>> cost = qml.apply(torch.sum, qnodes)
>>> cost = qml.collections.apply(torch.sum, qnodes)
This is a lazy composition --- no QNode evaluation has yet occured. Evaluation
only occurs when the returned function ``cost`` is evaluated:
>>> x = qml.init.strong_ent_layers_normal(3, 2)
>>> shape = qml.templates.StronglyEntanglingLayers.shape(layers=3, qubits=2)
>>> x = np.random.random(shape)
>>> cost(x)
tensor(0.9092, dtype=torch.float64, grad_fn=<SumBackward0>)
"""
Expand Down
3 changes: 2 additions & 1 deletion pennylane/collections/dot.py
Expand Up @@ -118,7 +118,8 @@ def dot(x, y):
This is a lazy dot product --- no QNode evaluation has yet occured. Evaluation
only occurs when the returned function ``cost`` is evaluated:
>>> x = qml.init.strong_ent_layers_normal(3, 2) # generate random parameters
>>> shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=3, n_wires=2)
>>> x = np.random.random(shape) # generate random parameters
>>> cost(x)
tensor(-0.2183, dtype=torch.float64, grad_fn=<DotBackward>)
"""
Expand Down
3 changes: 2 additions & 1 deletion pennylane/collections/qnode_collection.py
Expand Up @@ -140,7 +140,8 @@ def qnode2(x, y):
We can now create some parameters and evaluate the collection:
>>> params = qml.init.strong_ent_layers_normal(n_layers=4, n_wires=4)
>>> shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=4, n_wires=4)
>>> params = np.random.random(shape)
>>> qnodes(params)
array([0.046875 , 0.93164062])
Expand Down
4 changes: 3 additions & 1 deletion pennylane/collections/sum.py
Expand Up @@ -42,7 +42,9 @@ def sum(x):
This is a lazy summation --- no QNode evaluation has yet occured. Evaluation
only occurs when the returned function ``cost`` is evaluated:
>>> x = qml.init.strong_ent_layers_normal(3, 2, seed=42)
>>> np.random.seed(42)
>>> shape = qml.templates.StronglyEntanglingLayers.shape(n_layers=3, n_wires=2)
>>> x = np.random.random(shape)
>>> cost(x)
tensor(0.9177, dtype=torch.float64)
"""
Expand Down

0 comments on commit 55ad9ef

Please sign in to comment.