cirq-core 1.3.0.dev20231201141002__py3-none-any.whl → 1.4.0__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.
Files changed (157) hide show
  1. cirq/__init__.py +4 -0
  2. cirq/_compat.py +9 -11
  3. cirq/_compat_test.py +45 -56
  4. cirq/_version.py +31 -1
  5. cirq/_version_test.py +1 -1
  6. cirq/circuits/circuit.py +13 -8
  7. cirq/circuits/circuit_operation.py +2 -1
  8. cirq/circuits/circuit_test.py +2 -2
  9. cirq/circuits/frozen_circuit.py +3 -2
  10. cirq/circuits/moment.py +12 -10
  11. cirq/circuits/qasm_output.py +5 -1
  12. cirq/circuits/qasm_output_test.py +25 -10
  13. cirq/contrib/qcircuit/qcircuit_diagram_info.py +9 -7
  14. cirq/contrib/quimb/mps_simulator_test.py +1 -1
  15. cirq/contrib/quimb/state_vector.py +9 -2
  16. cirq/contrib/svg/svg.py +2 -1
  17. cirq/contrib/svg/svg_test.py +1 -0
  18. cirq/devices/grid_qubit.py +85 -32
  19. cirq/devices/grid_qubit_test.py +22 -4
  20. cirq/devices/line_qubit.py +74 -26
  21. cirq/devices/line_qubit_test.py +19 -0
  22. cirq/devices/noise_utils.py +33 -31
  23. cirq/devices/noise_utils_test.py +1 -84
  24. cirq/devices/superconducting_qubits_noise_properties.py +7 -6
  25. cirq/experiments/__init__.py +8 -0
  26. cirq/experiments/qubit_characterizations.py +288 -44
  27. cirq/experiments/qubit_characterizations_test.py +61 -7
  28. cirq/experiments/random_quantum_circuit_generation.py +1 -1
  29. cirq/experiments/single_qubit_readout_calibration.py +132 -6
  30. cirq/experiments/single_qubit_readout_calibration_test.py +3 -1
  31. cirq/experiments/t1_decay_experiment.py +14 -7
  32. cirq/experiments/t1_decay_experiment_test.py +14 -26
  33. cirq/experiments/two_qubit_xeb.py +483 -0
  34. cirq/experiments/two_qubit_xeb_test.py +304 -0
  35. cirq/json_resolver_cache.py +2 -0
  36. cirq/linalg/decompositions.py +11 -13
  37. cirq/linalg/decompositions_test.py +1 -3
  38. cirq/linalg/diagonalize.py +5 -4
  39. cirq/linalg/predicates.py +8 -6
  40. cirq/linalg/transformations.py +2 -1
  41. cirq/linalg/transformations_test.py +1 -1
  42. cirq/ops/__init__.py +2 -0
  43. cirq/ops/clifford_gate.py +59 -16
  44. cirq/ops/common_gates_test.py +1 -2
  45. cirq/ops/control_values.py +4 -3
  46. cirq/ops/controlled_gate_test.py +1 -3
  47. cirq/ops/gate_operation.py +10 -1
  48. cirq/ops/named_qubit.py +74 -28
  49. cirq/ops/named_qubit_test.py +19 -0
  50. cirq/ops/parity_gates.py +5 -0
  51. cirq/ops/parity_gates_test.py +2 -10
  52. cirq/ops/pauli_gates.py +5 -2
  53. cirq/ops/pauli_string.py +2 -2
  54. cirq/ops/permutation_gate.py +16 -18
  55. cirq/ops/phased_iswap_gate_test.py +1 -3
  56. cirq/ops/phased_x_gate.py +1 -1
  57. cirq/ops/phased_x_z_gate.py +17 -1
  58. cirq/ops/phased_x_z_gate_test.py +24 -0
  59. cirq/ops/qid_util.py +4 -8
  60. cirq/ops/qubit_manager.py +7 -4
  61. cirq/ops/qubit_manager_test.py +20 -0
  62. cirq/ops/raw_types.py +5 -2
  63. cirq/ops/raw_types_test.py +14 -15
  64. cirq/ops/uniform_superposition_gate.py +123 -0
  65. cirq/ops/uniform_superposition_gate_test.py +94 -0
  66. cirq/protocols/approximate_equality_protocol_test.py +2 -2
  67. cirq/protocols/circuit_diagram_info_protocol.py +6 -4
  68. cirq/protocols/commutes_protocol.py +2 -4
  69. cirq/protocols/decompose_protocol.py +7 -12
  70. cirq/protocols/decompose_protocol_test.py +7 -3
  71. cirq/protocols/has_stabilizer_effect_protocol.py +1 -5
  72. cirq/protocols/has_stabilizer_effect_protocol_test.py +13 -4
  73. cirq/protocols/json_serialization.py +51 -181
  74. cirq/protocols/json_serialization_test.py +13 -47
  75. cirq/protocols/json_test_data/CircuitOperation.json +131 -148
  76. cirq/protocols/json_test_data/CircuitOperation.json_inward +55 -0
  77. cirq/protocols/json_test_data/CircuitOperation.repr_inward +6 -0
  78. cirq/protocols/json_test_data/FrozenCircuit.json +196 -210
  79. cirq/protocols/json_test_data/FrozenCircuit.json_inward +35 -0
  80. cirq/protocols/json_test_data/FrozenCircuit.repr_inward +4 -0
  81. cirq/protocols/json_test_data/UniformSuperpositionGate.json +5 -0
  82. cirq/protocols/json_test_data/UniformSuperpositionGate.repr +1 -0
  83. cirq/protocols/json_test_data/cirq.MSGate.json +4 -0
  84. cirq/protocols/json_test_data/cirq.MSGate.repr +1 -0
  85. cirq/protocols/json_test_data/spec.py +2 -0
  86. cirq/protocols/pow_protocol_test.py +1 -3
  87. cirq/protocols/resolve_parameters.py +4 -2
  88. cirq/qis/__init__.py +10 -0
  89. cirq/qis/clifford_tableau.py +8 -2
  90. cirq/qis/noise_utils.py +123 -0
  91. cirq/qis/noise_utils_test.py +97 -0
  92. cirq/sim/classical_simulator.py +227 -87
  93. cirq/sim/classical_simulator_test.py +135 -0
  94. cirq/sim/clifford/clifford_simulator_test.py +4 -2
  95. cirq/sim/mux.py +5 -3
  96. cirq/sim/simulation_product_state.py +15 -10
  97. cirq/sim/simulation_state.py +1 -1
  98. cirq/sim/simulation_state_test.py +2 -2
  99. cirq/sim/simulator_base.py +3 -3
  100. cirq/sim/state_vector_simulation_state.py +4 -4
  101. cirq/sim/state_vector_simulator.py +17 -2
  102. cirq/study/__init__.py +1 -0
  103. cirq/study/result.py +14 -0
  104. cirq/study/result_test.py +6 -0
  105. cirq/study/sweeps.py +4 -2
  106. cirq/study/sweeps_test.py +8 -0
  107. cirq/testing/__init__.py +6 -1
  108. cirq/testing/_compat_test_data/__init__.py +3 -3
  109. cirq/testing/_compat_test_data/module_a/__init__.py +2 -2
  110. cirq/testing/circuit_compare.py +1 -1
  111. cirq/testing/consistent_qasm.py +6 -0
  112. cirq/testing/gate_features.py +10 -0
  113. cirq/testing/lin_alg_utils.py +5 -3
  114. cirq/transformers/__init__.py +15 -0
  115. cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +3 -1
  116. cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +24 -0
  117. cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +17 -0
  118. cirq/transformers/dynamical_decoupling.py +122 -0
  119. cirq/transformers/dynamical_decoupling_test.py +123 -0
  120. cirq/transformers/gauge_compiling/__init__.py +26 -0
  121. cirq/transformers/gauge_compiling/cz_gauge.py +46 -0
  122. cirq/transformers/gauge_compiling/cz_gauge_test.py +23 -0
  123. cirq/transformers/gauge_compiling/gauge_compiling.py +214 -0
  124. cirq/transformers/gauge_compiling/gauge_compiling_test.py +41 -0
  125. cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +83 -0
  126. cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py +52 -0
  127. cirq/transformers/gauge_compiling/iswap_gauge.py +105 -0
  128. cirq/transformers/gauge_compiling/iswap_gauge_test.py +23 -0
  129. cirq/transformers/gauge_compiling/spin_inversion_gauge.py +33 -0
  130. cirq/transformers/gauge_compiling/spin_inversion_gauge_test.py +37 -0
  131. cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +64 -0
  132. cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py +27 -0
  133. cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py +94 -0
  134. cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py +22 -0
  135. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -0
  136. cirq/transformers/merge_k_qubit_gates_test.py +23 -23
  137. cirq/transformers/merge_single_qubit_gates_test.py +14 -14
  138. cirq/transformers/optimize_for_target_gateset.py +39 -17
  139. cirq/transformers/optimize_for_target_gateset_test.py +189 -39
  140. cirq/transformers/qubit_management_transformers.py +1 -1
  141. cirq/transformers/routing/visualize_routed_circuit_test.py +17 -17
  142. cirq/transformers/stratify_test.py +13 -13
  143. cirq/transformers/target_gatesets/compilation_target_gateset.py +26 -2
  144. cirq/transformers/target_gatesets/compilation_target_gateset_test.py +16 -16
  145. cirq/transformers/target_gatesets/cz_gateset.py +4 -0
  146. cirq/transformers/transformer_api.py +1 -2
  147. cirq/transformers/transformer_primitives.py +15 -14
  148. cirq/transformers/transformer_primitives_test.py +99 -72
  149. cirq/value/classical_data.py +6 -6
  150. cirq/value/value_equality_attr.py +4 -0
  151. cirq/work/sampler.py +3 -4
  152. cirq/work/sampler_test.py +25 -0
  153. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/METADATA +10 -19
  154. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/RECORD +157 -130
  155. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/WHEEL +1 -1
  156. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/LICENSE +0 -0
  157. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,7 @@
14
14
 
15
15
  """Transformers to rewrite a circuit using gates from a given target gateset."""
16
16
 
17
- from typing import Optional, Callable, Hashable, Sequence, TYPE_CHECKING
17
+ from typing import Optional, Callable, Hashable, Sequence, TYPE_CHECKING, Union
18
18
 
19
19
  from cirq import circuits
20
20
  from cirq.protocols import decompose_protocol as dp
@@ -102,19 +102,29 @@ def optimize_for_target_gateset(
102
102
  context: Optional['cirq.TransformerContext'] = None,
103
103
  gateset: Optional['cirq.CompilationTargetGateset'] = None,
104
104
  ignore_failures: bool = True,
105
+ max_num_passes: Union[int, None] = 1,
105
106
  ) -> 'cirq.Circuit':
106
107
  """Transforms the given circuit into an equivalent circuit using gates accepted by `gateset`.
107
108
 
109
+ Repeat max_num_passes times or when `max_num_passes=None` until no further changes can be done
108
110
  1. Run all `gateset.preprocess_transformers`
109
111
  2. Convert operations using built-in cirq decompose + `gateset.decompose_to_target_gateset`.
110
112
  3. Run all `gateset.postprocess_transformers`
111
113
 
114
+ Note:
115
+ The optimizer is a heuristic and may not produce optimal results even with
116
+ max_num_passes=None. The preprocessors and postprocessors of the gate set
117
+ as well as their order yield different results.
118
+
119
+
112
120
  Args:
113
121
  circuit: Input circuit to transform. It will not be modified.
114
122
  context: `cirq.TransformerContext` storing common configurable options for transformers.
115
123
  gateset: Target gateset, which should be an instance of `cirq.CompilationTargetGateset`.
116
124
  ignore_failures: If set, operations that fail to convert are left unchanged. If not set,
117
125
  conversion failures raise a ValueError.
126
+ max_num_passes: The maximum number of passes to do. A value of `None` means to keep
127
+ iterating until no more changes happen to the number of moments or operations.
118
128
 
119
129
  Returns:
120
130
  An equivalent circuit containing gates accepted by `gateset`.
@@ -126,20 +136,32 @@ def optimize_for_target_gateset(
126
136
  return _decompose_operations_to_target_gateset(
127
137
  circuit, context=context, ignore_failures=ignore_failures
128
138
  )
129
-
130
- for transformer in gateset.preprocess_transformers:
131
- circuit = transformer(circuit, context=context)
132
-
133
- circuit = _decompose_operations_to_target_gateset(
134
- circuit,
135
- context=context,
136
- gateset=gateset,
137
- decomposer=gateset.decompose_to_target_gateset,
138
- ignore_failures=ignore_failures,
139
- tags_to_decompose=(gateset._intermediate_result_tag,),
140
- )
141
-
142
- for transformer in gateset.postprocess_transformers:
143
- circuit = transformer(circuit, context=context)
144
-
139
+ if isinstance(max_num_passes, int):
140
+ _outerloop = lambda: range(max_num_passes)
141
+ else:
142
+
143
+ def _outerloop():
144
+ while True:
145
+ yield 0
146
+
147
+ initial_num_moments, initial_num_ops = len(circuit), sum(1 for _ in circuit.all_operations())
148
+ for _ in _outerloop():
149
+ for transformer in gateset.preprocess_transformers:
150
+ circuit = transformer(circuit, context=context)
151
+ circuit = _decompose_operations_to_target_gateset(
152
+ circuit,
153
+ context=context,
154
+ gateset=gateset,
155
+ decomposer=gateset.decompose_to_target_gateset,
156
+ ignore_failures=ignore_failures,
157
+ tags_to_decompose=(gateset._intermediate_result_tag,),
158
+ )
159
+ for transformer in gateset.postprocess_transformers:
160
+ circuit = transformer(circuit, context=context)
161
+
162
+ num_moments, num_ops = len(circuit), sum(1 for _ in circuit.all_operations())
163
+ if (num_moments, num_ops) == (initial_num_moments, initial_num_ops):
164
+ # Stop early. No further optimizations can be done.
165
+ break
166
+ initial_num_moments, initial_num_ops = num_moments, num_ops
145
167
  return circuit.unfreeze(copy=False)
@@ -12,6 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ from typing import Union
16
+
15
17
  import cirq
16
18
  from cirq.protocols.decompose_protocol import DecomposeResult
17
19
  from cirq.transformers.optimize_for_target_gateset import _decompose_operations_to_target_gateset
@@ -51,22 +53,22 @@ def test_decompose_operations_to_target_gateset_default():
51
53
  cirq.testing.assert_has_diagram(
52
54
  c_orig,
53
55
  '''
54
- 0: ───T───×───T───×['ignore']───M───────T───×───T───
55
- │ │ ║ │
56
- 1: ───────×───────×─────────────╫───X───T───×───T───
57
- ║ ║
58
- m: ═════════════════════════════@═══^═══════════════''',
56
+ 0: ───T───×───T───×[ignore]───M───────T───×───T───
57
+ │ │ ║ │
58
+ 1: ───────×───────×───────────╫───X───T───×───T───
59
+ ║ ║
60
+ m: ═══════════════════════════@═══^═══════════════''',
59
61
  )
60
62
  context = cirq.TransformerContext(tags_to_ignore=("ignore",))
61
63
  c_new = _decompose_operations_to_target_gateset(c_orig, context=context)
62
64
  cirq.testing.assert_has_diagram(
63
65
  c_new,
64
66
  '''
65
- 0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×['ignore']───M───────T────────────@───Y^-0.5───@───Y^0.5────@───────────T───
66
- │ │ │ │ ║ │ │ │
67
- 1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───────×─────────────╫───X───T───Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───T───
68
- ║ ║
69
- m: ════════════════════════════════════════════════════════════════════════@═══^══════════════════════════════════════════════════════════
67
+ 0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×[ignore]───M───────T────────────@───Y^-0.5───@───Y^0.5────@───────────T───
68
+ │ │ │ │ ║ │ │ │
69
+ 1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───────×───────────╫───X───T───Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───T───
70
+ ║ ║
71
+ m: ══════════════════════════════════════════════════════════════════════@═══^══════════════════════════════════════════════════════════
70
72
  ''',
71
73
  )
72
74
 
@@ -85,8 +87,8 @@ def test_decompose_operations_to_target_gateset():
85
87
  cirq.T.on_each(*q),
86
88
  )
87
89
  gateset = cirq.Gateset(cirq.H, cirq.CNOT)
88
- decomposer = (
89
- lambda op, _: cirq.H(op.qubits[0])
90
+ decomposer = lambda op, _: (
91
+ cirq.H(op.qubits[0])
90
92
  if cirq.has_unitary(op) and cirq.num_qubits(op) == 1
91
93
  else NotImplemented
92
94
  )
@@ -97,11 +99,11 @@ def test_decompose_operations_to_target_gateset():
97
99
  cirq.testing.assert_has_diagram(
98
100
  c_new,
99
101
  '''
100
- 0: ───H───@───X───@───H───×['ignore']───M───────H───@───X───@───H───
101
- │ │ │ │ ║ │ │ │
102
- 1: ───────X───@───X───────×─────────────╫───X───H───X───@───X───H───
103
- ║ ║
104
- m: ═════════════════════════════════════@═══^═══════════════════════''',
102
+ 0: ───H───@───X───@───H───×[ignore]───M───────H───@───X───@───H───
103
+ │ │ │ │ ║ │ │ │
104
+ 1: ───────X───@───X───────×───────────╫───X───H───X───@───X───H───
105
+ ║ ║
106
+ m: ═══════════════════════════════════@═══^═══════════════════════''',
105
107
  )
106
108
 
107
109
  with pytest.raises(ValueError, match="Unable to convert"):
@@ -134,9 +136,9 @@ def test_optimize_for_target_gateset_default():
134
136
  cirq.testing.assert_has_diagram(
135
137
  c_new,
136
138
  '''
137
- 0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×['ignore']───
139
+ 0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×[ignore]───
138
140
  │ │ │ │
139
- 1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───────×─────────────
141
+ 1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───────×───────────
140
142
  ''',
141
143
  )
142
144
  cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(c_orig, c_new, atol=1e-6)
@@ -157,15 +159,15 @@ def test_optimize_for_target_gateset():
157
159
  cirq.testing.assert_has_diagram(
158
160
  c_orig,
159
161
  '''
160
- 0: ───qft───Y['ignore']───M───────qft^-1───
161
- ║ │
162
- 1: ───#2────Y['ignore']───M───────#2───────
163
- ║ │
164
- 2: ───#3────@['ignore']───╫───@───#3───────
165
- │ │ ║ ║ │
166
- 3: ───#4────X─────────────╫───@───#4───────
167
- ║ ║
168
- m: ═══════════════════════@═══^════════════
162
+ 0: ───qft───Y[ignore]───M───────qft^-1───
163
+ ║ │
164
+ 1: ───#2────Y[ignore]───M───────#2───────
165
+ ║ │
166
+ 2: ───#3────@[ignore]───╫───@───#3───────
167
+ │ │ ║ ║ │
168
+ 3: ───#4────X───────────╫───@───#4───────
169
+ ║ ║
170
+ m: ═════════════════════@═══^════════════
169
171
  ''',
170
172
  )
171
173
  gateset = MatrixGateTargetGateset()
@@ -174,17 +176,17 @@ m: ═══════════════════════@══
174
176
  cirq.testing.assert_has_diagram(
175
177
  c_new,
176
178
  '''
177
- ┌────────┐ ┌────────┐ ┌────────┐
178
- 0: ───M[1]──────────M[1]──────────────────────M[1]────Y['ignore']───M────────────M[1]───────────────────M[1]────────M[1]───M[1]───
179
- │ │ │ ║ │ │ │ │
180
- 1: ───M[2]───M[1]───┼─────────────M[1]────M[1]┼───────Y['ignore']───M────────M[1]┼──────────────M[1]────┼───M[1]────┼──────M[2]───
181
- │ │ │ │ │ ║ │ │ │ │ │ │
182
- 2: ──────────M[2]───M[2]───M[1]───┼───────M[2]┼───────@['ignore']───╫───@────M[2]┼───────M[1]───┼───────┼───M[2]────M[2]──────────
183
- │ │ │ │ ║ ║ │ │ │ │
184
- 3: ────────────────────────M[2]───M[2]────────M[2]────X─────────────╫───@────────M[2]────M[2]───M[2]────M[2]──────────────────────
185
- ║ ║
186
- m: ═════════════════════════════════════════════════════════════════@═══^═════════════════════════════════════════════════════════
187
- └────────┘ └────────┘ └────────┘
179
+ ┌────────┐ ┌────────┐ ┌────────┐
180
+ 0: ───M[1]──────────M[1]──────────────────────M[1]────Y[ignore]───M────────────M[1]───────────────────M[1]────────M[1]───M[1]───
181
+ │ │ │ ║ │ │ │ │
182
+ 1: ───M[2]───M[1]───┼─────────────M[1]────M[1]┼───────Y[ignore]───M────────M[1]┼──────────────M[1]────┼───M[1]────┼──────M[2]───
183
+ │ │ │ │ │ ║ │ │ │ │ │ │
184
+ 2: ──────────M[2]───M[2]───M[1]───┼───────M[2]┼───────@[ignore]───╫───@────M[2]┼───────M[1]───┼───────┼───M[2]────M[2]──────────
185
+ │ │ │ │ ║ ║ │ │ │ │
186
+ 3: ────────────────────────M[2]───M[2]────────M[2]────X───────────╫───@────────M[2]────M[2]───M[2]────M[2]──────────────────────
187
+ ║ ║
188
+ m: ═══════════════════════════════════════════════════════════════@═══^═════════════════════════════════════════════════════════
189
+ └────────┘ └────────┘ └────────┘
188
190
  ''',
189
191
  )
190
192
 
@@ -243,3 +245,151 @@ def test_optimize_for_target_gateset_deep():
243
245
  1: ───#2───────────────────────────────────────────────────────────────────────────
244
246
  ''',
245
247
  )
248
+
249
+
250
+ @pytest.mark.parametrize('max_num_passes', [2, None])
251
+ def test_optimize_for_target_gateset_multiple_passes(max_num_passes: Union[int, None]):
252
+ gateset = cirq.CZTargetGateset()
253
+
254
+ input_circuit = cirq.Circuit(
255
+ [
256
+ cirq.Moment(
257
+ cirq.X(cirq.LineQubit(1)),
258
+ cirq.X(cirq.LineQubit(2)),
259
+ cirq.X(cirq.LineQubit(3)),
260
+ cirq.X(cirq.LineQubit(6)),
261
+ ),
262
+ cirq.Moment(
263
+ cirq.H(cirq.LineQubit(0)),
264
+ cirq.H(cirq.LineQubit(1)),
265
+ cirq.H(cirq.LineQubit(2)),
266
+ cirq.H(cirq.LineQubit(3)),
267
+ cirq.H(cirq.LineQubit(4)),
268
+ cirq.H(cirq.LineQubit(5)),
269
+ cirq.H(cirq.LineQubit(6)),
270
+ ),
271
+ cirq.Moment(
272
+ cirq.H(cirq.LineQubit(1)), cirq.H(cirq.LineQubit(3)), cirq.H(cirq.LineQubit(5))
273
+ ),
274
+ cirq.Moment(
275
+ cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
276
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
277
+ cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5)),
278
+ ),
279
+ cirq.Moment(
280
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
281
+ cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
282
+ cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5)),
283
+ ),
284
+ ]
285
+ )
286
+ desired_circuit = cirq.Circuit.from_moments(
287
+ cirq.Moment(
288
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
289
+ cirq.LineQubit(4)
290
+ )
291
+ ),
292
+ cirq.Moment(cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5))),
293
+ cirq.Moment(
294
+ cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
295
+ cirq.LineQubit(1)
296
+ ),
297
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
298
+ cirq.LineQubit(0)
299
+ ),
300
+ cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
301
+ cirq.LineQubit(3)
302
+ ),
303
+ cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
304
+ cirq.LineQubit(2)
305
+ ),
306
+ ),
307
+ cirq.Moment(
308
+ cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
309
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
310
+ ),
311
+ cirq.Moment(
312
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
313
+ cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
314
+ ),
315
+ cirq.Moment(
316
+ cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
317
+ cirq.LineQubit(6)
318
+ )
319
+ ),
320
+ cirq.Moment(cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5))),
321
+ )
322
+ got = cirq.optimize_for_target_gateset(
323
+ input_circuit, gateset=gateset, max_num_passes=max_num_passes
324
+ )
325
+ cirq.testing.assert_same_circuits(got, desired_circuit)
326
+
327
+
328
+ @pytest.mark.parametrize('max_num_passes', [2, None])
329
+ def test_optimize_for_target_gateset_multiple_passes_dont_preserve_moment_structure(
330
+ max_num_passes: Union[int, None]
331
+ ):
332
+ gateset = cirq.CZTargetGateset(preserve_moment_structure=False)
333
+
334
+ input_circuit = cirq.Circuit(
335
+ [
336
+ cirq.Moment(
337
+ cirq.X(cirq.LineQubit(1)),
338
+ cirq.X(cirq.LineQubit(2)),
339
+ cirq.X(cirq.LineQubit(3)),
340
+ cirq.X(cirq.LineQubit(6)),
341
+ ),
342
+ cirq.Moment(
343
+ cirq.H(cirq.LineQubit(0)),
344
+ cirq.H(cirq.LineQubit(1)),
345
+ cirq.H(cirq.LineQubit(2)),
346
+ cirq.H(cirq.LineQubit(3)),
347
+ cirq.H(cirq.LineQubit(4)),
348
+ cirq.H(cirq.LineQubit(5)),
349
+ cirq.H(cirq.LineQubit(6)),
350
+ ),
351
+ cirq.Moment(
352
+ cirq.H(cirq.LineQubit(1)), cirq.H(cirq.LineQubit(3)), cirq.H(cirq.LineQubit(5))
353
+ ),
354
+ cirq.Moment(
355
+ cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
356
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
357
+ cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5)),
358
+ ),
359
+ cirq.Moment(
360
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
361
+ cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
362
+ cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5)),
363
+ ),
364
+ ]
365
+ )
366
+ desired_circuit = cirq.Circuit(
367
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
368
+ cirq.LineQubit(4)
369
+ ),
370
+ cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
371
+ cirq.LineQubit(1)
372
+ ),
373
+ cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
374
+ cirq.LineQubit(2)
375
+ ),
376
+ cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
377
+ cirq.LineQubit(0)
378
+ ),
379
+ cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
380
+ cirq.LineQubit(3)
381
+ ),
382
+ cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
383
+ cirq.LineQubit(6)
384
+ ),
385
+ cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5)),
386
+ cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
387
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
388
+ cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
389
+ cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
390
+ cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5)),
391
+ )
392
+ got = cirq.optimize_for_target_gateset(
393
+ input_circuit, gateset=gateset, max_num_passes=max_num_passes
394
+ )
395
+ cirq.testing.assert_same_circuits(got, desired_circuit)
@@ -71,7 +71,7 @@ def map_clean_and_borrowable_qubits(
71
71
  inserting all mapped operations in a resulting circuit using EARLIEST strategy. The reason
72
72
  is that preserving moment structure forces additional constraints on the qubit allocation
73
73
  strategy (i.e. if two operations `op1` and `op2` are in the same moment, then we cannot
74
- reuse ancilla across `op1` and `op2`). We leave it upto the user to force such constraints
74
+ reuse ancilla across `op1` and `op2`). We leave it up to the user to force such constraints
75
75
  using the qubit manager instead of making it part of the transformer.
76
76
  3. However, for borrowable system qubits managed by the transformer, we do not reuse qubits
77
77
  within the same moment.
@@ -20,15 +20,15 @@ def test_routed_circuit_with_mapping_simple():
20
20
  q = cirq.LineQubit.range(2)
21
21
  circuit = cirq.Circuit([cirq.Moment(cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag()))])
22
22
  expected_diagram = """
23
- 0: ───q(0)───×[cirq.RoutingSwapTag()]───q(1)───
24
- │ │
25
- 1: ───q(1)───×──────────────────────────q(0)───"""
23
+ 0: ───q(0)───×[<r>]───q(1)───
24
+ │ │
25
+ 1: ───q(1)───×────────q(0)───"""
26
26
  cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram)
27
27
 
28
28
  expected_diagram_with_initial_mapping = """
29
- 0: ───a───×[cirq.RoutingSwapTag()]───b───
30
- │ │
31
- 1: ───b───×──────────────────────────a───"""
29
+ 0: ───a───×[<r>]───b───
30
+ │ │
31
+ 1: ───b───×────────a───"""
32
32
  cirq.testing.assert_has_diagram(
33
33
  cirq.routed_circuit_with_mapping(
34
34
  circuit, {cirq.NamedQubit("a"): q[0], cirq.NamedQubit("b"): q[1]}
@@ -74,16 +74,16 @@ def test_routed_circuit_with_mapping_multi_swaps():
74
74
  ]
75
75
  )
76
76
  expected_diagram = """
77
- 0: ───q(0)──────────────────────────────────────q(0)───×[cirq.RoutingSwapTag()]───q(1)───────X───
78
- │ │ │ │
79
- 1: ───q(1)───────────X──────────────────────────q(1)───×──────────────────────────q(0)───X───@───
80
- │ │ │ │
81
- 2: ───q(2)───────@───@──────────────────────────q(2)───×──────────────────────────q(4)───@───────
82
- │ │ │ │
83
- 3: ───q(3)───@───X───×──────────────────────────q(4)───×[cirq.RoutingSwapTag()]───q(2)───────────
84
- │ │ │
85
- 4: ───q(4)───X───X───×[cirq.RoutingSwapTag()]───q(3)──────────────────────────────q(3)───────────
86
- │ │
87
- 5: ───q(5)───────@──────────────────────────────q(5)──────────────────────────────q(5)───────────
77
+ 0: ───q(0)────────────────────q(0)───×[<r>]───q(1)───────X───
78
+ │ │ │ │
79
+ 1: ───q(1)───────────X────────q(1)───×────────q(0)───X───@───
80
+ │ │ │ │
81
+ 2: ───q(2)───────@───@────────q(2)───×────────q(4)───@───────
82
+ │ │ │ │
83
+ 3: ───q(3)───@───X───×────────q(4)───×[<r>]───q(2)───────────
84
+ │ │ │
85
+ 4: ───q(4)───X───X───×[<r>]───q(3)────────────q(3)───────────
86
+ │ │
87
+ 5: ───q(5)───────@────────────q(5)────────────q(5)───────────
88
88
  """
89
89
  cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram)
@@ -237,29 +237,29 @@ def test_stratify_respects_no_compile_operations():
237
237
  cirq.testing.assert_has_diagram(
238
238
  input_circuit,
239
239
  '''
240
- 0: ───X['nocompile']───────X───────iSwap───
241
-
242
- 1: ───iSwap['nocompile']───────────iSwap───
240
+ 0: ───X[nocompile]───────X───────iSwap───
241
+
242
+ 1: ───iSwap[nocompile]───────────iSwap───
243
243
 
244
- 2: ───iSwap────────────────────────────────
244
+ 2: ───iSwap──────────────────────────────
245
245
 
246
- 3: ────────────────────────iSwap───X───────
247
-
248
- 4: ───Z────────────────────iSwap───────────
246
+ 3: ──────────────────────iSwap───X───────
247
+
248
+ 4: ───Z──────────────────iSwap───────────
249
249
  ''',
250
250
  )
251
251
  cirq.testing.assert_has_diagram(
252
252
  expected,
253
253
  '''
254
- 0: ───────────────X['nocompile']───────X───iSwap───
255
-
256
- 1: ───────────────iSwap['nocompile']───────iSwap───
254
+ 0: ───────────────X[nocompile]───────X───iSwap───
255
+
256
+ 1: ───────────────iSwap[nocompile]───────iSwap───
257
257
 
258
- 2: ───────────────iSwap────────────────────────────
258
+ 2: ───────────────iSwap──────────────────────────
259
259
 
260
- 3: ───────iSwap────────────────────────X───────────
260
+ 3: ───────iSwap──────────────────────X───────────
261
261
 
262
- 4: ───Z───iSwap────────────────────────────────────
262
+ 4: ───Z───iSwap──────────────────────────────────
263
263
  ''',
264
264
  )
265
265
  cirq.testing.assert_same_circuits(
@@ -14,7 +14,7 @@
14
14
 
15
15
  """Base class for creating custom target gatesets which can be used for compilation."""
16
16
 
17
- from typing import Optional, List, Hashable, TYPE_CHECKING
17
+ from typing import Optional, List, Hashable, TYPE_CHECKING, Union, Type
18
18
  import abc
19
19
 
20
20
  from cirq import circuits, ops, protocols, transformers
@@ -80,6 +80,27 @@ class CompilationTargetGateset(ops.Gateset, metaclass=abc.ABCMeta):
80
80
  which can transform any given circuit to contain gates accepted by this gateset.
81
81
  """
82
82
 
83
+ def __init__(
84
+ self,
85
+ *gates: Union[Type['cirq.Gate'], 'cirq.Gate', 'cirq.GateFamily'],
86
+ name: Optional[str] = None,
87
+ unroll_circuit_op: bool = True,
88
+ preserve_moment_structure: bool = True,
89
+ ):
90
+ """Initializes CompilationTargetGateset.
91
+
92
+ Args:
93
+ *gates: A list of `cirq.Gate` subclasses / `cirq.Gate` instances /
94
+ `cirq.GateFamily` instances to initialize the Gateset.
95
+ name: (Optional) Name for the Gateset. Useful for description.
96
+ unroll_circuit_op: If True, `cirq.CircuitOperation` is recursively
97
+ validated by validating the underlying `cirq.Circuit`.
98
+ preserve_moment_structure: Whether to preserve the moment structure of the
99
+ circuit during compilation or not.
100
+ """
101
+ super().__init__(*gates, name=name, unroll_circuit_op=unroll_circuit_op)
102
+ self._preserve_moment_structure = preserve_moment_structure
103
+
83
104
  @property
84
105
  @abc.abstractmethod
85
106
  def num_qubits(self) -> int:
@@ -140,11 +161,14 @@ class CompilationTargetGateset(ops.Gateset, metaclass=abc.ABCMeta):
140
161
  @property
141
162
  def postprocess_transformers(self) -> List['cirq.TRANSFORMER']:
142
163
  """List of transformers which should be run after decomposing individual operations."""
143
- return [
164
+ processors: List['cirq.TRANSFORMER'] = [
144
165
  merge_single_qubit_gates.merge_single_qubit_moments_to_phxz,
145
166
  transformers.drop_negligible_operations,
146
167
  transformers.drop_empty_moments,
147
168
  ]
169
+ if not self._preserve_moment_structure:
170
+ processors.append(transformers.stratified_circuit)
171
+ return processors
148
172
 
149
173
 
150
174
  class TwoQubitCompilationTargetGateset(CompilationTargetGateset):
@@ -138,9 +138,9 @@ def test_two_qubit_compilation_merge_and_replace_to_target_gateset():
138
138
  cirq.testing.assert_has_diagram(
139
139
  c_orig,
140
140
  '''
141
- 0: ───X───@['no_compile']───Z───X───@───Z───X───
142
-
143
- 1: ───Z───@─────────────────Z───────@───Z───────
141
+ 0: ───X───@[no_compile]───Z───X───@───Z───X───
142
+
143
+ 1: ───Z───@───────────────Z───────@───Z───────
144
144
  ''',
145
145
  )
146
146
  c_new = cirq.optimize_for_target_gateset(
@@ -151,9 +151,9 @@ def test_two_qubit_compilation_merge_and_replace_to_target_gateset():
151
151
  cirq.testing.assert_has_diagram(
152
152
  c_new,
153
153
  '''
154
- 0: ───X───@['no_compile']───X───@───Y───@───Z───
155
- │ │
156
- 1: ───Z───@─────────────────X───X───Y───X───Z───
154
+ 0: ───X───@[no_compile]───X───@───Y───@───Z───
155
+ │ │
156
+ 1: ───Z───@───────────────X───X───Y───X───Z───
157
157
  ''',
158
158
  )
159
159
 
@@ -178,11 +178,11 @@ def test_two_qubit_compilation_merge_and_replace_inefficient_component():
178
178
  cirq.testing.assert_has_diagram(
179
179
  c_orig,
180
180
  '''
181
- 0: ───X───@───X───@['no_compile']───Z───X───@───@───Z───X───@───M───────
182
- │ │ │ │ │ ║
183
- 1: ───────X───────@─────────────────Z───────X───X───Z───────X───╫───X───
184
- ║ ║
185
- m: ═════════════════════════════════════════════════════════════@═══^═══
181
+ 0: ───X───@───X───@[no_compile]───Z───X───@───@───Z───X───@───M───────
182
+ │ │ │ │ │ ║
183
+ 1: ───────X───────@───────────────Z───────X───X───Z───────X───╫───X───
184
+ ║ ║
185
+ m: ═══════════════════════════════════════════════════════════@═══^═══
186
186
  ''',
187
187
  )
188
188
  c_new = cirq.optimize_for_target_gateset(
@@ -193,11 +193,11 @@ m: ═════════════════════════
193
193
  cirq.testing.assert_has_diagram(
194
194
  c_new,
195
195
  '''
196
- 0: ───X───@───X───@['no_compile']───X───@───Y───@───Z───M───────
197
- │ │ │ │ ║
198
- 1: ───────X───────@─────────────────X───X───Y───X───Z───╫───X───
199
- ║ ║
200
- m: ═════════════════════════════════════════════════════@═══^═══
196
+ 0: ───X───@───X───@[no_compile]───X───@───Y───@───Z───M───────
197
+ │ │ │ │ ║
198
+ 1: ───────X───────@───────────────X───X───Y───X───Z───╫───X───
199
+ ║ ║
200
+ m: ═══════════════════════════════════════════════════@═══^═══
201
201
  ''',
202
202
  )
203
203
 
@@ -48,6 +48,7 @@ class CZTargetGateset(compilation_target_gateset.TwoQubitCompilationTargetGatese
48
48
  atol: float = 1e-8,
49
49
  allow_partial_czs: bool = False,
50
50
  additional_gates: Sequence[Union[Type['cirq.Gate'], 'cirq.Gate', 'cirq.GateFamily']] = (),
51
+ preserve_moment_structure: bool = True,
51
52
  ) -> None:
52
53
  """Initializes CZTargetGateset
53
54
 
@@ -57,6 +58,8 @@ class CZTargetGateset(compilation_target_gateset.TwoQubitCompilationTargetGatese
57
58
  `cirq.CZ`, are part of this gateset.
58
59
  additional_gates: Sequence of additional gates / gate families which should also
59
60
  be "accepted" by this gateset. This is empty by default.
61
+ preserve_moment_structure: Whether to preserve the moment structure of the
62
+ circuit during compilation or not.
60
63
  """
61
64
  super().__init__(
62
65
  ops.CZPowGate if allow_partial_czs else ops.CZ,
@@ -65,6 +68,7 @@ class CZTargetGateset(compilation_target_gateset.TwoQubitCompilationTargetGatese
65
68
  ops.GlobalPhaseGate,
66
69
  *additional_gates,
67
70
  name='CZPowTargetGateset' if allow_partial_czs else 'CZTargetGateset',
71
+ preserve_moment_structure=preserve_moment_structure,
68
72
  )
69
73
  self.additional_gates = tuple(
70
74
  g if isinstance(g, ops.GateFamily) else ops.GateFamily(gate=g) for g in additional_gates
@@ -262,8 +262,7 @@ class TRANSFORMER(Protocol):
262
262
 
263
263
  def __call__(
264
264
  self, circuit: 'cirq.AbstractCircuit', *, context: Optional[TransformerContext] = None
265
- ) -> 'cirq.AbstractCircuit':
266
- ...
265
+ ) -> 'cirq.AbstractCircuit': ...
267
266
 
268
267
 
269
268
  _TRANSFORMER_T = TypeVar('_TRANSFORMER_T', bound=TRANSFORMER)