Source code for pennylane.templates.layers.cv_neural_net
# 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.
r"""
Contains the CVNeuralNetLayers template.
"""
# pylint: disable-msg=too-many-branches,too-many-arguments,protected-access,arguments-differ
import pennylane as qml
from pennylane.operation import AnyWires, Operation
[docs]class CVNeuralNetLayers(Operation):
r"""A sequence of layers of a continuous-variable quantum neural network,
as specified in `Killoran et al. (2019) <https://doi.org/10.1103/PhysRevResearch.1.033063>`_.
The layer consists
of interferometers, displacement and squeezing gates mimicking the linear transformation of
a neural network in the x-basis of the quantum system, and uses a Kerr gate
to introduce a 'quantum' non-linearity.
The layers act on the :math:`M` modes given in ``wires``,
and include interferometers of :math:`K=M(M-1)/2` beamsplitters. The different weight parameters
contain the weights for each layer. The number of layers :math:`L` is therefore derived
from the first dimension of ``weights``.
This example shows a 4-mode CVNeuralNet layer with squeezing gates :math:`S`, displacement gates :math:`D` and
Kerr gates :math:`K`. The two big blocks are interferometers of type
:mod:`pennylane.Interferometer`:
.. figure:: ../../_static/layer_cvqnn.png
:align: center
:width: 60%
:target: javascript:void(0);
.. note::
The CV neural network architecture includes :class:`~pennylane.ops.Kerr` operations.
Make sure to use a suitable device, such as the :code:`strawberryfields.fock`
device of the `PennyLane-SF <https://github.com/XanaduAI/pennylane-sf>`_ plugin.
Args:
theta_1 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for first interferometer
phi_1 (tensor_like): shape :math:`(L, K)` tensor of phase angles for first interferometer
varphi_1 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after first interferometer
r (tensor_like): shape :math:`(L, M)` tensor of squeezing amounts for :class:`~pennylane.ops.Squeezing` operations
phi_r (tensor_like): shape :math:`(L, M)` tensor of squeezing angles for :class:`~pennylane.ops.Squeezing` operations
theta_2 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for second interferometer
phi_2 (tensor_like): shape :math:`(L, K)` tensor of phase angles for second interferometer
varphi_2 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after second interferometer
a (tensor_like): shape :math:`(L, M)` tensor of displacement magnitudes for :class:`~pennylane.ops.Displacement` operations
phi_a (tensor_like): shape :math:`(L, M)` tensor of displacement angles for :class:`~pennylane.ops.Displacement` operations
k (tensor_like): shape :math:`(L, M)` tensor of kerr parameters for :class:`~pennylane.ops.Kerr` operations
wires (Iterable): wires that the template acts on
.. details::
:title: Usage Details
**Parameter shapes**
A list of shapes for the 11 input parameter tensors can be computed by the static method
:meth:`~.CVNeuralNetLayers.shape` and used when creating randomly
initialised weights:
.. code-block:: python
shapes = CVNeuralNetLayers.shape(n_layers=2, n_wires=2)
weights = [np.random.random(shape) for shape in shapes]
def circuit():
CVNeuralNetLayers(*weights, wires=[0, 1])
return qml.expval(qml.QuadX(0))
"""
num_wires = AnyWires
grad_method = None
def __init__(
self,
theta_1,
phi_1,
varphi_1,
r,
phi_r,
theta_2,
phi_2,
varphi_2,
a,
phi_a,
k,
wires,
id=None,
):
n_wires = len(wires)
# n_if -> theta and phi shape for Interferometer
n_if = n_wires * (n_wires - 1) // 2
# check that first dimension is the same
weights_list = [theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k]
shapes = [qml.math.shape(w) for w in weights_list]
first_dims = [s[0] for s in shapes]
if len(set(first_dims)) > 1:
raise ValueError(
f"The first dimension of all parameters needs to be the same, got {first_dims}"
)
# check second dimensions
second_dims = [s[1] for s in shapes]
expected = [n_if] * 2 + [n_wires] * 3 + [n_if] * 2 + [n_wires] * 4
if not all(e == d for e, d in zip(expected, second_dims)):
raise ValueError("Got unexpected shape for one or more parameters.")
self.n_layers = shapes[0][0]
super().__init__(
theta_1,
phi_1,
varphi_1,
r,
phi_r,
theta_2,
phi_2,
varphi_2,
a,
phi_a,
k,
wires=wires,
id=id,
)
@property
def num_params(self):
return 11
[docs] @staticmethod
def compute_decomposition(
theta_1, phi_1, varphi_1, r, phi_r, theta_2, phi_2, varphi_2, a, phi_a, k, wires
): # pylint: disable=arguments-differ
r"""Representation of the operator as a product of other operators.
.. math:: O = O_1 O_2 \dots O_n.
.. seealso:: :meth:`~.CVNeuralNetLayers.decomposition`.
Args:
theta_1 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for first interferometer
phi_1 (tensor_like): shape :math:`(L, K)` tensor of phase angles for first interferometer
varphi_1 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after first interferometer
r (tensor_like): shape :math:`(L, M)` tensor of squeezing amounts for :class:`~pennylane.ops.Squeezing` operations
phi_r (tensor_like): shape :math:`(L, M)` tensor of squeezing angles for :class:`~pennylane.ops.Squeezing` operations
theta_2 (tensor_like): shape :math:`(L, K)` tensor of transmittivity angles for second interferometer
phi_2 (tensor_like): shape :math:`(L, K)` tensor of phase angles for second interferometer
varphi_2 (tensor_like): shape :math:`(L, M)` tensor of rotation angles to apply after second interferometer
a (tensor_like): shape :math:`(L, M)` tensor of displacement magnitudes for :class:`~pennylane.ops.Displacement` operations
phi_a (tensor_like): shape :math:`(L, M)` tensor of displacement angles for :class:`~pennylane.ops.Displacement` operations
k (tensor_like): shape :math:`(L, M)` tensor of kerr parameters for :class:`~pennylane.ops.Kerr` operations
wires (Any or Iterable[Any]): wires that the operator acts on
Returns:
list[.Operator]: decomposition of the operator
**Example**
>>> theta_1 = torch.tensor([[0.4]])
>>> phi_1 = torch.tensor([[-0.3]])
>>> varphi_1 = = torch.tensor([[1.7, 0.1]])
>>> r = torch.tensor([[-1., -0.2]])
>>> phi_r = torch.tensor([[0.2, -0.2]])
>>> theta_2 = torch.tensor([[1.4]])
>>> phi_2 = torch.tensor([[-0.4]])
>>> varphi_2 = torch.tensor([[0.1, 0.2]])
>>> a = torch.tensor([[0.1, 0.2]])
>>> phi_a = torch.tensor([[-1.1, 0.2]])
>>> k = torch.tensor([[0.1, 0.2]])
>>> qml.CVNeuralNetLayers.compute_decomposition(theta_1, phi_1, varphi_1, r, phi_r, theta_2,
... phi_2, varphi_2, a, phi_a, k, wires=["a", "b"])
[Interferometer(tensor([0.4000]), tensor([-0.3000]), tensor([1.7000, 0.1000]), wires=['a', 'b']),
Squeezing(tensor(-1.), tensor(0.2000), wires=['a']),
Squeezing(tensor(-0.2000), tensor(-0.2000), wires=['b']),
Interferometer(tensor([1.4000]), tensor([-0.4000]), tensor([0.1000, 0.2000]), wires=['a', 'b']),
Displacement(tensor(0.1000), tensor(-1.1000), wires=['a']),
Displacement(tensor(0.2000), tensor(0.2000), wires=['b']),
Kerr(tensor(0.1000), wires=['a']),
Kerr(tensor(0.2000), wires=['b'])]
"""
op_list = []
n_layers = qml.math.shape(theta_1)[0]
for m in range(n_layers):
op_list.append(
qml.Interferometer(
theta=theta_1[m],
phi=phi_1[m],
varphi=varphi_1[m],
wires=wires,
)
)
for i in range(len(wires)): # pylint:disable=consider-using-enumerate
op_list.append(qml.Squeezing(r[m, i], phi_r[m, i], wires=wires[i]))
op_list.append(
qml.Interferometer(
theta=theta_2[m],
phi=phi_2[m],
varphi=varphi_2[m],
wires=wires,
)
)
for i in range(len(wires)): # pylint: disable=consider-using-enumerate
op_list.append(qml.Displacement(a[m, i], phi_a[m, i], wires=wires[i]))
for i in range(len(wires)): # pylint:disable=consider-using-enumerate
op_list.append(qml.Kerr(k[m, i], wires=wires[i]))
return op_list
[docs] @staticmethod
def shape(n_layers, n_wires):
r"""Returns a list of shapes for the 11 parameter tensors.
Args:
n_layers (int): number of layers
n_wires (int): number of wires
Returns:
list[tuple[int]]: list of shapes
"""
# n_if -> theta and phi shape for Interferometer
n_if = n_wires * (n_wires - 1) // 2
shapes = (
[(n_layers, n_if)] * 2
+ [(n_layers, n_wires)] * 3
+ [(n_layers, n_if)] * 2
+ [(n_layers, n_wires)] * 4
)
return shapes
_modules/pennylane/templates/layers/cv_neural_net
Download Python script
Download Notebook
View on GitHub