-
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 inverse function to StandardGate in rust #13168
Conversation
One or more of the following people are relevant to this code:
|
The following gates do not have an inverse which is a
|
Pull Request Test Coverage Report for Build 11236384577Warning: This coverage report may be inaccurate.This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.
Details
💛 - 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.
Thanks for getting this started it looking good. I have a couple of inline comments and questions to start.
crates/circuit/src/operations.rs
Outdated
fn inverse(&self, _params: &[Param]) -> Option<(StandardGate, Vec<Param>)> { | ||
None | ||
} |
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.
Is there ever a case where a Python Operation
will have an inverse defined? I know it's not part of the abstract interface, but I'm wondering if we should try and fallback to None
? @alexanderivrii maybe you have some thoughts here?
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.
Clifford
(which is an Operation
) does not have an inverse
only adjoint
.
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.
But there are many generalized gates (like PermutationGate
) that have an inverse
method
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.
how would you suggest to change the code here?
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.
Following the discussion below, I think for now the main use-case is to add an inverse
only for the StandardGate
s (at least for most of them whose inverse is also a StandardGate
)
crates/circuit/src/operations.rs
Outdated
@@ -167,6 +167,7 @@ pub trait Operation { | |||
fn definition(&self, params: &[Param]) -> Option<CircuitData>; | |||
fn standard_gate(&self) -> Option<StandardGate>; | |||
fn directive(&self) -> bool; | |||
fn inverse(&self, params: &[Param]) -> Option<(StandardGate, Vec<Param>)>; |
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.
Does this work in the case of PyGate
? I don't think there is a guarantee that a Python defined gate's inverse will be a StandardGate
. I actually think it is pretty unlikely in practice, since if a custom gate were expressible as the inverse of a StandardGate
it'd probably just be a StandardGate
. I think you can just do:
fn inverse(&self, params: &[Param]) -> Option<(StandardGate, Vec<Param>)>; | |
fn inverse(&self, params: &[Param]) -> Option<(Self, Vec<Param>)>; |
then for impl Operation for StandardGate
.inverse()
returns a StandardGate
and for impl Operator for PyGate
.inverse()
returns a PyGate
.
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.
In this case, it's unclear to me which type to put in line 279. Any type I try gives a mismatched type 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.
We probably can't have Operation::inverse
return anything referring to Self
, because that's unimplementable by the implementors of Operation
that don't have an owned type (e.g. OperationRef
). Even within that, it's maybe suboptimal because not all StandardGate
s would be able to return an inverse, even though they can be inverted (e.g. DCX
).
The most general would potentially be to have Operation::inverse
return some iterator type over (potentially) PackedInstruction
(like CircuitData
), but in practice that's probably not super performant for many simple cases. Maybe in the near term, it's enough just to have StandardGate::try_inverse
on StandardGate
only?
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.
There is a problem in implementing inverse
in impl<'a> Operation for OperationRef<'a>
(line 279) that cause a mismatched type error. After talking with @jakelishman it seems that the best solution would be to implement inverse
only for StandadGate
(maybe also for PyGate
)
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'm a bit confused now. I can add inverse
as a stand-alone function (only for StandardGate
) - but then how can I test it from Python ?
pub fn inverse(gate:StandardGate, params: &[Param]) -> Option<(StandardGate, SmallVec<[Param; 3]>)> {
match gate {
StandardGate::HGate => Some((StandardGate::HGate, smallvec![])),
StandardGate::IGate => Some((StandardGate::IGate, smallvec![])),
...
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.
So I only replaced the function name inverse
by try_inverse
and the function for PyGate
will return None
.
crates/circuit/src/operations.rs
Outdated
Self::C3SXGate => None, // the inverse in not a StandardGate | ||
Self::RC3XGate => None, // the inverse in not a StandardGate | ||
} | ||
} | ||
} |
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.
Did you want to add a rust space test asserting that all the StandardGates multiplied with their inverse were the identity? It would be good to get in the practice of having more rust space tests, but since we have coverage from python I don't think it's strictly necessary.
@kevinhartman has been working on demonstrating a pattern of using the py
token from rust space tests: #13169 (since we still need it for multiply_param
even if there isn't a parameter expression).
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 added a test in test_rust_equivalence
that asserts that the output of the inverse in Rust (when not None) is the same as the output of the inverse in Python
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 know that the current state of the API in this PR was based on previous review suggestions, but I have some concerns about the naming and the exact interface here. I think the goal of making an inverse()
method on the operation trait makes a lot of sense, but I have concerns about the current api being called try_inverse
and it returning an Option
and then never working for anything besides a StandardGate
or a OperationRef
to a StandardGate
. I think if we are going to add this to the Operation
trait we should attempt to support all the implementations out there. The only way that makes sense I guess is a Box<dyn Operation>
or something similar for the return so that we can dynamically support whatever the inverse implementation returns. I"m not advocating for this though as that adds a layer of pointer indirection for what your immediate use case with StandardGate
which is less than desirable.
All of this is to say I think to start instead of adding this to Operation
we should just reduce the scope of this PR and define inverse()
as a method for StandardGate
for now. We can debate what an appropriate interface is for the Operation
trait in a separate issue or PR that's not blocking work on something else and then just pivot the StandardGate
method to follow that interface (or have the trait method call the StandardGate
method inside of it).
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 quick update. This LGTM now with the reduced scope of the new API. I think we can discuss how best to add an inverse method to the wider Operation
trait in a follow up. Thinking about how we make the trait's method work will be especially important as we add more types implementing the trait as part of #13264.
Summary
Close #13157
Details and comments