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

split_non_commuting transform for expectation values #2587

Merged
merged 42 commits into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
39cfb0b
Allow for non-commuting observables
Qottmann May 17, 2022
11b2d75
added test
Qottmann May 17, 2022
1f1248c
[skip ci] stylistic changes from codefactor
Qottmann May 17, 2022
87441a5
bug fix in reorder_fn and update test accordingly
Qottmann May 17, 2022
65acd1d
moved processing in device to a new function split_non_commuting
Qottmann May 17, 2022
a6155da
removed raise error
Qottmann May 17, 2022
4680949
Update pennylane/transforms/split_non_commuting.py
Qottmann May 18, 2022
ab0d536
Update pennylane/transforms/split_non_commuting.py
Qottmann May 18, 2022
56eb112
Update pennylane/transforms/split_non_commuting.py
Qottmann May 18, 2022
5a35e52
Update pennylane/transforms/split_non_commuting.py
Qottmann May 18, 2022
32cb1fc
Update pennylane/transforms/split_non_commuting.py
Qottmann May 18, 2022
455a3c3
Update pennylane/transforms/split_non_commuting.py
Qottmann May 18, 2022
aa2a9ed
simplify obtaining observable list
Qottmann May 18, 2022
6196afb
simplify obtaining observable list merge
Qottmann May 18, 2022
43660a4
moved tests and added unit tests, missing jax tf etc. tests
Qottmann May 18, 2022
95b92f8
fixed pylint errors
Qottmann May 18, 2022
6c989e0
[ci skip] corrected pylint warnings
Qottmann May 18, 2022
bade50b
[ci skip] added test for different measurement types expval, var, and…
Qottmann May 18, 2022
9f0148e
changed the reorder_fn, now with a permutation matrix, in order to ha…
Qottmann May 18, 2022
5ecdec4
forgot blacking
Qottmann May 18, 2022
9d8914b
refined test for more possibilities of permutations
Qottmann May 18, 2022
3dac740
autodiff tests
Qottmann May 19, 2022
4c9fe12
added probs in obs_fn
Qottmann May 19, 2022
d8a8edc
change jax import in test
Qottmann May 19, 2022
583df7e
[ci skip] remove diff in test_measurements
Qottmann May 19, 2022
66ddc0b
[ci skip] doc string update in test
Qottmann May 19, 2022
945d11c
updated test import formalities
Qottmann May 19, 2022
f80985b
Update pennylane/transforms/split_non_commuting.py
Qottmann May 20, 2022
087afa0
Update pennylane/transforms/split_non_commuting.py
Qottmann May 20, 2022
a6f6e67
Update pennylane/transforms/split_non_commuting.py
Qottmann May 20, 2022
a124f65
Update pennylane/transforms/split_non_commuting.py
Qottmann May 20, 2022
59c207d
Update pennylane/transforms/split_non_commuting.py
Qottmann May 20, 2022
d6426d8
changed autodiff tests to nonzero values and added batch_transform
Qottmann May 20, 2022
599a005
structured tests, added exception for samples and probabilities, adde…
Qottmann May 24, 2022
66790c5
update doc string to qnode usage
Qottmann May 25, 2022
48786c8
checking if sample or probs is part of measurements on the device lev…
Qottmann May 25, 2022
6656ab4
[ci skip] changed return description in doc string for qnode usage
Qottmann May 25, 2022
3da1144
updated transforms __init__.py and modified doc string following josh…
Qottmann May 25, 2022
c9652ee
Merge branch 'master' into expval-with-non-commuting
Qottmann May 26, 2022
3190c8e
Merge branch 'master' into expval-with-non-commuting
josh146 May 27, 2022
6100df4
changed import order for pylint
Qottmann May 27, 2022
0fcc85d
Merge branch 'expval-with-non-commuting' of https://github.com/PennyL…
Qottmann May 27, 2022
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
4 changes: 4 additions & 0 deletions pennylane/_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,10 @@ def batch_transform(self, circuit):
"Can only return the expectation of a single Hamiltonian observable"
) from e

if len(circuit._obs_sharing_wires) > 0 and not hamiltonian_in_obs:
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
# Check for case of non-commuting terms and that there are no Hamiltonians
return qml.transforms.split_non_commuting(circuit)
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

# otherwise, return an identity transform
return [circuit], lambda res: res[0]

Expand Down
2 changes: 2 additions & 0 deletions pennylane/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@

~transforms.measurement_grouping
~transforms.hamiltonian_expand
~transforms.split_non_commuting

Decorators and utility functions
--------------------------------
Expand Down Expand Up @@ -186,6 +187,7 @@
from .decompositions import zyz_decomposition, two_qubit_decomposition
from .defer_measurements import defer_measurements
from .hamiltonian_expand import hamiltonian_expand
from .split_non_commuting import split_non_commuting
from .measurement_grouping import measurement_grouping
from .metric_tensor import metric_tensor
from .adjoint_metric_tensor import adjoint_metric_tensor
Expand Down
115 changes: 115 additions & 0 deletions pennylane/transforms/split_non_commuting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# 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.
"""
Contains the tape transform that splits non-commuting terms
"""
# pylint: disable=protected-access
import pennylane as qml


Qottmann marked this conversation as resolved.
Show resolved Hide resolved
def split_non_commuting(tape):
r"""
Splits a tape measuring non-commuting observables into groups of commuting observables.

Args:
tape (.QuantumTape): the tape used when calculating the expectation value
of the Hamiltonian

Returns:
tuple[list[.QuantumTape], function]: Returns a tuple containing a list of
quantum tapes to be evaluated, and a function to be applied to these
tape executions to restore the ordering of the inputs.

**Example**

We can create a tape with non-commuting observables
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

.. code-block:: python3

import pennylane as qml
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
with qml.tape.QuantumTape() as tape:
qml.expval(qml.PauliZ(0))
qml.expval(qml.PauliY(0))
tapes, processing_fn = qml.transforms.split_non_commuting(tape)
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

Now ``tapes`` is a list of two tapes, each for one of the non-commuting terms.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

>>> [t.observables for t in tapes]
[[expval(PauliZ(wires=[0]))], [expval(PauliY(wires=[0]))]]

The processing function becomes important when creating the commuting groups distorts the orde of the inputs.
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

.. code-block:: python3

with qml.tape.QuantumTape() as tape:
qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
qml.expval(qml.PauliX(0) @ qml.PauliX(1))
qml.expval(qml.PauliZ(0))
qml.expval(qml.PauliX(0))
tapes, processing_fn = qml.transforms.split_non_commuting(tape)
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

In this example, the groupings are ``group_coeffs = [[0,2], [1,3]]`` and processing_fn makes sure that the final output is of the same shape and ordering:
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

>>> processing_fn(tapes)
tensor([tensor(expval(PauliZ(wires=[0]) @ PauliZ(wires=[1])), dtype=object, requires_grad=True),
tensor(expval(PauliX(wires=[0]) @ PauliX(wires=[1])), dtype=object, requires_grad=True),
tensor(expval(PauliZ(wires=[0])), dtype=object, requires_grad=True),
tensor(expval(PauliX(wires=[0])), dtype=object, requires_grad=True)],
dtype=object, requires_grad=True)
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

"""

measurements = tape.measurements

obs_list = []
# get the observables from the measurements
# TO DO: This loop should become superfluous when the reworked operator classes are implemented
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
for measurement in measurements:
if hasattr(measurement.obs, "obs"):
# this loop re-creates multi-qubit observables, e.g. PauliZ(0) @ PauliZ(1)
_list = (
measurement.obs.obs
) # the list of individual observables before composition
obs = _list[0]
for ob_i in _list[1:]:
obs @= ob_i # I am sure there must be a better way to do this?
else:
obs = measurement.obs
obs_list.append(obs)
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

# If there is more than one group of commuting observables, split tapes
groups, group_coeffs = qml.grouping.group_observables(obs_list, range(len(obs_list)))
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
if len(groups) > 1:
# make one tape per commuting group
tapes = []
for group in groups:
with tape.__class__() as new_tape:
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
[op.queue() for op in tape.operations]
[qml.expval(o) for o in group]
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

tapes.append(new_tape)

def reorder_fn(res):
"""re-order the output to the original shape and order"""
res = qml.math.concatenate(res)
new_res = res.copy() # to keep the same format as res
reorder_indxs = qml.math.concatenate(group_coeffs)
for i,out in zip(reorder_indxs, res):
new_res[i] = out
Qottmann marked this conversation as resolved.
Show resolved Hide resolved

return new_res

return tapes, reorder_fn
# if the group is already commuting, no need to do anything
Qottmann marked this conversation as resolved.
Show resolved Hide resolved
return [tapes], lambda res: res[0]
19 changes: 19 additions & 0 deletions tests/test_measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,25 @@ def test_ObservableReturnTypes(return_type, value):
assert isinstance(return_type, qml.measurements.ObservableReturnTypes)
assert repr(return_type) == value

def test_expval_non_commuting_observables():
"""Test expval with multiple non-commuting operators """
dev = qml.device("default.qubit", wires=5)

@qml.qnode(dev)
def circuit():
qml.Hadamard(1)
qml.Hadamard(0)
qml.PauliZ(0)
qml.Hadamard(3)
return [qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)),
qml.expval(qml.PauliX(0)),
qml.expval(qml.PauliZ(1)),
qml.expval(qml.PauliX(1) @ qml.PauliX(4)),
qml.expval(qml.PauliX(3))]

assert all(np.isclose(circuit(),np.array([0., -1., 0., 0., 1.])))
Qottmann marked this conversation as resolved.
Show resolved Hide resolved



def test_no_measure(tol):
"""Test that failing to specify a measurement
Expand Down