From 856583a377801e28a7c5514ea970807973cbfd3d Mon Sep 17 00:00:00 2001 From: Renyi Date: Tue, 23 Apr 2024 03:47:01 -0700 Subject: [PATCH] Address comments. --- .../DynamicalDecouplingModel.json | 4 +- .../DynamicalDecouplingModel.repr | 2 +- .../cirq/transformers/dynamical_decoupling.py | 74 +++++++++++---- .../transformers/dynamical_decoupling_test.py | 95 ++++++++++++++++++- 4 files changed, 154 insertions(+), 21 deletions(-) diff --git a/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.json b/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.json index 183726ac7035..e605e9d00612 100644 --- a/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.json +++ b/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.json @@ -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 } diff --git a/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.repr b/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.repr index 93e994447b5f..a4502182882e 100644 --- a/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.repr +++ b/cirq-core/cirq/protocols/json_test_data/DynamicalDecouplingModel.repr @@ -1,2 +1,2 @@ [cirq.DynamicalDecouplingModel.from_schema("XX_PAIR"), -cirq.DynamicalDecouplingModel(base_dd_sequence=[cirq.XPowGate(), cirq.XPowGate()])] \ No newline at end of file +cirq.DynamicalDecouplingModel.from_base_dd_sequence(base_dd_sequence=[cirq.X, cirq.X])] \ No newline at end of file diff --git a/cirq-core/cirq/transformers/dynamical_decoupling.py b/cirq-core/cirq/transformers/dynamical_decoupling.py index 6af91bf0107b..f7853ad2fe29 100644 --- a/cirq-core/cirq/transformers/dynamical_decoupling.py +++ b/cirq-core/cirq/transformers/dynamical_decoupling.py @@ -12,12 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. +<<<<<<< HEAD +<<<<<<< HEAD +"""Transformer pass that adds dynamical decoupling operations to a circuit.""" +======= """Transformer pass that adds dynamical decoupling moments to a circuit.""" +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= +"""Transformer pass that adds dynamical decoupling operations to a circuit.""" +>>>>>>> ad49c3c7 (Address comments.) import enum from functools import reduce from typing import Any, Dict, Optional, Tuple +<<<<<<< HEAD +<<<<<<< HEAD +from cirq.transformers import transformer_api +======= +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= +from cirq.transformers import transformer_api +>>>>>>> ad49c3c7 (Address comments.) import cirq from cirq import value import numpy as np @@ -28,7 +44,19 @@ class _DynamicalDecouplingSchema(enum.Enum): """Supported schemes of dynamical decoupling.""" XX_PAIR = 'XX_PAIR' +<<<<<<< HEAD +<<<<<<< HEAD + X_XINV = 'X_XINV' YY_PAIR = 'YY_PAIR' + Y_YINV = 'Y_YINV' +======= + YY_PAIR = 'YY_PAIR' +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= + X_XINV = 'X_XINV' + YY_PAIR = 'YY_PAIR' + Y_YINV = 'Y_YINV' +>>>>>>> ad49c3c7 (Address comments.) def _repeat_sequence(base_sequence: list['cirq.Gate'], num_idle_moments: int): @@ -41,9 +69,13 @@ 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: @@ -51,15 +83,17 @@ def _validate_dd_sequence(dd_sequence: list['cirq.Gate']) -> None: 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, @@ -81,10 +115,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): @@ -117,21 +151,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( @@ -141,6 +181,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 diff --git a/cirq-core/cirq/transformers/dynamical_decoupling_test.py b/cirq-core/cirq/transformers/dynamical_decoupling_test.py index 310822650df0..e94aed3fb867 100644 --- a/cirq-core/cirq/transformers/dynamical_decoupling_test.py +++ b/cirq-core/cirq/transformers/dynamical_decoupling_test.py @@ -24,10 +24,22 @@ def assert_dd( cirq.testing.assert_same_circuits(updated_circuit, expected_circuit) +<<<<<<< HEAD +<<<<<<< HEAD +def test_no_insert_due_to_no_consecutive_moments(): + a = cirq.NamedQubit("a") + b = cirq.NamedQubit("b") +======= def test_insert_provided_schema(): a = cirq.NamedQubit("a") b = cirq.NamedQubit("b") c = cirq.NamedQubit("c") +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= +def test_no_insert_due_to_no_consecutive_moments(): + a = cirq.NamedQubit("a") + b = cirq.NamedQubit("b") +>>>>>>> ad49c3c7 (Address comments.) # No insertion as there is no room for a dd sequence. assert_dd( @@ -40,6 +52,45 @@ def test_insert_provided_schema(): dd_model=DynamicalDecouplingModel.from_schema("XX_PAIR"), ) +<<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ad49c3c7 (Address comments.) + +@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)), +<<<<<<< HEAD + ) + + # Insert one dynamical decoupling sequence in idle moments. + assert_dd( + input_circuit, expected_circuit, dd_model=DynamicalDecouplingModel.from_schema(schema) +======= # Insert one XX_PAIR dynamical decoupling sequence in idle moments. assert_dd( input_circuit=cirq.Circuit( @@ -57,10 +108,13 @@ def test_insert_provided_schema(): cirq.Moment(cirq.measure_each(a, b, c)), ), dd_model=DynamicalDecouplingModel.from_schema("XX_PAIR"), +======= +>>>>>>> ad49c3c7 (Address comments.) ) - # Insert one XX_PAIR dynamical decoupling sequence in idle moments. + # Insert one dynamical decoupling sequence in idle moments. assert_dd( +<<<<<<< HEAD input_circuit=cirq.Circuit( cirq.Moment(cirq.H(a)), cirq.Moment(cirq.CNOT(a, b)), @@ -76,6 +130,10 @@ def test_insert_provided_schema(): cirq.Moment(cirq.measure_each(a, b, c)), ), dd_model=DynamicalDecouplingModel.from_schema("YY_PAIR"), +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= + input_circuit, expected_circuit, dd_model=DynamicalDecouplingModel.from_schema(schema) +>>>>>>> ad49c3c7 (Address comments.) ) @@ -99,7 +157,15 @@ def test_insert_by_customized_dd_sequence(): cirq.Moment(cirq.CNOT(b, c), cirq.X(a)), cirq.Moment(cirq.measure_each(a, b, c)), ), +<<<<<<< HEAD +<<<<<<< HEAD + dd_model=DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.X]), +======= dd_model=DynamicalDecouplingModel.from_base_dd_sequence([cirq.XPowGate(), cirq.XPowGate()]), +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= + dd_model=DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.X]), +>>>>>>> ad49c3c7 (Address comments.) ) @@ -107,15 +173,42 @@ def test_dd_model_constructor(): # Succeed DynamicalDecouplingModel.from_schema("XX_PAIR") DynamicalDecouplingModel.from_schema("YY_PAIR") +<<<<<<< HEAD +<<<<<<< HEAD + DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.X, cirq.Y, cirq.Y]) +======= DynamicalDecouplingModel.from_base_dd_sequence( [cirq.XPowGate(), cirq.XPowGate(), cirq.YPowGate(), cirq.YPowGate()] ) +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= + DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.X, cirq.Y, cirq.Y]) +>>>>>>> ad49c3c7 (Address comments.) # 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") +<<<<<<< HEAD +<<<<<<< HEAD +======= +>>>>>>> ad49c3c7 (Address comments.) + 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]) +<<<<<<< HEAD +======= 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()]) +>>>>>>> 6e65ad88 (Impl dynamical decoupling transfomer.) +======= +>>>>>>> ad49c3c7 (Address comments.)