Skip to content

Commit

Permalink
Address comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
babacry committed May 1, 2024
1 parent 6e65ad8 commit 3a6a9b4
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
"cirq_type": "DynamicalDecouplingModel",
"base_dd_sequence": [
{
"cirq_type": "XPowGate",
"cirq_type": "_PauliX",
"exponent": 1.0,
"global_shift": 0.0
},
{
"cirq_type": "XPowGate",
"cirq_type": "_PauliX",
"exponent": 1.0,
"global_shift": 0.0
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[cirq.DynamicalDecouplingModel.from_schema("XX_PAIR"),
cirq.DynamicalDecouplingModel(base_dd_sequence=[cirq.XPowGate(), cirq.XPowGate()])]
cirq.DynamicalDecouplingModel.from_base_dd_sequence(base_dd_sequence=[cirq.X, cirq.X])]
51 changes: 33 additions & 18 deletions cirq-core/cirq/transformers/dynamical_decoupling.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""Transformer pass that adds dynamical decoupling moments to a circuit."""
"""Transformer pass that adds dynamical decoupling operations to a circuit."""

import enum
from functools import reduce
from typing import Any, Dict, Optional, Tuple

from cirq.transformers import transformer_api
import cirq
from cirq import value
import numpy as np
Expand All @@ -28,7 +29,9 @@ class _DynamicalDecouplingSchema(enum.Enum):
"""Supported schemes of dynamical decoupling."""

XX_PAIR = 'XX_PAIR'
X_XINV = 'X_XINV'
YY_PAIR = 'YY_PAIR'
Y_YINV = 'Y_YINV'


def _repeat_sequence(base_sequence: list['cirq.Gate'], num_idle_moments: int):
Expand All @@ -41,25 +44,31 @@ def _generate_dd_sequence_from_schema(
) -> list['cirq.Gate']:
match schema:
case _DynamicalDecouplingSchema.XX_PAIR:
return _repeat_sequence([cirq.XPowGate(), cirq.XPowGate()], num_idle_moments)
return _repeat_sequence([cirq.X, cirq.X], num_idle_moments)
case _DynamicalDecouplingSchema.X_XINV:
return _repeat_sequence([cirq.X, cirq.X**-1], num_idle_moments)
case _DynamicalDecouplingSchema.YY_PAIR:
return _repeat_sequence([cirq.YPowGate(), cirq.YPowGate()], num_idle_moments)
return _repeat_sequence([cirq.Y, cirq.Y], num_idle_moments)
case _DynamicalDecouplingSchema.Y_YINV:
return _repeat_sequence([cirq.Y, cirq.Y**-1], num_idle_moments)


def _validate_dd_sequence(dd_sequence: list['cirq.Gate']) -> None:
if len(dd_sequence) < 2:
raise ValueError('Invalid dynamical decoupling sequence. Expect more than one gates.')
matrices = [cirq.unitary(gate) for gate in dd_sequence]
product = reduce(np.matmul, matrices)
if not np.array_equal(product, np.eye(2)):

if not cirq.equal_up_to_global_phase(product, np.eye(2)):
raise ValueError(
"Invalid dynamical decoupling sequence, sequence product doesn't equal" ' identity.'
"Invalid dynamical decoupling sequence. Expect sequence production equals identity"
f" up to a global phase, got {product}.".replace('\n', ' ')
)


@value.value_equality
class DynamicalDecouplingModel:
"""Dynamical decoupling model that generates dynamical decoupling gate sequences."""
"""Dynamical decoupling model that generates dynamical decoupling operation sequences."""

def __init__(
self,
Expand All @@ -81,10 +90,10 @@ def generate_dd_sequence(self, num_idle_moments: int = 2) -> list['cirq.Gate']:
if num_idle_moments <= 0:
return []
if self.schema:
return _generate_dd_sequence_from_schema(self.schema, num_idle_moments)
if self.base_dd_sequence:
return _repeat_sequence(self.base_dd_sequence, num_idle_moments)
return []
dd_sequence = _generate_dd_sequence_from_schema(self.schema, num_idle_moments)
elif self.base_dd_sequence:
dd_sequence = _repeat_sequence(self.base_dd_sequence, num_idle_moments)
return dd_sequence

@classmethod
def from_schema(cls, schema: str):
Expand Down Expand Up @@ -117,21 +126,27 @@ def _value_equality_values_(self) -> Any:
return self.schema, self.base_dd_sequence


@transformer_api.transformer
def add_dynamical_decoupling(
circuit: 'cirq.AbstractCircuit', dd_model: DynamicalDecouplingModel
circuit: 'cirq.AbstractCircuit',
*,
context: Optional['cirq.TransformerContext'] = None,
dd_model: DynamicalDecouplingModel = DynamicalDecouplingModel.from_schema("X_XINV"),
) -> 'cirq.Circuit':
"""Add dynamical decoupling gates in a given circuit.
"""Add dynamical decoupling gate operations to a given circuit.
Args:
circuit: Input circuit to transform.
dd_model: Dynamical decoupling model that defines the schema to generate
dynamical decoupling sequences.
context: `cirq.TransformerContext` storing common configurable options for transformers.
dd_model: Dynamical decoupling model that defines the schema to generate dynamical
decoupling sequences.
Return:
A circuit with dynamical decoupling operations.
A copy of the input circuit with dynamical decoupling operations.
"""
last_busy_moment_by_qubits: Dict['cirq.Qid', int] = {q: 0 for q in circuit.all_qubits()}
insert_into: list[Tuple[int, 'cirq.OP_TREE']] = []

for moment_id, moment in enumerate(circuit):
for q in moment.qubits:
insert_gates = dd_model.generate_dd_sequence(
Expand All @@ -141,6 +156,6 @@ def add_dynamical_decoupling(
insert_into.append((last_busy_moment_by_qubits[q] + idx + 1, gate.on(q)))
last_busy_moment_by_qubits[q] = moment_id

circuit.batch_insert_into(insert_into)

return circuit
updated_circuit = circuit.unfreeze(copy=True)
updated_circuit.batch_insert_into(insert_into)
return updated_circuit
86 changes: 43 additions & 43 deletions cirq-core/cirq/transformers/dynamical_decoupling_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ def assert_dd(
cirq.testing.assert_same_circuits(updated_circuit, expected_circuit)


def test_insert_provided_schema():
def test_no_insert_due_to_no_consecutive_moments():
a = cirq.NamedQubit("a")
b = cirq.NamedQubit("b")
c = cirq.NamedQubit("c")

# No insertion as there is no room for a dd sequence.
assert_dd(
Expand All @@ -40,42 +39,39 @@ def test_insert_provided_schema():
dd_model=DynamicalDecouplingModel.from_schema("XX_PAIR"),
)

# Insert one XX_PAIR dynamical decoupling sequence in idle moments.
assert_dd(
input_circuit=cirq.Circuit(
cirq.Moment(cirq.H(a)),
cirq.Moment(cirq.CNOT(a, b)),
cirq.Moment(cirq.CNOT(b, c)),
cirq.Moment(cirq.CNOT(b, c)),
cirq.Moment(cirq.measure_each(a, b, c)),
),
expected_circuit=cirq.Circuit(
cirq.Moment(cirq.H(a)),
cirq.Moment(cirq.CNOT(a, b)),
cirq.Moment(cirq.CNOT(b, c), cirq.X(a)),
cirq.Moment(cirq.CNOT(b, c), cirq.X(a)),
cirq.Moment(cirq.measure_each(a, b, c)),
),
dd_model=DynamicalDecouplingModel.from_schema("XX_PAIR"),

@pytest.mark.parametrize(
'schema,inserted_gates',
[
("XX_PAIR", [cirq.X, cirq.X]),
("X_XINV", [cirq.X, cirq.X**-1]),
("YY_PAIR", [cirq.Y, cirq.Y]),
("Y_YINV", [cirq.Y, cirq.Y**-1]),
],
)
def test_insert_provided_schema(schema: str, inserted_gates: list['cirq.Gate']):
a = cirq.NamedQubit("a")
b = cirq.NamedQubit("b")
c = cirq.NamedQubit("c")

input_circuit = cirq.Circuit(
cirq.Moment(cirq.H(a)),
cirq.Moment(cirq.CNOT(a, b)),
cirq.Moment(cirq.CNOT(b, c)),
cirq.Moment(cirq.CNOT(b, c)),
cirq.Moment(cirq.measure_each(a, b, c)),
)
expected_circuit = cirq.Circuit(
cirq.Moment(cirq.H(a)),
cirq.Moment(cirq.CNOT(a, b)),
cirq.Moment(cirq.CNOT(b, c), inserted_gates[0](a)),
cirq.Moment(cirq.CNOT(b, c), inserted_gates[1](a)),
cirq.Moment(cirq.measure_each(a, b, c)),
)

# Insert one XX_PAIR dynamical decoupling sequence in idle moments.
# Insert one dynamical decoupling sequence in idle moments.
assert_dd(
input_circuit=cirq.Circuit(
cirq.Moment(cirq.H(a)),
cirq.Moment(cirq.CNOT(a, b)),
cirq.Moment(cirq.CNOT(b, c)),
cirq.Moment(cirq.CNOT(b, c)),
cirq.Moment(cirq.measure_each(a, b, c)),
),
expected_circuit=cirq.Circuit(
cirq.Moment(cirq.H(a)),
cirq.Moment(cirq.CNOT(a, b)),
cirq.Moment(cirq.CNOT(b, c), cirq.Y(a)),
cirq.Moment(cirq.CNOT(b, c), cirq.Y(a)),
cirq.Moment(cirq.measure_each(a, b, c)),
),
dd_model=DynamicalDecouplingModel.from_schema("YY_PAIR"),
input_circuit, expected_circuit, dd_model=DynamicalDecouplingModel.from_schema(schema)
)


Expand All @@ -99,23 +95,27 @@ def test_insert_by_customized_dd_sequence():
cirq.Moment(cirq.CNOT(b, c), cirq.X(a)),
cirq.Moment(cirq.measure_each(a, b, c)),
),
dd_model=DynamicalDecouplingModel.from_base_dd_sequence([cirq.XPowGate(), cirq.XPowGate()]),
dd_model=DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.X]),
)


def test_dd_model_constructor():
# Succeed
DynamicalDecouplingModel.from_schema("XX_PAIR")
DynamicalDecouplingModel.from_schema("YY_PAIR")
DynamicalDecouplingModel.from_base_dd_sequence(
[cirq.XPowGate(), cirq.XPowGate(), cirq.YPowGate(), cirq.YPowGate()]
)
DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.X, cirq.Y, cirq.Y])
# Fail
with pytest.raises(ValueError, match="Specify either schema or base_dd_sequence"):
DynamicalDecouplingModel()
with pytest.raises(ValueError, match="Invalid schema name."):
DynamicalDecouplingModel.from_schema("unimplemented_schema")
with pytest.raises(ValueError, match="Invalid dynamical decoupling sequence. Expect more than one gates."):
DynamicalDecouplingModel.from_base_dd_sequence([cirq.XPowGate()])
with pytest.raises(ValueError, match="Invalid dynamical decoupling sequence"):
DynamicalDecouplingModel.from_base_dd_sequence([cirq.XPowGate(), cirq.YPowGate()])
with pytest.raises(
ValueError, match="Invalid dynamical decoupling sequence. Expect more than one gates."
):
DynamicalDecouplingModel.from_base_dd_sequence([cirq.X])
with pytest.raises(
ValueError,
match="Invalid dynamical decoupling sequence. Expect sequence production equals identity"
" up to a global phase, got",
):
DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.H])

0 comments on commit 3a6a9b4

Please sign in to comment.