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

Fix call signature on Gate to show that it takes Qids. #5235

Merged
merged 9 commits into from
Apr 27, 2022

Conversation

dabacon
Copy link
Collaborator

@dabacon dabacon commented Apr 8, 2022

This makes the API documentation for our gate constants better. Not sure why it trips the type notation.

image

Fixes #5232

@dabacon dabacon requested review from a team, vtomole and cduck as code owners April 8, 2022 19:13
@dabacon dabacon requested a review from maffoo April 8, 2022 19:13
@CirqBot CirqBot added the Size: XS <10 lines changed label Apr 8, 2022
def __call__(self, *args, **kwargs):
return self.on(*args, **kwargs)
def __call__(self, *qubits: Qid, **kwargs):
return self.on(*qubits)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also need to pass the **kwargs to self.on() call, so that cirq.CNOT(control=a, target=b) works ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cirq.Gate class defines the on method as:

def on(self, *qubits: Qid) -> 'Operation':
    ...

whereas it's derived class, CXPowGate, overrides the on method as

def on(self, *args: 'cirq.Qid', **kwargs: 'cirq.Qid') -> raw_types.Operation:
    ...

Technically, this does satisfy the liskov substitution principle because the derived class is implementing a more generic method (that accepts both *args and **kwargs) compared to the base class (that can accept only *args).
I think your current solution, to not specify type of **kwargs in __call__ and silently forwarding it to self.on(), should work.

One other way could be to enforce that the cirq.Gate's __call__ method will only accept *qubits and whenever a derived class, like CXPowGate, overrides the on method to be more generic, it should also override the __call__ method? But that would be a huge breaking change for all existing implementations, like CXPowGate, and something we probably don't want to do at this point.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes sorry thought I had fixed this. This is what happens when check/all doesn't run tests in parallel so I just push it out without running the tests (I can haz review for #5006 ?)

@tanujkhattar tanujkhattar self-assigned this Apr 8, 2022
@CirqBot CirqBot added the size: S 10< lines changed <50 label Apr 8, 2022
@@ -211,11 +211,13 @@ def validate_args(self, qubits: Sequence['cirq.Qid']) -> None:
"""
_validate_qid_shape(self, qubits)

def on(self, *qubits: Qid) -> 'Operation':
def on(self, *qubits: Qid, **kwargs) -> 'Operation':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change because all user defined classes which override the on method as def on(self, *qubits: Qid) will now not satisfy LSP and would need to be updated?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah agree this is less than happy, let me see if I can work around the other issue (we currently call on in call with the wrong signature and get away with it)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it wasn't an issue earlier because mypy was not type-checking the method at all, due to no type annotations. But now that's not the case and hence it raises an error.

We can also decide to make __call__ a more restrictive type on the base class, so if user overrides on, they must also override __call__ -- but that would also be a breaking change.

Maybe @maffoo knows some mypy magic to get around the situation without having to deal with breaking changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe @maffoo knows some mypy magic to get around the situation without having to deal with breaking changes.

I got nothin', sorry :-)

@dabacon
Copy link
Collaborator Author

dabacon commented Apr 8, 2022

@tanujkhattar PTAL

I think this is the minimal change. It leaves the signatures the same, and to get around the CXPow gate it adds an explicit override for the __call__, which is not horrible. There is also a change necessary in GateOperation repr for an assert, but I think that assert is way too defensive.

@tanujkhattar
Copy link
Collaborator

So, what we have now is also a breaking change because every user defined class which was dependant on Gate.__call__ to forward the **kwargs to it's on method (like CXPowGate) will now break. We would ideally need to go through a deprecation cycle to make this change to avoid breaking user code directly.

@dabacon
Copy link
Collaborator Author

dabacon commented Apr 11, 2022

So, what we have now is also a breaking change because every user defined class which was dependant on Gate.__call__ to forward the **kwargs to it's on method (like CXPowGate) will now break. We would ideally need to go through a deprecation cycle to make this change to avoid breaking user code directly.

Hm, not sure I agree about that. This doesn't break any public interface, and if someone chose to follow the pattern of CXPowGate, they would be doing so in a manner that isn't really explicitly supported by any documented interface on Cirq's end.

Copy link
Collaborator

@tanujkhattar tanujkhattar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I don't have very strong opinions here.

cc @MichaelBroughton and @95-martin-orion Do you have strong opinions on silently changing the behavior of a public interface and whether this should be a breaking change or not? We can merge if other maintainers don't have concerns.

Copy link
Collaborator

@95-martin-orion 95-martin-orion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, all interfaces remain the same (modulo better type annotation) and internal refactors have the same end behavior.

@MichaelBroughton
Copy link
Collaborator

I'm not a huge fan of this change solely in the name of getting better type hints (which has no functional impact by itself). There's also no super clear guidance on type hinting for *args and **kwargs. If it's just for docs, what if we just added type hints and then immediately washed them out with ignore comments ? That would get us the working docs (hopefully) and keep existing behavior.

@dabacon
Copy link
Collaborator Author

dabacon commented Apr 11, 2022

I'm not a huge fan of this change solely in the name of getting better type hints (which has no functional impact by itself). There's also no super clear guidance on type hinting for *args and **kwargs. If it's just for docs, what if we just added type hints and then immediately washed them out with ignore comments ? That would get us the working docs (hopefully) and keep existing behavior.

I'm not sure I follow. Why would we ignore the type hints?

@MichaelBroughton
Copy link
Collaborator

So that we could fix the API docs issue without having to introduce any code changes until we can come up with a fix everyone was on board with. Maybe I'm missing something here, is this a docs motivated PR or do we want the code fix itself in this PR too ?

@dabacon
Copy link
Collaborator Author

dabacon commented Apr 16, 2022

So that we could fix the API docs issue without having to introduce any code changes until we can come up with a fix everyone was on board with. Maybe I'm missing something here, is this a docs motivated PR or do we want the code fix itself in this PR too ?

The primary goal is to make sure all the gate constants have a reasonable doc string which shows they can be called on qubits. The type gets dropped by our beloved tensorflow doc overlords :)

This doesn't end up changing any interface and really just changes where passing in kwargs fails for on and __call__. Prior to this it will fail with a python error about on not supporting this if you call it on on or __call__. After this change it will fail by yelling about on when you call on and __call__ when you call _call__. So for tracing problems, it is actually slightly better. For overloading these methods if you want to accept kwargs, you will have to overload both now (this is what is done in the CX case). This reads somewhat more self contained, but is slightly worse from a code reuse perspective.

Really there was a bug as they should have been consistent with each other but fixing that is a breaking change.

@dabacon
Copy link
Collaborator Author

dabacon commented Apr 27, 2022

@MichaelBroughton let me know if you are OK with this, if not, also great and I will close this :)

@MichaelBroughton MichaelBroughton added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Apr 27, 2022
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Apr 27, 2022
@CirqBot CirqBot merged commit cdabadf into quantumlib:master Apr 27, 2022
@CirqBot CirqBot removed automerge Tells CirqBot to sync and merge this PR. (If it's running.) front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. labels Apr 27, 2022
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
size: S 10< lines changed <50 Size: XS <10 lines changed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Documentation on gate constants should show that it is called on Qids
6 participants