diff --git a/cirq-core/cirq/devices/grid_device_metadata.py b/cirq-core/cirq/devices/grid_device_metadata.py index f177523b54c..6e978698e7f 100644 --- a/cirq-core/cirq/devices/grid_device_metadata.py +++ b/cirq-core/cirq/devices/grid_device_metadata.py @@ -17,7 +17,7 @@ from __future__ import annotations from collections.abc import Iterable, Mapping -from typing import cast, TYPE_CHECKING +from typing import Any, cast, TYPE_CHECKING, TypeVar import networkx as nx @@ -28,6 +28,9 @@ import cirq +TQubitAttributeValue = TypeVar("TQubitAttributeValue", bound=bool | int | float | str | None) + + @value.value_equality class GridDeviceMetadata(device.DeviceMetadata): """Hardware metadata for homogenous 2d symmetric grid devices.""" @@ -39,6 +42,7 @@ def __init__( gate_durations: Mapping[cirq.GateFamily, cirq.Duration] | None = None, all_qubits: Iterable[cirq.GridQubit] | None = None, compilation_target_gatesets: Iterable[cirq.CompilationTargetGateset] = (), + qubit_attributes: Mapping[cirq.GridQubit, Mapping[str, TQubitAttributeValue]] | None = None, ): """Create a GridDeviceMetadata object. @@ -117,6 +121,11 @@ def __init__( ) self._gate_durations = gate_durations + self._qubit_attributes = ( + {q: dict(attrs) for q, attrs in qubit_attributes.items()} + if qubit_attributes is not None + else {} + ) @property def qubit_set(self) -> frozenset[cirq.GridQubit]: @@ -175,17 +184,28 @@ def gate_durations(self) -> Mapping[cirq.GateFamily, cirq.Duration] | None: return self._gate_durations + @property + def qubit_attributes(self) -> Mapping[cirq.GridQubit, Mapping[str, TQubitAttributeValue]]: + """Returns a mapping from qubit to its attributes (if applicable).""" + return self._qubit_attributes + def _value_equality_values_(self): duration_equality = '' if self._gate_durations is not None: duration_equality = sorted(self._gate_durations.items(), key=lambda x: repr(x[0])) + attributes_equality = sorted( + [(q, tuple(sorted(attrs.items()))) for q, attrs in self._qubit_attributes.items()], + key=lambda x: x[0], + ) + return ( self._qubit_pairs, self._gateset, tuple(duration_equality), tuple(sorted(self.qubit_set)), frozenset(self._compilation_target_gatesets), + tuple(attributes_equality), ) def __repr__(self) -> str: @@ -193,7 +213,8 @@ def __repr__(self) -> str: return ( f'cirq.GridDeviceMetadata({repr(qubit_pair_tuples)},' f' {repr(self._gateset)}, {repr(self._gate_durations)},' - f' {repr(self.qubit_set)}, {repr(self._compilation_target_gatesets)})' + f' {repr(self.qubit_set)}, {repr(self._compilation_target_gatesets)},' + f' {repr(self._qubit_attributes)})' ) def _json_dict_(self): @@ -207,6 +228,10 @@ def _json_dict_(self): 'gate_durations': duration_payload, 'all_qubits': sorted(self.qubit_set), 'compilation_target_gatesets': list(self._compilation_target_gatesets), + 'qubit_attributes': sorted( + ((q, sorted(attrs.items())) for q, attrs in self._qubit_attributes.items()), + key=lambda x: x[0], + ), } @classmethod @@ -217,12 +242,16 @@ def _from_json_dict_( gate_durations, all_qubits, compilation_target_gatesets=(), + qubit_attributes=None, **kwargs, ): + if qubit_attributes is not None: + qubit_attributes = {q: dict(attrs) for q, attrs in qubit_attributes} return cls( qubit_pairs, gateset, dict(gate_durations) if gate_durations is not None else None, all_qubits, compilation_target_gatesets, + qubit_attributes, ) diff --git a/cirq-core/cirq/devices/grid_device_metadata_test.py b/cirq-core/cirq/devices/grid_device_metadata_test.py index eb4c2d9bbb1..6f2a329705f 100644 --- a/cirq-core/cirq/devices/grid_device_metadata_test.py +++ b/cirq-core/cirq/devices/grid_device_metadata_test.py @@ -16,6 +16,8 @@ from __future__ import annotations +from typing import Any + import networkx as nx import pytest @@ -202,3 +204,43 @@ def test_repr() -> None: compilation_target_gatesets=target_gatesets, ) cirq.testing.assert_equivalent_repr(metadata) + + +def test_griddevice_metadata_qubit_attributes() -> None: + qubits = cirq.GridQubit.rect(1, 2) + gateset = cirq.Gateset(cirq.XPowGate) + qubit_attributes: dict[cirq.GridQubit, dict[str, Any]] = { + cirq.GridQubit(0, 0): {"type": "transmon", "frequency": 5.1}, + cirq.GridQubit(0, 1): {"index": 42}, + } + + metadata = cirq.GridDeviceMetadata( + qubit_pairs=[], gateset=gateset, all_qubits=qubits, qubit_attributes=qubit_attributes + ) + + assert metadata.qubit_attributes == qubit_attributes + + # test JSON serialization + rep_str = cirq.to_json(metadata) + assert metadata == cirq.read_json(json_text=rep_str) + + # test repr + cirq.testing.assert_equivalent_repr(metadata) + + # test equality + qubit_attributes2: dict[cirq.GridQubit, dict[str, Any]] = { + cirq.GridQubit(0, 0): {"type": "transmon", "frequency": 5.1}, + cirq.GridQubit(0, 1): {"index": 43}, # different index + } + metadata2 = cirq.GridDeviceMetadata( + qubit_pairs=[], gateset=gateset, all_qubits=qubits, qubit_attributes=qubit_attributes2 + ) + + metadata3 = cirq.GridDeviceMetadata( + qubit_pairs=[], gateset=gateset, all_qubits=qubits, qubit_attributes=None + ) + + eq = cirq.testing.EqualsTester() + eq.add_equality_group(metadata) + eq.add_equality_group(metadata2) + eq.add_equality_group(metadata3) diff --git a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json index 0843b9744d1..85f8aa9f664 100644 --- a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json +++ b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.json @@ -265,5 +265,6 @@ "required_sqrt_iswap_count": null, "use_sqrt_iswap_inv": false } - ] + ], + "qubit_attributes": [] } \ No newline at end of file diff --git a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr index 8d7ce4b2225..0bf7eef5f15 100644 --- a/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr +++ b/cirq-core/cirq/protocols/json_test_data/GridDeviceMetadata.repr @@ -1 +1 @@ -cirq.GridDeviceMetadata(frozenset({(cirq.GridQubit(1, 1), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), (cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 1), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), (cirq.GridQubit(0, 2), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 1), cirq.GridQubit(0, 2))}), cirq.Gateset(cirq.ops.common_gates.XPowGate, cirq.ops.common_gates.YPowGate, cirq.ops.common_gates.ZPowGate, cirq.CZ, (cirq.ISWAP**0.5), unroll_circuit_op = True), {cirq.GateFamily(gate=cirq.ops.common_gates.XPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(nanos=1), cirq.GateFamily(gate=cirq.ops.common_gates.YPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.ops.common_gates.ZPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.CZ, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=500), cirq.GateFamily(gate=(cirq.ISWAP**0.5), ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=600)}, frozenset({cirq.GridQubit(1, 0), cirq.GridQubit(0, 2), cirq.GridQubit(9, 9), cirq.GridQubit(10, 10), cirq.GridQubit(0, 1), cirq.GridQubit(1, 1), cirq.GridQubit(0, 0), cirq.GridQubit(1, 2)}), (cirq.CZTargetGateset(atol=1e-08, allow_partial_czs=False), cirq.SqrtIswapTargetGateset(atol=1e-08, required_sqrt_iswap_count=None, use_sqrt_iswap_inv=False))) \ No newline at end of file +cirq.GridDeviceMetadata(frozenset({(cirq.GridQubit(1, 1), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), (cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 1), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), (cirq.GridQubit(0, 2), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 1), cirq.GridQubit(0, 2))}), cirq.Gateset(cirq.ops.common_gates.XPowGate, cirq.ops.common_gates.YPowGate, cirq.ops.common_gates.ZPowGate, cirq.CZ, (cirq.ISWAP**0.5), unroll_circuit_op = True), {cirq.GateFamily(gate=cirq.ops.common_gates.XPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(nanos=1), cirq.GateFamily(gate=cirq.ops.common_gates.YPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.ops.common_gates.ZPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.CZ, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=500), cirq.GateFamily(gate=(cirq.ISWAP**0.5), ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=600)}, frozenset({cirq.GridQubit(1, 0), cirq.GridQubit(0, 2), cirq.GridQubit(9, 9), cirq.GridQubit(10, 10), cirq.GridQubit(0, 1), cirq.GridQubit(1, 1), cirq.GridQubit(0, 0), cirq.GridQubit(1, 2)}), (cirq.CZTargetGateset(atol=1e-08, allow_partial_czs=False), cirq.SqrtIswapTargetGateset(atol=1e-08, required_sqrt_iswap_count=None, use_sqrt_iswap_inv=False)), {}) \ No newline at end of file diff --git a/cirq-google/cirq_google/api/v2/device.proto b/cirq-google/cirq_google/api/v2/device.proto index 6a1689946bd..8b40ae79925 100644 --- a/cirq-google/cirq_google/api/v2/device.proto +++ b/cirq-google/cirq_google/api/v2/device.proto @@ -34,6 +34,9 @@ message DeviceSpecification { // are advice to users of the device, specified in English text // For instance, "All Z gates are converted to VirtualZ gates". string developer_recommendations = 4; + + // Qubit attributes for the device. + map qubit_attributes = 6; } // This contains information about a single device gate. @@ -199,3 +202,18 @@ message Target { // A list of qubit ids that form a valid gate target. repeated string ids = 1; } + +// Qubit attributes for a specific qubit. +message QubitAttributes { + map attributes = 1; +} + +// A generic value for a qubit attribute. +message QubitAttributeValue { + oneof val { + string string_value = 1; + int64 int_value = 2; + double double_value = 3; + bool bool_value = 4; + } +} diff --git a/cirq-google/cirq_google/api/v2/device_pb2.py b/cirq-google/cirq_google/api/v2/device_pb2.py index 13e8b0011d9..a5e3b9fa341 100644 --- a/cirq-google/cirq_google/api/v2/device_pb2.py +++ b/cirq-google/cirq_google/api/v2/device_pb2.py @@ -24,7 +24,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63irq_google/api/v2/device.proto\x12\x12\x63irq.google.api.v2\"\xfa\x01\n\x13\x44\x65viceSpecification\x12\x38\n\x0fvalid_gate_sets\x18\x01 \x03(\x0b\x32\x1b.cirq.google.api.v2.GateSetB\x02\x18\x01\x12:\n\x0bvalid_gates\x18\x05 \x03(\x0b\x32%.cirq.google.api.v2.GateSpecification\x12\x14\n\x0cvalid_qubits\x18\x02 \x03(\t\x12\x34\n\rvalid_targets\x18\x03 \x03(\x0b\x32\x1d.cirq.google.api.v2.TargetSet\x12!\n\x19\x64\x65veloper_recommendations\x18\x04 \x01(\t\"\xfb\x0c\n\x11GateSpecification\x12\x1b\n\x13gate_duration_picos\x18\x01 \x01(\x03\x12=\n\x03syc\x18\x02 \x01(\x0b\x32..cirq.google.api.v2.GateSpecification.SycamoreH\x00\x12\x45\n\nsqrt_iswap\x18\x03 \x01(\x0b\x32/.cirq.google.api.v2.GateSpecification.SqrtISwapH\x00\x12L\n\x0esqrt_iswap_inv\x18\x04 \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.SqrtISwapInvH\x00\x12\x36\n\x02\x63z\x18\x05 \x01(\x0b\x32(.cirq.google.api.v2.GateSpecification.CZH\x00\x12\x43\n\tphased_xz\x18\x06 \x01(\x0b\x32..cirq.google.api.v2.GateSpecification.PhasedXZH\x00\x12I\n\x0cvirtual_zpow\x18\x07 \x01(\x0b\x32\x31.cirq.google.api.v2.GateSpecification.VirtualZPowH\x00\x12K\n\rphysical_zpow\x18\x08 \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.PhysicalZPowH\x00\x12K\n\rcoupler_pulse\x18\t \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.CouplerPulseH\x00\x12\x41\n\x04meas\x18\n \x01(\x0b\x32\x31.cirq.google.api.v2.GateSpecification.MeasurementH\x00\x12:\n\x04wait\x18\x0b \x01(\x0b\x32*.cirq.google.api.v2.GateSpecification.WaitH\x00\x12L\n\x0e\x66sim_via_model\x18\x0c \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.FSimViaModelH\x00\x12L\n\x0etwo_pulse_fsim\x18\x13 \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.TwoPulseFSimH\x00\x12\x46\n\x0b\x63z_pow_gate\x18\r \x01(\x0b\x32/.cirq.google.api.v2.GateSpecification.CZPowGateH\x00\x12K\n\rinternal_gate\x18\x0e \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.InternalGateH\x00\x12<\n\x05reset\x18\x0f \x01(\x0b\x32+.cirq.google.api.v2.GateSpecification.ResetH\x00\x12V\n\x13\x61nalog_detune_qubit\x18\x10 \x01(\x0b\x32\x37.cirq.google.api.v2.GateSpecification.AnalogDetuneQubitH\x00\x12\x63\n\x1a\x61nalog_detune_coupler_only\x18\x11 \x01(\x0b\x32=.cirq.google.api.v2.GateSpecification.AnalogDetuneCouplerOnlyH\x00\x12U\n\x13wait_gate_with_unit\x18\x12 \x01(\x0b\x32\x36.cirq.google.api.v2.GateSpecification.WaitGateWithUnitH\x00\x1a\n\n\x08Sycamore\x1a\x0b\n\tSqrtISwap\x1a\x0e\n\x0cSqrtISwapInv\x1a\x04\n\x02\x43Z\x1a\n\n\x08PhasedXZ\x1a\r\n\x0bVirtualZPow\x1a\x0e\n\x0cPhysicalZPow\x1a\x0e\n\x0c\x43ouplerPulse\x1a\r\n\x0bMeasurement\x1a\x06\n\x04Wait\x1a\x0e\n\x0c\x46SimViaModel\x1a\x0e\n\x0cTwoPulseFSim\x1a\x0b\n\tCZPowGate\x1a\x0e\n\x0cInternalGate\x1a\x07\n\x05Reset\x1a\x13\n\x11\x41nalogDetuneQubit\x1a\x19\n\x17\x41nalogDetuneCouplerOnly\x1a\x12\n\x10WaitGateWithUnitB\x06\n\x04gate\"P\n\x07GateSet\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x37\n\x0bvalid_gates\x18\x02 \x03(\x0b\x32\".cirq.google.api.v2.GateDefinition\"\xa1\x01\n\x0eGateDefinition\x12\n\n\x02id\x18\x01 \x01(\t\x12\x18\n\x10number_of_qubits\x18\x02 \x01(\x05\x12\x35\n\nvalid_args\x18\x03 \x03(\x0b\x32!.cirq.google.api.v2.ArgDefinition\x12\x1b\n\x13gate_duration_picos\x18\x04 \x01(\x03\x12\x15\n\rvalid_targets\x18\x05 \x03(\t\"\xda\x01\n\rArgDefinition\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x37\n\x04type\x18\x02 \x01(\x0e\x32).cirq.google.api.v2.ArgDefinition.ArgType\x12\x39\n\x0e\x61llowed_ranges\x18\x03 \x03(\x0b\x32!.cirq.google.api.v2.ArgumentRange\"G\n\x07\x41rgType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\t\n\x05\x46LOAT\x10\x01\x12\x14\n\x10REPEATED_BOOLEAN\x10\x02\x12\n\n\x06STRING\x10\x03\"=\n\rArgumentRange\x12\x15\n\rminimum_value\x18\x01 \x01(\x02\x12\x15\n\rmaximum_value\x18\x02 \x01(\x02\"\xef\x01\n\tTargetSet\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x45\n\x0ftarget_ordering\x18\x02 \x01(\x0e\x32,.cirq.google.api.v2.TargetSet.TargetOrdering\x12+\n\x07targets\x18\x03 \x03(\x0b\x32\x1a.cirq.google.api.v2.Target\"`\n\x0eTargetOrdering\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\r\n\tSYMMETRIC\x10\x01\x12\x12\n\nASYMMETRIC\x10\x02\x1a\x02\x08\x01\x12\x1a\n\x12SUBSET_PERMUTATION\x10\x03\x1a\x02\x08\x01\"\x15\n\x06Target\x12\x0b\n\x03ids\x18\x01 \x03(\tB.\n\x1d\x63om.google.cirq.google.api.v2B\x0b\x44\x65viceProtoP\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x63irq_google/api/v2/device.proto\x12\x12\x63irq.google.api.v2\"\xaf\x03\n\x13\x44\x65viceSpecification\x12\x38\n\x0fvalid_gate_sets\x18\x01 \x03(\x0b\x32\x1b.cirq.google.api.v2.GateSetB\x02\x18\x01\x12:\n\x0bvalid_gates\x18\x05 \x03(\x0b\x32%.cirq.google.api.v2.GateSpecification\x12\x14\n\x0cvalid_qubits\x18\x02 \x03(\t\x12\x34\n\rvalid_targets\x18\x03 \x03(\x0b\x32\x1d.cirq.google.api.v2.TargetSet\x12!\n\x19\x64\x65veloper_recommendations\x18\x04 \x01(\t\x12V\n\x10qubit_attributes\x18\x06 \x03(\x0b\x32<.cirq.google.api.v2.DeviceSpecification.QubitAttributesEntry\x1a[\n\x14QubitAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x32\n\x05value\x18\x02 \x01(\x0b\x32#.cirq.google.api.v2.QubitAttributes:\x02\x38\x01\"\xfb\x0c\n\x11GateSpecification\x12\x1b\n\x13gate_duration_picos\x18\x01 \x01(\x03\x12=\n\x03syc\x18\x02 \x01(\x0b\x32..cirq.google.api.v2.GateSpecification.SycamoreH\x00\x12\x45\n\nsqrt_iswap\x18\x03 \x01(\x0b\x32/.cirq.google.api.v2.GateSpecification.SqrtISwapH\x00\x12L\n\x0esqrt_iswap_inv\x18\x04 \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.SqrtISwapInvH\x00\x12\x36\n\x02\x63z\x18\x05 \x01(\x0b\x32(.cirq.google.api.v2.GateSpecification.CZH\x00\x12\x43\n\tphased_xz\x18\x06 \x01(\x0b\x32..cirq.google.api.v2.GateSpecification.PhasedXZH\x00\x12I\n\x0cvirtual_zpow\x18\x07 \x01(\x0b\x32\x31.cirq.google.api.v2.GateSpecification.VirtualZPowH\x00\x12K\n\rphysical_zpow\x18\x08 \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.PhysicalZPowH\x00\x12K\n\rcoupler_pulse\x18\t \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.CouplerPulseH\x00\x12\x41\n\x04meas\x18\n \x01(\x0b\x32\x31.cirq.google.api.v2.GateSpecification.MeasurementH\x00\x12:\n\x04wait\x18\x0b \x01(\x0b\x32*.cirq.google.api.v2.GateSpecification.WaitH\x00\x12L\n\x0e\x66sim_via_model\x18\x0c \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.FSimViaModelH\x00\x12L\n\x0etwo_pulse_fsim\x18\x13 \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.TwoPulseFSimH\x00\x12\x46\n\x0b\x63z_pow_gate\x18\r \x01(\x0b\x32/.cirq.google.api.v2.GateSpecification.CZPowGateH\x00\x12K\n\rinternal_gate\x18\x0e \x01(\x0b\x32\x32.cirq.google.api.v2.GateSpecification.InternalGateH\x00\x12<\n\x05reset\x18\x0f \x01(\x0b\x32+.cirq.google.api.v2.GateSpecification.ResetH\x00\x12V\n\x13\x61nalog_detune_qubit\x18\x10 \x01(\x0b\x32\x37.cirq.google.api.v2.GateSpecification.AnalogDetuneQubitH\x00\x12\x63\n\x1a\x61nalog_detune_coupler_only\x18\x11 \x01(\x0b\x32=.cirq.google.api.v2.GateSpecification.AnalogDetuneCouplerOnlyH\x00\x12U\n\x13wait_gate_with_unit\x18\x12 \x01(\x0b\x32\x36.cirq.google.api.v2.GateSpecification.WaitGateWithUnitH\x00\x1a\n\n\x08Sycamore\x1a\x0b\n\tSqrtISwap\x1a\x0e\n\x0cSqrtISwapInv\x1a\x04\n\x02\x43Z\x1a\n\n\x08PhasedXZ\x1a\r\n\x0bVirtualZPow\x1a\x0e\n\x0cPhysicalZPow\x1a\x0e\n\x0c\x43ouplerPulse\x1a\r\n\x0bMeasurement\x1a\x06\n\x04Wait\x1a\x0e\n\x0c\x46SimViaModel\x1a\x0e\n\x0cTwoPulseFSim\x1a\x0b\n\tCZPowGate\x1a\x0e\n\x0cInternalGate\x1a\x07\n\x05Reset\x1a\x13\n\x11\x41nalogDetuneQubit\x1a\x19\n\x17\x41nalogDetuneCouplerOnly\x1a\x12\n\x10WaitGateWithUnitB\x06\n\x04gate\"P\n\x07GateSet\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x37\n\x0bvalid_gates\x18\x02 \x03(\x0b\x32\".cirq.google.api.v2.GateDefinition\"\xa1\x01\n\x0eGateDefinition\x12\n\n\x02id\x18\x01 \x01(\t\x12\x18\n\x10number_of_qubits\x18\x02 \x01(\x05\x12\x35\n\nvalid_args\x18\x03 \x03(\x0b\x32!.cirq.google.api.v2.ArgDefinition\x12\x1b\n\x13gate_duration_picos\x18\x04 \x01(\x03\x12\x15\n\rvalid_targets\x18\x05 \x03(\t\"\xda\x01\n\rArgDefinition\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x37\n\x04type\x18\x02 \x01(\x0e\x32).cirq.google.api.v2.ArgDefinition.ArgType\x12\x39\n\x0e\x61llowed_ranges\x18\x03 \x03(\x0b\x32!.cirq.google.api.v2.ArgumentRange\"G\n\x07\x41rgType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\t\n\x05\x46LOAT\x10\x01\x12\x14\n\x10REPEATED_BOOLEAN\x10\x02\x12\n\n\x06STRING\x10\x03\"=\n\rArgumentRange\x12\x15\n\rminimum_value\x18\x01 \x01(\x02\x12\x15\n\rmaximum_value\x18\x02 \x01(\x02\"\xef\x01\n\tTargetSet\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x45\n\x0ftarget_ordering\x18\x02 \x01(\x0e\x32,.cirq.google.api.v2.TargetSet.TargetOrdering\x12+\n\x07targets\x18\x03 \x03(\x0b\x32\x1a.cirq.google.api.v2.Target\"`\n\x0eTargetOrdering\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\r\n\tSYMMETRIC\x10\x01\x12\x12\n\nASYMMETRIC\x10\x02\x1a\x02\x08\x01\x12\x1a\n\x12SUBSET_PERMUTATION\x10\x03\x1a\x02\x08\x01\"\x15\n\x06Target\x12\x0b\n\x03ids\x18\x01 \x03(\t\"\xb6\x01\n\x0fQubitAttributes\x12G\n\nattributes\x18\x01 \x03(\x0b\x32\x33.cirq.google.api.v2.QubitAttributes.AttributesEntry\x1aZ\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x36\n\x05value\x18\x02 \x01(\x0b\x32\'.cirq.google.api.v2.QubitAttributeValue:\x02\x38\x01\"w\n\x13QubitAttributeValue\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x13\n\tint_value\x18\x02 \x01(\x03H\x00\x12\x16\n\x0c\x64ouble_value\x18\x03 \x01(\x01H\x00\x12\x14\n\nbool_value\x18\x04 \x01(\x08H\x00\x42\x05\n\x03valB.\n\x1d\x63om.google.cirq.google.api.v2B\x0b\x44\x65viceProtoP\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -32,66 +32,78 @@ if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\n\035com.google.cirq.google.api.v2B\013DeviceProtoP\001' + _globals['_DEVICESPECIFICATION_QUBITATTRIBUTESENTRY']._loaded_options = None + _globals['_DEVICESPECIFICATION_QUBITATTRIBUTESENTRY']._serialized_options = b'8\001' _globals['_DEVICESPECIFICATION'].fields_by_name['valid_gate_sets']._loaded_options = None _globals['_DEVICESPECIFICATION'].fields_by_name['valid_gate_sets']._serialized_options = b'\030\001' _globals['_TARGETSET_TARGETORDERING'].values_by_name["ASYMMETRIC"]._loaded_options = None _globals['_TARGETSET_TARGETORDERING'].values_by_name["ASYMMETRIC"]._serialized_options = b'\010\001' _globals['_TARGETSET_TARGETORDERING'].values_by_name["SUBSET_PERMUTATION"]._loaded_options = None _globals['_TARGETSET_TARGETORDERING'].values_by_name["SUBSET_PERMUTATION"]._serialized_options = b'\010\001' + _globals['_QUBITATTRIBUTES_ATTRIBUTESENTRY']._loaded_options = None + _globals['_QUBITATTRIBUTES_ATTRIBUTESENTRY']._serialized_options = b'8\001' _globals['_DEVICESPECIFICATION']._serialized_start=56 - _globals['_DEVICESPECIFICATION']._serialized_end=306 - _globals['_GATESPECIFICATION']._serialized_start=309 - _globals['_GATESPECIFICATION']._serialized_end=1968 - _globals['_GATESPECIFICATION_SYCAMORE']._serialized_start=1695 - _globals['_GATESPECIFICATION_SYCAMORE']._serialized_end=1705 - _globals['_GATESPECIFICATION_SQRTISWAP']._serialized_start=1707 - _globals['_GATESPECIFICATION_SQRTISWAP']._serialized_end=1718 - _globals['_GATESPECIFICATION_SQRTISWAPINV']._serialized_start=1720 - _globals['_GATESPECIFICATION_SQRTISWAPINV']._serialized_end=1734 - _globals['_GATESPECIFICATION_CZ']._serialized_start=1736 - _globals['_GATESPECIFICATION_CZ']._serialized_end=1740 - _globals['_GATESPECIFICATION_PHASEDXZ']._serialized_start=1742 - _globals['_GATESPECIFICATION_PHASEDXZ']._serialized_end=1752 - _globals['_GATESPECIFICATION_VIRTUALZPOW']._serialized_start=1754 - _globals['_GATESPECIFICATION_VIRTUALZPOW']._serialized_end=1767 - _globals['_GATESPECIFICATION_PHYSICALZPOW']._serialized_start=1769 - _globals['_GATESPECIFICATION_PHYSICALZPOW']._serialized_end=1783 - _globals['_GATESPECIFICATION_COUPLERPULSE']._serialized_start=1785 - _globals['_GATESPECIFICATION_COUPLERPULSE']._serialized_end=1799 - _globals['_GATESPECIFICATION_MEASUREMENT']._serialized_start=1801 - _globals['_GATESPECIFICATION_MEASUREMENT']._serialized_end=1814 - _globals['_GATESPECIFICATION_WAIT']._serialized_start=1816 - _globals['_GATESPECIFICATION_WAIT']._serialized_end=1822 - _globals['_GATESPECIFICATION_FSIMVIAMODEL']._serialized_start=1824 - _globals['_GATESPECIFICATION_FSIMVIAMODEL']._serialized_end=1838 - _globals['_GATESPECIFICATION_TWOPULSEFSIM']._serialized_start=1840 - _globals['_GATESPECIFICATION_TWOPULSEFSIM']._serialized_end=1854 - _globals['_GATESPECIFICATION_CZPOWGATE']._serialized_start=1856 - _globals['_GATESPECIFICATION_CZPOWGATE']._serialized_end=1867 - _globals['_GATESPECIFICATION_INTERNALGATE']._serialized_start=1869 - _globals['_GATESPECIFICATION_INTERNALGATE']._serialized_end=1883 - _globals['_GATESPECIFICATION_RESET']._serialized_start=1885 - _globals['_GATESPECIFICATION_RESET']._serialized_end=1892 - _globals['_GATESPECIFICATION_ANALOGDETUNEQUBIT']._serialized_start=1894 - _globals['_GATESPECIFICATION_ANALOGDETUNEQUBIT']._serialized_end=1913 - _globals['_GATESPECIFICATION_ANALOGDETUNECOUPLERONLY']._serialized_start=1915 - _globals['_GATESPECIFICATION_ANALOGDETUNECOUPLERONLY']._serialized_end=1940 - _globals['_GATESPECIFICATION_WAITGATEWITHUNIT']._serialized_start=1942 - _globals['_GATESPECIFICATION_WAITGATEWITHUNIT']._serialized_end=1960 - _globals['_GATESET']._serialized_start=1970 - _globals['_GATESET']._serialized_end=2050 - _globals['_GATEDEFINITION']._serialized_start=2053 - _globals['_GATEDEFINITION']._serialized_end=2214 - _globals['_ARGDEFINITION']._serialized_start=2217 - _globals['_ARGDEFINITION']._serialized_end=2435 - _globals['_ARGDEFINITION_ARGTYPE']._serialized_start=2364 - _globals['_ARGDEFINITION_ARGTYPE']._serialized_end=2435 - _globals['_ARGUMENTRANGE']._serialized_start=2437 - _globals['_ARGUMENTRANGE']._serialized_end=2498 - _globals['_TARGETSET']._serialized_start=2501 - _globals['_TARGETSET']._serialized_end=2740 - _globals['_TARGETSET_TARGETORDERING']._serialized_start=2644 - _globals['_TARGETSET_TARGETORDERING']._serialized_end=2740 - _globals['_TARGET']._serialized_start=2742 - _globals['_TARGET']._serialized_end=2763 + _globals['_DEVICESPECIFICATION']._serialized_end=487 + _globals['_DEVICESPECIFICATION_QUBITATTRIBUTESENTRY']._serialized_start=396 + _globals['_DEVICESPECIFICATION_QUBITATTRIBUTESENTRY']._serialized_end=487 + _globals['_GATESPECIFICATION']._serialized_start=490 + _globals['_GATESPECIFICATION']._serialized_end=2149 + _globals['_GATESPECIFICATION_SYCAMORE']._serialized_start=1876 + _globals['_GATESPECIFICATION_SYCAMORE']._serialized_end=1886 + _globals['_GATESPECIFICATION_SQRTISWAP']._serialized_start=1888 + _globals['_GATESPECIFICATION_SQRTISWAP']._serialized_end=1899 + _globals['_GATESPECIFICATION_SQRTISWAPINV']._serialized_start=1901 + _globals['_GATESPECIFICATION_SQRTISWAPINV']._serialized_end=1915 + _globals['_GATESPECIFICATION_CZ']._serialized_start=1917 + _globals['_GATESPECIFICATION_CZ']._serialized_end=1921 + _globals['_GATESPECIFICATION_PHASEDXZ']._serialized_start=1923 + _globals['_GATESPECIFICATION_PHASEDXZ']._serialized_end=1933 + _globals['_GATESPECIFICATION_VIRTUALZPOW']._serialized_start=1935 + _globals['_GATESPECIFICATION_VIRTUALZPOW']._serialized_end=1948 + _globals['_GATESPECIFICATION_PHYSICALZPOW']._serialized_start=1950 + _globals['_GATESPECIFICATION_PHYSICALZPOW']._serialized_end=1964 + _globals['_GATESPECIFICATION_COUPLERPULSE']._serialized_start=1966 + _globals['_GATESPECIFICATION_COUPLERPULSE']._serialized_end=1980 + _globals['_GATESPECIFICATION_MEASUREMENT']._serialized_start=1982 + _globals['_GATESPECIFICATION_MEASUREMENT']._serialized_end=1995 + _globals['_GATESPECIFICATION_WAIT']._serialized_start=1997 + _globals['_GATESPECIFICATION_WAIT']._serialized_end=2003 + _globals['_GATESPECIFICATION_FSIMVIAMODEL']._serialized_start=2005 + _globals['_GATESPECIFICATION_FSIMVIAMODEL']._serialized_end=2019 + _globals['_GATESPECIFICATION_TWOPULSEFSIM']._serialized_start=2021 + _globals['_GATESPECIFICATION_TWOPULSEFSIM']._serialized_end=2035 + _globals['_GATESPECIFICATION_CZPOWGATE']._serialized_start=2037 + _globals['_GATESPECIFICATION_CZPOWGATE']._serialized_end=2048 + _globals['_GATESPECIFICATION_INTERNALGATE']._serialized_start=2050 + _globals['_GATESPECIFICATION_INTERNALGATE']._serialized_end=2064 + _globals['_GATESPECIFICATION_RESET']._serialized_start=2066 + _globals['_GATESPECIFICATION_RESET']._serialized_end=2073 + _globals['_GATESPECIFICATION_ANALOGDETUNEQUBIT']._serialized_start=2075 + _globals['_GATESPECIFICATION_ANALOGDETUNEQUBIT']._serialized_end=2094 + _globals['_GATESPECIFICATION_ANALOGDETUNECOUPLERONLY']._serialized_start=2096 + _globals['_GATESPECIFICATION_ANALOGDETUNECOUPLERONLY']._serialized_end=2121 + _globals['_GATESPECIFICATION_WAITGATEWITHUNIT']._serialized_start=2123 + _globals['_GATESPECIFICATION_WAITGATEWITHUNIT']._serialized_end=2141 + _globals['_GATESET']._serialized_start=2151 + _globals['_GATESET']._serialized_end=2231 + _globals['_GATEDEFINITION']._serialized_start=2234 + _globals['_GATEDEFINITION']._serialized_end=2395 + _globals['_ARGDEFINITION']._serialized_start=2398 + _globals['_ARGDEFINITION']._serialized_end=2616 + _globals['_ARGDEFINITION_ARGTYPE']._serialized_start=2545 + _globals['_ARGDEFINITION_ARGTYPE']._serialized_end=2616 + _globals['_ARGUMENTRANGE']._serialized_start=2618 + _globals['_ARGUMENTRANGE']._serialized_end=2679 + _globals['_TARGETSET']._serialized_start=2682 + _globals['_TARGETSET']._serialized_end=2921 + _globals['_TARGETSET_TARGETORDERING']._serialized_start=2825 + _globals['_TARGETSET_TARGETORDERING']._serialized_end=2921 + _globals['_TARGET']._serialized_start=2923 + _globals['_TARGET']._serialized_end=2944 + _globals['_QUBITATTRIBUTES']._serialized_start=2947 + _globals['_QUBITATTRIBUTES']._serialized_end=3129 + _globals['_QUBITATTRIBUTES_ATTRIBUTESENTRY']._serialized_start=3039 + _globals['_QUBITATTRIBUTES_ATTRIBUTESENTRY']._serialized_end=3129 + _globals['_QUBITATTRIBUTEVALUE']._serialized_start=3131 + _globals['_QUBITATTRIBUTEVALUE']._serialized_end=3250 # @@protoc_insertion_point(module_scope) diff --git a/cirq-google/cirq_google/api/v2/device_pb2.pyi b/cirq-google/cirq_google/api/v2/device_pb2.pyi index 3cdecca125a..53aadab072f 100644 --- a/cirq-google/cirq_google/api/v2/device_pb2.pyi +++ b/cirq-google/cirq_google/api/v2/device_pb2.pyi @@ -34,11 +34,33 @@ class DeviceSpecification(_message.Message): DESCRIPTOR: _descriptor.Descriptor + @_typing.final + class QubitAttributesEntry(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + + KEY_FIELD_NUMBER: _builtins.int + VALUE_FIELD_NUMBER: _builtins.int + key: _builtins.str + @_builtins.property + def value(self) -> Global___QubitAttributes: ... + def __init__( + self, + *, + key: _builtins.str = ..., + value: Global___QubitAttributes | None = ..., + ) -> None: ... + _HasFieldArgType: _TypeAlias = _typing.Literal["value", b"value"] # noqa: Y015 + def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... + _ClearFieldArgType: _TypeAlias = _typing.Literal["key", b"key", "value", b"value"] # noqa: Y015 + def ClearField(self, field_name: _ClearFieldArgType) -> None: ... + def WhichOneof(self, oneof_group: _Never) -> None: ... + VALID_GATE_SETS_FIELD_NUMBER: _builtins.int VALID_GATES_FIELD_NUMBER: _builtins.int VALID_QUBITS_FIELD_NUMBER: _builtins.int VALID_TARGETS_FIELD_NUMBER: _builtins.int DEVELOPER_RECOMMENDATIONS_FIELD_NUMBER: _builtins.int + QUBIT_ATTRIBUTES_FIELD_NUMBER: _builtins.int developer_recommendations: _builtins.str """Additional recommendations, caveats, and soft requirements that are advice to users of the device, specified in English text @@ -71,6 +93,10 @@ class DeviceSpecification(_message.Message): def valid_targets(self) -> _containers.RepeatedCompositeFieldContainer[Global___TargetSet]: """A list of targets that gates can use.""" + @_builtins.property + def qubit_attributes(self) -> _containers.MessageMap[_builtins.str, Global___QubitAttributes]: + """Qubit attributes for the device.""" + def __init__( self, *, @@ -79,10 +105,11 @@ class DeviceSpecification(_message.Message): valid_qubits: _abc.Iterable[_builtins.str] | None = ..., valid_targets: _abc.Iterable[Global___TargetSet] | None = ..., developer_recommendations: _builtins.str = ..., + qubit_attributes: _abc.Mapping[_builtins.str, Global___QubitAttributes] | None = ..., ) -> None: ... _HasFieldArgType: _TypeAlias = _Never # noqa: Y015 def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... - _ClearFieldArgType: _TypeAlias = _typing.Literal["developer_recommendations", b"developer_recommendations", "valid_gate_sets", b"valid_gate_sets", "valid_gates", b"valid_gates", "valid_qubits", b"valid_qubits", "valid_targets", b"valid_targets"] # noqa: Y015 + _ClearFieldArgType: _TypeAlias = _typing.Literal["developer_recommendations", b"developer_recommendations", "qubit_attributes", b"qubit_attributes", "valid_gate_sets", b"valid_gate_sets", "valid_gates", b"valid_gates", "valid_qubits", b"valid_qubits", "valid_targets", b"valid_targets"] # noqa: Y015 def ClearField(self, field_name: _ClearFieldArgType) -> None: ... def WhichOneof(self, oneof_group: _Never) -> None: ... @@ -709,3 +736,78 @@ class Target(_message.Message): def WhichOneof(self, oneof_group: _Never) -> None: ... Global___Target: _TypeAlias = Target # noqa: Y015 + +@_typing.final +class QubitAttributes(_message.Message): + """Qubit attributes for a specific qubit.""" + + DESCRIPTOR: _descriptor.Descriptor + + @_typing.final + class AttributesEntry(_message.Message): + DESCRIPTOR: _descriptor.Descriptor + + KEY_FIELD_NUMBER: _builtins.int + VALUE_FIELD_NUMBER: _builtins.int + key: _builtins.str + @_builtins.property + def value(self) -> Global___QubitAttributeValue: ... + def __init__( + self, + *, + key: _builtins.str = ..., + value: Global___QubitAttributeValue | None = ..., + ) -> None: ... + _HasFieldArgType: _TypeAlias = _typing.Literal["value", b"value"] # noqa: Y015 + def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... + _ClearFieldArgType: _TypeAlias = _typing.Literal["key", b"key", "value", b"value"] # noqa: Y015 + def ClearField(self, field_name: _ClearFieldArgType) -> None: ... + def WhichOneof(self, oneof_group: _Never) -> None: ... + + ATTRIBUTES_FIELD_NUMBER: _builtins.int + @_builtins.property + def attributes(self) -> _containers.MessageMap[_builtins.str, Global___QubitAttributeValue]: ... + def __init__( + self, + *, + attributes: _abc.Mapping[_builtins.str, Global___QubitAttributeValue] | None = ..., + ) -> None: ... + _HasFieldArgType: _TypeAlias = _Never # noqa: Y015 + def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... + _ClearFieldArgType: _TypeAlias = _typing.Literal["attributes", b"attributes"] # noqa: Y015 + def ClearField(self, field_name: _ClearFieldArgType) -> None: ... + def WhichOneof(self, oneof_group: _Never) -> None: ... + +Global___QubitAttributes: _TypeAlias = QubitAttributes # noqa: Y015 + +@_typing.final +class QubitAttributeValue(_message.Message): + """A generic value for a qubit attribute.""" + + DESCRIPTOR: _descriptor.Descriptor + + STRING_VALUE_FIELD_NUMBER: _builtins.int + INT_VALUE_FIELD_NUMBER: _builtins.int + DOUBLE_VALUE_FIELD_NUMBER: _builtins.int + BOOL_VALUE_FIELD_NUMBER: _builtins.int + string_value: _builtins.str + int_value: _builtins.int + double_value: _builtins.float + bool_value: _builtins.bool + def __init__( + self, + *, + string_value: _builtins.str = ..., + int_value: _builtins.int = ..., + double_value: _builtins.float = ..., + bool_value: _builtins.bool = ..., + ) -> None: ... + _HasFieldArgType: _TypeAlias = _typing.Literal["bool_value", b"bool_value", "double_value", b"double_value", "int_value", b"int_value", "string_value", b"string_value", "val", b"val"] # noqa: Y015 + def HasField(self, field_name: _HasFieldArgType) -> _builtins.bool: ... + _ClearFieldArgType: _TypeAlias = _typing.Literal["bool_value", b"bool_value", "double_value", b"double_value", "int_value", b"int_value", "string_value", b"string_value", "val", b"val"] # noqa: Y015 + def ClearField(self, field_name: _ClearFieldArgType) -> None: ... + _WhichOneofReturnType_val: _TypeAlias = _typing.Literal["string_value", "int_value", "double_value", "bool_value"] # noqa: Y015 + _WhichOneofArgType_val: _TypeAlias = _typing.Literal["val", b"val"] # noqa: Y015 + def WhichOneof(self, oneof_group: _WhichOneofArgType_val) -> _WhichOneofReturnType_val | None: ... + +Global___QubitAttributeValue: _TypeAlias = QubitAttributeValue # noqa: Y015 diff --git a/cirq-google/cirq_google/devices/grid_device.py b/cirq-google/cirq_google/devices/grid_device.py index 5c197b84069..5842710c68a 100644 --- a/cirq-google/cirq_google/devices/grid_device.py +++ b/cirq-google/cirq_google/devices/grid_device.py @@ -192,6 +192,13 @@ class _GateRepresentations: ] +def _qubit_attribute_value_from_proto( + val_proto: v2.device_pb2.QubitAttributeValue, +) -> cirq.devices.TQubitAttributeValue: + which_val = val_proto.WhichOneof("val") + return getattr(val_proto, which_val) if which_val is not None else None + + def _validate_device_specification(proto: v2.device_pb2.DeviceSpecification) -> None: """Raises a ValueError if the `DeviceSpecification` proto is invalid.""" @@ -467,6 +474,12 @@ def __init__(self, metadata: cirq.GridDeviceMetadata): """ self._metadata = metadata + @property + def qubit_attributes( + self, + ) -> Mapping[cirq.GridQubit, Mapping[str, cirq.devices.TQubitAttributeValue]]: + return self._metadata.qubit_attributes + @classmethod def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> GridDevice: """Deserializes the `DeviceSpecification` to a `GridDevice`. @@ -499,6 +512,16 @@ def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> GridDevice: gateset, gate_durations = _deserialize_gateset_and_gate_durations(proto) + # Create qubit attributes + qubit_attributes = {} + for qubit_str, qubit_attrs_proto in proto.qubit_attributes.items(): + qubit = v2.grid_qubit_from_proto_id(qubit_str) + attrs = { + name: _qubit_attribute_value_from_proto(val_proto) + for name, val_proto in qubit_attrs_proto.attributes.items() + } + qubit_attributes[qubit] = attrs + try: metadata = cirq.GridDeviceMetadata( qubit_pairs=qubit_pairs, @@ -506,6 +529,7 @@ def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> GridDevice: gate_durations=gate_durations if len(gate_durations) > 0 else None, all_qubits=all_qubits, compilation_target_gatesets=_build_compilation_target_gatesets(gateset), + qubit_attributes=qubit_attributes, ) except ValueError as ve: # pragma: no cover # Spec errors should have been caught in validation above. @@ -547,6 +571,27 @@ def to_proto( _serialize_gateset_and_gate_durations( out, gateset, {} if gate_durations is None else gate_durations ) + for qubit, attrs in self.qubit_attributes.items(): + qubit_str = v2.qubit_to_proto_id(qubit) + qubit_attrs_proto = out.qubit_attributes[qubit_str] + for attr_name, attr_val in attrs.items(): + val_proto = qubit_attrs_proto.attributes[attr_name] + if isinstance(attr_val, bool): + val_proto.bool_value = attr_val + elif isinstance(attr_val, int): + val_proto.int_value = attr_val + elif isinstance(attr_val, float): + val_proto.double_value = attr_val + elif isinstance(attr_val, str): + val_proto.string_value = attr_val + elif attr_val is None: + # leave unset + pass + else: + raise ValueError( + f"Unsupported attribute value type: {type(attr_val)}" + ) # pragma: no cover + _validate_device_specification(out) return out diff --git a/cirq-google/cirq_google/devices/grid_device_test.py b/cirq-google/cirq_google/devices/grid_device_test.py index 1e46f0c2bd0..b0020ba70ce 100644 --- a/cirq-google/cirq_google/devices/grid_device_test.py +++ b/cirq-google/cirq_google/devices/grid_device_test.py @@ -811,3 +811,52 @@ def test_to_proto(): assert cirq_google.GridDevice.from_proto(spec) == cirq_google.GridDevice.from_proto( expected_spec ) + + +def test_qubit_attributes_serialization_deserialization(): + device_info, spec = _create_device_spec_with_horizontal_couplings() + + # Add qubit attributes to the proto + q1_str = v2.qubit_to_proto_id(cirq.GridQubit(0, 0)) + q2_str = v2.qubit_to_proto_id(cirq.GridQubit(0, 1)) + + # Add attributes to q1 + qa1 = spec.qubit_attributes[q1_str] + qa1.attributes["type"].string_value = "transmon" + qa1.attributes["frequency"].double_value = 5.123 + + # Add attributes to q2 + qa2 = spec.qubit_attributes[q2_str] + qa2.attributes["index"].int_value = 42 + qa2.attributes["is_active"].bool_value = True + + # Add an unset value (representing None) + _ = qa2.attributes["calibration_status"] + + # Deserialize and verify Python object attributes + device = cirq_google.GridDevice.from_proto(spec) + + expected_attrs = { + cirq.GridQubit(0, 0): {"type": "transmon", "frequency": 5.123}, + cirq.GridQubit(0, 1): {"index": 42, "is_active": True, "calibration_status": None}, + } + assert device.qubit_attributes == expected_attrs + + # Serialize back and verify it matches the spec with attributes + reserialized_spec = device.to_proto() + + # Check that they serialize to identical structures + # We can parse it back again to verify + device_parsed_again = cirq_google.GridDevice.from_proto(reserialized_spec) + assert device_parsed_again.qubit_attributes == expected_attrs + + +def test_qubit_attributes_unlisted_qubit(): + device_info, spec = _create_device_spec_with_horizontal_couplings() + + # Add attributes for an unlisted qubit + qa = spec.qubit_attributes["10_10"] # Not in valid_qubits + qa.attributes["foo"].string_value = "bar" + + device = cirq_google.GridDevice.from_proto(spec) + assert device.qubit_attributes[cirq.GridQubit(10, 10)] == {"foo": "bar"} diff --git a/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.json b/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.json index b4497a67584..1b76b8b684c 100644 --- a/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.json +++ b/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.json @@ -267,6 +267,7 @@ "required_sqrt_iswap_count": null, "use_sqrt_iswap_inv": false } - ] + ], + "qubit_attributes": [] } } \ No newline at end of file diff --git a/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.repr b/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.repr index a5ab82c61c1..d27ec9aa876 100644 --- a/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.repr +++ b/cirq-google/cirq_google/json_test_data/cirq.google.GridDevice.repr @@ -1 +1 @@ -cirq_google.GridDevice(cirq.GridDeviceMetadata(frozenset({(cirq.GridQubit(0, 1), cirq.GridQubit(0, 2)), (cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), (cirq.GridQubit(0, 1), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), (cirq.GridQubit(1, 1), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 2), cirq.GridQubit(1, 2))}), cirq.Gateset(cirq.ops.common_gates.XPowGate, cirq.ops.common_gates.YPowGate, cirq.ops.common_gates.ZPowGate, cirq.CZ, (cirq.ISWAP**0.5), unroll_circuit_op = True), {cirq.GateFamily(gate=cirq.ops.common_gates.XPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(nanos=1), cirq.GateFamily(gate=cirq.ops.common_gates.YPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.ops.common_gates.ZPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.CZ, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=500), cirq.GateFamily(gate=(cirq.ISWAP**0.5), ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=600)}, frozenset({cirq.GridQubit(9, 9), cirq.GridQubit(0, 1), cirq.GridQubit(1, 1), cirq.GridQubit(0, 0), cirq.GridQubit(1, 2), cirq.GridQubit(10, 10), cirq.GridQubit(1, 0), cirq.GridQubit(0, 2)}), (cirq.CZTargetGateset(atol=1e-08, allow_partial_czs=False), cirq.SqrtIswapTargetGateset(atol=1e-08, required_sqrt_iswap_count=None, use_sqrt_iswap_inv=False)))) \ No newline at end of file +cirq_google.GridDevice(cirq.GridDeviceMetadata(frozenset({(cirq.GridQubit(0, 1), cirq.GridQubit(0, 2)), (cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)), (cirq.GridQubit(0, 1), cirq.GridQubit(1, 1)), (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), (cirq.GridQubit(1, 1), cirq.GridQubit(1, 2)), (cirq.GridQubit(0, 2), cirq.GridQubit(1, 2))}), cirq.Gateset(cirq.ops.common_gates.XPowGate, cirq.ops.common_gates.YPowGate, cirq.ops.common_gates.ZPowGate, cirq.CZ, (cirq.ISWAP**0.5), unroll_circuit_op = True), {cirq.GateFamily(gate=cirq.ops.common_gates.XPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(nanos=1), cirq.GateFamily(gate=cirq.ops.common_gates.YPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.ops.common_gates.ZPowGate, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=1), cirq.GateFamily(gate=cirq.CZ, ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=500), cirq.GateFamily(gate=(cirq.ISWAP**0.5), ignore_global_phase=True, tags_to_accept=frozenset(), tags_to_ignore=frozenset()): cirq.Duration(picos=600)}, frozenset({cirq.GridQubit(9, 9), cirq.GridQubit(0, 1), cirq.GridQubit(1, 1), cirq.GridQubit(0, 0), cirq.GridQubit(1, 2), cirq.GridQubit(10, 10), cirq.GridQubit(1, 0), cirq.GridQubit(0, 2)}), (cirq.CZTargetGateset(atol=1e-08, allow_partial_czs=False), cirq.SqrtIswapTargetGateset(atol=1e-08, required_sqrt_iswap_count=None, use_sqrt_iswap_inv=False)), {})) \ No newline at end of file