-
Notifications
You must be signed in to change notification settings - Fork 586
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
Conversation
Hello. You may have forgotten to update the changelog!
|
qml.expval()
with non-commuting observables that are not Hamiltonians
Codecov Report
@@ Coverage Diff @@
## master #2587 +/- ##
=======================================
Coverage 99.59% 99.59%
=======================================
Files 243 244 +1
Lines 19633 19667 +34
=======================================
+ Hits 19553 19587 +34
Misses 80 80
Continue to review full report at Codecov.
|
I just realized that the failure from the local tests that I ran are likely due to some dependency problems in my pennylane environment as the tests seem to pass here. I will then continue and add new tests with non-commuting lists of observables. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend moving all of this into its own function and then calling that function here.
How about in a new file pennylane/transforms/split_noncommuting_observables.py
or something like that. The name needs a little work.
Devices are already highly bloated classes responsible for too many different things. What we want to do is create a self-contained black box that solves a problem. We abstract away the implementation details from places that don't need to know the implementation details.
This will improve readability in _device.py
and allow us to repurpose the same function later on as other things change. When we rewrite how we do devices, we'd still like to be able to use the same function, even if we use it in a different place.
If you notice in your chunk of code, it doesn't call any other device methods. That's a good sign it can be extracted somewhere else.
Small independent functions are also much easier to unit test.
Noticed a bug: 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 Further, this does not work with a mixture of Hamiltonians and regular observables. Since everything that is multiplied by a number becomes a Hamiltonian, this might be a bit restrictive in use cases for this addition. |
Thank you @albi3ro for the feedback, I now moved everything to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work @Qottmann! I've given it a quick first pass :)
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
…ve autograd working
Ready for review @josh146 @albi3ro Main update is that now it includes tests for different interfaces (jax-jit cannot handle multiple return values in qfuncs atm). Do you know how to get rid of the codefactor error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @Qottmann! This is looking very close, all my suggestions were minor. I only had two major ones:
-
(Optional) turn this into a batch transform, so people can use it on QNodes :)
-
Double check the gradient tests regarding the zero Jacobians 🤔
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
Co-authored-by: Josh Izaac <josh146@gmail.com>
…el to accomodate some jax tests
Even though I moved the entry in the doc string of I am not 100% sure how the doc-string should be written now that it is a batch_transform. I tried to cover both in the input and return descriptions, but focus on QNodes in the example and put the tape examples into usage details. Other than that I think it is good to go. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work @Qottmann! I read through the docstring, and it is very impressive - you did a good job describing both the functional (QNode) and object oriented (tape) user-interface 💪
[sc-17239] |
qml.expval()
with non-commuting observables that are not Hamiltonianssplit_non_commuting
transform for expectation values
split_non_commuting
transform for expectation valuessplit_non_commuting
transform for expectation values
Context:
Currently,
qml.expval()
does not allow for lists of operators that are not commuting. I.e. the following codeproduces an error message as the observables do not commute.
Description of the Change:
The idea is to split the tape into groups of commuting observables, compute those, and recombine them.
We want to do this in the device class on the call of batch_transform() in a similar way as Hamiltonians are expanded.
For this we first need the list of observables, which in the current state of the operator and observable classes is a bit cumbersome. This should be easier once those classes are updated.
Now this list of observables is grouped into commuting groups using
qml.groupings.group_observables()
. We keep track of the indices by also passing a list of dummy coefficients.Then a new tape for each of those groups is generated and passed. In the end we recombine all results into a single list with the same order as the observables have been initially given.
Re-structuring after grouping
We want the user not to know that this grouping has taken place internally and output the same shape and order as the input of expectation values. For this we need to pass a non-trivial classical processing function. I decided to go with the following:
res
is a list of results for each commuting group. So the first step, concatenating those results produces a list of the original size. The second step is re-ordering the results according to the original input. An example for the following circuitHere the we would internally have
res = [tensor([1., 1., 0.], requires_grad=True), tensor([0., 0.], requires_grad=True)]
andgroup_coeffs = [array([0, 2, 4]), array([1, 3])]
. Soreorder_fn
computesStatus at initial WIP PR
With the current changes, cases like the following work:
However, some of the tests in the test suite still fail, so I need to figure out why that is and make changes accordingly.
Possible Drawbacks:
Statevector devies can compute non-commuting observables straight away so they should be treated as a special case for speed ups in a separate PR.
Related GitHub Issues: