Skip to content

Commit

Permalink
Sycamore Device (#2485)
Browse files Browse the repository at this point in the history
Adding Sycamore Device described in known publications

- Added sycamore device with sqrt_iswap and sycamore gates.
- Added changes to allow devices to have multiple definitions for
the same gate type using the serialize can_serialize predicate.
  • Loading branch information
dstrain115 committed Nov 6, 2019
1 parent 4670173 commit ad490a7
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 45 deletions.
1 change: 1 addition & 0 deletions cirq/google/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
Bristlecone,
Foxtail,
SerializableDevice,
Sycamore,
XmonDevice,
)

Expand Down
12 changes: 6 additions & 6 deletions cirq/google/common_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,6 @@ def _near_mod_2(e, t, atol=1e-8):
cast(ops.PhasedXPowGate, x).exponent, 0.5)),
]

#############################################
#
# Two qubit serializers and deserializers
#
#############################################

#
# Deserializers for single qubit rotations confined to half-pi increments
#
Expand Down Expand Up @@ -259,6 +253,12 @@ def _near_mod_2(e, t, atol=1e-8):
]),
]

#############################################
#
# Two qubit serializers and deserializers
#
#############################################

#
# CZ Serializer and deserializer
#
Expand Down
1 change: 1 addition & 0 deletions cirq/google/devices/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from cirq.google.devices.known_devices import (
Bristlecone,
Foxtail,
Sycamore,
)

from cirq.google.devices.serializable_device import (
Expand Down
35 changes: 35 additions & 0 deletions cirq/google/devices/known_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from cirq.google import gate_sets, serializable_gate_set
from cirq.google.api import v2
from cirq.google.api.v2 import device_pb2
from cirq.google.devices.serializable_device import SerializableDevice
from cirq.google.devices.xmon_device import XmonDevice
from cirq.ops import MeasurementGate, SingleQubitGate
from cirq.value import Duration
Expand Down Expand Up @@ -239,3 +240,37 @@ def _json_dict_(self):
BRISTLECONE_PROTO = create_device_proto_from_diagram(_BRISTLECONE_GRID,
[gate_sets.XMON],
_DURATIONS_FOR_XMON)

_SYCAMORE_GRID = """
-----AB---
----ABCD--
---ABCDEF-
--ABCDEFGH
-ABCDEFGHI
ABCDEFGHI-
-CDEFGHI--
--EFGHI---
---GHI----
----I-----
"""

_SYCAMORE_DURATIONS_PICOS = {
'xy': 25_000,
'xy_half_pi': 25_000,
'xy_pi': 25_000,
'fsim_pi_4': 32_000,
'inv_fsim_pi_4': 32_000,
'syc': 12_000,
'z': 0,
'meas': 1_000_000,
}

SYCAMORE_PROTO = create_device_proto_from_diagram(
_SYCAMORE_GRID,
[gate_sets.SQRT_ISWAP_GATESET, gate_sets.SYC_GATESET],
_SYCAMORE_DURATIONS_PICOS,
)

Sycamore = SerializableDevice.from_proto(
proto=SYCAMORE_PROTO,
gate_sets=[gate_sets.SQRT_ISWAP_GATESET, gate_sets.SYC_GATESET])
13 changes: 13 additions & 0 deletions cirq/google/devices/known_devices_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import numpy as np

import cirq
import cirq.google as cg
import cirq.google.common_serializers as cgc
Expand Down Expand Up @@ -389,3 +391,14 @@ def test_json_dict():
'exp_11_duration': cirq.Duration(nanos=50),
'qubits': sorted(cirq.google.Bristlecone.qubits)
}


def test_sycamore_device():
q0 = cirq.GridQubit(5, 4)
q1 = cirq.GridQubit(5, 5)
syc = cirq.FSimGate(theta=np.pi / 2, phi=np.pi / 6)(q0, q1)
sqrt_iswap = cirq.FSimGate(theta=np.pi / 4, phi=0)(q0, q1)
cg.Sycamore.validate_operation(syc)
cg.Sycamore.validate_operation(sqrt_iswap)
assert cg.Sycamore.duration_of(syc) == cirq.Duration(nanos=12)
assert cg.Sycamore.duration_of(sqrt_iswap) == cirq.Duration(nanos=32)
54 changes: 42 additions & 12 deletions cirq/google/devices/serializable_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"""Device object for converting from device specification protos"""

from typing import (
Callable,
cast,
Dict,
Iterable,
Expand All @@ -37,19 +38,44 @@
class _GateDefinition:
"""Class for keeping track of gate definitions within SerializableDevice"""

def __init__(self, duration: 'cirq.DURATION_LIKE',
target_set: Set[Tuple['cirq.Qid', ...]], number_of_qubits: int,
is_permutation: bool):
def __init__(
self,
duration: 'cirq.DURATION_LIKE',
target_set: Set[Tuple['cirq.Qid', ...]],
number_of_qubits: int,
is_permutation: bool,
can_serialize_predicate: Callable[['cirq.Gate'], bool] = lambda x:
True,
):
self.duration = Duration(duration)
self.target_set = target_set
self.is_permutation = is_permutation
self.number_of_qubits = number_of_qubits
self.can_serialize_predicate = can_serialize_predicate

# Compute the set of all qubits in all target sets.
self.flattened_qubits = {
q for qubit_tuple in target_set for q in qubit_tuple
}

def with_can_serialize_predicate(
self, can_serialize_predicate: Callable[['cirq.Gate'], bool]
) -> '_GateDefinition':
"""Creates a new _GateDefintion as a copy of the existing definition
but with a new with_can_serialize_predicate. This is useful if multiple
definitions exist for the same gate, but with different conditions.
An example is if gates at certain angles of a gate take longer or are
not allowed.
"""
return _GateDefinition(
self.duration,
self.target_set,
self.number_of_qubits,
self.is_permutation,
can_serialize_predicate,
)

def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
Expand All @@ -71,8 +97,9 @@ class SerializableDevice(devices.Device):
deserialization.
"""

def __init__(self, qubits: List['cirq.Qid'],
gate_definitions: Dict[Type['cirq.Gate'], _GateDefinition]):
def __init__(
self, qubits: List['cirq.Qid'],
gate_definitions: Dict[Type['cirq.Gate'], List[_GateDefinition]]):
"""Constructor for SerializableDevice using python objects.
Note that the preferred method of constructing this object is through
Expand Down Expand Up @@ -141,7 +168,7 @@ def from_proto(
number_of_qubits=gate_def.number_of_qubits)

# Loop through serializers and map gate_definitions to type
gates_by_type: Dict[Type['cirq.Gate'], _GateDefinition] = {}
gates_by_type: Dict[Type['cirq.Gate'], List[_GateDefinition]] = {}
for gate_set in gate_sets:
for gate_type in gate_set.supported_gate_types():
for serializer in gate_set.serializers[gate_type]:
Expand All @@ -150,11 +177,12 @@ def from_proto(
raise ValueError(f'Serializer has {gate_id} which is '
'not supported by the device '
'specification')
if (gate_type in gates_by_type and gate_definitions[gate_id]
!= gates_by_type[gate_type]):
raise ValueError('Two conflicting definitions for '
f'gate_type {gate_type}')
gates_by_type[gate_type] = gate_definitions[gate_id]
if gate_type not in gates_by_type:
gates_by_type[gate_type] = []
gate_def = gate_definitions[
gate_id].with_can_serialize_predicate(
serializer.can_serialize_predicate)
gates_by_type[gate_type].append(gate_def)

return SerializableDevice(
qubits=SerializableDevice._qubits_from_ids(proto.valid_qubits),
Expand Down Expand Up @@ -199,7 +227,9 @@ def _find_operation_type(self,
gate_key = gate_op.gate
for type_key in self.gate_definitions:
if isinstance(gate_key, type_key):
return self.gate_definitions[type_key]
for gate_def in self.gate_definitions[type_key]:
if gate_def.can_serialize_predicate(gate_key):
return gate_def
return None

def duration_of(self, operation: 'cirq.Operation') -> Duration:
Expand Down
79 changes: 52 additions & 27 deletions cirq/google/devices/serializable_device_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,38 +324,63 @@ def test_multiple_gatesets():
dev.validate_operation(cirq.X(cirq.GridQubit(2, 2)))


def test_multiple_gatesets_conflicting_definitions():
conflictingSet = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='conflicting_phased_xpow',
def test_half_pi_takes_half_duration():
"""This tests that gate sets with two different definitions for the same
gate perform correctly. In this case, we set the XPowGate to be
half the duration of the full exponent and make sure it still works.
"""
half_pi_gs = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='half_pi',
serializers=[
cg.op_serializer.GateOpSerializer(
gate_type=cirq.PhasedXPowGate,
serialized_gate_id='not_xy',
args=[
cg.op_serializer.SerializingArg(
serialized_name='axis_half_turns',
serialized_type=float,
gate_getter='phase_exponent'),
cg.op_serializer.SerializingArg(
serialized_name='half_turns',
serialized_type=float,
gate_getter='exponent'),
]),
*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS,
],
deserializers=[
*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS,
],
deserializers=[],
)
durations_dict = {
'xy_pi': 20_000,
'xy_half_pi': 10_000,
'not_xy': 52_000,
'xy': 53_000,
'cz': 11_000,
'meas': 14_141
}
spec = cirq.google.devices.known_devices.create_device_proto_from_diagram(
"aa\naa", [cirq.google.XMON, conflictingSet], durations_dict)

# The two gate sets define two different serializations for PhasedXPowGate
with pytest.raises(ValueError, match='conflicting definitions'):
_ = cg.SerializableDevice.from_proto(
proto=spec, gate_sets=[cirq.google.XMON, conflictingSet])
"aa\naa", [half_pi_gs], durations_dict)

# The gate set defines two different serializations for PhasedXPowGate
device = cg.SerializableDevice.from_proto(proto=spec,
gate_sets=[half_pi_gs])
q = cirq.GridQubit(0, 0)
pi = cirq.XPowGate(exponent=1.0)
half_pi = cirq.XPowGate(exponent=0.5)
assert device.duration_of(pi(q)) == cirq.Duration(picos=20_000)
assert device.duration_of(half_pi(q)) == cirq.Duration(picos=10_000)


def test_multiple_fsim_gatesets():
"""This tests that gate sets with two different definitions for the same
gate perform correctly. In this case, we set the XPowGate to be
half the duration of the full exponent and make sure it still works.
"""
half_pi_gs = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='half_pi',
serializers=[
*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS,
],
deserializers=[
*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS,
],
)
durations_dict = {
'xy_pi': 20_000,
'xy_half_pi': 10_000,
}
spec = cirq.google.devices.known_devices.create_device_proto_from_diagram(
"aa\naa", [half_pi_gs], durations_dict)

# The gate set defines two different serializations for PhasedXPowGate
device = cg.SerializableDevice.from_proto(proto=spec,
gate_sets=[half_pi_gs])
q = cirq.GridQubit(0, 0)
pi = cirq.XPowGate(exponent=1.0)
half_pi = cirq.XPowGate(exponent=0.5)
assert device.duration_of(pi(q)) == cirq.Duration(picos=20_000)
assert device.duration_of(half_pi(q)) == cirq.Duration(picos=10_000)
1 change: 1 addition & 0 deletions cirq/protocols/json_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ def test_mutually_exclusive_blacklist():
'StabilizerStateChForm',
'StateVectorMixin',
'SYC_GATESET',
'Sycamore',
'TextDiagramDrawer',
'ThreeQubitDiagonalGate',
'Timestamp',
Expand Down
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ Functionality specific to quantum hardware and services from Google.
cirq.google.SerializableDevice
cirq.google.SerializableGateSet
cirq.google.SerializingArg
cirq.google.Sycamore
cirq.google.SycamoreGate
cirq.google.XmonDevice

Expand Down

0 comments on commit ad490a7

Please sign in to comment.