cirq-core 1.4.1__py3-none-any.whl → 1.5.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.

Potentially problematic release.


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

Files changed (590) hide show
  1. cirq/__init__.py +587 -569
  2. cirq/_compat.py +9 -0
  3. cirq/_compat_test.py +11 -9
  4. cirq/_import.py +7 -8
  5. cirq/_version.py +1 -1
  6. cirq/_version_test.py +1 -1
  7. cirq/circuits/__init__.py +15 -9
  8. cirq/circuits/_block_diagram_drawer.py +1 -2
  9. cirq/circuits/_block_diagram_drawer_test.py +3 -3
  10. cirq/circuits/_box_drawing_character_data.py +0 -1
  11. cirq/circuits/_box_drawing_character_data_test.py +2 -2
  12. cirq/circuits/_bucket_priority_queue.py +0 -1
  13. cirq/circuits/_bucket_priority_queue_test.py +1 -1
  14. cirq/circuits/circuit.py +336 -234
  15. cirq/circuits/circuit_operation.py +102 -52
  16. cirq/circuits/circuit_operation_test.py +85 -4
  17. cirq/circuits/circuit_test.py +101 -32
  18. cirq/circuits/frozen_circuit.py +36 -32
  19. cirq/circuits/insert_strategy.py +10 -0
  20. cirq/circuits/insert_strategy_test.py +20 -0
  21. cirq/circuits/moment.py +79 -80
  22. cirq/circuits/moment_test.py +105 -2
  23. cirq/circuits/optimization_pass.py +15 -15
  24. cirq/circuits/optimization_pass_test.py +8 -9
  25. cirq/circuits/qasm_output.py +64 -33
  26. cirq/circuits/qasm_output_test.py +63 -2
  27. cirq/circuits/text_diagram_drawer.py +26 -56
  28. cirq/circuits/text_diagram_drawer_test.py +5 -4
  29. cirq/contrib/__init__.py +2 -2
  30. cirq/contrib/acquaintance/__init__.py +44 -29
  31. cirq/contrib/acquaintance/bipartite.py +8 -7
  32. cirq/contrib/acquaintance/bipartite_test.py +11 -1
  33. cirq/contrib/acquaintance/devices.py +5 -4
  34. cirq/contrib/acquaintance/devices_test.py +5 -1
  35. cirq/contrib/acquaintance/executor.py +18 -21
  36. cirq/contrib/acquaintance/executor_test.py +3 -2
  37. cirq/contrib/acquaintance/gates.py +36 -27
  38. cirq/contrib/acquaintance/gates_test.py +1 -1
  39. cirq/contrib/acquaintance/inspection_utils.py +10 -9
  40. cirq/contrib/acquaintance/inspection_utils_test.py +6 -1
  41. cirq/contrib/acquaintance/mutation_utils.py +10 -10
  42. cirq/contrib/acquaintance/optimizers.py +7 -6
  43. cirq/contrib/acquaintance/optimizers_test.py +1 -1
  44. cirq/contrib/acquaintance/permutation.py +22 -21
  45. cirq/contrib/acquaintance/permutation_test.py +1 -1
  46. cirq/contrib/acquaintance/shift.py +8 -6
  47. cirq/contrib/acquaintance/shift_swap_network.py +6 -4
  48. cirq/contrib/acquaintance/strategies/__init__.py +9 -3
  49. cirq/contrib/acquaintance/strategies/complete.py +4 -3
  50. cirq/contrib/acquaintance/strategies/cubic.py +5 -3
  51. cirq/contrib/acquaintance/strategies/quartic_paired.py +8 -6
  52. cirq/contrib/acquaintance/topological_sort.py +4 -2
  53. cirq/contrib/bayesian_network/__init__.py +3 -1
  54. cirq/contrib/bayesian_network/bayesian_network_gate.py +5 -3
  55. cirq/contrib/circuitdag/__init__.py +1 -1
  56. cirq/contrib/circuitdag/circuit_dag.py +24 -24
  57. cirq/contrib/circuitdag/circuit_dag_test.py +1 -1
  58. cirq/contrib/custom_simulators/custom_state_simulator.py +10 -8
  59. cirq/contrib/custom_simulators/custom_state_simulator_test.py +15 -11
  60. cirq/contrib/graph_device/__init__.py +8 -8
  61. cirq/contrib/graph_device/graph_device.py +8 -8
  62. cirq/contrib/graph_device/graph_device_test.py +0 -1
  63. cirq/contrib/graph_device/hypergraph_test.py +1 -0
  64. cirq/contrib/json.py +1 -2
  65. cirq/contrib/json_test.py +2 -2
  66. cirq/contrib/noise_models/__init__.py +5 -6
  67. cirq/contrib/noise_models/noise_models.py +8 -6
  68. cirq/contrib/paulistring/__init__.py +22 -10
  69. cirq/contrib/paulistring/clifford_optimize.py +1 -1
  70. cirq/contrib/paulistring/clifford_optimize_test.py +0 -1
  71. cirq/contrib/paulistring/clifford_target_gateset.py +15 -12
  72. cirq/contrib/paulistring/optimize.py +2 -2
  73. cirq/contrib/paulistring/optimize_test.py +0 -1
  74. cirq/contrib/paulistring/pauli_string_dag_test.py +0 -1
  75. cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation.py +379 -0
  76. cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation_test.py +523 -0
  77. cirq/contrib/paulistring/pauli_string_optimize.py +3 -1
  78. cirq/contrib/paulistring/pauli_string_optimize_test.py +1 -3
  79. cirq/contrib/paulistring/recombine.py +2 -2
  80. cirq/contrib/paulistring/recombine_test.py +2 -2
  81. cirq/contrib/paulistring/separate.py +3 -4
  82. cirq/contrib/qasm_import/__init__.py +2 -2
  83. cirq/contrib/qasm_import/_lexer.py +21 -26
  84. cirq/contrib/qasm_import/_lexer_test.py +90 -6
  85. cirq/contrib/qasm_import/_parser.py +238 -47
  86. cirq/contrib/qasm_import/_parser_test.py +514 -59
  87. cirq/contrib/qasm_import/qasm_test.py +1 -1
  88. cirq/contrib/qcircuit/__init__.py +6 -4
  89. cirq/contrib/qcircuit/qcircuit_diagram.py +5 -2
  90. cirq/contrib/qcircuit/qcircuit_pdf.py +1 -2
  91. cirq/{experiments/grid_parallel_two_qubit_xeb_test.py → contrib/qcircuit/qcircuit_pdf_test.py} +13 -12
  92. cirq/contrib/qcircuit/qcircuit_test.py +1 -1
  93. cirq/contrib/quantum_volume/__init__.py +7 -7
  94. cirq/contrib/quantum_volume/quantum_volume.py +6 -11
  95. cirq/contrib/quantum_volume/quantum_volume_test.py +3 -1
  96. cirq/contrib/quimb/__init__.py +16 -13
  97. cirq/contrib/quimb/density_matrix.py +1 -1
  98. cirq/contrib/quimb/mps_simulator.py +27 -28
  99. cirq/contrib/quimb/mps_simulator_test.py +5 -0
  100. cirq/contrib/quimb/state_vector.py +3 -10
  101. cirq/contrib/quirk/__init__.py +1 -1
  102. cirq/contrib/quirk/export_to_quirk.py +3 -3
  103. cirq/contrib/routing/__init__.py +12 -9
  104. cirq/contrib/routing/device.py +1 -1
  105. cirq/contrib/routing/device_test.py +1 -2
  106. cirq/contrib/routing/greedy.py +7 -5
  107. cirq/contrib/routing/greedy_test.py +5 -3
  108. cirq/contrib/routing/initialization.py +3 -1
  109. cirq/contrib/routing/initialization_test.py +1 -1
  110. cirq/contrib/routing/swap_network.py +6 -6
  111. cirq/contrib/routing/utils.py +6 -4
  112. cirq/contrib/routing/utils_test.py +1 -2
  113. cirq/{type_workarounds.py → contrib/shuffle_circuits/__init__.py} +5 -10
  114. cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking.py +250 -0
  115. cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking_test.py +363 -0
  116. cirq/contrib/svg/__init__.py +1 -1
  117. cirq/contrib/svg/svg.py +12 -10
  118. cirq/contrib/svg/svg_test.py +3 -2
  119. cirq/devices/__init__.py +34 -25
  120. cirq/devices/device.py +16 -12
  121. cirq/devices/device_test.py +1 -0
  122. cirq/devices/grid_device_metadata.py +16 -12
  123. cirq/devices/grid_device_metadata_test.py +2 -1
  124. cirq/devices/grid_qubit.py +31 -26
  125. cirq/devices/grid_qubit_test.py +30 -1
  126. cirq/devices/insertion_noise_model.py +6 -6
  127. cirq/devices/insertion_noise_model_test.py +1 -1
  128. cirq/devices/line_qubit.py +28 -20
  129. cirq/devices/line_qubit_test.py +26 -0
  130. cirq/devices/named_topologies.py +12 -10
  131. cirq/devices/named_topologies_test.py +5 -4
  132. cirq/devices/noise_model.py +29 -33
  133. cirq/devices/noise_properties.py +2 -2
  134. cirq/devices/noise_properties_test.py +2 -2
  135. cirq/devices/noise_utils.py +3 -3
  136. cirq/devices/superconducting_qubits_noise_properties.py +2 -2
  137. cirq/devices/superconducting_qubits_noise_properties_test.py +3 -3
  138. cirq/devices/thermal_noise_model.py +2 -1
  139. cirq/devices/unconstrained_device.py +1 -1
  140. cirq/devices/unconstrained_device_test.py +6 -0
  141. cirq/experiments/__init__.py +51 -34
  142. cirq/experiments/qubit_characterizations.py +17 -15
  143. cirq/experiments/qubit_characterizations_test.py +4 -6
  144. cirq/experiments/random_quantum_circuit_generation.py +10 -9
  145. cirq/experiments/random_quantum_circuit_generation_test.py +21 -4
  146. cirq/experiments/readout_confusion_matrix.py +73 -8
  147. cirq/experiments/readout_confusion_matrix_test.py +104 -1
  148. cirq/experiments/single_qubit_readout_calibration.py +8 -6
  149. cirq/experiments/single_qubit_readout_calibration_test.py +1 -1
  150. cirq/experiments/t1_decay_experiment.py +4 -5
  151. cirq/experiments/t1_decay_experiment_test.py +1 -2
  152. cirq/experiments/t2_decay_experiment.py +0 -1
  153. cirq/experiments/t2_decay_experiment_test.py +1 -2
  154. cirq/experiments/two_qubit_xeb.py +157 -33
  155. cirq/experiments/two_qubit_xeb_test.py +38 -22
  156. cirq/experiments/xeb_fitting.py +99 -19
  157. cirq/experiments/xeb_fitting_test.py +64 -25
  158. cirq/experiments/xeb_sampling.py +14 -18
  159. cirq/experiments/xeb_simulation.py +4 -3
  160. cirq/experiments/xeb_simulation_test.py +20 -14
  161. cirq/experiments/z_phase_calibration.py +368 -0
  162. cirq/experiments/z_phase_calibration_test.py +241 -0
  163. cirq/interop/__init__.py +4 -1
  164. cirq/interop/quirk/__init__.py +7 -4
  165. cirq/interop/quirk/cells/__init__.py +17 -6
  166. cirq/interop/quirk/cells/arithmetic_cells.py +8 -8
  167. cirq/interop/quirk/cells/arithmetic_cells_test.py +1 -1
  168. cirq/interop/quirk/cells/cell.py +6 -6
  169. cirq/interop/quirk/cells/composite_cell.py +5 -5
  170. cirq/interop/quirk/cells/composite_cell_test.py +1 -1
  171. cirq/interop/quirk/cells/control_cells.py +1 -1
  172. cirq/interop/quirk/cells/frequency_space_cells.py +2 -2
  173. cirq/interop/quirk/cells/ignored_cells.py +1 -1
  174. cirq/interop/quirk/cells/input_cells.py +1 -1
  175. cirq/interop/quirk/cells/input_cells_test.py +1 -1
  176. cirq/interop/quirk/cells/input_rotation_cells.py +1 -1
  177. cirq/interop/quirk/cells/input_rotation_cells_test.py +1 -1
  178. cirq/interop/quirk/cells/measurement_cells.py +1 -1
  179. cirq/interop/quirk/cells/parse.py +8 -7
  180. cirq/interop/quirk/cells/parse_test.py +2 -2
  181. cirq/interop/quirk/cells/single_qubit_rotation_cells.py +1 -1
  182. cirq/interop/quirk/cells/swap_cell_test.py +1 -1
  183. cirq/interop/quirk/cells/unsupported_cells.py +1 -1
  184. cirq/interop/quirk/url_to_circuit.py +7 -7
  185. cirq/interop/quirk/url_to_circuit_test.py +1 -1
  186. cirq/ion/__init__.py +4 -2
  187. cirq/json_resolver_cache.py +15 -7
  188. cirq/linalg/__init__.py +62 -51
  189. cirq/linalg/combinators.py +4 -4
  190. cirq/linalg/combinators_test.py +4 -1
  191. cirq/linalg/decompositions.py +15 -40
  192. cirq/linalg/decompositions_test.py +16 -22
  193. cirq/linalg/diagonalize.py +1 -1
  194. cirq/linalg/diagonalize_test.py +1 -1
  195. cirq/linalg/operator_spaces.py +20 -4
  196. cirq/linalg/operator_spaces_test.py +15 -2
  197. cirq/linalg/predicates.py +3 -3
  198. cirq/linalg/predicates_test.py +1 -0
  199. cirq/linalg/tolerance.py +2 -2
  200. cirq/linalg/transformations.py +30 -12
  201. cirq/linalg/transformations_test.py +13 -0
  202. cirq/neutral_atoms/__init__.py +2 -2
  203. cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py +0 -1
  204. cirq/ops/__init__.py +172 -132
  205. cirq/ops/arithmetic_operation.py +2 -2
  206. cirq/ops/arithmetic_operation_test.py +2 -2
  207. cirq/ops/boolean_hamiltonian.py +3 -2
  208. cirq/ops/classically_controlled_operation.py +39 -12
  209. cirq/ops/classically_controlled_operation_test.py +147 -1
  210. cirq/ops/clifford_gate.py +38 -36
  211. cirq/ops/clifford_gate_test.py +75 -1
  212. cirq/ops/common_channels.py +16 -45
  213. cirq/ops/common_channels_test.py +10 -0
  214. cirq/ops/common_gate_families.py +1 -1
  215. cirq/ops/common_gate_families_test.py +1 -0
  216. cirq/ops/common_gates.py +48 -49
  217. cirq/ops/common_gates_test.py +18 -2
  218. cirq/ops/control_values.py +3 -3
  219. cirq/ops/control_values_test.py +2 -1
  220. cirq/ops/controlled_gate.py +36 -23
  221. cirq/ops/controlled_gate_test.py +70 -3
  222. cirq/ops/controlled_operation.py +6 -5
  223. cirq/ops/controlled_operation_test.py +7 -3
  224. cirq/ops/dense_pauli_string.py +11 -11
  225. cirq/ops/diagonal_gate.py +2 -2
  226. cirq/ops/diagonal_gate_test.py +1 -0
  227. cirq/ops/eigen_gate.py +16 -36
  228. cirq/ops/eigen_gate_test.py +60 -10
  229. cirq/ops/fourier_transform.py +1 -3
  230. cirq/ops/fourier_transform_test.py +2 -1
  231. cirq/ops/fsim_gate.py +42 -3
  232. cirq/ops/fsim_gate_test.py +21 -0
  233. cirq/ops/gate_operation.py +8 -8
  234. cirq/ops/gate_operation_test.py +4 -2
  235. cirq/ops/gateset_test.py +11 -2
  236. cirq/ops/global_phase_op.py +8 -7
  237. cirq/ops/global_phase_op_test.py +1 -1
  238. cirq/ops/greedy_qubit_manager_test.py +5 -0
  239. cirq/ops/identity.py +14 -4
  240. cirq/ops/identity_test.py +24 -0
  241. cirq/ops/kraus_channel.py +1 -0
  242. cirq/ops/kraus_channel_test.py +3 -1
  243. cirq/ops/linear_combinations.py +27 -21
  244. cirq/ops/linear_combinations_test.py +23 -4
  245. cirq/ops/matrix_gates.py +24 -8
  246. cirq/ops/measure_util.py +2 -2
  247. cirq/ops/measurement_gate.py +7 -4
  248. cirq/ops/measurement_gate_test.py +2 -1
  249. cirq/ops/mixed_unitary_channel.py +1 -0
  250. cirq/ops/mixed_unitary_channel_test.py +3 -1
  251. cirq/ops/named_qubit.py +8 -1
  252. cirq/ops/op_tree.py +3 -30
  253. cirq/ops/op_tree_test.py +4 -0
  254. cirq/ops/parallel_gate.py +2 -3
  255. cirq/ops/parallel_gate_test.py +2 -1
  256. cirq/ops/parity_gates.py +7 -8
  257. cirq/ops/parity_gates_test.py +1 -0
  258. cirq/ops/pauli_gates.py +5 -11
  259. cirq/ops/pauli_gates_test.py +1 -0
  260. cirq/ops/pauli_interaction_gate.py +11 -5
  261. cirq/ops/pauli_interaction_gate_test.py +2 -3
  262. cirq/ops/pauli_measurement_gate.py +6 -5
  263. cirq/ops/pauli_measurement_gate_test.py +1 -0
  264. cirq/ops/pauli_string.py +115 -130
  265. cirq/ops/pauli_string_phasor.py +21 -20
  266. cirq/ops/pauli_string_phasor_test.py +13 -3
  267. cirq/ops/pauli_string_raw_types.py +1 -0
  268. cirq/ops/pauli_string_test.py +192 -55
  269. cirq/ops/pauli_sum_exponential.py +3 -4
  270. cirq/ops/pauli_sum_exponential_test.py +0 -1
  271. cirq/ops/permutation_gate.py +2 -2
  272. cirq/ops/permutation_gate_test.py +1 -1
  273. cirq/ops/phased_iswap_gate.py +6 -7
  274. cirq/ops/phased_iswap_gate_test.py +21 -5
  275. cirq/ops/phased_x_gate.py +11 -25
  276. cirq/ops/phased_x_gate_test.py +19 -3
  277. cirq/ops/phased_x_z_gate.py +12 -11
  278. cirq/ops/projector.py +4 -5
  279. cirq/ops/qubit_manager.py +2 -1
  280. cirq/ops/qubit_manager_test.py +2 -1
  281. cirq/ops/qubit_order.py +1 -1
  282. cirq/ops/random_gate_channel.py +1 -1
  283. cirq/ops/random_gate_channel_test.py +0 -6
  284. cirq/ops/raw_types.py +146 -50
  285. cirq/ops/raw_types_test.py +37 -3
  286. cirq/ops/state_preparation_channel.py +2 -2
  287. cirq/ops/state_preparation_channel_test.py +2 -1
  288. cirq/ops/swap_gates.py +9 -4
  289. cirq/ops/three_qubit_gates.py +8 -8
  290. cirq/ops/three_qubit_gates_test.py +1 -0
  291. cirq/ops/two_qubit_diagonal_gate.py +4 -3
  292. cirq/ops/uniform_superposition_gate.py +4 -4
  293. cirq/ops/uniform_superposition_gate_test.py +1 -0
  294. cirq/ops/wait_gate.py +6 -8
  295. cirq/protocols/__init__.py +135 -83
  296. cirq/protocols/act_on_protocol.py +1 -1
  297. cirq/protocols/act_on_protocol_test.py +1 -1
  298. cirq/protocols/apply_channel_protocol.py +3 -3
  299. cirq/protocols/apply_mixture_protocol.py +15 -9
  300. cirq/protocols/apply_mixture_protocol_test.py +11 -0
  301. cirq/protocols/apply_unitary_protocol.py +2 -2
  302. cirq/protocols/apply_unitary_protocol_test.py +2 -1
  303. cirq/protocols/approximate_equality_protocol.py +7 -8
  304. cirq/protocols/approximate_equality_protocol_test.py +3 -1
  305. cirq/protocols/circuit_diagram_info_protocol.py +8 -6
  306. cirq/protocols/circuit_diagram_info_protocol_test.py +5 -0
  307. cirq/protocols/commutes_protocol.py +6 -6
  308. cirq/protocols/control_key_protocol.py +1 -1
  309. cirq/protocols/decompose_protocol.py +4 -5
  310. cirq/protocols/decompose_protocol_test.py +2 -1
  311. cirq/protocols/equal_up_to_global_phase_protocol.py +3 -3
  312. cirq/protocols/equal_up_to_global_phase_protocol_test.py +7 -0
  313. cirq/protocols/has_stabilizer_effect_protocol.py +5 -5
  314. cirq/protocols/has_unitary_protocol.py +1 -1
  315. cirq/protocols/has_unitary_protocol_test.py +8 -7
  316. cirq/protocols/hash_from_pickle_test.py +120 -0
  317. cirq/protocols/inverse_protocol.py +1 -1
  318. cirq/protocols/json_serialization.py +14 -1
  319. cirq/protocols/json_serialization_test.py +28 -7
  320. cirq/protocols/json_test_data/BitMaskKeyCondition.json +86 -0
  321. cirq/protocols/json_test_data/BitMaskKeyCondition.repr +7 -0
  322. cirq/protocols/json_test_data/Concat.json +19 -0
  323. cirq/protocols/json_test_data/Concat.repr +1 -0
  324. cirq/protocols/json_test_data/README.md +4 -2
  325. cirq/protocols/json_test_data/SympyCondition.json +60 -15
  326. cirq/protocols/json_test_data/SympyCondition.repr +4 -1
  327. cirq/protocols/json_test_data/_InverseCompositeGate.json +10 -0
  328. cirq/protocols/json_test_data/_InverseCompositeGate.repr +1 -0
  329. cirq/protocols/json_test_data/__init__.py +1 -1
  330. cirq/protocols/json_test_data/sympy.And.json +13 -0
  331. cirq/protocols/json_test_data/sympy.And.repr +1 -0
  332. cirq/protocols/json_test_data/sympy.Indexed.json +18 -0
  333. cirq/protocols/json_test_data/sympy.Indexed.repr +1 -0
  334. cirq/protocols/json_test_data/sympy.IndexedBase.json +9 -0
  335. cirq/protocols/json_test_data/sympy.IndexedBase.repr +1 -0
  336. cirq/protocols/json_test_data/sympy.Not.json +9 -0
  337. cirq/protocols/json_test_data/sympy.Not.repr +1 -0
  338. cirq/protocols/json_test_data/sympy.Or.json +13 -0
  339. cirq/protocols/json_test_data/sympy.Or.repr +1 -0
  340. cirq/protocols/json_test_data/sympy.Xor.json +13 -0
  341. cirq/protocols/json_test_data/sympy.Xor.repr +1 -0
  342. cirq/protocols/kraus_protocol.py +8 -8
  343. cirq/protocols/kraus_protocol_test.py +0 -1
  344. cirq/protocols/measurement_key_protocol.py +1 -1
  345. cirq/protocols/measurement_key_protocol_test.py +7 -7
  346. cirq/protocols/mixture_protocol.py +6 -4
  347. cirq/protocols/mixture_protocol_test.py +21 -13
  348. cirq/protocols/pauli_expansion_protocol.py +1 -0
  349. cirq/protocols/pow_protocol.py +1 -1
  350. cirq/protocols/qasm.py +25 -6
  351. cirq/protocols/qasm_test.py +17 -0
  352. cirq/protocols/qid_shape_protocol.py +2 -2
  353. cirq/protocols/resolve_parameters.py +2 -3
  354. cirq/protocols/resolve_parameters_test.py +2 -1
  355. cirq/protocols/trace_distance_bound.py +1 -1
  356. cirq/protocols/trace_distance_bound_test.py +1 -0
  357. cirq/protocols/unitary_protocol.py +3 -3
  358. cirq/protocols/unitary_protocol_test.py +1 -1
  359. cirq/qis/__init__.py +48 -35
  360. cirq/qis/channels_test.py +0 -9
  361. cirq/qis/clifford_tableau.py +46 -26
  362. cirq/qis/clifford_tableau_test.py +2 -1
  363. cirq/qis/entropy.py +115 -0
  364. cirq/qis/entropy_test.py +43 -0
  365. cirq/qis/measures.py +5 -4
  366. cirq/qis/measures_test.py +7 -0
  367. cirq/qis/noise_utils_test.py +4 -4
  368. cirq/qis/quantum_state_representation.py +1 -1
  369. cirq/qis/states.py +7 -7
  370. cirq/sim/__init__.py +55 -37
  371. cirq/sim/classical_simulator.py +7 -6
  372. cirq/sim/classical_simulator_test.py +3 -1
  373. cirq/sim/clifford/__init__.py +17 -9
  374. cirq/sim/clifford/clifford_simulator.py +5 -4
  375. cirq/sim/clifford/clifford_simulator_test.py +32 -9
  376. cirq/sim/clifford/clifford_tableau_simulation_state.py +1 -1
  377. cirq/sim/clifford/stabilizer_simulation_state.py +1 -1
  378. cirq/sim/clifford/stabilizer_state_ch_form.py +4 -3
  379. cirq/sim/density_matrix_simulator.py +3 -2
  380. cirq/sim/density_matrix_simulator_test.py +12 -4
  381. cirq/sim/density_matrix_utils.py +1 -1
  382. cirq/sim/mux.py +2 -2
  383. cirq/sim/simulation_state.py +4 -5
  384. cirq/sim/simulation_state_base.py +2 -2
  385. cirq/sim/simulation_state_test.py +1 -1
  386. cirq/sim/simulation_utils.py +3 -1
  387. cirq/sim/simulation_utils_test.py +2 -3
  388. cirq/sim/simulator.py +7 -6
  389. cirq/sim/simulator_base.py +5 -5
  390. cirq/sim/simulator_test.py +14 -3
  391. cirq/sim/sparse_simulator.py +4 -3
  392. cirq/sim/sparse_simulator_test.py +17 -9
  393. cirq/sim/state_vector.py +2 -2
  394. cirq/sim/state_vector_simulation_state_test.py +1 -1
  395. cirq/sim/state_vector_simulator.py +4 -4
  396. cirq/sim/state_vector_test.py +27 -32
  397. cirq/study/__init__.py +27 -21
  398. cirq/study/flatten_expressions.py +5 -6
  399. cirq/study/flatten_expressions_test.py +1 -1
  400. cirq/study/resolver.py +14 -11
  401. cirq/study/resolver_test.py +10 -1
  402. cirq/study/result.py +3 -3
  403. cirq/study/sweepable.py +15 -9
  404. cirq/study/sweepable_test.py +27 -0
  405. cirq/study/sweeps.py +65 -10
  406. cirq/study/sweeps_test.py +123 -0
  407. cirq/testing/__init__.py +86 -57
  408. cirq/testing/_compat_test_data/module_a/__init__.py +2 -2
  409. cirq/testing/_compat_test_data/module_a/sub/subsub/__init__.py +1 -1
  410. cirq/testing/circuit_compare.py +3 -4
  411. cirq/testing/circuit_compare_test.py +7 -8
  412. cirq/testing/consistent_act_on.py +3 -3
  413. cirq/testing/consistent_channels_test.py +2 -1
  414. cirq/testing/consistent_controlled_gate_op.py +3 -2
  415. cirq/testing/consistent_controlled_gate_op_test.py +2 -3
  416. cirq/testing/consistent_decomposition.py +1 -1
  417. cirq/testing/consistent_decomposition_test.py +1 -2
  418. cirq/testing/consistent_pauli_expansion_test.py +1 -1
  419. cirq/testing/consistent_phase_by.py +1 -1
  420. cirq/testing/consistent_phase_by_test.py +1 -2
  421. cirq/testing/consistent_protocols.py +11 -11
  422. cirq/testing/consistent_protocols_test.py +4 -5
  423. cirq/testing/consistent_qasm.py +8 -12
  424. cirq/testing/consistent_qasm_test.py +1 -1
  425. cirq/testing/consistent_resolve_parameters.py +2 -1
  426. cirq/testing/consistent_specified_has_unitary_test.py +1 -1
  427. cirq/testing/consistent_unitary.py +3 -1
  428. cirq/testing/consistent_unitary_test.py +3 -3
  429. cirq/testing/devices.py +1 -1
  430. cirq/testing/devices_test.py +1 -0
  431. cirq/testing/equals_tester.py +2 -4
  432. cirq/testing/equals_tester_test.py +6 -5
  433. cirq/testing/equivalent_basis_map.py +1 -0
  434. cirq/testing/equivalent_basis_map_test.py +0 -1
  435. cirq/testing/gate_features_test.py +5 -0
  436. cirq/testing/json.py +4 -4
  437. cirq/testing/lin_alg_utils_test.py +1 -1
  438. cirq/testing/order_tester.py +1 -1
  439. cirq/testing/order_tester_test.py +1 -1
  440. cirq/testing/pytest_utils.py +57 -0
  441. cirq/testing/pytest_utils_test.py +35 -0
  442. cirq/testing/random_circuit.py +2 -2
  443. cirq/testing/random_circuit_test.py +2 -2
  444. cirq/testing/routing_devices_test.py +2 -1
  445. cirq/testing/sample_circuits.py +1 -1
  446. cirq/testing/sample_gates.py +5 -4
  447. cirq/testing/sample_gates_test.py +2 -2
  448. cirq/transformers/__init__.py +101 -82
  449. cirq/transformers/align.py +12 -1
  450. cirq/transformers/align_test.py +13 -0
  451. cirq/transformers/analytical_decompositions/__init__.py +27 -24
  452. cirq/transformers/analytical_decompositions/clifford_decomposition.py +2 -1
  453. cirq/transformers/analytical_decompositions/clifford_decomposition_test.py +1 -1
  454. cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +1 -1
  455. cirq/transformers/analytical_decompositions/controlled_gate_decomposition_test.py +2 -0
  456. cirq/transformers/analytical_decompositions/cphase_to_fsim.py +1 -1
  457. cirq/transformers/analytical_decompositions/cphase_to_fsim_test.py +1 -1
  458. cirq/transformers/analytical_decompositions/pauli_string_decomposition.py +2 -2
  459. cirq/transformers/analytical_decompositions/pauli_string_decomposition_test.py +4 -4
  460. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +99 -24
  461. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +105 -14
  462. cirq/transformers/analytical_decompositions/single_qubit_decompositions.py +1 -1
  463. cirq/transformers/analytical_decompositions/single_to_two_qubit_isometry.py +1 -1
  464. cirq/transformers/analytical_decompositions/single_to_two_qubit_isometry_test.py +1 -0
  465. cirq/transformers/analytical_decompositions/three_qubit_decomposition.py +3 -4
  466. cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +1 -1
  467. cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py +2 -1
  468. cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +2 -1
  469. cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +5 -6
  470. cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +2 -2
  471. cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py +1 -1
  472. cirq/transformers/analytical_decompositions/two_qubit_to_fsim_test.py +1 -2
  473. cirq/transformers/analytical_decompositions/two_qubit_to_ms.py +2 -2
  474. cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap.py +2 -2
  475. cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py +2 -1
  476. cirq/transformers/drop_empty_moments.py +1 -0
  477. cirq/transformers/drop_negligible_operations.py +1 -0
  478. cirq/transformers/dynamical_decoupling.py +255 -43
  479. cirq/transformers/dynamical_decoupling_test.py +730 -17
  480. cirq/transformers/eject_phased_paulis.py +29 -15
  481. cirq/transformers/eject_phased_paulis_test.py +3 -8
  482. cirq/transformers/eject_z.py +3 -2
  483. cirq/transformers/eject_z_test.py +3 -3
  484. cirq/transformers/gauge_compiling/__init__.py +25 -9
  485. cirq/transformers/gauge_compiling/cphase_gauge.py +146 -0
  486. cirq/transformers/gauge_compiling/cphase_gauge_test.py +42 -0
  487. cirq/transformers/gauge_compiling/cz_gauge.py +4 -4
  488. cirq/transformers/gauge_compiling/gauge_compiling.py +245 -6
  489. cirq/transformers/gauge_compiling/gauge_compiling_test.py +107 -2
  490. cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +39 -2
  491. cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py +10 -1
  492. cirq/transformers/gauge_compiling/iswap_gauge.py +2 -2
  493. cirq/transformers/gauge_compiling/spin_inversion_gauge.py +2 -2
  494. cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +23 -5
  495. cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py +3 -2
  496. cirq/transformers/heuristic_decompositions/__init__.py +3 -3
  497. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +2 -1
  498. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py +1 -1
  499. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +4 -4
  500. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation_test.py +4 -4
  501. cirq/transformers/insertion_sort.py +64 -0
  502. cirq/transformers/insertion_sort_test.py +34 -0
  503. cirq/transformers/measurement_transformers.py +14 -1
  504. cirq/transformers/measurement_transformers_test.py +35 -0
  505. cirq/transformers/merge_k_qubit_gates.py +2 -2
  506. cirq/transformers/merge_single_qubit_gates.py +1 -1
  507. cirq/transformers/merge_single_qubit_gates_test.py +1 -1
  508. cirq/transformers/noise_adding.py +115 -0
  509. cirq/transformers/noise_adding_test.py +54 -0
  510. cirq/transformers/optimize_for_target_gateset.py +1 -1
  511. cirq/transformers/optimize_for_target_gateset_test.py +3 -2
  512. cirq/transformers/qubit_management_transformers.py +1 -1
  513. cirq/transformers/randomized_measurements.py +171 -0
  514. cirq/transformers/randomized_measurements_test.py +68 -0
  515. cirq/transformers/routing/__init__.py +14 -5
  516. cirq/transformers/routing/initial_mapper.py +1 -1
  517. cirq/transformers/routing/initial_mapper_test.py +1 -0
  518. cirq/transformers/routing/line_initial_mapper.py +3 -2
  519. cirq/transformers/routing/mapping_manager.py +2 -2
  520. cirq/transformers/routing/mapping_manager_test.py +2 -2
  521. cirq/transformers/routing/route_circuit_cqc.py +3 -2
  522. cirq/transformers/routing/route_circuit_cqc_test.py +2 -1
  523. cirq/transformers/routing/visualize_routed_circuit.py +1 -0
  524. cirq/transformers/routing/visualize_routed_circuit_test.py +1 -0
  525. cirq/transformers/stratify.py +2 -2
  526. cirq/transformers/synchronize_terminal_measurements.py +2 -1
  527. cirq/transformers/target_gatesets/__init__.py +7 -5
  528. cirq/transformers/target_gatesets/compilation_target_gateset.py +16 -3
  529. cirq/transformers/target_gatesets/compilation_target_gateset_test.py +2 -0
  530. cirq/transformers/target_gatesets/cz_gateset.py +5 -1
  531. cirq/transformers/target_gatesets/cz_gateset_test.py +23 -2
  532. cirq/transformers/target_gatesets/sqrt_iswap_gateset.py +1 -1
  533. cirq/transformers/target_gatesets/sqrt_iswap_gateset_test.py +3 -2
  534. cirq/transformers/transformer_api.py +5 -4
  535. cirq/transformers/transformer_api_test.py +11 -3
  536. cirq/transformers/transformer_primitives.py +9 -31
  537. cirq/transformers/transformer_primitives_test.py +6 -5
  538. cirq/value/__init__.py +51 -30
  539. cirq/value/abc_alt.py +1 -2
  540. cirq/value/angle.py +2 -0
  541. cirq/value/classical_data.py +1 -0
  542. cirq/value/condition.py +149 -3
  543. cirq/value/condition_test.py +254 -0
  544. cirq/value/digits.py +1 -1
  545. cirq/value/duration.py +4 -4
  546. cirq/value/duration_test.py +2 -1
  547. cirq/value/linear_dict.py +85 -24
  548. cirq/value/linear_dict_test.py +94 -3
  549. cirq/value/measurement_key.py +9 -2
  550. cirq/value/periodic_value.py +2 -3
  551. cirq/value/periodic_value_test.py +5 -0
  552. cirq/value/probability.py +1 -0
  553. cirq/value/random_state.py +1 -1
  554. cirq/value/timestamp.py +2 -4
  555. cirq/value/timestamp_test.py +2 -1
  556. cirq/value/type_alias.py +2 -2
  557. cirq/value/value_equality_attr.py +14 -2
  558. cirq/value/value_equality_attr_test.py +1 -1
  559. cirq/vis/__init__.py +9 -6
  560. cirq/vis/density_matrix.py +1 -1
  561. cirq/vis/density_matrix_test.py +2 -5
  562. cirq/vis/heatmap.py +49 -12
  563. cirq/vis/heatmap_test.py +168 -4
  564. cirq/vis/histogram.py +1 -1
  565. cirq/vis/histogram_test.py +1 -2
  566. cirq/vis/state_histogram.py +7 -5
  567. cirq/vis/state_histogram_test.py +2 -2
  568. cirq/work/__init__.py +19 -13
  569. cirq/work/collector.py +2 -2
  570. cirq/work/observable_grouping.py +2 -2
  571. cirq/work/observable_measurement.py +3 -3
  572. cirq/work/observable_measurement_data.py +5 -2
  573. cirq/work/observable_measurement_test.py +8 -8
  574. cirq/work/observable_readout_calibration.py +2 -2
  575. cirq/work/observable_readout_calibration_test.py +2 -1
  576. cirq/work/observable_settings.py +8 -7
  577. cirq/work/observable_settings_test.py +3 -2
  578. cirq/work/pauli_sum_collector.py +1 -1
  579. cirq/work/sampler.py +8 -20
  580. cirq/work/sampler_test.py +4 -3
  581. cirq/work/zeros_sampler.py +1 -1
  582. cirq_core-1.5.0.dist-info/METADATA +125 -0
  583. {cirq_core-1.4.1.dist-info → cirq_core-1.5.0.dist-info}/RECORD +586 -552
  584. {cirq_core-1.4.1.dist-info → cirq_core-1.5.0.dist-info}/WHEEL +1 -1
  585. cirq/experiments/grid_parallel_two_qubit_xeb.py +0 -62
  586. cirq/protocols/json_test_data/GridParallelXEBMetadata.json +0 -119
  587. cirq/protocols/json_test_data/GridParallelXEBMetadata.repr +0 -1
  588. cirq_core-1.4.1.dist-info/METADATA +0 -45
  589. {cirq_core-1.4.1.dist-info → cirq_core-1.5.0.dist-info}/LICENSE +0 -0
  590. {cirq_core-1.4.1.dist-info → cirq_core-1.5.0.dist-info}/top_level.txt +0 -0
@@ -15,72 +15,191 @@
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 itertools import cycle
19
+ from typing import Dict, Optional, Tuple, TYPE_CHECKING, Union
19
20
 
20
- from cirq.transformers import transformer_api
21
- import cirq
22
21
  import numpy as np
23
22
 
23
+ from cirq import circuits, ops, protocols
24
+ from cirq.protocols import unitary_protocol
25
+ from cirq.protocols.has_stabilizer_effect_protocol import has_stabilizer_effect
26
+ from cirq.protocols.has_unitary_protocol import has_unitary
27
+ from cirq.transformers import transformer_api
28
+ from cirq.transformers.analytical_decompositions import single_qubit_decompositions
24
29
 
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
30
+ if TYPE_CHECKING:
31
+ import cirq
31
32
 
32
33
 
33
- def _get_dd_sequence_from_schema_name(schema: str) -> Sequence['cirq.Gate']:
34
+ def _get_dd_sequence_from_schema_name(schema: str) -> Tuple[ops.Gate, ...]:
34
35
  """Gets dynamical decoupling sequence from a schema name."""
35
- dd_sequence: Sequence['cirq.Gate']
36
36
  match schema:
37
+ case 'DEFAULT':
38
+ return (ops.X, ops.Y, ops.X, ops.Y)
37
39
  case 'XX_PAIR':
38
- dd_sequence = (cirq.X, cirq.X)
40
+ return (ops.X, ops.X)
39
41
  case 'X_XINV':
40
- dd_sequence = (cirq.X, cirq.X**-1)
42
+ return (ops.X, ops.X**-1)
41
43
  case 'YY_PAIR':
42
- dd_sequence = (cirq.Y, cirq.Y)
44
+ return (ops.Y, ops.Y)
43
45
  case 'Y_YINV':
44
- dd_sequence = (cirq.Y, cirq.Y**-1)
46
+ return (ops.Y, ops.Y**-1)
45
47
  case _:
46
48
  raise ValueError('Invalid schema name.')
47
- return dd_sequence
48
49
 
49
50
 
50
- def _validate_dd_sequence(dd_sequence: Sequence['cirq.Gate']) -> None:
51
+ def _pauli_up_to_global_phase(gate: ops.Gate) -> Union[ops.Pauli, None]:
52
+ for pauli_gate in [ops.X, ops.Y, ops.Z]:
53
+ if protocols.equal_up_to_global_phase(gate, pauli_gate):
54
+ return pauli_gate
55
+ return None
56
+
57
+
58
+ def _validate_dd_sequence(dd_sequence: Tuple[ops.Gate, ...]) -> None:
51
59
  """Validates a given dynamical decoupling sequence.
52
60
 
61
+ The sequence should only consists of Pauli gates and is essentially an identity gate.
62
+
53
63
  Args:
54
64
  dd_sequence: Input dynamical sequence to be validated.
55
65
 
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
66
  Raises:
62
67
  ValueError: If dd_sequence is not valid.
63
68
  """
64
69
  if len(dd_sequence) < 2:
65
70
  raise ValueError('Invalid dynamical decoupling sequence. Expect more than one gates.')
66
- matrices = [cirq.unitary(gate) for gate in dd_sequence]
71
+ for gate in dd_sequence:
72
+ if _pauli_up_to_global_phase(gate) is None:
73
+ raise ValueError(
74
+ 'Dynamical decoupling sequence should only contain gates that are essentially'
75
+ ' Pauli gates.'
76
+ )
77
+ matrices = [unitary_protocol.unitary(gate) for gate in dd_sequence]
67
78
  product = reduce(np.matmul, matrices)
68
79
 
69
- if not cirq.equal_up_to_global_phase(product, np.eye(2)):
80
+ if not protocols.equal_up_to_global_phase(product, np.eye(2)):
70
81
  raise ValueError(
71
82
  'Invalid dynamical decoupling sequence. Expect sequence production equals'
72
83
  f' identity up to a global phase, got {product}.'.replace('\n', ' ')
73
84
  )
74
85
 
75
86
 
76
- def _parse_dd_sequence(schema: Union[str, Sequence['cirq.Gate']]) -> Sequence['cirq.Gate']:
77
- """Parses and returns dynamical decoupling sequence from schema."""
87
+ def _parse_dd_sequence(
88
+ schema: Union[str, Tuple[ops.Gate, ...]],
89
+ ) -> Tuple[Tuple[ops.Gate, ...], Dict[ops.Gate, ops.Pauli]]:
90
+ """Parses and returns dynamical decoupling sequence and its associated pauli map from schema."""
91
+ dd_sequence = None
78
92
  if isinstance(schema, str):
79
93
  dd_sequence = _get_dd_sequence_from_schema_name(schema)
80
94
  else:
81
95
  _validate_dd_sequence(schema)
82
96
  dd_sequence = schema
83
- return dd_sequence
97
+
98
+ # Map gate to Pauli gate. This is necessary as dd sequence might contain gates like X^-1.
99
+ pauli_map: Dict[ops.Gate, ops.Pauli] = {}
100
+ for gate in dd_sequence:
101
+ pauli_gate = _pauli_up_to_global_phase(gate)
102
+ if pauli_gate is not None:
103
+ pauli_map[gate] = pauli_gate
104
+ for gate in [ops.X, ops.Y, ops.Z]:
105
+ pauli_map[gate] = gate
106
+
107
+ return (dd_sequence, pauli_map)
108
+
109
+
110
+ def _is_single_qubit_operation(operation: ops.Operation) -> bool:
111
+ return len(operation.qubits) == 1
112
+
113
+
114
+ def _is_single_qubit_gate_moment(moment: circuits.Moment) -> bool:
115
+ return all(_is_single_qubit_operation(op) for op in moment)
116
+
117
+
118
+ def _is_clifford_op(op: ops.Operation) -> bool:
119
+ return has_unitary(op) and has_stabilizer_effect(op)
120
+
121
+
122
+ def _calc_busy_moment_range_of_each_qubit(
123
+ circuit: circuits.FrozenCircuit,
124
+ ) -> Dict[ops.Qid, list[int]]:
125
+ busy_moment_range_by_qubit: Dict[ops.Qid, list[int]] = {
126
+ q: [len(circuit), -1] for q in circuit.all_qubits()
127
+ }
128
+ for moment_id, moment in enumerate(circuit):
129
+ for q in moment.qubits:
130
+ busy_moment_range_by_qubit[q][0] = min(busy_moment_range_by_qubit[q][0], moment_id)
131
+ busy_moment_range_by_qubit[q][1] = max(busy_moment_range_by_qubit[q][1], moment_id)
132
+ return busy_moment_range_by_qubit
133
+
134
+
135
+ def _is_insertable_moment(moment: circuits.Moment, single_qubit_gate_moments_only: bool) -> bool:
136
+ return not single_qubit_gate_moments_only or _is_single_qubit_gate_moment(moment)
137
+
138
+
139
+ def _merge_single_qubit_ops_to_phxz(
140
+ q: ops.Qid, operations: Tuple[ops.Operation, ...]
141
+ ) -> ops.Operation:
142
+ """Merges [op1, op2, ...] and returns an equivalent op"""
143
+ if len(operations) == 1:
144
+ return operations[0]
145
+ matrices = [unitary_protocol.unitary(op) for op in reversed(operations)]
146
+ product = reduce(np.matmul, matrices)
147
+ gate = single_qubit_decompositions.single_qubit_matrix_to_phxz(product) or ops.I
148
+ return gate.on(q)
149
+
150
+
151
+ def _try_merge_single_qubit_ops_of_two_moments(
152
+ m1: circuits.Moment, m2: circuits.Moment
153
+ ) -> Tuple[circuits.Moment, ...]:
154
+ """Merge single qubit ops of 2 moments if possible, returns 2 moments otherwise."""
155
+ for q in m1.qubits & m2.qubits:
156
+ op1 = m1.operation_at(q)
157
+ op2 = m2.operation_at(q)
158
+ if any(
159
+ not (_is_single_qubit_operation(op) and has_unitary(op))
160
+ for op in [op1, op2]
161
+ if op is not None
162
+ ):
163
+ return (m1, m2)
164
+ merged_ops: set[ops.Operation] = set()
165
+ # Merge all operators on q to a single op.
166
+ for q in m1.qubits | m2.qubits:
167
+ # ops_on_q may contain 1 op or 2 ops.
168
+ ops_on_q = [op for op in [m.operation_at(q) for m in [m1, m2]] if op is not None]
169
+ merged_ops.add(_merge_single_qubit_ops_to_phxz(q, tuple(ops_on_q)))
170
+ return (circuits.Moment(merged_ops),)
171
+
172
+
173
+ def _calc_pulled_through(
174
+ moment: circuits.Moment, input_pauli_ops: ops.PauliString
175
+ ) -> ops.PauliString:
176
+ """Calculates the pulled_through such that circuit(input_pauli_ops, moment.clifford_ops) is
177
+ equivalent to circuit(moment.clifford_ops, pulled_through).
178
+ """
179
+ clifford_ops_in_moment: list[ops.Operation] = [
180
+ op for op in moment.operations if _is_clifford_op(op)
181
+ ]
182
+ return input_pauli_ops.after(clifford_ops_in_moment)
183
+
184
+
185
+ def _get_stop_qubits(moment: circuits.Moment) -> set[ops.Qid]:
186
+ stop_pulling_through_qubits: set[ops.Qid] = set()
187
+ for op in moment:
188
+ if (not _is_clifford_op(op) and not _is_single_qubit_operation(op)) or not has_unitary(
189
+ op
190
+ ): # multi-qubit clifford op or non-mergable op.
191
+ stop_pulling_through_qubits.update(op.qubits)
192
+ return stop_pulling_through_qubits
193
+
194
+
195
+ def _need_merge_pulled_through(op_at_q: ops.Operation, is_at_last_busy_moment: bool) -> bool:
196
+ """With a pulling through pauli gate before op_at_q, need to merge with the
197
+ pauli in the conditions below."""
198
+ # The op must be mergable and single-qubit
199
+ if not (_is_single_qubit_operation(op_at_q) and has_unitary(op_at_q)):
200
+ return False
201
+ # Either non-Clifford or at the last busy moment
202
+ return is_at_last_busy_moment or not _is_clifford_op(op_at_q)
84
203
 
85
204
 
86
205
  @transformer_api.transformer
@@ -88,10 +207,11 @@ def add_dynamical_decoupling(
88
207
  circuit: 'cirq.AbstractCircuit',
89
208
  *,
90
209
  context: Optional['cirq.TransformerContext'] = None,
91
- schema: Union[str, Sequence['cirq.Gate']] = 'X_XINV',
210
+ schema: Union[str, Tuple[ops.Gate, ...]] = 'DEFAULT',
211
+ single_qubit_gate_moments_only: bool = True,
92
212
  ) -> '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.
213
+ """Adds dynamical decoupling gate operations to a given circuit.
214
+ This transformer might add new moments thus change structure of the original circuit.
95
215
 
96
216
  Args:
97
217
  circuit: Input circuit to transform.
@@ -99,24 +219,116 @@ def add_dynamical_decoupling(
99
219
  schema: Dynamical decoupling schema name or a dynamical decoupling sequence.
100
220
  If a schema is specified, provided dynamical decouping sequence will be used.
101
221
  Otherwise, customized dynamical decoupling sequence will be applied.
222
+ single_qubit_gate_moments_only: If set True, dynamical decoupling operation will only be
223
+ added in single-qubit gate moments.
102
224
 
103
225
  Returns:
104
226
  A copy of the input circuit with dynamical decoupling operations.
105
227
  """
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']] = []
228
+ base_dd_sequence, pauli_map = _parse_dd_sequence(schema)
229
+ orig_circuit = circuit.freeze()
108
230
 
109
- base_dd_sequence = _parse_dd_sequence(schema)
231
+ busy_moment_range_by_qubit = _calc_busy_moment_range_of_each_qubit(orig_circuit)
110
232
 
111
- for moment_id, moment in enumerate(circuit):
112
- 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
233
+ # Stores all the moments of the output circuit chronologically.
234
+ transformed_moments: list[circuits.Moment] = []
235
+ # A PauliString stores the result of 'pulling' Pauli gates past each operations
236
+ # right before the current moment.
237
+ pulled_through: ops.PauliString = ops.PauliString()
238
+ # Iterator of gate to be used in dd sequence for each qubit.
239
+ dd_iter_by_qubits = {q: cycle(base_dd_sequence) for q in circuit.all_qubits()}
240
+
241
+ def _update_pulled_through(q: ops.Qid, insert_gate: ops.Gate) -> ops.Operation:
242
+ nonlocal pulled_through, pauli_map
243
+ pulled_through *= pauli_map[insert_gate].on(q)
244
+ return insert_gate.on(q)
245
+
246
+ # Insert and pull remaining Pauli ops through the whole circuit.
247
+ # General ideas are
248
+ # * Pull through Clifford gates.
249
+ # * Stop at multi-qubit non-Clifford ops (and other non-mergable ops).
250
+ # * Merge to single-qubit non-Clifford ops.
251
+ # * Insert a new moment if necessary.
252
+ # After pulling through pulled_through at `moment`, we expect a transformation of
253
+ # (pulled_through, moment) -> (updated_moment, updated_pulled_through) or
254
+ # (pulled_through, moment) -> (new_moment, updated_moment, updated_pulled_through)
255
+ # Moments structure changes are split into 3 steps:
256
+ # 1, (..., last_moment, pulled_through1, moment, ...)
257
+ # -> (..., try_merge(last_moment, new_moment or None), pulled_through2, moment, ...)
258
+ # 2, (..., pulled_through2, moment, ...) -> (..., pulled_through3, updated_moment, ...)
259
+ # 3, (..., pulled_through3, updated_moment, ...)
260
+ # -> (..., updated_moment, pulled_through4, ...)
261
+ for moment_id, moment in enumerate(orig_circuit.moments):
262
+ # Step 1, insert new_moment if necessary.
263
+ # In detail: stop pulling through for multi-qubit non-Clifford ops or gates without
264
+ # unitary representation (e.g., measure gates). If there are remaining pulled through ops,
265
+ # insert into a new moment before current moment.
266
+ stop_pulling_through_qubits: set[ops.Qid] = _get_stop_qubits(moment)
267
+ new_moment_ops = []
268
+ for q in stop_pulling_through_qubits:
269
+ # Insert the remaining pulled_through
270
+ remaining_pulled_through_gate = pulled_through.get(q)
271
+ if remaining_pulled_through_gate is not None:
272
+ new_moment_ops.append(_update_pulled_through(q, remaining_pulled_through_gate))
273
+ # Reset dd sequence
274
+ dd_iter_by_qubits[q] = cycle(base_dd_sequence)
275
+ # Need to insert a new moment before current moment
276
+ if new_moment_ops:
277
+ # Fill insertable idle moments in the new moment using dd sequence
278
+ for q in orig_circuit.all_qubits() - stop_pulling_through_qubits:
279
+ if busy_moment_range_by_qubit[q][0] < moment_id <= busy_moment_range_by_qubit[q][1]:
280
+ new_moment_ops.append(_update_pulled_through(q, next(dd_iter_by_qubits[q])))
281
+ moments_to_be_appended = _try_merge_single_qubit_ops_of_two_moments(
282
+ transformed_moments.pop(), circuits.Moment(new_moment_ops)
115
283
  )
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
284
+ transformed_moments.extend(moments_to_be_appended)
285
+
286
+ # Step 2, calc updated_moment with insertions / merges.
287
+ updated_moment_ops: set['cirq.Operation'] = set()
288
+ for q in orig_circuit.all_qubits():
289
+ op_at_q = moment.operation_at(q)
290
+ remaining_pulled_through_gate = pulled_through.get(q)
291
+ updated_op = op_at_q
292
+ if op_at_q is None: # insert into idle op
293
+ if not _is_insertable_moment(moment, single_qubit_gate_moments_only):
294
+ continue
295
+ if (
296
+ busy_moment_range_by_qubit[q][0] < moment_id < busy_moment_range_by_qubit[q][1]
297
+ ): # insert next pauli gate in the dd sequence
298
+ updated_op = _update_pulled_through(q, next(dd_iter_by_qubits[q]))
299
+ elif ( # insert the remaining pulled through if beyond the ending busy moment
300
+ moment_id > busy_moment_range_by_qubit[q][1]
301
+ and remaining_pulled_through_gate is not None
302
+ ):
303
+ updated_op = _update_pulled_through(q, remaining_pulled_through_gate)
304
+ elif (
305
+ remaining_pulled_through_gate is not None
306
+ ): # merge pulled-through of q to op_at_q if needed
307
+ if _need_merge_pulled_through(
308
+ op_at_q, moment_id == busy_moment_range_by_qubit[q][1]
309
+ ):
310
+ remaining_op = _update_pulled_through(q, remaining_pulled_through_gate)
311
+ updated_op = _merge_single_qubit_ops_to_phxz(q, (remaining_op, op_at_q))
312
+ if updated_op is not None:
313
+ updated_moment_ops.add(updated_op)
314
+
315
+ if updated_moment_ops:
316
+ updated_moment = circuits.Moment(updated_moment_ops)
317
+ transformed_moments.append(updated_moment)
318
+
319
+ # Step 3, update pulled through.
320
+ # In detail: pulling current `pulled_through` through updated_moment.
321
+ pulled_through = _calc_pulled_through(updated_moment, pulled_through)
322
+
323
+ # Insert a new moment if there are remaining pulled-through operations.
324
+ ending_moment_ops = []
325
+ for affected_q, combined_op_in_pauli in pulled_through.items():
326
+ ending_moment_ops.append(combined_op_in_pauli.on(affected_q))
327
+ if ending_moment_ops:
328
+ transformed_moments.extend(
329
+ _try_merge_single_qubit_ops_of_two_moments(
330
+ transformed_moments.pop(), circuits.Moment(ending_moment_ops)
331
+ )
332
+ )
119
333
 
120
- updated_circuit = circuit.unfreeze(copy=True)
121
- updated_circuit.batch_insert_into(insert_into)
122
- return updated_circuit
334
+ return circuits.Circuit(transformed_moments)