-
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
Add argument assume_unitary
to Operator.power
.
#13319
Conversation
One or more of the following people are relevant to this code:
|
Pull Request Test Coverage Report for Build 11717584033Details
💛 - Coveralls |
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.
This generally seems sensible to me, thanks for the fix.
A minor question (and I'm fine either way): do you prefer adding a separate power_if_unitary
method, or adding an assume_unitary=False
flag to the existing power
method? Either's fine, I'm just making sure we've thought about the interface we want.
Operator.power
for non-unitary operators.assume_unitary
to Operator.power
.
@jakelishman, thanks for fixing the original issue. Based on review comments, I have added the |
Hmm, I have made a mistake of renaming the release notes file, while keeping the hash, now I have no idea how to make the tests pass. |
Yeah, reno doesn't like it when it can't track what happened to a note. Might be easiest to rebase this on top of |
1c74d06
to
12625b0
Compare
This is approximately a 3x performance improvement over the generalised `fractional_matrix_power` method, but only works for unitary matrices.
12625b0
to
68ad7be
Compare
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.
This looks good thanks. The one thing I just thought of now is that I'm not 100% certain the condition for success is whether the matrix is unitary-like - I think actually the condition is that the matrix is normal (i.e.
Maybe in that sense, the argument should be called assume_normal
?
assume_unitary (bool): if `True`, the operator is assumed to be unitary. In this case, | ||
for fractional powers we employ a faster implementation based on Schur's decomposition. |
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.
assume_unitary (bool): if `True`, the operator is assumed to be unitary. In this case, | |
for fractional powers we employ a faster implementation based on Schur's decomposition. | |
assume_unitary (bool): if ``True``, the operator is assumed to be unitary. In this case, | |
for fractional powers we employ a faster implementation based on Schur's decomposition. |
It might be worth mentioning that the output will just be wrong if this is set True
and the matrix isn't unitary.
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.
A added a note, please see 47f7f90.
) * scipy.linalg.fractional_matrix_power( | ||
cmath.rect(1, -branch_cut_rotation) * self.data, n | ||
) | ||
if not assume_unitary: |
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 this is an if/else
, it might be slightly cleaner to have the positive case (if assume_unitary
) first, so there's no "double negative" implied by the else
.
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.
Done in 47f7f90.
Thanks @jakelishman, I think that you are fully correct that the matrix only needs to be normal for our implementation to work. I feel that the name "assume_normal" might be a bit less intuitive to users than "assume_unitary", so I have kept the name "assume_unitary" but briefly mentioned this in the docstring. I am happy to change it, if you prefer. |
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 agree with @alexanderivrii, I think it's safe to leave assume_unitary
(as it's more restrictive than normal
) and a bit more expected in this context. I don't think many users with non-unitary-normal-operators would be affected by not being fully general in the name (the output would still be correct, just a bit less efficient), and at least it's documented in the docstring. I do have one comment for @alexanderivrii and it's to add a bugfix reno on top of the feature reno mentioning that the issue with non-unitary inputs has been fixed. Other than that LGTM.
Thanks @ElePT. Technically, the bug has been fixed by @jakelishman in #13358 (along with the branch cut improvement), and (I've double-checked) the bugfix note already appears there. |
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.
Ahaa, noted. Then let's go!
Summary
Fixes #13307 following @jakelishman's suggestion in the description.
Details and comments
After a quick round of profiling on my laptop (Python 10, Windows 11), the old (Schur-based) way to raise an operator to a power is 3x-5x faster than calling
scipy.linalg.fractional_matrix_power
, so it makes sense to use it when the operator is known to be unitary.I have renamed the old method to be called
Operator.power_if_unitary
, this assumes that the given operator is unitary. TheOperator.is_power
does not make this assumption (and callsfractional_matrix_power
). The gate class uses thepower_if_unitary
method.P.S. In addition, for the
power
method and an integer power, I don't see much runtime difference when callingfractional_matrix_power
andmatrix_power
. For thepower_if_unitary
method callingmatrix_power
is faster than calling the Schur-based implementation.