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 cirq_google.FSimGateFamily class. #4586

Merged
merged 18 commits into from
Nov 11, 2021

Conversation

tanujkhattar
Copy link
Collaborator

@tanujkhattar tanujkhattar commented Oct 19, 2021

This PR adds a new cirq_google.FSimGateFamily class which can be used to:

  1. Convert any compatible FSimGate type to another compatible FSimGate type.
  2. Accept (potentially parameterized) instances of compatible FSimGate types.

The gate family is also used to replace existing logic in cirq_google for checking / converting against fsim types. Once merged, this would also be useful to perform similar checks and conversions in pyle.

This is part of the roadmap item: #3243

@tanujkhattar tanujkhattar requested review from cduck, vtomole, wcourtney and a team as code owners October 19, 2021 01:01
@tanujkhattar tanujkhattar requested a review from maffoo October 19, 2021 01:01
@google-cla google-cla bot added the cla: yes Makes googlebot stop complaining. label Oct 19, 2021
@CirqBot CirqBot added the size: L 250< lines changed <1000 label Oct 19, 2021
@MichaelBroughton
Copy link
Collaborator

as proposed in #4585. Based on the discussion on the other issue, I will pull out this independent change into a separate PR if needed.

Could we separate these two out, before we start in on the review ?

@tanujkhattar
Copy link
Collaborator Author

@MichaelBroughton The major contribution of the this PR is in the new cirq_google/ops/common_gate_families.py and it's test file.
I think we can proceed with it's review and in parallel I'll also pull out the unrelated change into the separate PR -- since this is a large PR, I don't think it's worth blocking it's review. LMK your thoughts.

@tanujkhattar
Copy link
Collaborator Author

@MichaelBroughton I removed the unrelated change and have added a manual override for the specific case. This should work for the next couple of releases -- till the other issue is fixed and has gone through a deprecation cycle before the changes are released.

This PR is ready for review.

@tanujkhattar
Copy link
Collaborator Author

@MichaelBroughton @dstrain115 Gentle reminder. The PR is ready for review.

Copy link
Collaborator

@MichaelBroughton MichaelBroughton left a comment

Choose a reason for hiding this comment

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

This is looking good. I've left a few comments in the implementation side of things. @dstrain115 if you want to weigh in on serializer stuff feel free.

cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families_test.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families_test.py Outdated Show resolved Hide resolved
@tanujkhattar
Copy link
Collaborator Author

Resolved all comments. The PR is ready for a re-review. @dstrain115 @MichaelBroughton

cirq-google/cirq_google/ops/common_gate_families_test.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families_test.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families_test.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families_test.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families_test.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/common_gate_families.py Outdated Show resolved Hide resolved

def _get_value_equality_values(self, g: POSSIBLE_FSIM_GATES) -> Any:
# TODO: Remove condition once https://github.com/quantumlib/Cirq/issues/4585 is fixed.
if type(g) == cirq.PhasedISwapPowGate:
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: use isinstance (this should also allow removing the type: ignore on the next line)

Suggested change
if type(g) == cirq.PhasedISwapPowGate:
if isinstance(g, cirq.PhasedISwapPowGate):

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 reason to use type equality comparison instead of isinstance was to ensure that we override this special case only for the class defined in Cirq and do not override the behavior for any user-defined derived class. So, a CustomPhasedISwapGate that derives from PhasedISwapGate and overrides the _value_equality_values_ method wouldn't be special cased here.


def _get_value_equality_values_cls(self, g: POSSIBLE_FSIM_GATES) -> Any:
# TODO: Remove condition once https://github.com/quantumlib/Cirq/issues/4585 is fixed.
if type(g) == cirq.PhasedISwapPowGate:
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: as commented above, use isinstance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

same here as replied above. Will resolve the comment once the discussion above is resolved.

"""Inits `cirq_google.FSimGateFamily`.

Args:
gates_to_accept: List of gate types or instances to be accepted. All elements
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't quite understand why this should include types as well as instances, when gate_types_to_check already contains gate types. Can you clarify?

Copy link
Collaborator Author

@tanujkhattar tanujkhattar Nov 2, 2021

Choose a reason for hiding this comment

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

  • gate_types_to_check puts an initial filter on which compatible gate instances should be checked for containment.
  • gates_to_accept defines the class of compatible gate unitaries that should be accepted.

So, for example, if I want to accept all iswappow gates which can be specified as either an fsim gate or an iswap gate, I would use:

>>> cg.FSimGateFamily(
    gates_to_accept=[cirq.ISwapPowGate], 
    gate_types_to_check=[cirq.FSimGate, cirq.ISwapPowGate]
)
>>> assert cirq.PhasedISwapPowGate(phase_exponent = 0, exponent = 0.5) not in gf
>>> assert cirq.ISWAP ** 0.5 in gf


def convert(
self, gate: cirq.Gate, target_gate_type: Type[POSSIBLE_FSIM_GATES]
) -> Optional[POSSIBLE_FSIM_GATES]:
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we can fix the typing issue here, by using a type var, something like:

from typing import TypeVar
...
T = TypeVar('T', bound=POSSIBLE_FSIM_GATES)
...
def convert(self, gate: cirq.Gate, target_gate_type: Type[T]) -> Optional[T]:
    ...

If this can work I think it would eliminate a bunch of casts when calling convert, but I'm not quite sure whether bounds on type vars work on Unions like this.

If the generic convert function can't be made typesafe, something we might want to do is make the specific conversion functions public so they can be called in a type-safe way, e.g. if you know you want an FSimGate you could call family.convert_to_fsim(gate) instead of cast(cirq.FSimGate, family.convert(gate, cirq.FSimGate)).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

After getting stuck in the mypy hell for quite some time, I've made a change which seems to work and is typesafe at the callsite. Though there's an (unavoidable?) issue that if we call convert(g, target) where target's type is Union[Type[FSimGate], Type[ISwapPowGate], etc.] (for example by iterating over a non-homogenous list of conversion targets), then mypy complaints.

LMK if you have a better solution. I personally do prefer having a single convert method over having explicit methods for each target type.

@tanujkhattar
Copy link
Collaborator Author

@maffoo @dstrain115 @MichaelBroughton Gentle reminder. The PR is waiting for a re-review. Thanks!

Copy link
Collaborator

@dstrain115 dstrain115 left a comment

Choose a reason for hiding this comment

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

Only minor nits left in my review.

Raises:
ValueError: If any gate in `gates_to_accept` is not a non-parameterized instance of /
or a gate type from `POSSIBLE_FSIM_GATES`.
ValueError: If any gate type in `gate_types_to_check` is not one of
Copy link
Collaborator

Choose a reason for hiding this comment

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

optional nit: can we combine these two docstrings?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Since the two value errors get triggered in different scenarios, I'd prefer to keep them separate.

ValueError: If any gate type in `gate_types_to_check` is not one of
`POSSIBLE_FSIM_GATES`.
"""
self._supported_types: Dict[
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be a module-level constant instead of a member variable instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This should be a member variable because it's a dictionary where values are member functions of the class. The _convert_to_* need to be member functions instead of free functions because they in turn call _approx_eq_or_symbol member function which depends on self.allow_symbols variable.

We could refactor to make allow_symbols a function parameter and then move the member functions out of the class but I don't see much value in doing that. LMK if you feel otherwise.

cirq-google/cirq_google/ops/fsim_gate_family.py Outdated Show resolved Hide resolved
cirq-google/cirq_google/ops/fsim_gate_family_test.py Outdated Show resolved Hide resolved
@tanujkhattar
Copy link
Collaborator Author

@dstrain115 Thanks for the review. I've addressed your comments.

I'll merge once @maffoo takes another look.

Copy link
Contributor

@maffoo maffoo left a comment

Choose a reason for hiding this comment

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

Minor comments, then LGTM.

Comment on lines +171 to +172
self.gates_to_accept = tuple(dict.fromkeys(gates_to_accept))
self.gate_types_to_check = tuple(dict.fromkeys(gate_types_to_check))
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems odd here to convert to dict but then back to tuple which iterates over the dict keys and ignores the value anyway. I'd suggest converting to set instead:

Suggested change
self.gates_to_accept = tuple(dict.fromkeys(gates_to_accept))
self.gate_types_to_check = tuple(dict.fromkeys(gate_types_to_check))
self.gates_to_accept = tuple(set(gates_to_accept))
self.gate_types_to_check = tuple(set(gate_types_to_check))

Or just use frozenset directly instead of a tuple:

Suggested change
self.gates_to_accept = tuple(dict.fromkeys(gates_to_accept))
self.gate_types_to_check = tuple(dict.fromkeys(gate_types_to_check))
self.gates_to_accept = frozenset(gates_to_accept)
self.gate_types_to_check = frozenset(gate_types_to_check)

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 goal here is to keep only the unique values from the input but also preserve the input ordering so that repr/str look as expected to the users. Iterable --> dict --> tuple is a succinct way to achieve this.

This has also been done earlier for gatesets, which had a similar requirement:

unique_gate_list: List[GateFamily] = list(

cirq-google/cirq_google/ops/fsim_gate_family.py Outdated Show resolved Hide resolved
@tanujkhattar tanujkhattar dismissed MichaelBroughton’s stale review November 11, 2021 17:55

Stale review, merging now.

@tanujkhattar tanujkhattar added the automerge Tells CirqBot to sync and merge this PR. (If it's running.) label Nov 11, 2021
@CirqBot CirqBot added the front_of_queue_automerge CirqBot uses this label to indicate (and remember) what's being merged next. label Nov 11, 2021
@CirqBot CirqBot merged commit c647079 into quantumlib:master Nov 11, 2021
@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 Nov 11, 2021
rht pushed a commit to rht/Cirq that referenced this pull request May 1, 2023
This PR adds a new `cirq_google.FSimGateFamily` class which can be used to:

1. Convert any compatible FSimGate type to another compatible FSimGate type.
2. Accept (potentially parameterized) instances of compatible FSimGate types.

The gate family is also used to replace existing logic in cirq_google for checking / converting against fsim types. Once merged, this would also be useful to perform similar checks and conversions in pyle. 

This is part of the roadmap item: quantumlib#3243
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. size: L 250< lines changed <1000
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants