# Source code for pennylane.templates.embeddings

```
# Copyright 2018 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"""
This module provides quantum circuit architectures that can embed features into a quantum state.
"""
#pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
from collections.abc import Iterable
from pennylane.ops import RX, RY, RZ, BasisState, Squeezing, Displacement, QubitStateVector
import numpy as np
[docs]def AngleEmbedding(features, wires, rotation='X'):
r"""
Encodes :math:`N` features into the rotation angles of :math:`n` qubits, where :math:`N \leq n`.
The rotations can be chosen as either :class:`~pennylane.ops.RX`, :class:`~pennylane.ops.RY`
or :class:`~pennylane.ops.RZ` gates, as defined by the ``rotation`` parameter:
* ``rotation='X'`` uses the features as angles of RX rotations
* ``rotation='Y'`` uses the features as angles of RY rotations
* ``rotation='Z'`` uses the features as angles of RZ rotations
The length of ``features`` has to be smaller or equal to the number of qubits. If there are fewer entries in
``features`` than rotations, the circuit does not apply the remaining rotation gates.
This embedding method can also be used to encode a binary sequence into a basis state. For example, to prepare
basis state :math:`|0,1,1,0\rangle`, choose ``rotation='X'`` and use the
feature vector :math:`[0, \pi/2, \pi/2, 0]`. Alternatively, one can use the :mod:`BasisEmbedding()` template.
Args:
features (array): Input array of shape ``(N,)``, where N is the number of input features to embed,
with :math:`N\leq n`
wires (Sequence[int]): sequence of qubit indices that the template acts on
Keyword Args:
rotation (str): Type of rotations used
"""
if not isinstance(wires, Iterable):
raise ValueError("Wires needs to be a list of wires that the embedding uses; got {}.".format(wires))
if len(features) > len(wires):
raise ValueError("Number of features to embed cannot be larger than number of wires, which is {}; "
"got {}.".format(len(wires), len(features)))
if rotation == 'X':
for f, w in zip(features, wires):
RX(f, wires=w)
elif rotation == 'Y':
for f, w in zip(features, wires):
RY(f, wires=w)
elif rotation == 'Z':
for f, w in zip(features, wires):
RZ(f, wires=w)
else:
raise ValueError("Rotation has to be `X`, `Y` or `Z`; got {}.".format(rotation))
[docs]def AmplitudeEmbedding(features, wires, pad=False, normalize=False):
r"""Encodes :math:`2^n` features into the amplitude vector of :math:`n` qubits.
If the total number of features to embed are less than the :math:`2^n` available amplitudes,
non-informative constants (zeros) can be padded to ``features``. To enable this, the argument
``pad`` should be set to ``True``.
The L2-norm of ``features`` must be one. By default, AmplitudeEmbedding expects a normalized
feature vector. The argument ``normalize`` can be set to ``True`` to automatically normalize it.
.. note::
AmplitudeEmbedding uses PennyLane's :class:`~pennylane.ops.QubitStateVector` and only works in conjunction with
devices that implement this function.
Args:
features (array): input array of shape ``(2**n,)``
wires (Sequence[int]): sequence of qubit indices that the template acts on
pad (Boolean): controls the activation of the padding option
normalize (Boolean): controls the activation of automatic normalization
"""
if not isinstance(wires, Iterable):
raise ValueError("Wires needs to be a list of wires that the embedding uses; got {}.".format(wires))
n_features = len(features)
n_amplitudes = 2**len(wires)
if n_amplitudes < n_features:
raise ValueError("AmplitudeEmbedding requires the size of feature vector to be smaller than or equal to 2**len(wires), which is {}; "
"got {}.".format(n_amplitudes, n_features))
if pad and n_amplitudes >= n_features:
features = np.pad(features, (0, n_amplitudes-n_features), 'constant')
if not pad and n_amplitudes != n_features:
raise ValueError("AmplitudeEmbedding with no padding requires a feature vector of size 2**len(wires), which is {}; "
"got {}.".format(n_amplitudes, n_features))
if normalize and np.linalg.norm(features, 2) != 1:
features = features * (1/np.linalg.norm(features, 2))
if not normalize and np.linalg.norm(features, 2) != 1:
raise ValueError("AmplitudeEmbedding requires a normalized feature vector. The argument normalize can be set to True to automatically normalize it.")
QubitStateVector(features, wires=wires)
[docs]def BasisEmbedding(features, wires):
r"""Encodes :math:`n` binary features into a basis state of :math:`n` qubits.
For example, for ``features=[0, 1, 0]``, the quantum system will be prepared in state :math:`|010 \rangle`.
.. note::
BasisEmbedding uses PennyLane's :class:`~pennylane.ops.BasisState` and only works in conjunction with
devices that implement this function.
Args:
features (array): Binary input array of shape ``(n, )``
wires (Sequence[int]): sequence of qubit indices that the template acts on
"""
if not isinstance(wires, Iterable):
raise ValueError("Wires needs to be a list of wires that the embedding uses; got {}.".format(wires))
if len(features) > len(wires):
raise ValueError("Number of bits to embed cannot be larger than number of wires, which is {}; "
"got {}.".format(len(wires), len(features)))
BasisState(features, wires=wires)
[docs]def SqueezingEmbedding(features, wires, method='amplitude', c=0.1):
r"""Encodes :math:`N` features into the squeezing amplitudes :math:`r \geq 0` or phases :math:`\phi \in [0, 2\pi)`
of :math:`M` modes, where :math:`N\leq M`.
The mathematical definition of the squeezing gate is given by the operator
.. math::
S(z) = \exp\left(\frac{r}{2}\left(e^{-i\phi}\a^2 -e^{i\phi}{\ad}^{2} \right) \right),
where :math:`\a` and :math:`\ad` are the bosonic creation and annihilation operators.
``features`` has to be an array of at most ``len(wires)`` floats. If there are fewer entries in
``features`` than wires, the circuit does not apply the remaining squeezing gates.
Args:
features (array): Array of features of size (N,)
wires (Sequence[int]): sequence of mode indices that the template acts on
Keyword Args:
method (str): ``'phase'`` encodes the input into the phase of single-mode squeezing, while
``'amplitude'`` uses the amplitude
c (float): value of the phase of all squeezing gates if ``execution='amplitude'``, or the
amplitude of all squeezing gates if ``execution='phase'``
"""
if not isinstance(wires, Iterable):
raise ValueError("Wires needs to be a list of wires that the embedding uses; got {}.".format(wires))
if len(wires) < len(features):
raise ValueError("Number of features to embed cannot be larger than number of wires, which is {}; "
"got {}.".format(len(wires), len(features)))
for idx, f in enumerate(features):
if method == 'amplitude':
Squeezing(f, c, wires=wires[idx])
elif method == 'phase':
Squeezing(c, f, wires=wires[idx])
else:
raise ValueError("Execution method '{}' not known. Has to be 'phase' or 'amplitude'.".format(method))
[docs]def DisplacementEmbedding(features, wires, method='amplitude', c=0.1):
r"""Encodes :math:`N` features into the displacement amplitudes :math:`r` or phases :math:`\phi` of :math:`M` modes,
where :math:`N\leq M`.
The mathematical definition of the displacement gate is given by the operator
.. math::
D(\alpha) = \exp(r (e^{i\phi}\ad -e^{-i\phi}\a)),
where :math:`\a` and :math:`\ad` are the bosonic creation and annihilation operators.
``features`` has to be an array of at most ``len(wires)`` floats. If there are fewer entries in
``features`` than wires, the circuit does not apply the remaining displacement gates.
Args:
features (array): Array of features of size (N,)
wires (Sequence[int]): sequence of mode indices that the template acts on
Keyword Args:
method (str): ``'phase'`` encodes the input into the phase of single-mode displacement, while
``'amplitude'`` uses the amplitude
c (float): value of the phase of all displacement gates if ``execution='amplitude'``, or
the amplitude of all displacement gates if ``execution='phase'``
"""
if not isinstance(wires, Iterable):
raise ValueError("Wires needs to be a list of wires that the embedding uses; got {}.".format(wires))
if len(wires) < len(features):
raise ValueError("Number of features to embed cannot be larger than number of wires, which is {}; "
"got {}.".format(len(wires), len(features)))
for idx, f in enumerate(features):
if method == 'amplitude':
Displacement(f, c, wires=wires[idx])
elif method == 'phase':
Displacement(c, f, wires=wires[idx])
else:
raise ValueError("Execution method '{}' not known. Has to be 'phase' or 'amplitude'.".format(method))
```