|
14 | 14 |
|
15 | 15 | """Transformer pass that adds dynamical decoupling operations to a circuit.""" |
16 | 16 |
|
17 | | -import enum |
18 | 17 | from functools import reduce |
19 | | -from typing import Any, Dict, Optional, Tuple |
| 18 | +from typing import Dict, Optional, Sequence, Tuple, Union |
20 | 19 |
|
21 | 20 | from cirq.transformers import transformer_api |
22 | 21 | import cirq |
23 | | -from cirq import value |
24 | 22 | import numpy as np |
25 | 23 |
|
26 | 24 |
|
27 | | -@enum.unique |
28 | | -class _DynamicalDecouplingSchema(enum.Enum): |
29 | | - """Supported schemes of dynamical decoupling.""" |
30 | | - |
31 | | - XX_PAIR = 'XX_PAIR' |
32 | | - X_XINV = 'X_XINV' |
33 | | - YY_PAIR = 'YY_PAIR' |
34 | | - Y_YINV = 'Y_YINV' |
35 | | - |
36 | | - |
37 | | -def _repeat_sequence(base_sequence: list['cirq.Gate'], num_idle_moments: int): |
| 25 | +def _repeat_sequence( |
| 26 | + base_sequence: Sequence['cirq.Gate'], num_idle_moments: int |
| 27 | +) -> Sequence['cirq.Gate']: |
| 28 | + """Returns the longest possible dynamical decoupling sequence.""" |
38 | 29 | repeat_times = num_idle_moments // len(base_sequence) |
39 | | - return base_sequence * repeat_times |
| 30 | + return list(base_sequence) * repeat_times |
40 | 31 |
|
41 | 32 |
|
42 | | -def _generate_dd_sequence_from_schema( |
43 | | - schema: _DynamicalDecouplingSchema, num_idle_moments: int = 2 |
44 | | -) -> list['cirq.Gate']: |
| 33 | +def _get_dd_sequence_from_schema_name(schema: str) -> Sequence['cirq.Gate']: |
| 34 | + """Gets dynamical decoupling sequence from a schema name.""" |
| 35 | + dd_sequence: Sequence['cirq.Gate'] |
45 | 36 | match schema: |
46 | | - case _DynamicalDecouplingSchema.XX_PAIR: |
47 | | - return _repeat_sequence([cirq.X, cirq.X], num_idle_moments) |
48 | | - case _DynamicalDecouplingSchema.X_XINV: |
49 | | - return _repeat_sequence([cirq.X, cirq.X**-1], num_idle_moments) |
50 | | - case _DynamicalDecouplingSchema.YY_PAIR: |
51 | | - return _repeat_sequence([cirq.Y, cirq.Y], num_idle_moments) |
52 | | - case _DynamicalDecouplingSchema.Y_YINV: |
53 | | - return _repeat_sequence([cirq.Y, cirq.Y**-1], num_idle_moments) |
| 37 | + case 'XX_PAIR': |
| 38 | + dd_sequence = (cirq.X, cirq.X) |
| 39 | + case 'X_XINV': |
| 40 | + dd_sequence = (cirq.X, cirq.X**-1) |
| 41 | + case 'YY_PAIR': |
| 42 | + dd_sequence = (cirq.Y, cirq.Y) |
| 43 | + case 'Y_YINV': |
| 44 | + dd_sequence = (cirq.Y, cirq.Y**-1) |
| 45 | + case _: |
| 46 | + raise ValueError('Invalid schema name.') |
| 47 | + return dd_sequence |
| 48 | + |
| 49 | + |
| 50 | +def _validate_dd_sequence(dd_sequence: Sequence['cirq.Gate']) -> Tuple[bool, Optional[str]]: |
| 51 | + """Validates a given dynamical decoupling sequence. |
54 | 52 |
|
| 53 | + Args: |
| 54 | + dd_sequence: Input dynamical sequence to be validated. |
55 | 55 |
|
56 | | -def _validate_dd_sequence(dd_sequence: list['cirq.Gate']) -> None: |
| 56 | + Returns: |
| 57 | + A tuple containing: |
| 58 | + - is_valid (bool): True if the dd sequence is valid, False otherwise. |
| 59 | + - error_message (str): An error message if the dd sequence is invalid, else None. |
| 60 | + """ |
57 | 61 | if len(dd_sequence) < 2: |
58 | | - raise ValueError('Invalid dynamical decoupling sequence. Expect more than one gates.') |
| 62 | + return False, 'Invalid dynamical decoupling sequence. Expect more than one gates.' |
59 | 63 | matrices = [cirq.unitary(gate) for gate in dd_sequence] |
60 | 64 | product = reduce(np.matmul, matrices) |
61 | 65 |
|
62 | 66 | if not cirq.equal_up_to_global_phase(product, np.eye(2)): |
63 | | - raise ValueError( |
64 | | - "Invalid dynamical decoupling sequence. Expect sequence production equals identity" |
65 | | - f" up to a global phase, got {product}.".replace('\n', ' ') |
| 67 | + return False, ( |
| 68 | + 'Invalid dynamical decoupling sequence. Expect sequence production equals' |
| 69 | + f' identity up to a global phase, got {product}.'.replace('\n', ' ') |
66 | 70 | ) |
67 | | - |
68 | | - |
69 | | -@value.value_equality |
70 | | -class DynamicalDecouplingModel: |
71 | | - """Dynamical decoupling model that generates dynamical decoupling operation sequences.""" |
72 | | - |
73 | | - def __init__( |
74 | | - self, |
75 | | - schema: Optional[_DynamicalDecouplingSchema] = None, |
76 | | - base_dd_sequence: Optional[list['cirq.Gate']] = None, |
77 | | - ): |
78 | | - if not schema and not base_dd_sequence: |
79 | | - raise ValueError( |
80 | | - 'Specify either schema or base_dd_sequence to construct a valid' |
81 | | - ' DynamicalDecouplingModel.' |
82 | | - ) |
83 | | - self.schema = schema |
84 | | - self.base_dd_sequence = base_dd_sequence |
85 | | - if base_dd_sequence: |
86 | | - _validate_dd_sequence(base_dd_sequence) |
87 | | - |
88 | | - def generate_dd_sequence(self, num_idle_moments: int = 2) -> list['cirq.Gate']: |
89 | | - """Returns the longest possible dynamical decoupling sequence.""" |
90 | | - if num_idle_moments <= 0: |
91 | | - return [] |
92 | | - if self.schema: |
93 | | - dd_sequence = _generate_dd_sequence_from_schema(self.schema, num_idle_moments) |
94 | | - elif self.base_dd_sequence: |
95 | | - dd_sequence = _repeat_sequence(self.base_dd_sequence, num_idle_moments) |
96 | | - return dd_sequence |
97 | | - |
98 | | - @classmethod |
99 | | - def from_schema(cls, schema: str): |
100 | | - """Create dynamical decoupling model according to a given schema.""" |
101 | | - if not schema in _DynamicalDecouplingSchema.__members__: |
102 | | - raise ValueError("Invalid schema name.") |
103 | | - return cls(schema=_DynamicalDecouplingSchema[schema]) |
104 | | - |
105 | | - @classmethod |
106 | | - def from_base_dd_sequence(cls, base_dd_sequence: list['cirq.Gate']): |
107 | | - """Create dynamical decoupling model according to a base sequence.""" |
108 | | - return cls(base_dd_sequence=base_dd_sequence) |
109 | | - |
110 | | - def _json_dict_(self) -> Dict[str, Any]: |
111 | | - d: Dict[str, Any] = {} |
112 | | - if self.schema: |
113 | | - d['schema'] = self.schema.name |
114 | | - if self.base_dd_sequence: |
115 | | - d['base_dd_sequence'] = self.base_dd_sequence |
116 | | - return d |
117 | | - |
118 | | - @classmethod |
119 | | - def _from_json_dict_(cls, schema=None, base_dd_sequence=None, **kwargs): |
120 | | - if schema: |
121 | | - return cls(schema=_DynamicalDecouplingSchema[schema]) |
122 | | - if base_dd_sequence: |
123 | | - return cls(base_dd_sequence=base_dd_sequence) |
124 | | - |
125 | | - def _value_equality_values_(self) -> Any: |
126 | | - return self.schema, self.base_dd_sequence |
| 71 | + return True, None |
127 | 72 |
|
128 | 73 |
|
129 | 74 | @transformer_api.transformer |
130 | 75 | def add_dynamical_decoupling( |
131 | 76 | circuit: 'cirq.AbstractCircuit', |
132 | 77 | *, |
133 | 78 | context: Optional['cirq.TransformerContext'] = None, |
134 | | - dd_model: DynamicalDecouplingModel = DynamicalDecouplingModel.from_schema("X_XINV"), |
| 79 | + schema: Union[str, Sequence['cirq.Gate']] = 'X_XINV', |
135 | 80 | ) -> 'cirq.Circuit': |
136 | | - """Add dynamical decoupling gate operations to a given circuit. |
| 81 | + """Adds dynamical decoupling gate operations to idle moments of a given circuit. |
| 82 | + This transformer preserves the moment structure of the circuit. |
137 | 83 |
|
138 | 84 | Args: |
139 | 85 | circuit: Input circuit to transform. |
140 | 86 | context: `cirq.TransformerContext` storing common configurable options for transformers. |
141 | | - dd_model: Dynamical decoupling model that defines the schema to generate dynamical |
142 | | - decoupling sequences. |
| 87 | + schema: Dynamical decoupling schema name or a dynamical decoupling sequence. |
| 88 | + If a schema is specified, provided dynamical decouping sequence will be used. |
| 89 | + Otherwise, customized dynamical decoupling sequence will be applied. |
143 | 90 |
|
144 | | - Return: |
| 91 | + Returns: |
145 | 92 | A copy of the input circuit with dynamical decoupling operations. |
| 93 | +
|
| 94 | + Raises: |
| 95 | + ValueError: If schema is not valid. |
146 | 96 | """ |
147 | 97 | last_busy_moment_by_qubits: Dict['cirq.Qid', int] = {q: 0 for q in circuit.all_qubits()} |
148 | 98 | insert_into: list[Tuple[int, 'cirq.OP_TREE']] = [] |
149 | 99 |
|
| 100 | + if isinstance(schema, str): |
| 101 | + try: |
| 102 | + base_dd_sequence = _get_dd_sequence_from_schema_name(schema) |
| 103 | + except ValueError: |
| 104 | + raise |
| 105 | + else: |
| 106 | + is_valid, error_message = _validate_dd_sequence(schema) |
| 107 | + if is_valid: |
| 108 | + base_dd_sequence = schema |
| 109 | + else: |
| 110 | + raise ValueError(error_message) |
| 111 | + |
150 | 112 | for moment_id, moment in enumerate(circuit): |
151 | 113 | for q in moment.qubits: |
152 | | - insert_gates = dd_model.generate_dd_sequence( |
153 | | - num_idle_moments=moment_id - last_busy_moment_by_qubits[q] - 1 |
| 114 | + insert_gates = _repeat_sequence( |
| 115 | + base_dd_sequence, num_idle_moments=moment_id - last_busy_moment_by_qubits[q] - 1 |
154 | 116 | ) |
155 | 117 | for idx, gate in enumerate(insert_gates): |
156 | 118 | insert_into.append((last_busy_moment_by_qubits[q] + idx + 1, gate.on(q))) |
|
0 commit comments