-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Fix setter so that SparsePauliOp.paulis.phase
stays zero
#12884
Conversation
Pull Request Test Coverage Report for Build 10389703463Details
💛 - Coveralls |
Trying to decide between a few approaches. Approaches I'm leaning away from:
Proposed new approach is to mutate the input and raise a warning:
I think that Alternatively we could raise an error when I don't have experience making this kind of call in a large codebase, so am very open to suggestions, but will try the warning for now. |
Thank you for opening a new pull request. Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient. While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone. One or more of the following people are relevant to this code:
|
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 fix! Just two small comments below.
@@ -256,6 +257,14 @@ def paulis(self, value): | |||
raise ValueError( | |||
f"incorrect number of operators: expected {len(self.paulis)}, got {len(value)}" | |||
) | |||
if np.any(value.phase): |
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.
Since the initializer also silently maps the phase to the coefficients, I think we don't need a warning here, as this is the standard behavior. Rather, the setter was inconsistent with the initializer 🙂
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.
Got it, thanks for the feedback on this.
I'm still a little concerned users may get burned if they try to use the input PauliList
for other stuff after the assignment, like
spo_storing_interim_result.paulis = paulis_with_phase
final_result = some_function_that_depends_critically_on_phase(paulis_with_phase)
but agree that 1) this PR is consistent with the initializer, 2) the above example is a somewhat unusual code structure, 3) as Jake mentioned on the issue page, this PR is kind of a band-aid since really SparsePauliOp
should not have a phase attribute at all.
I'll go ahead and remove the warning per your suggestion.
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.
Andrew: I'd suggest that that code example is a problem outside SparsePauliOp
- after the line spo_storing_interim_result.paulis = paulis_with_phase
, it's not valid to make any assumptions about the content of paulis_with_phase
, because the SparsePauliOp
owns the object. I think in general, users can hopefully either re-order the functions so that they still haven't given away ownership of the object while they're using it, or they explicitly copy it if they're still using it.
Thanks for thinking about this, though - it's good to have performance and/or mutability concerns in mind when dealing with Python.
@@ -1097,6 +1097,21 @@ def test_paulis_setter_rejects_bad_inputs(self): | |||
with self.assertRaisesRegex(ValueError, "incorrect number of operators"): | |||
op.paulis = PauliList([Pauli("XY"), Pauli("ZX"), Pauli("YZ")]) | |||
|
|||
def test_paulis_setter_absorbs_phase(self): |
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.
Could you also add a test to check the simplify()
behavior you described in your original issue?
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.
Added.
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 for the fix! 🙂
Summary
Goal is to fix #12883.
Currently several
SPO
methods assume thatSPO.paulis.phase
is all zero. However nonzero phase is allowed by thepaulis()
setter, leading to incorrect results when those other methods are called.Details and comments
In this small PR we update the
paulis()
setter to absorb the phase into the SPOcoeffs
.