cirq-core 1.5.0.dev20240808235317__py3-none-any.whl → 1.5.0.dev20240814221152__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cirq-core might be problematic. Click here for more details.

cirq/_version.py CHANGED
@@ -28,4 +28,4 @@ if sys.version_info < (3, 10, 0): # pragma: no cover
28
28
  'of cirq (e.g. "python -m pip install cirq==1.1.*")'
29
29
  )
30
30
 
31
- __version__ = "1.5.0.dev20240808235317"
31
+ __version__ = "1.5.0.dev20240814221152"
cirq/_version_test.py CHANGED
@@ -3,4 +3,4 @@ import cirq
3
3
 
4
4
 
5
5
  def test_version():
6
- assert cirq.__version__ == "1.5.0.dev20240808235317"
6
+ assert cirq.__version__ == "1.5.0.dev20240814221152"
@@ -741,6 +741,7 @@ def test_kak_decompose(unitary: np.ndarray):
741
741
  assert len(list(circuit.all_operations())) == 8
742
742
 
743
743
 
744
+ @cirq.testing.retry_once_with_later_random_values
744
745
  def test_num_two_qubit_gates_required():
745
746
  for i in range(4):
746
747
  assert (
cirq/testing/__init__.py CHANGED
@@ -96,6 +96,8 @@ from cirq.testing.op_tree import assert_equivalent_op_tree
96
96
 
97
97
  from cirq.testing.order_tester import OrderTester
98
98
 
99
+ from cirq.testing.pytest_randomly_utils import retry_once_with_later_random_values
100
+
99
101
  from cirq.testing.random_circuit import (
100
102
  DEFAULT_GATE_DOMAIN,
101
103
  random_circuit,
@@ -0,0 +1,38 @@
1
+ # Copyright 2024 The Cirq Developers
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Support one retry of tests that fail for a specific seed from pytest-randomly."""
16
+
17
+ import functools
18
+ import warnings
19
+ from typing import Any, Callable
20
+
21
+
22
+ def retry_once_with_later_random_values(testfunc: Callable) -> Callable:
23
+ """Marks a test function for one retry with later random values.
24
+
25
+ This decorator is intended for test functions which occasionally fail
26
+ for specific random seeds from pytest-randomly.
27
+ """
28
+
29
+ @functools.wraps(testfunc)
30
+ def wrapped_func(*args, **kwargs) -> Any:
31
+ try:
32
+ return testfunc(*args, **kwargs)
33
+ except AssertionError:
34
+ pass
35
+ warnings.warn("Retrying in case we got a failing seed from pytest-randomly.")
36
+ return testfunc(*args, **kwargs)
37
+
38
+ return wrapped_func
@@ -0,0 +1,27 @@
1
+ # Copyright 2024 The Cirq Developers
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from unittest.mock import Mock
16
+
17
+ import pytest
18
+
19
+ import cirq
20
+
21
+
22
+ def test_retry_once_with_later_random_values():
23
+ testfunc = Mock(side_effect=[AssertionError("first call fails"), None])
24
+ decoratedfunc = cirq.testing.retry_once_with_later_random_values(testfunc)
25
+ with pytest.warns(UserWarning, match="Retrying.*failing seed.*pytest-randomly"):
26
+ decoratedfunc()
27
+ assert testfunc.call_count == 2
@@ -87,6 +87,7 @@ def test_decompose_specific_matrices():
87
87
  _test_decompose(cirq.unitary(gate), controls_count)
88
88
 
89
89
 
90
+ @cirq.testing.retry_once_with_later_random_values
90
91
  def test_decompose_random_unitary():
91
92
  for controls_count in range(5):
92
93
  for _ in range(10):
@@ -15,54 +15,57 @@
15
15
  """Transformer pass that adds dynamical decoupling operations to a circuit."""
16
16
 
17
17
  from functools import reduce
18
- from typing import Dict, Optional, Sequence, Tuple, Union
18
+ from typing import Dict, Optional, Tuple, Union
19
+ from itertools import cycle
19
20
 
20
21
  from cirq.transformers import transformer_api
22
+ from cirq.transformers.analytical_decompositions import single_qubit_decompositions
23
+ from cirq.transformers.analytical_decompositions import unitary_to_pauli_string
21
24
  import cirq
22
25
  import numpy as np
23
26
 
24
27
 
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."""
29
- repeat_times = num_idle_moments // len(base_sequence)
30
- return list(base_sequence) * repeat_times
31
-
32
-
33
- def _get_dd_sequence_from_schema_name(schema: str) -> Sequence['cirq.Gate']:
28
+ def _get_dd_sequence_from_schema_name(schema: str) -> Tuple['cirq.Gate', ...]:
34
29
  """Gets dynamical decoupling sequence from a schema name."""
35
- dd_sequence: Sequence['cirq.Gate']
36
30
  match schema:
31
+ case 'DEFAULT':
32
+ return (cirq.X, cirq.Y, cirq.X, cirq.Y)
37
33
  case 'XX_PAIR':
38
- dd_sequence = (cirq.X, cirq.X)
34
+ return (cirq.X, cirq.X)
39
35
  case 'X_XINV':
40
- dd_sequence = (cirq.X, cirq.X**-1)
36
+ return (cirq.X, cirq.X**-1)
41
37
  case 'YY_PAIR':
42
- dd_sequence = (cirq.Y, cirq.Y)
38
+ return (cirq.Y, cirq.Y)
43
39
  case 'Y_YINV':
44
- dd_sequence = (cirq.Y, cirq.Y**-1)
40
+ return (cirq.Y, cirq.Y**-1)
45
41
  case _:
46
42
  raise ValueError('Invalid schema name.')
47
- return dd_sequence
48
43
 
49
44
 
50
- def _validate_dd_sequence(dd_sequence: Sequence['cirq.Gate']) -> None:
45
+ def _pauli_up_to_global_phase(gate: 'cirq.Gate') -> Union['cirq.Pauli', None]:
46
+ for pauli_gate in [cirq.X, cirq.Y, cirq.Z]:
47
+ if cirq.equal_up_to_global_phase(gate, pauli_gate):
48
+ return pauli_gate
49
+ return None
50
+
51
+
52
+ def _validate_dd_sequence(dd_sequence: Tuple['cirq.Gate', ...]) -> None:
51
53
  """Validates a given dynamical decoupling sequence.
52
54
 
53
55
  Args:
54
56
  dd_sequence: Input dynamical sequence to be validated.
55
57
 
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
-
61
58
  Raises:
62
59
  ValueError: If dd_sequence is not valid.
63
60
  """
64
61
  if len(dd_sequence) < 2:
65
62
  raise ValueError('Invalid dynamical decoupling sequence. Expect more than one gates.')
63
+ for gate in dd_sequence:
64
+ if _pauli_up_to_global_phase(gate) is None:
65
+ raise ValueError(
66
+ 'Dynamical decoupling sequence should only contain gates that are essentially'
67
+ ' Pauli gates.'
68
+ )
66
69
  matrices = [cirq.unitary(gate) for gate in dd_sequence]
67
70
  product = reduce(np.matmul, matrices)
68
71
 
@@ -73,14 +76,134 @@ def _validate_dd_sequence(dd_sequence: Sequence['cirq.Gate']) -> None:
73
76
  )
74
77
 
75
78
 
76
- def _parse_dd_sequence(schema: Union[str, Sequence['cirq.Gate']]) -> Sequence['cirq.Gate']:
79
+ def _parse_dd_sequence(schema: Union[str, Tuple['cirq.Gate', ...]]) -> Tuple['cirq.Gate', ...]:
77
80
  """Parses and returns dynamical decoupling sequence from schema."""
78
81
  if isinstance(schema, str):
79
- dd_sequence = _get_dd_sequence_from_schema_name(schema)
82
+ return _get_dd_sequence_from_schema_name(schema)
80
83
  else:
81
84
  _validate_dd_sequence(schema)
82
- dd_sequence = schema
83
- return dd_sequence
85
+ return schema
86
+
87
+
88
+ def _is_single_qubit_operation(operation: 'cirq.Operation') -> bool:
89
+ if len(operation.qubits) != 1:
90
+ return False
91
+ return True
92
+
93
+
94
+ def _is_single_qubit_gate_moment(moment: 'cirq.Moment') -> bool:
95
+ for operation in moment:
96
+ if not _is_single_qubit_operation(operation):
97
+ return False
98
+ return True
99
+
100
+
101
+ def _is_clifford_moment(moment: 'cirq.Moment') -> bool:
102
+ for op in moment.operations:
103
+ if op.gate is not None and isinstance(op.gate, cirq.MeasurementGate):
104
+ return False
105
+ if not cirq.has_stabilizer_effect(op):
106
+ return False
107
+ return True
108
+
109
+
110
+ def _get_clifford_pieces(circuit: 'cirq.AbstractCircuit') -> list[Tuple[int, int]]:
111
+ clifford_pieces: list[Tuple[int, int]] = []
112
+ left = 0
113
+ for moment_id, moment in enumerate(circuit):
114
+ if not _is_clifford_moment(moment):
115
+ clifford_pieces.append((left, moment_id))
116
+ left = moment_id + 1
117
+ if left < len(circuit):
118
+ clifford_pieces.append((left, len(circuit)))
119
+ return clifford_pieces
120
+
121
+
122
+ def _is_insertable_moment(moment: 'cirq.Moment', single_qubit_gate_moments_only: bool) -> bool:
123
+ return _is_single_qubit_gate_moment(moment) or not single_qubit_gate_moments_only
124
+
125
+
126
+ def _calc_pulled_through(
127
+ moment: 'cirq.Moment', input_pauli_ops: 'cirq.PauliString'
128
+ ) -> 'cirq.PauliString':
129
+ """Calculates the pulled_through after pulling through moment with the input.
130
+
131
+ We assume that the moment is Clifford here. Then, pulling through is essentially
132
+ decomposing a matrix into Pauli operations on each qubit.
133
+ """
134
+ pulled_through: 'cirq.PauliString' = cirq.PauliString()
135
+ for affected_q, combined_op_in_pauli in input_pauli_ops.items():
136
+ op_at_moment = moment.operation_at(affected_q)
137
+ if op_at_moment is None:
138
+ pulled_through *= combined_op_in_pauli.on(affected_q)
139
+ continue
140
+ prev_circuit = cirq.Circuit(cirq.Moment(op_at_moment))
141
+ new_circuit = cirq.Circuit(
142
+ cirq.Moment(combined_op_in_pauli.on(affected_q)), cirq.Moment(op_at_moment)
143
+ )
144
+ qubit_order = op_at_moment.qubits
145
+ pulled_through_pauli_ops = unitary_to_pauli_string(
146
+ prev_circuit.unitary(qubit_order=qubit_order)
147
+ @ new_circuit.unitary(qubit_order=qubit_order).conj().T
148
+ )
149
+ if pulled_through_pauli_ops is not None:
150
+ for qid, gate in enumerate(pulled_through_pauli_ops):
151
+ pulled_through *= gate.on(qubit_order[qid])
152
+ return pulled_through
153
+
154
+
155
+ def _merge_pulled_through(
156
+ mutable_circuit: 'cirq.Circuit',
157
+ pulled_through: 'cirq.PauliString',
158
+ clifford_piece_range: Tuple[int, int],
159
+ single_qubit_gate_moments_only: bool,
160
+ ) -> 'cirq.PauliString':
161
+ """Merges pulled through Pauli gates into the last single-qubit gate operation or the insert it
162
+ into the first idle moment if idle moments exist.
163
+ Args:
164
+ mutable_circuit: Mutable circuit to transform.
165
+ pulled_through: Pauli gates to be merged.
166
+ clifford_piece_range: Specifies the [l, r) moments within which pulled-through gate merging
167
+ is to be performed.
168
+ single_qubit_gate_moments_only: If set True, dynamical decoupling operation will only be
169
+ added in single-qubit gate moments.
170
+
171
+ Returns:
172
+ The remaining pulled through operations after merging.
173
+ """
174
+ insert_intos: list[Tuple[int, 'cirq.Operation']] = []
175
+ batch_replaces: list[Tuple[int, 'cirq.Operation', 'cirq.Operation']] = []
176
+ remaining_pulled_through = pulled_through
177
+ for affected_q, combined_op_in_pauli in pulled_through.items():
178
+ moment_id = mutable_circuit.prev_moment_operating_on([affected_q], clifford_piece_range[1])
179
+ if moment_id is not None:
180
+ op = mutable_circuit.operation_at(affected_q, moment_id)
181
+ # Try to merge op into an existing single-qubit gate operation.
182
+ if op is not None and _is_single_qubit_operation(op):
183
+ updated_gate_mat = cirq.unitary(combined_op_in_pauli) @ cirq.unitary(op)
184
+ updated_gate: Optional['cirq.Gate'] = (
185
+ single_qubit_decompositions.single_qubit_matrix_to_phxz(updated_gate_mat)
186
+ )
187
+ if updated_gate is None:
188
+ # updated_gate is close to Identity.
189
+ updated_gate = cirq.I
190
+ batch_replaces.append((moment_id, op, updated_gate.on(affected_q)))
191
+ remaining_pulled_through *= combined_op_in_pauli.on(affected_q)
192
+ continue
193
+ # Insert into the first empty moment for the qubit if such moment exists.
194
+ while moment_id < clifford_piece_range[1]:
195
+ if affected_q not in mutable_circuit.moments[
196
+ moment_id
197
+ ].qubits and _is_insertable_moment(
198
+ mutable_circuit.moments[moment_id], single_qubit_gate_moments_only
199
+ ):
200
+ insert_intos.append((moment_id, combined_op_in_pauli.on(affected_q)))
201
+ remaining_pulled_through *= combined_op_in_pauli.on(affected_q)
202
+ break
203
+ moment_id += 1
204
+ mutable_circuit.batch_insert_into(insert_intos)
205
+ mutable_circuit.batch_replace(batch_replaces)
206
+ return remaining_pulled_through
84
207
 
85
208
 
86
209
  @transformer_api.transformer
@@ -88,10 +211,12 @@ def add_dynamical_decoupling(
88
211
  circuit: 'cirq.AbstractCircuit',
89
212
  *,
90
213
  context: Optional['cirq.TransformerContext'] = None,
91
- schema: Union[str, Sequence['cirq.Gate']] = 'X_XINV',
214
+ schema: Union[str, Tuple['cirq.Gate', ...]] = 'DEFAULT',
215
+ single_qubit_gate_moments_only: bool = True,
92
216
  ) -> 'cirq.Circuit':
93
- """Adds dynamical decoupling gate operations to idle moments of a given circuit.
94
- This transformer preserves the moment structure of the circuit.
217
+ """Adds dynamical decoupling gate operations to a given circuit.
218
+ This transformer might add a new moment after each piece of Clifford moments, so the original
219
+ moment structure could change.
95
220
 
96
221
  Args:
97
222
  circuit: Input circuit to transform.
@@ -99,24 +224,70 @@ def add_dynamical_decoupling(
99
224
  schema: Dynamical decoupling schema name or a dynamical decoupling sequence.
100
225
  If a schema is specified, provided dynamical decouping sequence will be used.
101
226
  Otherwise, customized dynamical decoupling sequence will be applied.
227
+ single_qubit_gate_moments_only: If set True, dynamical decoupling operation will only be
228
+ added in single-qubit gate moments.
102
229
 
103
230
  Returns:
104
231
  A copy of the input circuit with dynamical decoupling operations.
105
232
  """
106
- last_busy_moment_by_qubits: Dict['cirq.Qid', int] = {q: 0 for q in circuit.all_qubits()}
107
- insert_into: list[Tuple[int, 'cirq.OP_TREE']] = []
233
+ base_dd_sequence: Tuple['cirq.Gate', ...] = _parse_dd_sequence(schema)
234
+ mutable_circuit = circuit.unfreeze(copy=True)
108
235
 
109
- base_dd_sequence = _parse_dd_sequence(schema)
236
+ pauli_map: Dict['cirq.Gate', 'cirq.Pauli'] = {}
237
+ for gate in base_dd_sequence:
238
+ pauli_gate = _pauli_up_to_global_phase(gate)
239
+ if pauli_gate is not None:
240
+ pauli_map[gate] = pauli_gate
110
241
 
242
+ busy_moment_range_by_qubit: Dict['cirq.Qid', list[int]] = {
243
+ q: [len(circuit), -1] for q in circuit.all_qubits()
244
+ }
111
245
  for moment_id, moment in enumerate(circuit):
112
246
  for q in moment.qubits:
113
- insert_gates = _repeat_sequence(
114
- base_dd_sequence, num_idle_moments=moment_id - last_busy_moment_by_qubits[q] - 1
115
- )
116
- for idx, gate in enumerate(insert_gates):
117
- insert_into.append((last_busy_moment_by_qubits[q] + idx + 1, gate.on(q)))
118
- last_busy_moment_by_qubits[q] = moment_id
247
+ busy_moment_range_by_qubit[q][0] = min(busy_moment_range_by_qubit[q][0], moment_id)
248
+ busy_moment_range_by_qubit[q][1] = max(busy_moment_range_by_qubit[q][1], moment_id)
249
+ clifford_pieces = _get_clifford_pieces(circuit)
250
+
251
+ insert_intos: list[Tuple[int, 'cirq.Operation']] = []
252
+ insert_moments: list[Tuple[int, 'cirq.Moment']] = []
253
+ for l, r in clifford_pieces: # [l, r)
254
+ # A PauliString stores the result of 'pulling' Pauli gates past each operations
255
+ # right before the current moment.
256
+ pulled_through: 'cirq.PauliString' = cirq.PauliString()
257
+ iter_by_qubits = {q: cycle(base_dd_sequence) for q in circuit.all_qubits()}
258
+
259
+ # Iterate over the Clifford piece.
260
+ for moment_id in range(l, r):
261
+ moment = circuit.moments[moment_id]
262
+
263
+ # Insert
264
+ if _is_insertable_moment(moment, single_qubit_gate_moments_only):
265
+ for q in circuit.all_qubits() - moment.qubits:
266
+ if (
267
+ busy_moment_range_by_qubit[q][0]
268
+ < moment_id
269
+ < busy_moment_range_by_qubit[q][1]
270
+ ):
271
+ insert_gate = next(iter_by_qubits[q])
272
+ insert_intos.append((moment_id, insert_gate.on(q)))
273
+ pulled_through *= pauli_map[insert_gate].on(q)
274
+
275
+ # Pull through
276
+ pulled_through = _calc_pulled_through(moment, pulled_through)
277
+
278
+ mutable_circuit.batch_insert_into(insert_intos)
279
+ insert_intos.clear()
280
+
281
+ pulled_through = _merge_pulled_through(
282
+ mutable_circuit, pulled_through, (l, r), single_qubit_gate_moments_only
283
+ )
284
+
285
+ # Insert a new moment if there are remaining pulled through operations.
286
+ new_moment_ops = []
287
+ for affected_q, combined_op_in_pauli in pulled_through.items():
288
+ new_moment_ops.append(combined_op_in_pauli.on(affected_q))
289
+ if len(new_moment_ops) != 0:
290
+ insert_moments.append((r, cirq.Moment(new_moment_ops)))
119
291
 
120
- updated_circuit = circuit.unfreeze(copy=True)
121
- updated_circuit.batch_insert_into(insert_into)
122
- return updated_circuit
292
+ mutable_circuit.batch_insert(insert_moments)
293
+ return mutable_circuit
@@ -12,26 +12,54 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Sequence, Union
15
+ from typing import Sequence, Tuple, Union
16
16
  import cirq
17
17
  from cirq import add_dynamical_decoupling
18
18
  import pytest
19
+ import numpy as np
20
+
21
+
22
+ def assert_sim_eq(circuit1: 'cirq.AbstractCircuit', circuit2: 'cirq.AbstractCircuit'):
23
+ # Simulate 2 circuits and compare final states.
24
+ sampler = cirq.Simulator(dtype=np.complex128)
25
+ psi0 = sampler.simulate(cirq.drop_terminal_measurements(circuit1)).final_state_vector
26
+ psi1 = sampler.simulate(cirq.drop_terminal_measurements(circuit2)).final_state_vector
27
+
28
+ assert np.isclose(np.abs(np.vdot(psi0, psi1)) ** 2, 1.0)
19
29
 
20
30
 
21
31
  def assert_dd(
22
- input_circuit: cirq.Circuit,
23
- expected_circuit: cirq.Circuit,
24
- schema: Union[str, Sequence['cirq.Gate']],
32
+ input_circuit: 'cirq.AbstractCircuit',
33
+ expected_circuit: 'cirq.AbstractCircuit',
34
+ schema: Union[str, Tuple['cirq.Gate', ...]] = 'DEFAULT',
35
+ single_qubit_gate_moments_only: bool = True,
25
36
  ):
26
- updated_circuit = add_dynamical_decoupling(input_circuit, schema=schema)
27
- cirq.testing.assert_same_circuits(updated_circuit, expected_circuit)
37
+ transformed_circuit = add_dynamical_decoupling(
38
+ input_circuit, schema=schema, single_qubit_gate_moments_only=single_qubit_gate_moments_only
39
+ ).freeze()
40
+ cirq.testing.assert_same_circuits(transformed_circuit, expected_circuit)
41
+ cirq.testing.assert_circuits_have_same_unitary_given_final_permutation(
42
+ cirq.drop_terminal_measurements(input_circuit),
43
+ cirq.drop_terminal_measurements(transformed_circuit),
44
+ {q: q for q in input_circuit.all_qubits()},
45
+ )
46
+ assert_sim_eq(input_circuit, transformed_circuit)
28
47
 
29
48
 
30
- def test_no_insert_due_to_no_consecutive_moments():
49
+ def test_no_insertion():
50
+ """Test case diagrams.
51
+ Input:
52
+ a: ───H───@───────
53
+
54
+ b: ───────X───H───
55
+ Output:
56
+ a: ───H───@───────
57
+
58
+ b: ───────X───H───
59
+ """
31
60
  a = cirq.NamedQubit('a')
32
61
  b = cirq.NamedQubit('b')
33
62
 
34
- # No insertion as there is no room for a dd sequence.
35
63
  assert_dd(
36
64
  input_circuit=cirq.Circuit(
37
65
  cirq.Moment(cirq.H(a)), cirq.Moment(cirq.CNOT(a, b)), cirq.Moment(cirq.H(b))
@@ -40,6 +68,7 @@ def test_no_insert_due_to_no_consecutive_moments():
40
68
  cirq.Moment(cirq.H(a)), cirq.Moment(cirq.CNOT(a, b)), cirq.Moment(cirq.H(b))
41
69
  ),
42
70
  schema='XX_PAIR',
71
+ single_qubit_gate_moments_only=False,
43
72
  )
44
73
 
45
74
 
@@ -53,6 +82,14 @@ def test_no_insert_due_to_no_consecutive_moments():
53
82
  ],
54
83
  )
55
84
  def test_insert_provided_schema(schema: str, inserted_gates: Sequence['cirq.Gate']):
85
+ """Test case diagrams.
86
+ Input:
87
+ a: ───H───@───────────M───
88
+
89
+ b: ───────X───@───@───M───
90
+ │ │
91
+ c: ───────────X───X───M───
92
+ """
56
93
  a = cirq.NamedQubit('a')
57
94
  b = cirq.NamedQubit('b')
58
95
  c = cirq.NamedQubit('c')
@@ -62,21 +99,35 @@ def test_insert_provided_schema(schema: str, inserted_gates: Sequence['cirq.Gate
62
99
  cirq.Moment(cirq.CNOT(a, b)),
63
100
  cirq.Moment(cirq.CNOT(b, c)),
64
101
  cirq.Moment(cirq.CNOT(b, c)),
65
- cirq.Moment(cirq.measure_each(a, b, c)),
102
+ cirq.Moment([cirq.M(qubit) for qubit in [a, b, c]]),
66
103
  )
67
104
  expected_circuit = cirq.Circuit(
68
105
  cirq.Moment(cirq.H(a)),
69
106
  cirq.Moment(cirq.CNOT(a, b)),
70
107
  cirq.Moment(cirq.CNOT(b, c), inserted_gates[0](a)),
71
108
  cirq.Moment(cirq.CNOT(b, c), inserted_gates[1](a)),
72
- cirq.Moment(cirq.measure_each(a, b, c)),
109
+ cirq.Moment([cirq.M(qubit) for qubit in [a, b, c]]),
73
110
  )
74
111
 
75
112
  # Insert one dynamical decoupling sequence in idle moments.
76
- assert_dd(input_circuit, expected_circuit, schema=schema)
113
+ assert_dd(input_circuit, expected_circuit, schema=schema, single_qubit_gate_moments_only=False)
77
114
 
78
115
 
79
116
  def test_insert_by_customized_dd_sequence():
117
+ """Test case diagrams.
118
+ Input:
119
+ a: ───H───@───────────────────H───
120
+
121
+ b: ───────X───@───@───@───@───H───
122
+ │ │ │ │
123
+ c: ───────────X───X───X───X───H───
124
+ Output:
125
+ a: ───H───@───X───X───Y───Y───H───
126
+
127
+ b: ───────X───@───@───@───@───H───
128
+ │ │ │ │
129
+ c: ───────────X───X───X───X───H───
130
+ """
80
131
  a = cirq.NamedQubit('a')
81
132
  b = cirq.NamedQubit('b')
82
133
  c = cirq.NamedQubit('c')
@@ -89,7 +140,7 @@ def test_insert_by_customized_dd_sequence():
89
140
  cirq.Moment(cirq.CNOT(b, c)),
90
141
  cirq.Moment(cirq.CNOT(b, c)),
91
142
  cirq.Moment(cirq.CNOT(b, c)),
92
- cirq.Moment(cirq.measure_each(a, b, c)),
143
+ cirq.Moment([cirq.H(qubit) for qubit in [a, b, c]]),
93
144
  ),
94
145
  expected_circuit=cirq.Circuit(
95
146
  cirq.Moment(cirq.H(a)),
@@ -98,9 +149,84 @@ def test_insert_by_customized_dd_sequence():
98
149
  cirq.Moment(cirq.CNOT(b, c), cirq.X(a)),
99
150
  cirq.Moment(cirq.CNOT(b, c), cirq.Y(a)),
100
151
  cirq.Moment(cirq.CNOT(b, c), cirq.Y(a)),
101
- cirq.Moment(cirq.measure_each(a, b, c)),
152
+ cirq.Moment([cirq.H(qubit) for qubit in [a, b, c]]),
102
153
  ),
103
154
  schema=[cirq.X, cirq.X, cirq.Y, cirq.Y],
155
+ single_qubit_gate_moments_only=False,
156
+ )
157
+
158
+
159
+ @pytest.mark.parametrize('single_qubit_gate_moments_only', [True, False])
160
+ def test_pull_through_h_gate_case1(single_qubit_gate_moments_only: bool):
161
+ """Test case diagrams.
162
+ Input:
163
+ a: ───H───────H───────@───
164
+
165
+ b: ───H───H───H───H───X───
166
+ Output:
167
+ a: ───H───X───H───X───@───Y───
168
+
169
+ b: ───H───H───H───H───X───X───
170
+ """
171
+ a = cirq.NamedQubit('a')
172
+ b = cirq.NamedQubit('b')
173
+
174
+ assert_dd(
175
+ input_circuit=cirq.Circuit(
176
+ cirq.Moment(cirq.H(a), cirq.H(b)),
177
+ cirq.Moment(cirq.H(b)),
178
+ cirq.Moment(cirq.H(a), cirq.H(b)),
179
+ cirq.Moment(cirq.H(b)),
180
+ cirq.Moment(cirq.CNOT(a, b)),
181
+ ),
182
+ expected_circuit=cirq.Circuit(
183
+ cirq.Moment(cirq.H(a), cirq.H(b)),
184
+ cirq.Moment(cirq.H(b), cirq.X(a)),
185
+ cirq.Moment(cirq.H(a), cirq.H(b)),
186
+ cirq.Moment(cirq.H(b), cirq.X(a)),
187
+ cirq.Moment(cirq.CNOT(a, b)),
188
+ cirq.Moment(cirq.Y(a), cirq.X(b)),
189
+ ),
190
+ schema="XX_PAIR",
191
+ single_qubit_gate_moments_only=single_qubit_gate_moments_only,
192
+ )
193
+
194
+
195
+ @pytest.mark.parametrize('single_qubit_gate_moments_only', [True, False])
196
+ def test_pull_through_h_gate_case2(single_qubit_gate_moments_only: bool):
197
+ """Test case diagrams.
198
+ Input:
199
+ a: ───H───────H───────H───
200
+
201
+ b: ───H───H───H───H───H───
202
+ Output:
203
+ a: ───H───X───H───X───PhXZ(a=0.5,x=0.5,z=1)───
204
+
205
+ b: ───H───H───H───H───H───────────────────────
206
+ """
207
+ a = cirq.NamedQubit('a')
208
+ b = cirq.NamedQubit('b')
209
+
210
+ assert_dd(
211
+ input_circuit=cirq.Circuit(
212
+ cirq.Moment(cirq.H(a), cirq.H(b)),
213
+ cirq.Moment(cirq.H(b)),
214
+ cirq.Moment(cirq.H(a), cirq.H(b)),
215
+ cirq.Moment(cirq.H(b)),
216
+ cirq.Moment(cirq.H(a), cirq.H(b)),
217
+ ),
218
+ expected_circuit=cirq.Circuit(
219
+ cirq.Moment(cirq.H(a), cirq.H(b)),
220
+ cirq.Moment(cirq.H(b), cirq.X(a)),
221
+ cirq.Moment(cirq.H(a), cirq.H(b)),
222
+ cirq.Moment(cirq.H(b), cirq.X(a)),
223
+ cirq.Moment(
224
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0.5, z_exponent=1).on(a),
225
+ cirq.H(b),
226
+ ),
227
+ ),
228
+ schema="XX_PAIR",
229
+ single_qubit_gate_moments_only=single_qubit_gate_moments_only,
104
230
  )
105
231
 
106
232
 
@@ -110,14 +236,393 @@ def test_insert_by_customized_dd_sequence():
110
236
  ('INVALID_SCHEMA', 'Invalid schema name.'),
111
237
  ([cirq.X], 'Invalid dynamical decoupling sequence. Expect more than one gates.'),
112
238
  (
113
- [cirq.X, cirq.H],
239
+ [cirq.X, cirq.Y],
114
240
  'Invalid dynamical decoupling sequence. Expect sequence production equals identity'
115
241
  ' up to a global phase, got',
116
242
  ),
243
+ (
244
+ [cirq.H, cirq.H],
245
+ 'Dynamical decoupling sequence should only contain gates that are essentially'
246
+ ' Pauli gates.',
247
+ ),
117
248
  ],
118
249
  )
119
- def test_invalid_dd_schema(schema: Union[str, Sequence['cirq.Gate']], error_msg_regex):
250
+ def test_invalid_dd_schema(schema: Union[str, Tuple['cirq.Gate', ...]], error_msg_regex):
120
251
  a = cirq.NamedQubit('a')
121
252
  input_circuit = cirq.Circuit(cirq.H(a))
122
253
  with pytest.raises(ValueError, match=error_msg_regex):
123
- add_dynamical_decoupling(input_circuit, schema=schema)
254
+ add_dynamical_decoupling(input_circuit, schema=schema, single_qubit_gate_moments_only=False)
255
+
256
+
257
+ def test_single_qubit_gate_moments_only_no_updates_succeeds():
258
+ qubits = cirq.LineQubit.range(9)
259
+ input_circuit = cirq.Circuit(
260
+ cirq.Moment([cirq.H(qubits[i]) for i in [3, 4, 5]]),
261
+ cirq.Moment(cirq.CZ(*qubits[4:6])),
262
+ cirq.Moment(cirq.CZ(*qubits[3:5])),
263
+ cirq.Moment([cirq.H(qubits[i]) for i in [2, 3, 5, 6]]),
264
+ cirq.Moment(cirq.CZ(*qubits[2:4]), cirq.CNOT(*qubits[5:7])),
265
+ cirq.Moment([cirq.H(qubits[i]) for i in [1, 2, 6, 7]]),
266
+ cirq.Moment(cirq.CZ(*qubits[1:3]), cirq.CNOT(*qubits[6:8])),
267
+ cirq.Moment([cirq.H(qubits[i]) for i in [0, 1, 7, 8]]),
268
+ cirq.Moment(cirq.CZ(*qubits[0:2]), cirq.CNOT(*qubits[7:])),
269
+ )
270
+ add_dynamical_decoupling(input_circuit, schema='X_XINV', single_qubit_gate_moments_only=True)
271
+
272
+
273
+ def test_scattered_circuit():
274
+ """Test case diagrams.
275
+ Input:
276
+ 0: ───────────────────────────────H───@───H───
277
+
278
+ 1: ───────────────────────H───@───H───@───H───
279
+
280
+ 2: ───────────────H───@───H───@───────────H───
281
+
282
+ 3: ───H───────@───H───@───────────────────H───
283
+
284
+ 4: ───H───@───@───────────────────────────H───
285
+
286
+ 5: ───H───@───────H───@───────────────────H───
287
+
288
+ 6: ───────────────H───@───H───@───────────H───
289
+
290
+ 7: ───────────────────────H───@───H───@───H───
291
+
292
+ 8: ───────────────────────────────H───@───H───
293
+
294
+ Output (single_qubit_gate_moment_only_on):
295
+ 0: ───────────────────────────────H───@───H────────────────────────
296
+
297
+ 1: ───────────────────────H───@───H───@───H────────────────────────
298
+
299
+ 2: ───────────────H───@───H───@───X───────PhXZ(a=-0.5,x=0.5,z=0)───
300
+
301
+ 3: ───H───────@───H───@───X───────Y───────PhXZ(a=0.5,x=0.5,z=0)────
302
+
303
+ 4: ───H───@───@───X───────Y───────X───────PhXZ(a=0.5,x=0.5,z=-1)───
304
+
305
+ 5: ───H───@───────H───@───X───────Y───────PhXZ(a=0.5,x=0.5,z=0)────
306
+
307
+ 6: ───────────────H───@───H───@───X───────PhXZ(a=-0.5,x=0.5,z=0)───
308
+
309
+ 7: ───────────────────────H───@───H───@───H────────────────────────
310
+
311
+ 8: ───────────────────────────────H───@───H────────────────────────
312
+
313
+ Output (single_qubit_gate_moment_only_off):
314
+ 0: ───────────────────────────────H───@───H───────────────────────
315
+
316
+ 1: ───────────────────────H───@───H───@───H───────────────────────
317
+
318
+ 2: ───────────────H───@───H───@───X───Y───PhXZ(a=0.5,x=0.5,z=0)───
319
+
320
+ 3: ───H───X───@───H───@───Y───X───Y───X───PhXZ(a=0.5,x=0.5,z=0)───
321
+
322
+ 4: ───H───@───@───X───Y───X───Y───X───Y───H───────────────────────
323
+
324
+ 5: ───H───@───X───H───@───Y───X───Y───X───PhXZ(a=0.5,x=0.5,z=0)───
325
+
326
+ 6: ───────────────H───@───H───@───X───Y───PhXZ(a=0.5,x=0.5,z=0)───
327
+
328
+ 7: ───────────────────────H───@───H───@───H───────────────────────
329
+
330
+ 8: ───────────────────────────────H───@───H───────────────────────
331
+ """
332
+ qubits = cirq.LineQubit.range(9)
333
+ input_circuit = cirq.Circuit(
334
+ cirq.Moment([cirq.H(qubits[i]) for i in [3, 4, 5]]),
335
+ cirq.Moment(cirq.CZ(*qubits[4:6])),
336
+ cirq.Moment(cirq.CZ(*qubits[3:5])),
337
+ cirq.Moment([cirq.H(qubits[i]) for i in [2, 3, 5, 6]]),
338
+ cirq.Moment(cirq.CZ(*qubits[2:4]), cirq.CZ(*qubits[5:7])),
339
+ cirq.Moment([cirq.H(qubits[i]) for i in [1, 2, 6, 7]]),
340
+ cirq.Moment(cirq.CZ(*qubits[1:3]), cirq.CZ(*qubits[6:8])),
341
+ cirq.Moment([cirq.H(qubits[i]) for i in [0, 1, 7, 8]]),
342
+ cirq.Moment(cirq.CZ(*qubits[0:2]), cirq.CZ(*qubits[7:])),
343
+ cirq.Moment([cirq.H(q) for q in qubits]),
344
+ )
345
+ expected_circuit_single_qubit_gate_on = cirq.Circuit(
346
+ cirq.Moment([cirq.H(qubits[i]) for i in [3, 4, 5]]),
347
+ cirq.Moment(cirq.CZ(*qubits[4:6])),
348
+ cirq.Moment(cirq.CZ(*qubits[3:5])),
349
+ cirq.Moment([cirq.H(qubits[i]) for i in [2, 3, 5, 6]] + [cirq.X(qubits[4])]),
350
+ cirq.Moment(cirq.CZ(*qubits[2:4]), cirq.CZ(*qubits[5:7])),
351
+ cirq.Moment(
352
+ [cirq.H(qubits[i]) for i in [1, 2, 6, 7]]
353
+ + [cirq.X(qubits[i]) for i in [3, 5]]
354
+ + [cirq.Y(qubits[4])]
355
+ ),
356
+ cirq.Moment(cirq.CZ(*qubits[1:3]), cirq.CZ(*qubits[6:8])),
357
+ cirq.Moment(
358
+ [cirq.H(qubits[i]) for i in [0, 1, 7, 8]]
359
+ + [cirq.X(qubits[i]) for i in [2, 4, 6]]
360
+ + [cirq.Y(qubits[i]) for i in [3, 5]]
361
+ ),
362
+ cirq.Moment(cirq.CZ(*qubits[0:2]), cirq.CZ(*qubits[7:])),
363
+ cirq.Moment(
364
+ [cirq.H(qubits[i]) for i in [0, 1, 7, 8]]
365
+ + [
366
+ cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0).on(
367
+ qubits[2]
368
+ ),
369
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0.5, z_exponent=0).on(
370
+ qubits[3]
371
+ ),
372
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0.5, z_exponent=-1).on(
373
+ qubits[4]
374
+ ),
375
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0.5, z_exponent=0).on(
376
+ qubits[5]
377
+ ),
378
+ cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0).on(
379
+ qubits[6]
380
+ ),
381
+ ]
382
+ ),
383
+ )
384
+ expected_circuit_single_qubit_gates_off = cirq.Circuit(
385
+ cirq.Moment([cirq.H(qubits[i]) for i in [3, 4, 5]]),
386
+ cirq.Moment(cirq.CZ(*qubits[4:6]), cirq.X(qubits[3])),
387
+ cirq.Moment(cirq.CZ(*qubits[3:5]), cirq.X(qubits[5])),
388
+ cirq.Moment([cirq.H(qubits[i]) for i in [2, 3, 5, 6]] + [cirq.X(qubits[i]) for i in [4]]),
389
+ cirq.Moment(cirq.CZ(*qubits[2:4]), cirq.CZ(*qubits[5:7]), cirq.Y(qubits[4])),
390
+ cirq.Moment(
391
+ [cirq.H(qubits[i]) for i in [1, 2, 6, 7]]
392
+ + [cirq.Y(qubits[i]) for i in [3, 5]]
393
+ + [cirq.X(qubits[4])]
394
+ ),
395
+ cirq.Moment(
396
+ [cirq.CZ(*qubits[1:3]), cirq.CZ(*qubits[6:8])]
397
+ + [cirq.X(qubits[i]) for i in [3, 5]]
398
+ + [cirq.Y(qubits[4])]
399
+ ),
400
+ cirq.Moment(
401
+ [cirq.H(qubits[i]) for i in [0, 1, 7, 8]]
402
+ + [cirq.X(qubits[i]) for i in [2, 4, 6]]
403
+ + [cirq.Y(qubits[i]) for i in [3, 5]]
404
+ ),
405
+ cirq.Moment(
406
+ [cirq.CZ(*qubits[0:2]), cirq.CZ(*qubits[7:])]
407
+ + [cirq.X(qubits[i]) for i in [3, 5]]
408
+ + [cirq.Y(qubits[i]) for i in [2, 4, 6]]
409
+ ),
410
+ cirq.Moment(
411
+ [cirq.H(qubits[i]) for i in [0, 1, 4, 7, 8]]
412
+ + [
413
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0.5, z_exponent=0).on(
414
+ qubits[i]
415
+ )
416
+ for i in [2, 3, 5, 6]
417
+ ]
418
+ ),
419
+ )
420
+ assert_dd(
421
+ input_circuit,
422
+ expected_circuit_single_qubit_gate_on,
423
+ schema='DEFAULT',
424
+ single_qubit_gate_moments_only=True,
425
+ )
426
+ assert_dd(
427
+ input_circuit,
428
+ expected_circuit_single_qubit_gates_off,
429
+ schema='DEFAULT',
430
+ single_qubit_gate_moments_only=False,
431
+ )
432
+
433
+
434
+ def test_scattered_circuit2():
435
+ """Test case diagrams.
436
+ Input:
437
+ 0: ───────────────────@───
438
+
439
+ 1: ───────────────@───@───
440
+
441
+ 2: ───────────@───@───────
442
+
443
+ 3: ───────@───@───────────
444
+
445
+ 4: ───@───@───────────────
446
+
447
+ 5: ───@───────@───────────
448
+
449
+ 6: ───────────@───@───────
450
+
451
+ 7: ───────────────@───@───
452
+
453
+ 8: ───────────────────@───
454
+ Output:
455
+ 0: ───────────────────@───
456
+
457
+ 1: ───────────────@───@───
458
+
459
+ 2: ───────────@───@───────
460
+
461
+ 3: ───────@───@───────────
462
+
463
+ 4: ───@───@───────────────
464
+
465
+ 5: ───@───X───@───X───────
466
+
467
+ 6: ───────────@───@───Z───
468
+
469
+ 7: ───────────────@───@───
470
+
471
+ 8: ───────────────────@───
472
+ """
473
+ qubits = cirq.LineQubit.range(9)
474
+ assert_dd(
475
+ input_circuit=cirq.Circuit(
476
+ cirq.Moment(cirq.CZ(*qubits[4:6])),
477
+ cirq.Moment(cirq.CZ(*qubits[3:5])),
478
+ cirq.Moment(cirq.CZ(*qubits[2:4]), cirq.CZ(*qubits[5:7])),
479
+ cirq.Moment(cirq.CZ(*qubits[1:3]), cirq.CZ(*qubits[6:8])),
480
+ cirq.Moment(cirq.CZ(*qubits[0:2]), cirq.CZ(*qubits[7:])),
481
+ ),
482
+ expected_circuit=cirq.Circuit(
483
+ cirq.Moment(cirq.CZ(*qubits[4:6])),
484
+ cirq.Moment(cirq.CZ(*qubits[3:5]), cirq.X(qubits[5])),
485
+ cirq.Moment(cirq.CZ(*qubits[2:4]), cirq.CZ(*qubits[5:7])),
486
+ cirq.Moment(cirq.CZ(*qubits[1:3]), cirq.CZ(*qubits[6:8]), cirq.X(qubits[5])),
487
+ cirq.Moment(cirq.CZ(*qubits[0:2]), cirq.CZ(*qubits[7:]), cirq.Z(qubits[6])),
488
+ ),
489
+ schema="XX_PAIR",
490
+ single_qubit_gate_moments_only=False,
491
+ )
492
+
493
+
494
+ def test_pull_through_chain():
495
+ """Test case diagrams.
496
+ Input:
497
+ 0: ───X───────×───────────X───
498
+
499
+ 1: ───────Y───×───×───────X───
500
+
501
+ 2: ───────────────×───×───X───
502
+
503
+ 3: ───────────────────×───X───
504
+ Output:
505
+ 0: ───X───X───×───X───X───X───
506
+
507
+ 1: ───────Y───×───×───X───I───
508
+
509
+ 2: ───────────────×───×───X───
510
+
511
+ 3: ───────────────────×───I───
512
+ """
513
+ qubits = cirq.LineQubit.range(4)
514
+ assert_dd(
515
+ input_circuit=cirq.Circuit(
516
+ cirq.Moment(cirq.X(qubits[0])),
517
+ cirq.Moment(cirq.Y(qubits[1])),
518
+ cirq.Moment(cirq.SWAP(*qubits[0:2])),
519
+ cirq.Moment(cirq.SWAP(*qubits[1:3])),
520
+ cirq.Moment(cirq.SWAP(*qubits[2:4])),
521
+ cirq.Moment([cirq.X(qubits[i]) for i in range(4)]),
522
+ ),
523
+ expected_circuit=cirq.Circuit(
524
+ cirq.Moment(cirq.X(qubits[0])),
525
+ cirq.Moment(cirq.Y(qubits[1]), cirq.X(qubits[0])),
526
+ cirq.Moment(cirq.SWAP(*qubits[0:2])),
527
+ cirq.Moment([cirq.SWAP(*qubits[1:3])] + [cirq.X(qubits[0])]),
528
+ cirq.Moment([cirq.SWAP(*qubits[2:4])] + [cirq.X(qubits[0]), cirq.X(qubits[1])]),
529
+ cirq.Moment(cirq.X(qubits[0]), cirq.I(qubits[1]), cirq.X(qubits[2]), cirq.I(qubits[3])),
530
+ ),
531
+ schema='XX_PAIR',
532
+ single_qubit_gate_moments_only=False,
533
+ )
534
+
535
+
536
+ def test_multiple_clifford_pieces():
537
+ """Test case diagrams.
538
+ Input:
539
+ a: ───H───────H───────@───────────H───────H───
540
+
541
+ b: ───H───H───H───H───@^0.5───H───H───H───H───
542
+ Output:
543
+ a: ───H───X───H───PhXZ(a=0.5,x=0,z=-1)───@───────X───H───X───PhXZ(a=0.5,x=0.5,z=-1)───
544
+
545
+ b: ───H───H───H───H──────────────────────@^0.5───H───H───H───H────────────────────────
546
+ """
547
+ a = cirq.NamedQubit('a')
548
+ b = cirq.NamedQubit('b')
549
+ assert_dd(
550
+ input_circuit=cirq.Circuit(
551
+ cirq.Moment(cirq.H(a), cirq.H(b)),
552
+ cirq.Moment(cirq.H(b)),
553
+ cirq.Moment(cirq.H(a), cirq.H(b)),
554
+ cirq.Moment(cirq.H(b)),
555
+ cirq.Moment(cirq.CZPowGate(exponent=0.5).on(a, b)),
556
+ cirq.Moment(cirq.H(b)),
557
+ cirq.Moment(cirq.H(a), cirq.H(b)),
558
+ cirq.Moment(cirq.H(b)),
559
+ cirq.Moment(cirq.H(a), cirq.H(b)),
560
+ ),
561
+ expected_circuit=cirq.Circuit(
562
+ cirq.Moment(cirq.H(a), cirq.H(b)),
563
+ cirq.Moment(cirq.H(b), cirq.X(a)),
564
+ cirq.Moment(cirq.H(a), cirq.H(b)),
565
+ cirq.Moment(
566
+ cirq.H(b),
567
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0, z_exponent=-1).on(a),
568
+ ),
569
+ cirq.Moment(cirq.CZPowGate(exponent=0.5).on(a, b)),
570
+ cirq.Moment(cirq.H(b), cirq.X(a)),
571
+ cirq.Moment(cirq.H(a), cirq.H(b)),
572
+ cirq.Moment(cirq.H(b), cirq.X(a)),
573
+ cirq.Moment(
574
+ cirq.H(b),
575
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0.5, z_exponent=-1).on(a),
576
+ ),
577
+ ),
578
+ schema="XX_PAIR",
579
+ )
580
+
581
+
582
+ def test_with_non_clifford_measurements():
583
+ """Test case diagrams.
584
+ Input:
585
+ 0: ───────────H───@───H───M───
586
+
587
+ 1: ───H───@───────@───────M───
588
+
589
+ 2: ───H───@───H───@───────M───
590
+
591
+ 3: ───────────H───@───H───M───
592
+ Output:
593
+ 0: ───────────H───@───PhXZ(a=0.5,x=0.5,z=0)───M───
594
+
595
+ 1: ───H───@───X───@───X───────────────────────M───
596
+
597
+ 2: ───H───@───H───@───I───────────────────────M───
598
+
599
+ 3: ───────────H───@───H───────────────────────M───
600
+ """
601
+ qubits = cirq.LineQubit.range(4)
602
+ assert_dd(
603
+ input_circuit=cirq.Circuit(
604
+ cirq.Moment([cirq.H(qubits[i]) for i in [1, 2]]),
605
+ cirq.Moment(cirq.CZ(*qubits[1:3])),
606
+ cirq.Moment([cirq.H(qubits[i]) for i in [0, 2, 3]]),
607
+ cirq.Moment(cirq.CZ(*qubits[0:2]), cirq.CZ(*qubits[2:])),
608
+ cirq.Moment([cirq.H(qubits[i]) for i in [0, 3]]),
609
+ cirq.Moment([cirq.M(qubits[i]) for i in [0, 1, 2, 3]]),
610
+ ),
611
+ expected_circuit=cirq.Circuit(
612
+ cirq.Moment([cirq.H(qubits[i]) for i in [1, 2]]),
613
+ cirq.Moment(cirq.CZ(*qubits[1:3])),
614
+ cirq.Moment([cirq.H(qubits[i]) for i in [0, 2, 3]] + [cirq.X(qubits[1])]),
615
+ cirq.Moment(cirq.CZ(*qubits[0:2]), cirq.CZ(*qubits[2:])),
616
+ cirq.Moment(
617
+ cirq.H(qubits[3]),
618
+ cirq.I(qubits[2]),
619
+ cirq.X(qubits[1]),
620
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=0.5, z_exponent=0).on(
621
+ qubits[0]
622
+ ),
623
+ ),
624
+ cirq.Moment([cirq.M(qubits[i]) for i in [0, 1, 2, 3]]),
625
+ ),
626
+ schema="XX_PAIR",
627
+ single_qubit_gate_moments_only=True,
628
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cirq-core
3
- Version: 1.5.0.dev20240808235317
3
+ Version: 1.5.0.dev20240814221152
4
4
  Summary: A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
5
5
  Home-page: http://github.com/quantumlib/cirq
6
6
  Author: The Cirq Developers
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=Qq3ZcfgD-Nb81cEppQdJqhAyrVqXKtfXZYGXT0p-Wh0,34718
4
4
  cirq/_doc.py,sha256=yDyWUD_2JDS0gShfGRb-rdqRt9-WeL7DhkqX7np0Nko,2879
5
5
  cirq/_import.py,sha256=p9gMHJscbtDDkfHOaulvd3Aer0pwUF5AXpL89XR8dNw,8402
6
6
  cirq/_import_test.py,sha256=6K_v0riZJXOXUphHNkGA8MY-JcmGlezFaGmvrNhm3OQ,1015
7
- cirq/_version.py,sha256=EP1nJeAwmQmSW8pS1Pg_Tua6jz_K6YOpxPsaOb9X8PY,1206
8
- cirq/_version_test.py,sha256=q3YYDQXqiJdnwjv2NEiKt-39-sgV49azjzJKgsK5Fp4,147
7
+ cirq/_version.py,sha256=1bB16cK2tv4gErFbbZYs77OQSV_hic5PnSGQwqGHZmI,1206
8
+ cirq/_version_test.py,sha256=5rnH4LiOiOoi5GNI-BWvy9b5Euhv8m4oyCqfz4nKqnM,147
9
9
  cirq/conftest.py,sha256=X7yLFL8GLhg2CjPw0hp5e_dGASfvHx1-QT03aUbhKJw,1168
10
10
  cirq/json_resolver_cache.py,sha256=ytePZtNZgKjOF2NiVpUTuotB-JKZmQNOFIFdvXqsxHw,13271
11
11
  cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
@@ -247,7 +247,7 @@ cirq/linalg/__init__.py,sha256=iMN1jVWzuZrSdHuE-gN2N1QyP0q6wgj6F4zetxJLCJQ,2527
247
247
  cirq/linalg/combinators.py,sha256=5q_cNjnJrDgC7qMX8rYdnCmBKXT_iVbtxnCeJQ4ZPTM,5350
248
248
  cirq/linalg/combinators_test.py,sha256=nZ3snkVA2nAOZ6WJK1hNd1f_i2a5xNdnostfMD1upbc,4699
249
249
  cirq/linalg/decompositions.py,sha256=cNIcKylK4tnGgIreO7X4cpsRmwvOycKRSS8xf9gaIG0,39229
250
- cirq/linalg/decompositions_test.py,sha256=-6q2u3BDG9b32QSUAjhmBNWrL3KzyiMH_R0WIHhdOqU,25790
250
+ cirq/linalg/decompositions_test.py,sha256=S6p9AJO_9wwQdmNHYTAgBK4TL6_lz1U93sLNNuUJmBw,25840
251
251
  cirq/linalg/diagonalize.py,sha256=Ym7C0JTAC9xfRQDYI72U6NxMYg0DfchjfXcbdg_92QE,10051
252
252
  cirq/linalg/diagonalize_test.py,sha256=H-JcLvcCBdN-DrKo2o1Gs7B8Q9SU70oAZmdT4yTLAi4,9089
253
253
  cirq/linalg/operator_spaces.py,sha256=uZSc9154p63R2UqFw6pniuWcUo20epe5RK6bL3tpkbM,3813
@@ -943,7 +943,7 @@ cirq/study/sweepable.py,sha256=hHBXn5MQZJawiiIXWZdn_qygbPeIFtt300wIqVHiYl8,4356
943
943
  cirq/study/sweepable_test.py,sha256=ENv03_GJmbUc_ukJoqfgG-H5C_yyx1jCcvxohSMyQVU,5502
944
944
  cirq/study/sweeps.py,sha256=zKz8hmLnpukNzUp7YjsrD8wEGxf2peX19522kUKUfWI,19850
945
945
  cirq/study/sweeps_test.py,sha256=YdXHzZO10OHoPTU2ifmsfH7KByIJeeANy91AHqX8nwg,12135
946
- cirq/testing/__init__.py,sha256=cACho8s-V5tNOjBcDUtr2DipQxQcbUgbr4MESJb4l1I,3870
946
+ cirq/testing/__init__.py,sha256=nYda18836pCF9HkhLUYQD5Nd_tI-3Fyd-ZQ4cGmwX-Y,3954
947
947
  cirq/testing/circuit_compare.py,sha256=nBQES45wLVThOqC3WrPrYKLQP7HQ2pH5DjlJ5bHkrtU,19176
948
948
  cirq/testing/circuit_compare_test.py,sha256=AduZCzwBNFCYrjEpyS1DvIR6jU8GaFqQBBgPXyIALoU,19743
949
949
  cirq/testing/consistent_act_on.py,sha256=ofYxdotw7fDfEVaAoM3NJEG2-hTHmi5FlLZkLYfVBWE,7733
@@ -991,6 +991,8 @@ cirq/testing/op_tree.py,sha256=fb3bOoTihGWp_NvPHekI7s9YZIaBoiufDVhh4ARCfhQ,954
991
991
  cirq/testing/op_tree_test.py,sha256=MwDpi5Lw1e3Y_-9Psx9MPvbbW2x4JlOpp7hl8mBvOQA,1073
992
992
  cirq/testing/order_tester.py,sha256=0_tfixWyX_HrjTXpNrVFZT9bA6J585Ad8tjS-DsX8yU,6690
993
993
  cirq/testing/order_tester_test.py,sha256=2mifnUW_BT17jwWZjmM9p7EoJjq0Ouz54o3G8BqvDqw,5111
994
+ cirq/testing/pytest_randomly_utils.py,sha256=9zF-7EdoBoNO8bl5DRzvmttXSPZMsuoQ9ktEqv8sCZw,1352
995
+ cirq/testing/pytest_randomly_utils_test.py,sha256=gUz2C-azAtgDilAnWXLsOq5NzSJqLg7ykX8-86fLzoc,994
994
996
  cirq/testing/random_circuit.py,sha256=oMoz0_VWWVmUGmJ9mu1R7NByNKtSadxdLjFyDEE1qT0,5900
995
997
  cirq/testing/random_circuit_test.py,sha256=x0ovYOIc3-QNKFjCc87LbLkWNwFZKx6T97ZCN3QGRsI,7026
996
998
  cirq/testing/repr_pretty_tester.py,sha256=dtk713qmE87Biaez3c8R83Wb7tlb4xWFfzt44iKkbto,2899
@@ -1027,8 +1029,8 @@ cirq/transformers/drop_empty_moments.py,sha256=Rtn_BrpwkLXyZBdLzwdnsnEGWTdYuf1xO
1027
1029
  cirq/transformers/drop_empty_moments_test.py,sha256=G8pZmTfi8NG2NpGz_K3LZu5NQoqa-xPMCuZjwEu07xk,1907
1028
1030
  cirq/transformers/drop_negligible_operations.py,sha256=8eyOMy7bra2wJAjORbk6QjwHiLdL5SfwRaz8D2Dazbw,2083
1029
1031
  cirq/transformers/drop_negligible_operations_test.py,sha256=gqL6RoDPm6Zf4RxtprBenFyIsZQPUxmPur9oRl0Yr3U,3823
1030
- cirq/transformers/dynamical_decoupling.py,sha256=iALBcjaO3uwcAla-smjapp2z9YezGecGFo_OGgJkD_8,4704
1031
- cirq/transformers/dynamical_decoupling_test.py,sha256=hGUzTDV1UL3VBIueri91j3_ycxGmr20mGfQSnRAjegM,4149
1032
+ cirq/transformers/dynamical_decoupling.py,sha256=OSaJy55nYJmCVgan0VBlxcXeTKPuiJMJDTnnsaKGBFs,12329
1033
+ cirq/transformers/dynamical_decoupling_test.py,sha256=kXngZhzV_58cPqpx-zhMmabGzaUKEN_9iS3YV7U7fEE,28410
1032
1034
  cirq/transformers/eject_phased_paulis.py,sha256=mTgRT5aw5_c9ccTkP4Np_4YTWnLzxsMKRO8pOQ7CtYM,13955
1033
1035
  cirq/transformers/eject_phased_paulis_test.py,sha256=-mXsfbi3V0ojC_YqoQM5otzdW4kjGusCx6F-kCv8M98,15834
1034
1036
  cirq/transformers/eject_z.py,sha256=0kOyvh6FDfrCrrTCVfpHKNc_kNC_pBdEKoXv11kuqGA,5803
@@ -1061,7 +1063,7 @@ cirq/transformers/analytical_decompositions/__init__.py,sha256=ZNtETntol3G_n6uqz
1061
1063
  cirq/transformers/analytical_decompositions/clifford_decomposition.py,sha256=DsuuP91pm2dX0CO4rWwmJAJyAfuXMcA1UJK0g8krp7k,6726
1062
1064
  cirq/transformers/analytical_decompositions/clifford_decomposition_test.py,sha256=AAZh_9vEb5f2E_EItPZTlMRNdv0d47AwqTn4BytX0UI,7102
1063
1065
  cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py,sha256=iFF2vb5tI4PVQVHBOP_tuy8EKUtGg8aMDZSdK-74YMI,8675
1064
- cirq/transformers/analytical_decompositions/controlled_gate_decomposition_test.py,sha256=Pc1vNvRxcYJEERASHbCqPX1bqImGd7FzWnQcUcIo_YU,4950
1066
+ cirq/transformers/analytical_decompositions/controlled_gate_decomposition_test.py,sha256=oVr92Kdvent8x5xLpK3clT1a3OM0QTs2DmrsuJhV7ag,5000
1065
1067
  cirq/transformers/analytical_decompositions/cphase_to_fsim.py,sha256=RDg0wzYa_YXBJepCgloD_OIwTOwNco98dqGoe0UsnhI,9108
1066
1068
  cirq/transformers/analytical_decompositions/cphase_to_fsim_test.py,sha256=bwZa0BDclAd1sX3bD-GdNF2MO5DtH7mw2YLppEK0LG0,5568
1067
1069
  cirq/transformers/analytical_decompositions/pauli_string_decomposition.py,sha256=bU9IoY0igVZTmF_wsTdTxAfqPKWyqZ14Gt2AJoK5D_4,4524
@@ -1182,8 +1184,8 @@ cirq/work/sampler.py,sha256=JEAeQQRF3bqlO9AkOf4XbrTATDI5f5JgyM_FAUCNxao,19751
1182
1184
  cirq/work/sampler_test.py,sha256=B2ZsuqGT854gQtBIAh8k0LiG9Vj5wSzcGvkxOUoTcW4,13217
1183
1185
  cirq/work/zeros_sampler.py,sha256=x1C7cup66a43n-3tm8QjhiqJa07qcJW10FxNp9jJ59Q,2356
1184
1186
  cirq/work/zeros_sampler_test.py,sha256=JIkpBBFPJe5Ba4142vzogyWyboG1Q1ZAm0UVGgOoZn8,3279
1185
- cirq_core-1.5.0.dev20240808235317.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1186
- cirq_core-1.5.0.dev20240808235317.dist-info/METADATA,sha256=YHl6ngm8VO-9Zmnv3IMG4zhiKrOfB_6IzCja6RxikbI,1992
1187
- cirq_core-1.5.0.dev20240808235317.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
1188
- cirq_core-1.5.0.dev20240808235317.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1189
- cirq_core-1.5.0.dev20240808235317.dist-info/RECORD,,
1187
+ cirq_core-1.5.0.dev20240814221152.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1188
+ cirq_core-1.5.0.dev20240814221152.dist-info/METADATA,sha256=gQBwwQrQXe5-T8Eqh4McfR1gYpPpnnVEBJxxbdz1nDY,1992
1189
+ cirq_core-1.5.0.dev20240814221152.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
1190
+ cirq_core-1.5.0.dev20240814221152.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1191
+ cirq_core-1.5.0.dev20240814221152.dist-info/RECORD,,