-
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
Circuit cutting: add mid-circuit measurement integration tests #2234
Conversation
Hello. You may have forgotten to update the changelog!
|
copy_ops = [copy.copy(op) for _, op in ordered_ops if not isinstance(op, MeasurementProcess)] | ||
copy_meas = [copy.copy(op) for _, op in ordered_ops if isinstance(op, MeasurementProcess)] |
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.
Previously we were treating the ops (gates) and measurements together. However, when isinstance(op, MeasurementProcess)
, then setting op._wires = ...
does not work when the MeasurementProcess
is an expectation value of an observable, because the measurement process uses the contained observable to determine op.wires
.
In this updated version, we treat the MeasurementProcesses
more carefully after applying the gates.
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!
I'm curious what where the cases that revealed this? Mid circuit measurements and tensor products of obs in measurements?
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.
Right, previously if your observable (or part of it) was on wire i
, but wire i
had mid-circuit measurements and was relabelled to wire i'
, then the observable wasn't getting its wire updated from wire i
to i'
.
pennylane/transforms/qcut.py
Outdated
@@ -424,7 +441,7 @@ def _get_measurements( | |||
|
|||
obs = measurement.obs | |||
|
|||
return [expval(obs @ g) for g in group] | |||
return [expval(copy.deepcopy(obs) @ g) for g in group] |
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 am very confused by this. the tensor product works in-place 🤔 Perhaps it is a queuing-related quirk.
obs = qml.PauliZ(0) @ qml.PauliZ(1)
obs2 = obs @ qml.PauliZ(2)
assert obs == obs2
print(obs)
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 see, so the original obs
is being overwritten in when we take a tensor product of it with another tensor. This is weird!
But deepcopy
seems to solve for our needs, or is there potential for more problems with this?
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.
Also works with copy.copy
so I changed to that. I think copy.copy
is a sufficient workaround for now, but long run it'd be better for observables not to be altered when taking the tensor product. Issue posted: #2235
Codecov Report
@@ Coverage Diff @@
## master #2234 +/- ##
=======================================
Coverage 99.26% 99.27%
=======================================
Files 231 231
Lines 18349 18359 +10
=======================================
+ Hits 18215 18225 +10
Misses 134 134
Continue to review full report at Codecov.
|
[sc-14724] |
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 spots here! The pipeline is becoming robust 🦾
Would be interested to find out whats going on with the tensor product quirk you've found 🤔
qml.CNOT(wires=[0, 1]) | ||
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) | ||
|
||
spy = mocker.spy(qcut, "qcut_processing_fn") |
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.
perhaps it would be more appropriate to spy on _get_measurements
in this test
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.
Happy to change, but why do you think _get_measurements
is more appropriate?
pennylane/transforms/qcut.py
Outdated
@@ -424,7 +441,7 @@ def _get_measurements( | |||
|
|||
obs = measurement.obs | |||
|
|||
return [expval(obs @ g) for g in group] | |||
return [expval(copy.deepcopy(obs) @ g) for g in group] |
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 see, so the original obs
is being overwritten in when we take a tensor product of it with another tensor. This is weird!
But deepcopy
seems to solve for our needs, or is there potential for more problems with this?
copy_ops = [copy.copy(op) for _, op in ordered_ops if not isinstance(op, MeasurementProcess)] | ||
copy_meas = [copy.copy(op) for _, op in ordered_ops if isinstance(op, MeasurementProcess)] |
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!
I'm curious what where the cases that revealed this? Mid circuit measurements and tensor products of obs in measurements?
… qcut_integ_tests
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 @anthayes92!
copy_ops = [copy.copy(op) for _, op in ordered_ops if not isinstance(op, MeasurementProcess)] | ||
copy_meas = [copy.copy(op) for _, op in ordered_ops if isinstance(op, MeasurementProcess)] |
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.
Right, previously if your observable (or part of it) was on wire i
, but wire i
had mid-circuit measurements and was relabelled to wire i'
, then the observable wasn't getting its wire updated from wire i
to i'
.
pennylane/transforms/qcut.py
Outdated
@@ -424,7 +441,7 @@ def _get_measurements( | |||
|
|||
obs = measurement.obs | |||
|
|||
return [expval(obs @ g) for g in group] | |||
return [expval(copy.deepcopy(obs) @ g) for g in group] |
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.
Also works with copy.copy
so I changed to that. I think copy.copy
is a sufficient workaround for now, but long run it'd be better for observables not to be altered when taking the tensor product. Issue posted: #2235
qml.CNOT(wires=[0, 1]) | ||
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) | ||
|
||
spy = mocker.spy(qcut, "qcut_processing_fn") |
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.
Happy to change, but why do you think _get_measurements
is more appropriate?
Context:
Adds tests for the
cut_circuit
transform that ensures circuits resulting in fragments that contain mid-circuit measurements are supported.