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 commutes protocol #1853

Merged
merged 37 commits into from
Dec 17, 2019
Merged

Add commutes protocol #1853

merged 37 commits into from
Dec 17, 2019

Conversation

bryano
Copy link
Collaborator

@bryano bryano commented Jul 19, 2019

Minimal version so far. Wanted to get some feedback.

Some thoughts:

  • A lot of issues seemed to be caused by values_equality being the name of both a module and a method, so we should probably change at least one of those for commutes.
  • Maybe _commutes_with_ for the magic method? (I just went with convention in the initial issue for now.)
  • I changed cirq.commutes to be the protocol, which delegates to cirq.linalg.commutes in the relevant cases. Shouldn't be a problem, but may cause confusion.
  • Some classes already define a commute_with method. Should I add some underscores to that or add a new method that delegates to the existing one?
  • If passed a Gate and GateOperation whose gate commutes with the Gate, what should be returned?

Fixes #1125

@googlebot googlebot added the cla: yes Makes googlebot stop complaining. label Jul 19, 2019
@bryano bryano requested a review from mpharrigan July 19, 2019 21:13
return NotImplemented


def _strat_commutes_from_matrix(left_val: Any, right_val: Any, atol: Union[int, float] = 1e-8
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't appear to take into consideration the possibility that left_val and right_val are operations that act on different qubits.

Copy link
Collaborator

Choose a reason for hiding this comment

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

As an extension to this train of thought, I think that if one is a Gate_Operation and the other is a Gate then the protocol should treat it as a strictest case scenario where the Gate acts on the same qubits as the Gate_Operation.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The more I think about it, the less I think this general strategy is worth it. Makes more sense to have all the logic in the _commutes_with_ method of the various objects. For example, qubit order using the unitary protocol is tricky with circuits.

) -> Union[NotImplementedType, bool]:
"""Attempts to determine commutativity via the objects' _commutes_ method."""
for a, b in [(left_val, right_val), (right_val, left_val)]:
getter = getattr(a, '_commutes_', None)
Copy link
Collaborator

Choose a reason for hiding this comment

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

+1 on changing to _commutes_with_ and refactoring classes that already have this semantics.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


This is determined by any one of the following techniques:

- Either value has a `_commutes_` method that returns something besides
Copy link
Collaborator

Choose a reason for hiding this comment

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

note that left_val is considered first

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done


- Either value has a `_commutes_` method that returns something besides
NotImplemented. The return value is whatever the method returned.
- Each value is either a matrix or defines one via the unitary protocol.
Copy link
Collaborator

Choose a reason for hiding this comment

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

split this into two cases? 1. is a matrix 2. defines one via unitary protocol

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Obsoleted (delegation to unitary moved to Gate._commutes_with_)

This is determined by any one of the following techniques:

- Either value has a `_commutes_` method that returns something besides
NotImplemented. The return value is whatever the method returned.
Copy link
Collaborator

Choose a reason for hiding this comment

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

"whatever the method returned" -> "the boolean result from this function" or something to emphasize that you can't return whatever you want in _commutes_

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done

result = strat(left_val, right_val, atol)
if result is not NotImplemented:
return result
return False
Copy link
Collaborator

Choose a reason for hiding this comment

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

is this a sensible default? Why not raise a NotImplementedError or return NotImplemented all the way up to the caller? cirq.unitary doesn't return the identity matrix if it can't figure out the unitary representation of an object :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I imagine this being used for circuit reordering, where what you want to know whether two things definitely commute. (Note that this is the documented behavior.)

Do you have an example where false negatives would be problematic?

getter = getattr(a, '_commutes_', None)
if getter is None:
continue
val = getter(right_val, atol)
Copy link
Collaborator

Choose a reason for hiding this comment

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

this should be b

Copy link
Collaborator Author

@bryano bryano Aug 7, 2019

Choose a reason for hiding this comment

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

Done

@mpharrigan
Copy link
Collaborator

Some classes already define a commute_with method. Should I add some underscores to that or add a new method that delegates to the existing one?

I'd advocate for adding underscores, keeping the old signature which dispatches to the underscore version with a deprecation warning for ~1 release cycle

@bryano
Copy link
Collaborator Author

bryano commented Aug 7, 2019

Some classes already define a commute_with method. Should I add some underscores to that or add a new method that delegates to the existing one?

I'd advocate for adding underscores, keeping the old signature which dispatches to the underscore version with a deprecation warning for ~1 release cycle

Done

@bryano bryano changed the title [WIP, Fixes #1125] adds commutes protocol [Fixes #1125] adds commutes protocol Aug 7, 2019
@bryano
Copy link
Collaborator Author

bryano commented Aug 7, 2019

Ready for re-review.

@bryano
Copy link
Collaborator Author

bryano commented Oct 2, 2019

What should commutes(A, B) mean when A and B are both gates?

(1) They have the same qid_shape and commute when applied to the same qudits; indeterminate when different qid_shape.

(2) They commute for all possible qubits.

For example CNOT commutes with itself under (1) but not (2), while Z and ZZ commute under (2) but not (1).

@Strilanc
Copy link
Contributor

Strilanc commented Oct 2, 2019

Good question. I think I like (1) best. Specifically:

if qid_shape(a) != qid_shape(b):
    raise ValueError("Different sized gates.")
qs = cirq.LineQid(i, s) for i, s in enumerate(qid_shape(a))
return commutes(a(*qs), b(*qs))

@cduck
Copy link
Collaborator

cduck commented Oct 2, 2019

@Strilanc This is what LineQid.for_qid_shape is for:

qs = cirq.LineQid.for_qid_shape(qid_shape)

@bryano
Copy link
Collaborator Author

bryano commented Dec 9, 2019

@Strilanc PTAL

@Strilanc Strilanc added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Dec 16, 2019
@CirqBot
Copy link
Collaborator

CirqBot commented Dec 16, 2019

Automerge cancelled: A status check is failing.

@CirqBot CirqBot removed the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Dec 16, 2019
@vtomole
Copy link
Collaborator

vtomole commented Dec 16, 2019

@Strilanc We can't merge anything until #2648 is merged.

@Strilanc Strilanc changed the title [Fixes #1125] adds commutes protocol Add commutes protocol Dec 17, 2019
@Strilanc Strilanc added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Dec 17, 2019
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Dec 17, 2019
@CirqBot CirqBot merged commit ed6188b into quantumlib:master Dec 17, 2019
@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 Dec 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Makes googlebot stop complaining.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Should we add a cirq.commutes/_commutes_(other) protocol?
9 participants