Skip to content
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

Merged
merged 13 commits into from
Oct 8, 2024

Conversation

ShellyGarion
Copy link
Member

Summary

Close #13157

Details and comments

@ShellyGarion ShellyGarion added the Rust This PR or issue is related to Rust code in the repository label Sep 17, 2024
@ShellyGarion ShellyGarion added this to the 1.3.0 milestone Sep 17, 2024
@ShellyGarion ShellyGarion requested a review from a team as a code owner September 17, 2024 14:08
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core
  • @kevinhartman
  • @mtreinish

@ShellyGarion
Copy link
Member Author

ShellyGarion commented Sep 18, 2024

The following gates do not have an inverse which is a StandardGate:

  • DCXGate
  • ISwapGate
  • CSXGate
  • RCCXGate
  • C3SXGate
  • RC3XGate

@coveralls
Copy link

coveralls commented Sep 18, 2024

Pull Request Test Coverage Report for Build 11236384577

Warning: 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

  • 151 of 164 (92.07%) changed or added relevant lines in 1 file are covered.
  • 996 unchanged lines in 33 files lost coverage.
  • Overall coverage increased (+0.09%) to 88.871%

Changes Missing Coverage Covered Lines Changed/Added Lines %
crates/circuit/src/operations.rs 151 164 92.07%
Files with Coverage Reduction New Missed Lines %
qiskit/transpiler/passes/layout/disjoint_utils.py 1 96.83%
qiskit/transpiler/passes/optimization/elide_permutations.py 1 96.0%
qiskit/circuit/library/quantum_volume.py 1 97.44%
crates/accelerate/src/split_2q_unitaries.rs 1 96.0%
crates/accelerate/src/two_qubit_decompose.rs 1 91.45%
qiskit/transpiler/passes/synthesis/unitary_synthesis.py 2 88.26%
qiskit/transpiler/passes/optimization/collect_cliffords.py 2 92.59%
crates/circuit/src/imports.rs 2 77.78%
qiskit/circuit/library/standard_gates/u3.py 2 97.03%
qiskit/circuit/library/blueprintcircuit.py 4 96.46%
Totals Coverage Status
Change from base Build 11016050512: 0.09%
Covered Lines: 74746
Relevant Lines: 84106

💛 - Coveralls

@ShellyGarion ShellyGarion changed the title [WIP] Add inverse function to Operation in rust Add inverse function to Operation in rust Sep 18, 2024
Copy link
Member

@mtreinish mtreinish left a 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.

Comment on lines 2498 to 2500
fn inverse(&self, _params: &[Param]) -> Option<(StandardGate, Vec<Param>)> {
None
}
Copy link
Member

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?

Copy link
Member Author

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.

Copy link
Member Author

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

Copy link
Member Author

@ShellyGarion ShellyGarion Sep 22, 2024

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?

Copy link
Member Author

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 StandardGates (at least for most of them whose inverse is also a StandardGate)

crates/circuit/src/operations.rs Outdated Show resolved Hide resolved
@@ -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>)>;
Copy link
Member

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:

Suggested change
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.

Copy link
Member Author

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

Copy link
Member

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 StandardGates 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?

Copy link
Member Author

@ShellyGarion ShellyGarion Sep 23, 2024

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)

Copy link
Member Author

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![])),
        ...

Copy link
Member Author

@ShellyGarion ShellyGarion Sep 23, 2024

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.

Self::C3SXGate => None, // the inverse in not a StandardGate
Self::RC3XGate => None, // the inverse in not a StandardGate
}
}
}
Copy link
Member

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).

Copy link
Member Author

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

@mtreinish mtreinish added the Changelog: None Do not include in changelog label Oct 1, 2024
Copy link
Member

@mtreinish mtreinish left a 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).

test/python/circuit/test_rust_equivalence.py Outdated Show resolved Hide resolved
crates/circuit/src/operations.rs Outdated Show resolved Hide resolved
@ShellyGarion ShellyGarion changed the title Add inverse function to Operation in rust Add inverse function to StandardGate in rust Oct 8, 2024
Copy link
Member

@mtreinish mtreinish left a 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.

@mtreinish mtreinish added this pull request to the merge queue Oct 8, 2024
Merged via the queue into Qiskit:main with commit 90e92a4 Oct 8, 2024
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: None Do not include in changelog Rust This PR or issue is related to Rust code in the repository
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add inverse function to StandardGate in rust
5 participants