diff --git a/cirq-core/cirq/ops/gate_operation_test.py b/cirq-core/cirq/ops/gate_operation_test.py index 46cf3cae1a59..971962b24f64 100644 --- a/cirq-core/cirq/ops/gate_operation_test.py +++ b/cirq-core/cirq/ops/gate_operation_test.py @@ -322,6 +322,12 @@ def on(self, *qubits): (cirq.ZZPowGate(global_shift=0.5), cirq.ZZ, True), (cirq.ZPowGate(global_shift=0.5) ** sympy.Symbol('e'), cirq.Z, False), (cirq.Z ** sympy.Symbol('e'), cirq.Z ** sympy.Symbol('f'), False), + ( + cirq.PhasedXPowGate(phase_exponent=1.5, exponent=1.0), + cirq.PhasedXPowGate(phase_exponent=0.5, exponent=1.0), + True, + ), + (cirq.XPowGate(exponent=2.0), cirq.I, True), ], ) def test_equal_up_to_global_phase_on_gates(gate1, gate2, eq_up_to_global_phase): diff --git a/cirq-core/cirq/transformers/dynamical_decoupling.py b/cirq-core/cirq/transformers/dynamical_decoupling.py index 6af91bf0107b..0fc1a421704d 100644 --- a/cirq-core/cirq/transformers/dynamical_decoupling.py +++ b/cirq-core/cirq/transformers/dynamical_decoupling.py @@ -12,16 +12,20 @@ # 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, transformer_primitives import cirq from cirq import value import numpy as np +# DO NOT SUBMIT +import logging + @enum.unique class _DynamicalDecouplingSchema(enum.Enum): @@ -41,9 +45,9 @@ 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.YY_PAIR: - return _repeat_sequence([cirq.YPowGate(), cirq.YPowGate()], num_idle_moments) + return _repeat_sequence([cirq.Y, cirq.Y], num_idle_moments) def _validate_dd_sequence(dd_sequence: list['cirq.Gate']) -> None: @@ -51,15 +55,18 @@ 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)): + product_gate = cirq.MatrixGate(product) + + a = cirq.NamedQubit("a") + if cirq.equal_up_to_global_phase(product_gate(a), cirq.I(a)): raise ValueError( - "Invalid dynamical decoupling sequence, sequence product doesn't equal" ' identity.' + "Invalid dynamical decoupling sequence. Sequence should equal to identity operation up to a global phase." ) @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, @@ -84,7 +91,6 @@ def generate_dd_sequence(self, num_idle_moments: int = 2) -> list['cirq.Gate']: 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 [] @classmethod def from_schema(cls, schema: str): @@ -117,18 +123,23 @@ def _value_equality_values_(self) -> Any: return self.schema, self.base_dd_sequence +@transformer_api.transformer(add_deep_support=True) def add_dynamical_decoupling( - circuit: 'cirq.AbstractCircuit', dd_model: DynamicalDecouplingModel + circuit: 'cirq.AbstractCircuit', + dd_model: DynamicalDecouplingModel, + *, + context: Optional['cirq.TransformerContext'] = None, ) -> 'cirq.Circuit': """Add dynamical decoupling gates in a given circuit. Args: circuit: Input circuit to transform. + 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']] = [] @@ -141,6 +152,7 @@ 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) + updated_circuit = circuit.unfreeze(copy=True) + updated_circuit.batch_insert_into(insert_into) - return circuit + 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..0037eb876e05 100644 --- a/cirq-core/cirq/transformers/dynamical_decoupling_test.py +++ b/cirq-core/cirq/transformers/dynamical_decoupling_test.py @@ -59,7 +59,7 @@ def test_insert_provided_schema(): dd_model=DynamicalDecouplingModel.from_schema("XX_PAIR"), ) - # Insert one XX_PAIR dynamical decoupling sequence in idle moments. + # Insert one YY_PAIR dynamical decoupling sequence in idle moments. assert_dd( input_circuit=cirq.Circuit( cirq.Moment(cirq.H(a)), @@ -99,7 +99,7 @@ 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]), ) @@ -107,15 +107,15 @@ 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. Expect more than one gates." + ): + DynamicalDecouplingModel.from_base_dd_sequence([cirq.X]) with pytest.raises(ValueError, match="Invalid dynamical decoupling sequence"): - DynamicalDecouplingModel.from_base_dd_sequence([cirq.XPowGate(), cirq.YPowGate()]) + DynamicalDecouplingModel.from_base_dd_sequence([cirq.X, cirq.H])