-
Notifications
You must be signed in to change notification settings - Fork 1k
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
cirq_google.GridDevice, minus gateset and gate durations #5203
Conversation
3cd7c1b
to
53880e6
Compare
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.
Looking really good. Just a few minor tweaks then we should be good to merge.
return cls( | ||
qubit_pairs, | ||
gateset, | ||
None if gate_durations is None else dict(gate_durations), | ||
all_qubits, | ||
) |
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.
Why add this None if gate durations
?
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.
dict(None)
throws TypeError: 'NoneType' object is not iterable
. Alternatively we could also give gate_durations a default value: gate_durations=[]
. That might be cleaner.
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.
Non-default arguments can't be after default arguments. Will shifting the order of method parameters here break JSON backward compatibility?
cirq-core/cirq/ops/gateset.py
Outdated
gates_str = f'{self._gates_repr_str}, ' if len(self._gates_repr_str) > 0 else '' | ||
return ( | ||
f'cirq.Gateset(' | ||
f'{self._gates_repr_str}, ' | ||
f'{gates_str}' |
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.
Why change 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.
This addresses #5197
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.
Please add a test to gateset_test.py
. Also, If the change is unrelated and is not blocking this PR, then I'd prefer if we can split it out in a separate PR.
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.
Added and moved to #5322
|
||
|
||
@cirq.value_equality | ||
class GoogleDevice(cirq.Device): |
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.
Might want to call it something like UniformGoogleDevice
or GoogleGridDevice
just to convey the message that this type identifies and works for devices that have uniform gates in all directions etc. Since we are surfacing a GridDeviceMetadata here we want to ensure that the user knows they won't get a device that doesn't have the same gates and connectivity everywhere.
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.
Sounds good. How about just GridDevice
? I'm cautious with "uniform" because for example some 2-qubit gates may be missing. The "Google" descriptor is implicit from the package name and was there only because I couldn't think of a better descriptor :)
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 am in favor of GoogleGridDevice or GridDevice.
https://github.com/quantumlib/Cirq/blob/3969c2d3964cea56df33b329f036ba6810683882/cirq-google/cirq_google/api/v2/device.proto#L13 | ||
) | ||
is the main specification for device information surfaced by the Quantum Computing Service. | ||
Thus, this class is should be instantiated using a `DeviceSpecification` proto via the | ||
`from_proto()` class 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.
Love the verbosity here!
@classmethod | ||
def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GoogleDevice': |
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.
Why is the from_proto
method preffered over init design-wise ?
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.
Hmm good question, I don't have any good reasons other than following the existing pattern in SerializableDevice
. We also don't ever use the SerializableDevice
constructor anywhere else. @dstrain115 do you happen to know?
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.
The full DeviceSpecification would have to be stored in the repr and json, which could potentially be verbose?
try: | ||
metadata = cirq.GridDeviceMetadata( | ||
qubit_pairs=qubit_pairs, | ||
gateset=cirq.Gateset(), # TODO(#5050) implement | ||
all_qubits=all_qubits, | ||
) | ||
except ValueError as ve: | ||
raise ValueError("DeviceSpecification is invalid.") from ve |
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.
Nit: Do we want to catch the exception here and introduce this dependency on the behavior or GridDeviceMetaData's error behavior ? By extending our API for from_proto
here to accomodate errors in downstream code we are kind of bringing the error checking from downstream code upstream here to some extent. Do we want to do that in this case ?
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.
Yeah good point. It's perhaps better to explicitly call a validation function before doing any parsing, and instead leave this exception here as a last line of defense. Better not to tightly couple with downstream errors. Will update.
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 review!
return cls( | ||
qubit_pairs, | ||
gateset, | ||
None if gate_durations is None else dict(gate_durations), | ||
all_qubits, | ||
) |
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.
dict(None)
throws TypeError: 'NoneType' object is not iterable
. Alternatively we could also give gate_durations a default value: gate_durations=[]
. That might be cleaner.
cirq-core/cirq/ops/gateset.py
Outdated
gates_str = f'{self._gates_repr_str}, ' if len(self._gates_repr_str) > 0 else '' | ||
return ( | ||
f'cirq.Gateset(' | ||
f'{self._gates_repr_str}, ' | ||
f'{gates_str}' |
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.
This addresses #5197
@classmethod | ||
def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GoogleDevice': |
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.
Hmm good question, I don't have any good reasons other than following the existing pattern in SerializableDevice
. We also don't ever use the SerializableDevice
constructor anywhere else. @dstrain115 do you happen to know?
try: | ||
metadata = cirq.GridDeviceMetadata( | ||
qubit_pairs=qubit_pairs, | ||
gateset=cirq.Gateset(), # TODO(#5050) implement | ||
all_qubits=all_qubits, | ||
) | ||
except ValueError as ve: | ||
raise ValueError("DeviceSpecification is invalid.") from ve |
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.
Yeah good point. It's perhaps better to explicitly call a validation function before doing any parsing, and instead leave this exception here as a last line of defense. Better not to tightly couple with downstream errors. Will update.
|
||
|
||
@cirq.value_equality | ||
class GoogleDevice(cirq.Device): |
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.
Sounds good. How about just GridDevice
? I'm cautious with "uniform" because for example some 2-qubit gates may be missing. The "Google" descriptor is implicit from the package name and was there only because I couldn't think of a better descriptor :)
b86ca7b
to
a14e2df
Compare
Addressed comments, and added a validation function which is called at the beginning of |
2edef8f
to
5c75527
Compare
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.
Looking good! I like the very descriptive docstring blocks within the class.
return cls( | ||
qubit_pairs, | ||
gateset, | ||
None if gate_durations is None else dict(gate_durations), |
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.
Optional: Do you think dict(gate_durations) if gate_durations is not None else None
is more readable?
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.
Keeping the slightly more complicated part up front sgtm, will update
|
||
|
||
@cirq.value_equality | ||
class GoogleDevice(cirq.Device): |
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 am in favor of GoogleGridDevice or GridDevice.
Example use cases: | ||
|
||
* Get an instance of a Google grid device. | ||
>>> device = cirq_google.get_engine().get_processor('weber').get_device() |
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 wonder if we should create an example that does not require access to the API. This could be a follow-up issue/PR if this is not yet done. For instance, using the virtual engine stuff that we are building.
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.
Sounds good. Once the virtual engine work is available I imagine we'd do a docs sweep to update all the examples that access the API. Leaving this as no-op for now.
>>> print(device) | ||
|
||
* Determine whether a circuit can be run on the device. | ||
>>> device.validate_circuit(circuit) # Raises an exception if the circuit is invalid. |
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.
nit: may be more descriptive to say which exception is raised, e.g. ValueError so that people can put try catch around it easily.
|
||
For Google devices, the | ||
[DeviceSpecification proto]( | ||
https://github.com/quantumlib/Cirq/blob/3969c2d3964cea56df33b329f036ba6810683882/cirq-google/cirq_google/api/v2/device.proto#L13 |
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.
Change this like to point to master and not a specific commit.
|
||
This class only supports `cirq.GridQubit`s and `cirq.NamedQubit`s. If a | ||
`DeviceSpecification.valid_qubits` string is in the form `<int>_<int>`, it is parsed as a | ||
GridQubit. Otherwise it is parsed as a NamedQubit. |
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.
Interesting choice. If this is a GridDevice
, do you think the qubits should be restricted to GridQubits? If there a use case for named qubits. I would guess that it is likely a mistake if a non-grid qubit id happens.
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.
Sg. This came from the SerializableDevice
implementation, but will remove since we don't ever use NamedQubits.
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.
Will also add validation for the GridQubit string format.
raise ValueError(f'Qubit pair is not valid on device: {operation.qubits!r}') | ||
|
||
def __str__(self) -> str: | ||
# If all qubits are grid qubits, render an appropriate text diagram. |
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 am surprised that the below code for rendering a grid device doesn't exist somewhere already. Is the default insufficient because we do not assume pairwise connectivity between qubits?
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 got this code from SerializableDevice
, and there's also something similar for XmonDevice
. This GridDevice
will eventually replace both of them so I don't think there's a need to define a common function.
cirq.Device
doesn't have a default, and I presume it's because yeah connectivity can be more general and would be hard to render. cc @MichaelBroughton
return grid_qubits, spec | ||
|
||
|
||
def _create_device_spec_qubit_pair_self_loops() -> v2.device_pb2.DeviceSpecification: |
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.
Optional: consider adding a docstring for this and the below private functions for improved clarity.
c8743b6
to
9f3afb5
Compare
9f3afb5
to
93c1c5d
Compare
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.
Looking really good. Only concern is around proto validation for asymmetric and subset permutation targets.
gateset=cirq.Gateset(), # TODO(#5050) implement | ||
all_qubits=all_qubits, | ||
) | ||
except ValueError as ve: # coverage: ignore |
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.
Can we get rid of the coverage: ignore here and below?
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 them because these lines are currently unreachable if everything works correctly, and only act as a last line of defense.
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.
Can we add a test which gives a bad input that triggers this condition?
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.
The same conditions that would trigger this exception would also cause DeviceSpecification
validation to fail, so this is captured by the test test_grid_device_invalid_device_specification
. If validation is buggy and this exception here is triggered, the test should get an exception but will fail because there's an error pattern mismatch.
Per @MichaelBroughton 's suggestion I added spec validation logic at the top of from_proto
to make it explicit that errors that would've caused GridDeviceMetadata
instantiation to fail are due to an invalid DeviceSpecification
.
Will update the error match patterns to include "Invalid DeviceSpecification" to make sure that validation error messages are matched instead of the GridDeviceMetadata ones, in case error messages accidentally match up in the future between the two.
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 it even worth having the ValueError catching if unreachable?
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 think so - it would protect against future bugs in validation logic. For example, when GridDeviceMetadata gets an additional constraint but validation isn't updated, it's useful to give users the hint that the error they are getting is due to the DeviceSpecification being invalid rather than a mistake they made.
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 also think we should remove this check since the code path is unreachable.
We can add an assertion instead if we want to make sure we catch the inconsistencies between validation logic vs new constraints in GridDeviceMetadata but I'm not sure if putting everything under the umbrella of device specification errors is the right thing to do in this case.
…sary fields in DeviceSpecification proto.
… same GridDeviceMetadata as GridDeviceMetadata.repr
Ready for final approvals! |
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.
My bad, realized I never sent out my comment replies after pushing the code.
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.
LGTM, just a few minor nits.
gateset=cirq.Gateset(), # TODO(#5050) implement | ||
all_qubits=all_qubits, | ||
) | ||
except ValueError as ve: # coverage: ignore |
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 it even worth having the ValueError catching if unreachable?
Automerge cancelled: The automerge label was removed. |
…5203) Part of quantumlib#5050 * Implemented qubit set, qubit pairs, and op validation. Gateset and gate duration will be done in a follow-up. * Added a `device_specification_validation` module shell, to be used by the QCS server side to validate the DeviceSpecification proto against things like: * Qubit self-loops in qubit pairs * Qubit pairs contain qubits not in the valid qubit set. * Gate durations contains a gate which is missing in the gateset. `__str__()` and `_repr_pretty_` are nearly all copied from `SerializableDevice`. Part of quantumlib#5050 Also fixes quantumlib#5197 **Question:** Does this definition of device equality make sense, or should it contain device name as well? Implementing equality is convenient for json and repr tests but not sure if it make sense in general. @dstrain115 @MichaelBroughton
…5203) Part of quantumlib#5050 * Implemented qubit set, qubit pairs, and op validation. Gateset and gate duration will be done in a follow-up. * Added a `device_specification_validation` module shell, to be used by the QCS server side to validate the DeviceSpecification proto against things like: * Qubit self-loops in qubit pairs * Qubit pairs contain qubits not in the valid qubit set. * Gate durations contains a gate which is missing in the gateset. `__str__()` and `_repr_pretty_` are nearly all copied from `SerializableDevice`. Part of quantumlib#5050 Also fixes quantumlib#5197 **Question:** Does this definition of device equality make sense, or should it contain device name as well? Implementing equality is convenient for json and repr tests but not sure if it make sense in general. @dstrain115 @MichaelBroughton
Part of #5050
device_specification_validation
module shell, to be used by the QCS server side to validate the DeviceSpecification proto against things like:__str__()
and_repr_pretty_
are nearly all copied fromSerializableDevice
.Part of #5050
Also fixes #5197
Question: Does this definition of device equality make sense, or should it contain device name as well? Implementing equality is convenient for json and repr tests but not sure if it make sense in general.
@dstrain115 @MichaelBroughton