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 DynamicalDecouplingTag to cirq-google #6782

Merged
merged 15 commits into from
Nov 11, 2024
12 changes: 12 additions & 0 deletions cirq-google/cirq_google/api/v2/program.proto
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,18 @@ message Operation {
string token_value = 4;
int32 token_constant_index = 5;
}

repeated Tag tags = 22;
}

message DynamicalDecouplingTag {
optional string protocol = 1;
}

message Tag {
oneof tag {
DynamicalDecouplingTag dynamical_decoupling = 1;
}
}

// The instruction identifying the action taken on the quantum computer.
Expand Down
116 changes: 60 additions & 56 deletions cirq-google/cirq_google/api/v2/program_pb2.py

Large diffs are not rendered by default.

41 changes: 40 additions & 1 deletion cirq-google/cirq_google/api/v2/program_pb2.pyi

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions cirq-google/cirq_google/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@
from cirq_google.ops.sycamore_gate import SycamoreGate as SycamoreGate, SYC as SYC

from cirq_google.ops.internal_gate import InternalGate as InternalGate

from cirq_google.ops.dynamical_decoupling_tag import (
DynamicalDecouplingTag as DynamicalDecouplingTag,
)
56 changes: 56 additions & 0 deletions cirq-google/cirq_google/ops/dynamical_decoupling_tag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright 2024 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.

from typing import Optional

import attrs

from cirq_google.api.v2 import program_pb2


SUPPORTED_DD_PROTOCOLS = frozenset(
[
"X", # An even number of X
"Y", # An even number of Y
"XY4", # Repetitions of XYXY blocks.
"XY8", # Repetitions of XYXYYXYX blocks.
]
)


@attrs.frozen
class DynamicalDecouplingTag:
"""A tag to indicate using DD to fill qubit time.

Attributes:
protocol: The name of the decoupling protocol (eg 'X', 'XY4').
"""

protocol: str = attrs.field() # Which DD protocol to use.
NoureldinYosri marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: attrs.field() seems unnecessary. Also please describe in the docstring, e.g.,

    Attributes:
        protocol: The name of the decoupling protocol (eg 'X', 'XY4').

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added the docstring

Nit: attrs.field() seems unnecessary. Also please describe in the docstring, e.g.,

this is requried by attrs for the validation code


@protocol.validator
def _validate_protocol(self, attribute, value):
assert value in SUPPORTED_DD_PROTOCOLS

def to_proto(
self, msg: Optional[program_pb2.DynamicalDecouplingTag] = None
) -> program_pb2.DynamicalDecouplingTag:
if msg is None:
msg = program_pb2.DynamicalDecouplingTag()
msg.protocol = self.protocol
return msg

@staticmethod
def from_proto(msg: program_pb2.DynamicalDecouplingTag) -> 'DynamicalDecouplingTag':
return DynamicalDecouplingTag(protocol=msg.protocol)
28 changes: 28 additions & 0 deletions cirq-google/cirq_google/ops/dynamical_decoupling_tag_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Copyright 2024 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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 pytest
NoureldinYosri marked this conversation as resolved.
Show resolved Hide resolved

from cirq_google.ops.dynamical_decoupling_tag import DynamicalDecouplingTag


def test_invalid_value():
with pytest.raises(AssertionError):
_ = DynamicalDecouplingTag('_Z')


def test_proto_serialization():
tag = DynamicalDecouplingTag('X')
msg = tag.to_proto()
assert tag == DynamicalDecouplingTag.from_proto(msg)
13 changes: 11 additions & 2 deletions cirq-google/cirq_google/serialization/circuit_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import cirq
from cirq_google.api import v2
from cirq_google.ops import PhysicalZTag, InternalGate, FSimViaModelTag
from cirq_google.ops import PhysicalZTag, InternalGate, FSimViaModelTag, DynamicalDecouplingTag
from cirq_google.ops.calibration_tag import CalibrationTag
from cirq_google.experimental.ops import CouplerPulse
from cirq_google.serialization import serializer, op_deserializer, op_serializer, arg_func_langs
Expand Down Expand Up @@ -296,6 +296,8 @@ def _serialize_gate_op(
constants.append(constant)
if raw_constants is not None:
raw_constants[tag.token] = msg.token_constant_index
elif isinstance(tag, DynamicalDecouplingTag):
tag.to_proto(msg=msg.tags.add().dynamical_decoupling)
return msg

def _serialize_circuit_op(
Expand Down Expand Up @@ -417,13 +419,14 @@ def _deserialize_circuit(
for moment_proto in circuit_proto.moments:
moment_ops = []
for op in moment_proto.operations:
tags = [self._deserialize_tag(tag) for tag in op.tags]
moment_ops.append(
self._deserialize_gate_op(
op,
arg_function_language=arg_function_language,
constants=constants,
deserialized_constants=deserialized_constants,
)
).with_tags(*tags)
)
for op in moment_proto.circuit_operations:
moment_ops.append(
Expand Down Expand Up @@ -737,5 +740,11 @@ def _deserialize_circuit_op(
deserialized_constants=deserialized_constants,
)

def _deserialize_tag(self, msg: v2.program_pb2.Tag):
which = msg.WhichOneof('tag')
if which == 'dynamical_decoupling':
return DynamicalDecouplingTag.from_proto(msg.dynamical_decoupling)
raise ValueError(f'unsupported tag {msg=}') # pragma: no cover


CIRCUIT_SERIALIZER = CircuitSerializer()
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,15 @@ def test_circuit_with_couplerpulse():
assert cg.CIRCUIT_SERIALIZER.deserialize(msg) == circuit


def test_circuit_with_dd_tag():
tag = cg.ops.DynamicalDecouplingTag('X')
c = cirq.Circuit(cirq.X(cirq.q(0)).with_tags(tag))
msg = cg.CIRCUIT_SERIALIZER.serialize(c)
nc = cg.CIRCUIT_SERIALIZER.deserialize(msg)
assert c == nc
assert nc[0].operations[0].tags == (tag,)


def test_circuit_with_units():
c = cirq.Circuit(
cg.InternalGate(
Expand Down