Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TEMPLATES] Rewrite QAOAEmbedding and BasicEntanglerLayers #1138

Merged
merged 90 commits into from
Mar 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
21e30b8
Remove old core
josh146 Jan 29, 2021
bab8c08
more
josh146 Jan 29, 2021
6a62534
more tests
josh146 Jan 29, 2021
25e6085
more tests
josh146 Jan 29, 2021
5074218
more tests
josh146 Jan 29, 2021
23147d6
merge master
josh146 Jan 29, 2021
0f31b36
fixed more tests
josh146 Jan 29, 2021
8b5828a
Merge branch 'master' into rip-out-core
josh146 Jan 30, 2021
e13a1e5
more tests passing
josh146 Jan 30, 2021
f79f20f
Merge branch 'master' into rip-out-core
josh146 Feb 3, 2021
f6408fe
merge master
josh146 Feb 8, 2021
f014c89
update jax test
josh146 Feb 8, 2021
6d1fb63
merge master
josh146 Feb 11, 2021
33f4c8e
merge master
josh146 Feb 16, 2021
95e35e4
merge master
josh146 Feb 18, 2021
8c7fbc6
Merge branch 'master' into rip-out-core
josh146 Feb 22, 2021
14ebf35
tests passing
josh146 Feb 22, 2021
643113f
fix
josh146 Feb 22, 2021
153401d
fix
josh146 Feb 22, 2021
951b924
linting
josh146 Feb 22, 2021
0fdf4b4
fix docs
josh146 Feb 22, 2021
1d6d32d
Merge branch 'master' into rip-out-core
mariaschuld Feb 24, 2021
24119bc
Merge branch 'master' into rip-out-core
josh146 Mar 1, 2021
da62820
merge master:
josh146 Mar 4, 2021
4443b57
fix
josh146 Mar 4, 2021
672d963
fix
josh146 Mar 4, 2021
f4dc056
Merge branch 'master' into rip-out-core
josh146 Mar 8, 2021
4d100f3
merge master
josh146 Mar 10, 2021
c4f609a
fix
josh146 Mar 10, 2021
f8a2747
Update pennylane/tape/operation_recorder.py
josh146 Mar 10, 2021
5e1df17
fix tests after changing observable underline
josh146 Mar 10, 2021
27d11b3
backup
mariaschuld Mar 11, 2021
1bc1852
Merge branch 'rip-out-core' into templates_as_operations
mariaschuld Mar 11, 2021
9407fe7
prototype - all tests pass
mariaschuld Mar 11, 2021
a4cd153
change two more templates
mariaschuld Mar 11, 2021
bf74287
black
mariaschuld Mar 11, 2021
be672e3
Update tests/test_queuing.py
josh146 Mar 12, 2021
50b074a
Update pennylane/circuit_graph.py
josh146 Mar 12, 2021
30c3bb1
Update pennylane/tape/tape.py
josh146 Mar 12, 2021
426c1ed
merge master
josh146 Mar 12, 2021
55e2206
merge master
josh146 Mar 12, 2021
c7f1a41
Merge branch 'master' into rip-out-core
antalszava Mar 14, 2021
5d92768
Update pennylane/templates/embeddings/qaoa.py
mariaschuld Mar 15, 2021
c1d2874
Update pennylane/measure.py
josh146 Mar 15, 2021
39e7768
Update doc/code/qml_tape.rst
josh146 Mar 15, 2021
899911b
Update doc/code/qml_tape.rst
josh146 Mar 15, 2021
3647cc7
finish qaoa embedding
mariaschuld Mar 15, 2021
e3b73e3
polish tests for qaoa embedding
mariaschuld Mar 15, 2021
54c6ede
Merge branch 'templates_as_operations' of github.com:PennyLaneAI/penn…
mariaschuld Mar 15, 2021
e5cefff
backup
mariaschuld Mar 15, 2021
d49d65b
finish qaoa
mariaschuld Mar 15, 2021
d7d5225
finish basic entangler
mariaschuld Mar 15, 2021
988d3fc
update double-excitation
mariaschuld Mar 16, 2021
02965db
black tests
mariaschuld Mar 16, 2021
f4d1dbc
reverted old tests back to normal
mariaschuld Mar 16, 2021
f31fd43
fix some stuff
mariaschuld Mar 16, 2021
bed00d7
some polishing
mariaschuld Mar 16, 2021
a39ad53
black
mariaschuld Mar 16, 2021
84989cc
increase coverage
mariaschuld Mar 16, 2021
006bc97
Update tests/interfaces/test_qnode_torch.py
josh146 Mar 16, 2021
595186d
Update tests/interfaces/test_qnode_torch.py
josh146 Mar 16, 2021
16ba95c
Update tests/interfaces/test_qnode_autograd.py
josh146 Mar 16, 2021
9c99910
Update tests/interfaces/test_qnode_autograd.py
josh146 Mar 16, 2021
72c688e
Update tests/interfaces/test_qnode_tf.py
josh146 Mar 16, 2021
7884630
Update tests/interfaces/test_tape_tf.py
josh146 Mar 16, 2021
4eb9b76
Update tests/interfaces/test_qnode_tf.py
josh146 Mar 16, 2021
ebea0b0
Update tests/interfaces/test_tape_autograd.py
josh146 Mar 16, 2021
b86e613
Update tests/interfaces/test_tape_torch.py
josh146 Mar 16, 2021
4cad2e4
Update pennylane/interfaces/torch.py
josh146 Mar 16, 2021
c0632db
suggested changes
josh146 Mar 16, 2021
0343c22
Update pennylane/qnode.py
josh146 Mar 18, 2021
e54694b
suggested changes
josh146 Mar 18, 2021
a3cf592
Merge branch 'rip-out-core' of github.com:PennyLaneAI/pennylane into …
josh146 Mar 18, 2021
840fd79
merge master
josh146 Mar 18, 2021
032d2b1
fix test
josh146 Mar 18, 2021
25555cb
Merge branch 'master' into rip-out-core
trbromley Mar 18, 2021
672e5e9
changed weight initialisation methods to return shape
mariaschuld Mar 22, 2021
2cb10de
black
mariaschuld Mar 22, 2021
cecb38d
Merge branch 'rip-out-core' of github.com:PennyLaneAI/pennylane into …
mariaschuld Mar 22, 2021
4c2b807
simplify preprocess logic
mariaschuld Mar 22, 2021
408d1c8
black
mariaschuld Mar 22, 2021
c62cfde
use pl numpy in autograd tests
mariaschuld Mar 22, 2021
b4f7598
merge master
josh146 Mar 22, 2021
8d1303c
update docstring
mariaschuld Mar 22, 2021
71572cd
Merge branch 'rewrite_qaoaembedding_and_basicentanglerlayers' of gith…
mariaschuld Mar 22, 2021
7dd2292
Merge branch 'master' into rewrite_qaoaembedding_and_basicentanglerla…
ixfoduap Mar 22, 2021
a051b07
Merge branch 'master' into rewrite_qaoaembedding_and_basicentanglerla…
mariaschuld Mar 23, 2021
4132efd
polish
mariaschuld Mar 23, 2021
70a03e6
Merge branch 'rewrite_qaoaembedding_and_basicentanglerlayers' of gith…
mariaschuld Mar 23, 2021
92466bb
merge master
mariaschuld Mar 23, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,18 @@

<h3>Improvements</h3>

- The `QAOAEmbedding` and `BasicEntanglerLayers` are now classes inheriting
from `Operation`, and define the ansatz in their `expand()` method. This
change does not affect the user interface.

For convenience, the class has a method that returns the shape of the
trainable parameter tensor, i.e.,

```python
shape = qml.templates.BasicEntanglerLayers.shape(n_layers=2, n_wires=4)
weights = np.random.random(shape)
```

- ``QubitUnitary`` now validates to ensure the input matrix is two dimensional.
[(#1128)](https://github.com/PennyLaneAI/pennylane/pull/1128)

Expand Down
225 changes: 124 additions & 101 deletions pennylane/templates/embeddings/qaoa.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2018-2020 Xanadu Quantum Technologies Inc.
# 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.
Expand All @@ -12,82 +12,27 @@
# See the License for the specific language governing permissions and
# limitations under the License.
r"""
Contains the ``QAOAEmbedding`` template.
Contains the QAOAEmbedding template.
"""
# pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
import pennylane as qml
from pennylane.templates.decorator import template
from pennylane.ops import RX, RY, RZ, MultiRZ, Hadamard
from pennylane.templates import broadcast
from pennylane.operation import Operation, AnyWires
from pennylane.wires import Wires


def _preprocess(features, wires, weights):
"""Validate and pre-process inputs as follows:

* Check that the features tensor is one-dimensional.
* Check that the first dimension of the features tensor
has length :math:`n` or less, where :math:`n` is the number of qubits.
* Check that the shape of the weights tensor is correct for the number of qubits.

Args:
features (tensor_like): input features to pre-process
wires (Wires): wires that template acts on
weights (tensor_like): weights of the embedding

Returns:
int: number of times that embedding is repeated
"""
shape = qml.math.shape(features)

if len(shape) != 1:
raise ValueError(f"Features must be a one-dimensional tensor; got shape {shape}.")

n_features = shape[0]
if n_features > len(wires):
raise ValueError(
f"Features must be of length {len(wires)} or less; got length {n_features}."
)

shape = qml.math.shape(weights)
repeat = shape[0]

if len(wires) == 1:
if shape != (repeat, 1):
raise ValueError(f"Weights tensor must be of shape {(repeat, 1)}; got {shape}")

elif len(wires) == 2:
if shape != (repeat, 3):
raise ValueError(f"Weights tensor must be of shape {(repeat, 3)}; got {shape}")
else:
if shape != (repeat, 2 * len(wires)):
raise ValueError(
f"Weights tensor must be of shape {(repeat, 2*len(wires))}; got {shape}"
)

return repeat


mariaschuld marked this conversation as resolved.
Show resolved Hide resolved
def qaoa_feature_encoding_hamiltonian(features, wires):
"""Implements the encoding Hamiltonian of the QAOA embedding.

Args:
features (tensor_like): array of features to encode
wires (Wires): wires that the template acts on
"""
n_features = qml.math.shape(features)[0]

try:
# works for tensors
n_features = features.shape[0]
except AttributeError:
# works for lists and tuples
n_features = len(features)

feature_encoding_wires = wires[:n_features]
remaining_wires = wires[n_features:]

broadcast(unitary=RX, pattern="single", wires=feature_encoding_wires, parameters=features)
broadcast(unitary=Hadamard, pattern="single", wires=remaining_wires)
for i in range(n_features):
mariaschuld marked this conversation as resolved.
Show resolved Hide resolved
qml.RX(features[i], wires=wires[i])
for i in range(n_features, len(wires)):
qml.Hadamard(wires=wires[i])
mariaschuld marked this conversation as resolved.
Show resolved Hide resolved


def qaoa_ising_hamiltonian(weights, wires, local_fields):
Expand All @@ -100,27 +45,23 @@ def qaoa_ising_hamiltonian(weights, wires, local_fields):
"""

if len(wires) == 1:
weights_zz = []
weights_fields = weights
local_fields(weights[0], wires=wires)

elif len(wires) == 2:
# for 2 wires the periodic boundary condition is dropped in broadcast's "ring" pattern
# only feed in 1 parameter
weights_zz = weights[:1]
weights_fields = weights[1:]
# deviation for 2 wires: we do not connect last to first qubit
# with the entangling gates
qml.MultiRZ(weights[0], wires=wires.subset([0, 1]))
local_fields(weights[1], wires=wires[0:1])
local_fields(weights[2], wires=wires[1:2])

else:
weights_zz = weights[: len(wires)]
weights_fields = weights[len(wires) :]

# zz couplings
broadcast(unitary=MultiRZ, pattern="ring", wires=wires, parameters=weights_zz)
# local fields
broadcast(unitary=local_fields, pattern="single", wires=wires, parameters=weights_fields)
for i in range(len(wires)):
qml.MultiRZ(weights[i], wires=wires.subset([i, i + 1], periodic_boundary=True))
for i in range(len(wires)):
local_fields(weights[len(wires) + i], wires=wires[i])


@template
def QAOAEmbedding(features, weights, wires, local_field="Y"):
class QAOAEmbedding(Operation):
r"""
Encodes :math:`N` features into :math:`n>N` qubits, using a layered, trainable quantum
circuit that is inspired by the QAOA ansatz.
Expand Down Expand Up @@ -195,16 +136,16 @@ def circuit(weights, f=None):

print(circuit(weights, f=features))

**Using parameter initialization functions**
**Parameter shape**

The initial weight parameters can alternatively be generated by utility functions from the
``pennylane.init`` module, for example using the function :func:`~.qaoa_embedding_normal`:
The shape of the weights argument can be computed by the static method
:meth:`~.QAOAEmbedding.shape` and used when creating randomly
initialised weight tensors:

.. code-block:: python

from pennylane.init import qaoa_embedding_normal
weights = qaoa_embedding_normal(n_layers=2, n_wires=2, mean=0, std=0.2)

shape = QAOAEmbedding.shape(n_layers=2, n_wires=2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it!

weights = np.random.random(shape)

**Training the embedding**

Expand Down Expand Up @@ -260,22 +201,104 @@ def circuit(weights, f=None):
1-dimensional Ising model.

"""
wires = Wires(wires)
repeat = _preprocess(features, wires, weights)

if local_field == "Z":
local_fields = RZ
elif local_field == "X":
local_fields = RX
elif local_field == "Y":
local_fields = RY
else:
raise ValueError(f"did not recognize local field {local_field}")

for l in range(repeat):
# apply alternating Hamiltonians
qaoa_feature_encoding_hamiltonian(features, wires)
qaoa_ising_hamiltonian(weights[l], wires, local_fields)
num_params = 2
num_wires = AnyWires
par_domain = "A"

def __init__(self, features, weights, wires, local_field="Y", do_queue=True):

if local_field == "Z":
self.local_field = qml.RZ
elif local_field == "X":
self.local_field = qml.RX
elif local_field == "Y":
self.local_field = qml.RY
else:
raise ValueError(f"did not recognize local field {local_field}")

wires = Wires(wires)
self._preprocess(features, weights, wires)
super().__init__(features, weights, wires=wires, do_queue=do_queue)

def expand(self):

features = self.parameters[0]
weights = self.parameters[1]

# first dimension of the weights tensor determines
# the number of layers
repeat = qml.math.shape(weights)[0]

with qml.tape.QuantumTape() as tape:

for l in range(repeat):
mariaschuld marked this conversation as resolved.
Show resolved Hide resolved
# apply alternating Hamiltonians
qaoa_feature_encoding_hamiltonian(features, self.wires)
qaoa_ising_hamiltonian(weights[l], self.wires, self.local_field)

# repeat the feature encoding once more at the end
qaoa_feature_encoding_hamiltonian(features, self.wires)

return tape

@staticmethod
def _preprocess(features, weights, wires):
"""Validate and pre-process inputs as follows:

* Check that the features tensor is one-dimensional.
* Check that the first dimension of the features tensor
has length :math:`n` or less, where :math:`n` is the number of qubits.
* Check that the shape of the weights tensor is correct for the number of qubits.

Args:
features (tensor-like): feature tensor
weights (tensor-like): weight tensor
"""

shape = qml.math.shape(features)

if len(shape) != 1:
raise ValueError(f"Features must be a one-dimensional tensor; got shape {shape}.")

n_features = shape[0]
if n_features > len(wires):
raise ValueError(
f"Features must be of length {len(wires)} or less; got length {n_features}."
)

shape = qml.math.shape(weights)
repeat = shape[0]

if len(wires) == 1:
if shape != (repeat, 1):
raise ValueError(f"Weights tensor must be of shape {(repeat, 1)}; got {shape}")

elif len(wires) == 2:
if shape != (repeat, 3):
raise ValueError(f"Weights tensor must be of shape {(repeat, 3)}; got {shape}")
else:
if shape != (repeat, 2 * len(wires)):
raise ValueError(
f"Weights tensor must be of shape {(repeat, 2*len(wires))}; got {shape}"
)

@staticmethod
mariaschuld marked this conversation as resolved.
Show resolved Hide resolved
def shape(n_layers, n_wires):
r"""Returns the shape of the weight tensor required for this template.

Args:
n_layers (int): number of layers
n_wires (int): number of qubits

Returns:
tuple[int]: shape
"""

if n_wires == 1:
return n_layers, 1

if n_wires == 2:
return n_layers, 3

# repeat the feature encoding once more at the end
qaoa_feature_encoding_hamiltonian(features, wires)
return n_layers, 2 * n_wires
Loading