cirq-core 1.5.0.dev20250409225226__py3-none-any.whl → 1.6.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 (732) hide show
  1. cirq/__init__.py +16 -17
  2. cirq/_compat.py +21 -20
  3. cirq/_compat_test.py +14 -34
  4. cirq/_doc.py +4 -2
  5. cirq/_import.py +8 -6
  6. cirq/_import_test.py +4 -2
  7. cirq/_version.py +6 -6
  8. cirq/_version_test.py +2 -2
  9. cirq/circuits/_block_diagram_drawer.py +11 -10
  10. cirq/circuits/_block_diagram_drawer_test.py +8 -6
  11. cirq/circuits/_box_drawing_character_data.py +8 -8
  12. cirq/circuits/_box_drawing_character_data_test.py +3 -1
  13. cirq/circuits/_bucket_priority_queue.py +9 -7
  14. cirq/circuits/_bucket_priority_queue_test.py +22 -20
  15. cirq/circuits/circuit.py +248 -172
  16. cirq/circuits/circuit_operation.py +73 -83
  17. cirq/circuits/circuit_operation_test.py +128 -90
  18. cirq/circuits/circuit_test.py +211 -151
  19. cirq/circuits/frozen_circuit.py +23 -60
  20. cirq/circuits/frozen_circuit_test.py +31 -8
  21. cirq/circuits/insert_strategy.py +7 -5
  22. cirq/circuits/insert_strategy_test.py +4 -2
  23. cirq/circuits/moment.py +88 -40
  24. cirq/circuits/moment_test.py +128 -51
  25. cirq/circuits/optimization_pass.py +5 -5
  26. cirq/circuits/optimization_pass_test.py +10 -10
  27. cirq/circuits/qasm_output.py +11 -11
  28. cirq/circuits/qasm_output_test.py +25 -22
  29. cirq/circuits/text_diagram_drawer.py +23 -38
  30. cirq/circuits/text_diagram_drawer_test.py +19 -17
  31. cirq/conftest.py +4 -3
  32. cirq/contrib/__init__.py +4 -4
  33. cirq/contrib/acquaintance/__init__.py +1 -1
  34. cirq/contrib/acquaintance/bipartite.py +5 -8
  35. cirq/contrib/acquaintance/bipartite_test.py +18 -13
  36. cirq/contrib/acquaintance/devices.py +2 -2
  37. cirq/contrib/acquaintance/devices_test.py +5 -3
  38. cirq/contrib/acquaintance/executor.py +5 -5
  39. cirq/contrib/acquaintance/executor_test.py +13 -9
  40. cirq/contrib/acquaintance/gates.py +18 -28
  41. cirq/contrib/acquaintance/gates_test.py +24 -20
  42. cirq/contrib/acquaintance/inspection_utils.py +8 -4
  43. cirq/contrib/acquaintance/inspection_utils_test.py +4 -2
  44. cirq/contrib/acquaintance/mutation_utils.py +4 -4
  45. cirq/contrib/acquaintance/mutation_utils_test.py +4 -2
  46. cirq/contrib/acquaintance/optimizers.py +4 -4
  47. cirq/contrib/acquaintance/optimizers_test.py +4 -1
  48. cirq/contrib/acquaintance/permutation.py +15 -27
  49. cirq/contrib/acquaintance/permutation_test.py +26 -17
  50. cirq/contrib/acquaintance/shift.py +4 -4
  51. cirq/contrib/acquaintance/shift_swap_network.py +4 -4
  52. cirq/contrib/acquaintance/shift_swap_network_test.py +9 -6
  53. cirq/contrib/acquaintance/shift_test.py +8 -6
  54. cirq/contrib/acquaintance/strategies/cubic.py +2 -2
  55. cirq/contrib/acquaintance/strategies/cubic_test.py +4 -2
  56. cirq/contrib/acquaintance/strategies/quartic_paired.py +6 -6
  57. cirq/contrib/acquaintance/strategies/quartic_paired_test.py +10 -6
  58. cirq/contrib/acquaintance/testing.py +2 -0
  59. cirq/contrib/acquaintance/topological_sort.py +2 -2
  60. cirq/contrib/acquaintance/topological_sort_test.py +3 -1
  61. cirq/contrib/bayesian_network/bayesian_network_gate.py +9 -10
  62. cirq/contrib/bayesian_network/bayesian_network_gate_test.py +14 -9
  63. cirq/contrib/circuitdag/circuit_dag.py +4 -4
  64. cirq/contrib/circuitdag/circuit_dag_test.py +17 -15
  65. cirq/contrib/custom_simulators/custom_state_simulator.py +5 -5
  66. cirq/contrib/custom_simulators/custom_state_simulator_test.py +22 -17
  67. cirq/contrib/graph_device/graph_device.py +12 -11
  68. cirq/contrib/graph_device/graph_device_test.py +18 -14
  69. cirq/contrib/graph_device/hypergraph.py +16 -14
  70. cirq/contrib/graph_device/hypergraph_test.py +13 -11
  71. cirq/contrib/graph_device/uniform_graph_device.py +6 -4
  72. cirq/contrib/graph_device/uniform_graph_device_test.py +11 -3
  73. cirq/contrib/hacks/disable_validation.py +6 -1
  74. cirq/contrib/hacks/disable_validation_test.py +3 -1
  75. cirq/contrib/json.py +31 -5
  76. cirq/contrib/json_test.py +6 -3
  77. cirq/contrib/json_test_data/DampedReadoutNoiseModel.json +12 -0
  78. cirq/contrib/json_test_data/DampedReadoutNoiseModel.repr +4 -0
  79. cirq/contrib/json_test_data/DepolarizingNoiseModel.json +12 -0
  80. cirq/contrib/json_test_data/DepolarizingNoiseModel.repr +4 -0
  81. cirq/contrib/json_test_data/DepolarizingWithDampedReadoutNoiseModel.json +6 -0
  82. cirq/contrib/json_test_data/DepolarizingWithDampedReadoutNoiseModel.repr +1 -0
  83. cirq/contrib/json_test_data/DepolarizingWithReadoutNoiseModel.json +5 -0
  84. cirq/contrib/json_test_data/DepolarizingWithReadoutNoiseModel.repr +1 -0
  85. cirq/contrib/json_test_data/ReadoutNoiseModel.json +12 -0
  86. cirq/contrib/json_test_data/ReadoutNoiseModel.repr +4 -0
  87. cirq/contrib/json_test_data/__init__.py +17 -0
  88. cirq/contrib/json_test_data/spec.py +32 -0
  89. cirq/contrib/noise_models/noise_models.py +119 -5
  90. cirq/contrib/noise_models/noise_models_test.py +37 -9
  91. cirq/contrib/paulistring/clifford_optimize.py +6 -4
  92. cirq/contrib/paulistring/clifford_optimize_test.py +6 -5
  93. cirq/contrib/paulistring/clifford_target_gateset.py +10 -10
  94. cirq/contrib/paulistring/clifford_target_gateset_test.py +13 -11
  95. cirq/contrib/paulistring/optimize.py +2 -0
  96. cirq/contrib/paulistring/optimize_test.py +4 -3
  97. cirq/contrib/paulistring/pauli_string_dag.py +2 -0
  98. cirq/contrib/paulistring/pauli_string_dag_test.py +3 -1
  99. cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation.py +255 -120
  100. cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation_test.py +398 -19
  101. cirq/contrib/paulistring/pauli_string_optimize.py +7 -1
  102. cirq/contrib/paulistring/pauli_string_optimize_test.py +5 -3
  103. cirq/contrib/paulistring/recombine.py +6 -4
  104. cirq/contrib/paulistring/recombine_test.py +3 -1
  105. cirq/contrib/paulistring/separate.py +9 -6
  106. cirq/contrib/paulistring/separate_test.py +3 -1
  107. cirq/contrib/qasm_import/_lexer.py +3 -2
  108. cirq/contrib/qasm_import/_lexer_test.py +49 -13
  109. cirq/contrib/qasm_import/_parser.py +547 -83
  110. cirq/contrib/qasm_import/_parser_test.py +988 -97
  111. cirq/contrib/qasm_import/exception.py +2 -0
  112. cirq/contrib/qasm_import/qasm.py +8 -2
  113. cirq/contrib/qasm_import/qasm_test.py +7 -4
  114. cirq/contrib/qcircuit/qcircuit_diagram_info.py +5 -5
  115. cirq/contrib/qcircuit/qcircuit_diagram_info_test.py +4 -1
  116. cirq/contrib/qcircuit/qcircuit_pdf.py +7 -3
  117. cirq/contrib/qcircuit/qcircuit_pdf_test.py +3 -1
  118. cirq/contrib/qcircuit/qcircuit_test.py +10 -8
  119. cirq/contrib/quantum_volume/quantum_volume.py +31 -27
  120. cirq/contrib/quantum_volume/quantum_volume_test.py +19 -16
  121. cirq/contrib/quimb/density_matrix.py +15 -14
  122. cirq/contrib/quimb/density_matrix_test.py +10 -7
  123. cirq/contrib/quimb/grid_circuits.py +5 -2
  124. cirq/contrib/quimb/grid_circuits_test.py +3 -0
  125. cirq/contrib/quimb/mps_simulator.py +20 -20
  126. cirq/contrib/quimb/mps_simulator_test.py +3 -0
  127. cirq/contrib/quimb/state_vector.py +12 -11
  128. cirq/contrib/quimb/state_vector_test.py +3 -0
  129. cirq/contrib/quirk/export_to_quirk.py +5 -3
  130. cirq/contrib/quirk/export_to_quirk_test.py +18 -16
  131. cirq/contrib/quirk/linearize_circuit.py +2 -0
  132. cirq/contrib/quirk/quirk_gate.py +18 -17
  133. cirq/contrib/routing/device.py +5 -3
  134. cirq/contrib/routing/device_test.py +2 -0
  135. cirq/contrib/routing/greedy.py +10 -21
  136. cirq/contrib/routing/greedy_test.py +4 -2
  137. cirq/contrib/routing/initialization.py +2 -2
  138. cirq/contrib/routing/initialization_test.py +5 -3
  139. cirq/contrib/routing/router.py +9 -5
  140. cirq/contrib/routing/router_test.py +2 -0
  141. cirq/contrib/routing/swap_network.py +3 -3
  142. cirq/contrib/routing/swap_network_test.py +3 -1
  143. cirq/contrib/routing/utils.py +2 -2
  144. cirq/contrib/routing/utils_test.py +3 -0
  145. cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking.py +15 -9
  146. cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking_test.py +3 -0
  147. cirq/contrib/svg/svg.py +3 -3
  148. cirq/contrib/svg/svg_test.py +8 -5
  149. cirq/devices/device.py +4 -4
  150. cirq/devices/device_test.py +7 -4
  151. cirq/devices/grid_device_metadata.py +10 -10
  152. cirq/devices/grid_device_metadata_test.py +3 -0
  153. cirq/devices/grid_qubit.py +29 -21
  154. cirq/devices/grid_qubit_test.py +3 -0
  155. cirq/devices/insertion_noise_model.py +7 -7
  156. cirq/devices/insertion_noise_model_test.py +7 -5
  157. cirq/devices/line_qubit.py +13 -13
  158. cirq/devices/line_qubit_test.py +2 -0
  159. cirq/devices/named_topologies.py +18 -29
  160. cirq/devices/named_topologies_test.py +13 -10
  161. cirq/devices/noise_model.py +3 -3
  162. cirq/devices/noise_model_test.py +19 -15
  163. cirq/devices/noise_properties.py +15 -6
  164. cirq/devices/noise_properties_test.py +34 -3
  165. cirq/devices/noise_utils.py +11 -9
  166. cirq/devices/noise_utils_test.py +2 -0
  167. cirq/devices/superconducting_qubits_noise_properties.py +23 -22
  168. cirq/devices/superconducting_qubits_noise_properties_test.py +6 -6
  169. cirq/devices/thermal_noise_model.py +107 -37
  170. cirq/devices/thermal_noise_model_test.py +21 -0
  171. cirq/devices/unconstrained_device.py +5 -3
  172. cirq/devices/unconstrained_device_test.py +2 -0
  173. cirq/experiments/__init__.py +4 -2
  174. cirq/experiments/benchmarking/__init__.py +17 -0
  175. cirq/experiments/benchmarking/parallel_xeb.py +677 -0
  176. cirq/experiments/benchmarking/parallel_xeb_test.py +447 -0
  177. cirq/experiments/fidelity_estimation.py +14 -8
  178. cirq/experiments/fidelity_estimation_test.py +3 -0
  179. cirq/experiments/n_qubit_tomography.py +17 -16
  180. cirq/experiments/n_qubit_tomography_test.py +8 -5
  181. cirq/experiments/purity_estimation.py +2 -0
  182. cirq/experiments/purity_estimation_test.py +2 -0
  183. cirq/experiments/qubit_characterizations.py +207 -103
  184. cirq/experiments/qubit_characterizations_test.py +40 -12
  185. cirq/experiments/random_quantum_circuit_generation.py +56 -70
  186. cirq/experiments/random_quantum_circuit_generation_test.py +11 -8
  187. cirq/experiments/readout_confusion_matrix.py +24 -22
  188. cirq/experiments/readout_confusion_matrix_test.py +2 -0
  189. cirq/experiments/single_qubit_readout_calibration.py +30 -15
  190. cirq/experiments/single_qubit_readout_calibration_test.py +5 -2
  191. cirq/experiments/t1_decay_experiment.py +9 -7
  192. cirq/experiments/t1_decay_experiment_test.py +13 -11
  193. cirq/experiments/t2_decay_experiment.py +16 -13
  194. cirq/experiments/t2_decay_experiment_test.py +2 -0
  195. cirq/experiments/two_qubit_xeb.py +64 -57
  196. cirq/experiments/two_qubit_xeb_test.py +10 -6
  197. cirq/experiments/xeb_fitting.py +39 -35
  198. cirq/experiments/xeb_sampling.py +37 -44
  199. cirq/experiments/xeb_sampling_test.py +3 -0
  200. cirq/experiments/xeb_simulation.py +14 -10
  201. cirq/experiments/xeb_simulation_test.py +5 -5
  202. cirq/experiments/z_phase_calibration.py +32 -29
  203. cirq/experiments/z_phase_calibration_test.py +3 -4
  204. cirq/interop/quirk/cells/__init__.py +1 -1
  205. cirq/interop/quirk/cells/all_cells.py +7 -2
  206. cirq/interop/quirk/cells/arithmetic_cells.py +29 -41
  207. cirq/interop/quirk/cells/arithmetic_cells_test.py +17 -14
  208. cirq/interop/quirk/cells/cell.py +19 -28
  209. cirq/interop/quirk/cells/cell_test.py +3 -0
  210. cirq/interop/quirk/cells/composite_cell.py +13 -28
  211. cirq/interop/quirk/cells/composite_cell_test.py +2 -0
  212. cirq/interop/quirk/cells/control_cells.py +15 -15
  213. cirq/interop/quirk/cells/control_cells_test.py +7 -5
  214. cirq/interop/quirk/cells/frequency_space_cells.py +4 -3
  215. cirq/interop/quirk/cells/frequency_space_cells_test.py +3 -1
  216. cirq/interop/quirk/cells/ignored_cells.py +3 -0
  217. cirq/interop/quirk/cells/ignored_cells_test.py +3 -1
  218. cirq/interop/quirk/cells/input_cells.py +7 -5
  219. cirq/interop/quirk/cells/input_cells_test.py +7 -5
  220. cirq/interop/quirk/cells/input_rotation_cells.py +15 -13
  221. cirq/interop/quirk/cells/input_rotation_cells_test.py +9 -7
  222. cirq/interop/quirk/cells/measurement_cells.py +5 -2
  223. cirq/interop/quirk/cells/measurement_cells_test.py +3 -1
  224. cirq/interop/quirk/cells/parse.py +22 -23
  225. cirq/interop/quirk/cells/parse_test.py +12 -10
  226. cirq/interop/quirk/cells/qubit_permutation_cells.py +5 -3
  227. cirq/interop/quirk/cells/qubit_permutation_cells_test.py +9 -7
  228. cirq/interop/quirk/cells/scalar_cells.py +4 -1
  229. cirq/interop/quirk/cells/scalar_cells_test.py +3 -1
  230. cirq/interop/quirk/cells/single_qubit_rotation_cells.py +5 -2
  231. cirq/interop/quirk/cells/single_qubit_rotation_cells_test.py +5 -3
  232. cirq/interop/quirk/cells/swap_cell.py +8 -6
  233. cirq/interop/quirk/cells/swap_cell_test.py +6 -4
  234. cirq/interop/quirk/cells/testing.py +6 -6
  235. cirq/interop/quirk/cells/testing_test.py +8 -6
  236. cirq/interop/quirk/cells/unsupported_cells.py +3 -0
  237. cirq/interop/quirk/cells/unsupported_cells_test.py +4 -2
  238. cirq/interop/quirk/url_to_circuit.py +23 -36
  239. cirq/interop/quirk/url_to_circuit_test.py +4 -1
  240. cirq/json_resolver_cache.py +14 -12
  241. cirq/linalg/__init__.py +4 -6
  242. cirq/linalg/combinators.py +7 -5
  243. cirq/linalg/combinators_test.py +10 -7
  244. cirq/linalg/decompositions.py +24 -35
  245. cirq/linalg/decompositions_test.py +3 -1
  246. cirq/linalg/diagonalize.py +6 -4
  247. cirq/linalg/diagonalize_test.py +15 -14
  248. cirq/linalg/operator_spaces.py +14 -14
  249. cirq/linalg/operator_spaces_test.py +13 -11
  250. cirq/linalg/predicates.py +18 -9
  251. cirq/linalg/predicates_test.py +5 -0
  252. cirq/linalg/tolerance.py +6 -3
  253. cirq/linalg/tolerance_test.py +6 -4
  254. cirq/linalg/transformations.py +23 -20
  255. cirq/linalg/transformations_test.py +73 -43
  256. cirq/neutral_atoms/convert_to_neutral_atom_gates.py +9 -3
  257. cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py +3 -1
  258. cirq/neutral_atoms/neutral_atom_devices.py +2 -0
  259. cirq/ops/__init__.py +2 -0
  260. cirq/ops/arithmetic_operation.py +21 -21
  261. cirq/ops/arithmetic_operation_test.py +7 -8
  262. cirq/ops/boolean_hamiltonian.py +23 -22
  263. cirq/ops/boolean_hamiltonian_test.py +12 -9
  264. cirq/ops/classically_controlled_operation.py +31 -36
  265. cirq/ops/classically_controlled_operation_test.py +121 -117
  266. cirq/ops/clifford_gate.py +98 -81
  267. cirq/ops/clifford_gate_test.py +72 -57
  268. cirq/ops/common_channels.py +44 -44
  269. cirq/ops/common_channels_test.py +83 -81
  270. cirq/ops/common_gate_families.py +9 -7
  271. cirq/ops/common_gate_families_test.py +11 -7
  272. cirq/ops/common_gates.py +164 -183
  273. cirq/ops/common_gates_test.py +135 -95
  274. cirq/ops/control_values.py +23 -26
  275. cirq/ops/control_values_test.py +22 -20
  276. cirq/ops/controlled_gate.py +64 -112
  277. cirq/ops/controlled_gate_test.py +130 -35
  278. cirq/ops/controlled_operation.py +24 -35
  279. cirq/ops/controlled_operation_test.py +8 -6
  280. cirq/ops/dense_pauli_string.py +38 -49
  281. cirq/ops/dense_pauli_string_test.py +4 -2
  282. cirq/ops/diagonal_gate.py +18 -31
  283. cirq/ops/diagonal_gate_test.py +13 -13
  284. cirq/ops/eigen_gate.py +29 -29
  285. cirq/ops/eigen_gate_test.py +45 -28
  286. cirq/ops/fourier_transform.py +14 -20
  287. cirq/ops/fourier_transform_test.py +15 -12
  288. cirq/ops/fsim_gate.py +43 -42
  289. cirq/ops/fsim_gate_test.py +29 -29
  290. cirq/ops/gate_features.py +2 -0
  291. cirq/ops/gate_features_test.py +5 -3
  292. cirq/ops/gate_operation.py +43 -65
  293. cirq/ops/gate_operation_test.py +46 -42
  294. cirq/ops/gateset.py +28 -40
  295. cirq/ops/gateset_test.py +4 -2
  296. cirq/ops/global_phase_op.py +45 -20
  297. cirq/ops/global_phase_op_test.py +44 -20
  298. cirq/ops/greedy_qubit_manager.py +10 -8
  299. cirq/ops/greedy_qubit_manager_test.py +5 -3
  300. cirq/ops/identity.py +14 -12
  301. cirq/ops/identity_test.py +24 -20
  302. cirq/ops/kraus_channel.py +11 -8
  303. cirq/ops/kraus_channel_test.py +14 -11
  304. cirq/ops/linear_combinations.py +65 -77
  305. cirq/ops/linear_combinations_test.py +14 -9
  306. cirq/ops/matrix_gates.py +21 -18
  307. cirq/ops/matrix_gates_test.py +16 -0
  308. cirq/ops/measure_util.py +15 -20
  309. cirq/ops/measure_util_test.py +2 -0
  310. cirq/ops/measurement_gate.py +26 -37
  311. cirq/ops/measurement_gate_test.py +2 -0
  312. cirq/ops/mixed_unitary_channel.py +12 -9
  313. cirq/ops/mixed_unitary_channel_test.py +14 -11
  314. cirq/ops/named_qubit.py +16 -13
  315. cirq/ops/named_qubit_test.py +15 -13
  316. cirq/ops/op_tree.py +9 -7
  317. cirq/ops/op_tree_test.py +22 -19
  318. cirq/ops/parallel_gate.py +15 -17
  319. cirq/ops/parallel_gate_test.py +18 -16
  320. cirq/ops/parity_gates.py +23 -25
  321. cirq/ops/parity_gates_test.py +36 -32
  322. cirq/ops/pauli_gates.py +22 -21
  323. cirq/ops/pauli_gates_test.py +29 -20
  324. cirq/ops/pauli_interaction_gate.py +15 -19
  325. cirq/ops/pauli_interaction_gate_test.py +10 -8
  326. cirq/ops/pauli_measurement_gate.py +23 -35
  327. cirq/ops/pauli_measurement_gate_test.py +2 -0
  328. cirq/ops/pauli_string.py +92 -120
  329. cirq/ops/pauli_string_phasor.py +52 -45
  330. cirq/ops/pauli_string_phasor_test.py +4 -5
  331. cirq/ops/pauli_string_raw_types.py +9 -7
  332. cirq/ops/pauli_string_raw_types_test.py +2 -0
  333. cirq/ops/pauli_string_test.py +31 -154
  334. cirq/ops/pauli_sum_exponential.py +12 -12
  335. cirq/ops/pauli_sum_exponential_test.py +12 -10
  336. cirq/ops/permutation_gate.py +8 -6
  337. cirq/ops/permutation_gate_test.py +10 -8
  338. cirq/ops/phased_iswap_gate.py +16 -16
  339. cirq/ops/phased_iswap_gate_test.py +17 -15
  340. cirq/ops/phased_x_gate.py +16 -17
  341. cirq/ops/phased_x_gate_test.py +18 -16
  342. cirq/ops/phased_x_z_gate.py +24 -22
  343. cirq/ops/phased_x_z_gate_test.py +17 -11
  344. cirq/ops/projector.py +16 -11
  345. cirq/ops/projector_test.py +19 -16
  346. cirq/ops/qid_util.py +7 -5
  347. cirq/ops/qid_util_test.py +2 -0
  348. cirq/ops/qubit_manager.py +11 -9
  349. cirq/ops/qubit_manager_test.py +6 -4
  350. cirq/ops/qubit_order.py +11 -14
  351. cirq/ops/qubit_order_or_list.py +4 -2
  352. cirq/ops/qubit_order_test.py +12 -10
  353. cirq/ops/random_gate_channel.py +12 -10
  354. cirq/ops/random_gate_channel_test.py +14 -11
  355. cirq/ops/raw_types.py +109 -129
  356. cirq/ops/raw_types_test.py +63 -57
  357. cirq/ops/state_preparation_channel.py +7 -7
  358. cirq/ops/state_preparation_channel_test.py +11 -9
  359. cirq/ops/swap_gates.py +13 -15
  360. cirq/ops/swap_gates_test.py +19 -17
  361. cirq/ops/tags.py +5 -3
  362. cirq/ops/tags_test.py +4 -2
  363. cirq/ops/three_qubit_gates.py +43 -76
  364. cirq/ops/three_qubit_gates_test.py +19 -17
  365. cirq/ops/two_qubit_diagonal_gate.py +13 -13
  366. cirq/ops/two_qubit_diagonal_gate_test.py +10 -8
  367. cirq/ops/uniform_superposition_gate.py +5 -3
  368. cirq/ops/uniform_superposition_gate_test.py +5 -3
  369. cirq/ops/wait_gate.py +17 -14
  370. cirq/ops/wait_gate_test.py +9 -6
  371. cirq/protocols/__init__.py +0 -3
  372. cirq/protocols/act_on_protocol.py +8 -6
  373. cirq/protocols/act_on_protocol_test.py +15 -12
  374. cirq/protocols/apply_channel_protocol.py +10 -14
  375. cirq/protocols/apply_channel_protocol_test.py +2 -0
  376. cirq/protocols/apply_mixture_protocol.py +13 -42
  377. cirq/protocols/apply_mixture_protocol_test.py +7 -5
  378. cirq/protocols/apply_unitary_protocol.py +39 -34
  379. cirq/protocols/apply_unitary_protocol_test.py +4 -1
  380. cirq/protocols/approximate_equality_protocol.py +2 -0
  381. cirq/protocols/approximate_equality_protocol_test.py +2 -0
  382. cirq/protocols/circuit_diagram_info_protocol.py +58 -42
  383. cirq/protocols/circuit_diagram_info_protocol_test.py +70 -12
  384. cirq/protocols/commutes_protocol.py +8 -7
  385. cirq/protocols/commutes_protocol_test.py +2 -0
  386. cirq/protocols/control_key_protocol.py +6 -4
  387. cirq/protocols/control_key_protocol_test.py +3 -1
  388. cirq/protocols/decompose_protocol.py +49 -48
  389. cirq/protocols/decompose_protocol_test.py +27 -16
  390. cirq/protocols/equal_up_to_global_phase_protocol.py +2 -0
  391. cirq/protocols/equal_up_to_global_phase_protocol_test.py +9 -6
  392. cirq/protocols/has_stabilizer_effect_protocol.py +7 -5
  393. cirq/protocols/has_stabilizer_effect_protocol_test.py +7 -5
  394. cirq/protocols/has_unitary_protocol.py +10 -6
  395. cirq/protocols/has_unitary_protocol_test.py +13 -8
  396. cirq/protocols/hash_from_pickle_test.py +2 -11
  397. cirq/protocols/inverse_protocol.py +13 -16
  398. cirq/protocols/inverse_protocol_test.py +5 -3
  399. cirq/protocols/json_serialization.py +35 -54
  400. cirq/protocols/json_serialization_test.py +14 -21
  401. cirq/protocols/json_test_data/CXSWAP.json +46 -0
  402. cirq/protocols/json_test_data/CXSWAP.repr +13 -0
  403. cirq/protocols/json_test_data/CZSWAP.json +46 -0
  404. cirq/protocols/json_test_data/CZSWAP.repr +13 -0
  405. cirq/protocols/json_test_data/CircuitOperation.json +6 -3
  406. cirq/protocols/json_test_data/CircuitOperation.repr_inward +4 -2
  407. cirq/protocols/json_test_data/Moment.json +24 -1
  408. cirq/protocols/json_test_data/Moment.repr +6 -1
  409. cirq/protocols/json_test_data/ThermalNoiseModel.json +32 -0
  410. cirq/protocols/json_test_data/ThermalNoiseModel.repr +1 -0
  411. cirq/protocols/json_test_data/spec.py +6 -2
  412. cirq/protocols/kraus_protocol.py +47 -7
  413. cirq/protocols/kraus_protocol_test.py +86 -12
  414. cirq/protocols/measurement_key_protocol.py +15 -16
  415. cirq/protocols/measurement_key_protocol_test.py +13 -11
  416. cirq/protocols/mixture_protocol.py +7 -5
  417. cirq/protocols/mixture_protocol_test.py +4 -2
  418. cirq/protocols/mul_protocol.py +2 -3
  419. cirq/protocols/mul_protocol_test.py +2 -0
  420. cirq/protocols/pauli_expansion_protocol.py +6 -3
  421. cirq/protocols/pauli_expansion_protocol_test.py +5 -3
  422. cirq/protocols/phase_protocol.py +2 -0
  423. cirq/protocols/phase_protocol_test.py +3 -1
  424. cirq/protocols/pow_protocol.py +11 -16
  425. cirq/protocols/pow_protocol_test.py +2 -0
  426. cirq/protocols/qasm.py +14 -20
  427. cirq/protocols/qasm_test.py +6 -3
  428. cirq/protocols/qid_shape_protocol.py +8 -8
  429. cirq/protocols/qid_shape_protocol_test.py +3 -1
  430. cirq/protocols/resolve_parameters.py +5 -3
  431. cirq/protocols/resolve_parameters_test.py +8 -7
  432. cirq/protocols/trace_distance_bound.py +6 -4
  433. cirq/protocols/trace_distance_bound_test.py +3 -1
  434. cirq/protocols/unitary_protocol.py +17 -7
  435. cirq/protocols/unitary_protocol_test.py +12 -2
  436. cirq/qis/channels.py +6 -2
  437. cirq/qis/channels_test.py +20 -16
  438. cirq/qis/clifford_tableau.py +21 -19
  439. cirq/qis/clifford_tableau_test.py +2 -2
  440. cirq/qis/entropy.py +14 -3
  441. cirq/qis/entropy_test.py +3 -1
  442. cirq/qis/measures.py +13 -13
  443. cirq/qis/measures_test.py +20 -14
  444. cirq/qis/noise_utils.py +2 -0
  445. cirq/qis/noise_utils_test.py +9 -7
  446. cirq/qis/quantum_state_representation.py +7 -8
  447. cirq/qis/states.py +58 -56
  448. cirq/qis/states_test.py +2 -0
  449. cirq/sim/classical_simulator.py +23 -22
  450. cirq/sim/classical_simulator_test.py +2 -0
  451. cirq/sim/clifford/clifford_simulator.py +23 -21
  452. cirq/sim/clifford/clifford_simulator_test.py +7 -4
  453. cirq/sim/clifford/clifford_tableau_simulation_state.py +10 -7
  454. cirq/sim/clifford/clifford_tableau_simulation_state_test.py +5 -5
  455. cirq/sim/clifford/stabilizer_ch_form_simulation_state.py +8 -6
  456. cirq/sim/clifford/stabilizer_ch_form_simulation_state_test.py +8 -6
  457. cirq/sim/clifford/stabilizer_sampler.py +9 -7
  458. cirq/sim/clifford/stabilizer_sampler_test.py +4 -2
  459. cirq/sim/clifford/stabilizer_simulation_state.py +14 -13
  460. cirq/sim/clifford/stabilizer_simulation_state_test.py +6 -4
  461. cirq/sim/clifford/stabilizer_state_ch_form.py +13 -11
  462. cirq/sim/clifford/stabilizer_state_ch_form_test.py +4 -2
  463. cirq/sim/density_matrix_simulation_state.py +26 -27
  464. cirq/sim/density_matrix_simulation_state_test.py +10 -8
  465. cirq/sim/density_matrix_simulator.py +30 -28
  466. cirq/sim/density_matrix_simulator_test.py +48 -48
  467. cirq/sim/density_matrix_utils.py +13 -11
  468. cirq/sim/density_matrix_utils_test.py +38 -36
  469. cirq/sim/mux.py +33 -31
  470. cirq/sim/mux_test.py +3 -0
  471. cirq/sim/simulation_product_state.py +15 -15
  472. cirq/sim/simulation_product_state_test.py +29 -26
  473. cirq/sim/simulation_state.py +29 -38
  474. cirq/sim/simulation_state_base.py +21 -32
  475. cirq/sim/simulation_state_test.py +15 -13
  476. cirq/sim/simulation_utils.py +5 -2
  477. cirq/sim/simulation_utils_test.py +5 -2
  478. cirq/sim/simulator.py +90 -106
  479. cirq/sim/simulator_base.py +33 -45
  480. cirq/sim/simulator_base_test.py +20 -15
  481. cirq/sim/simulator_test.py +23 -14
  482. cirq/sim/sparse_simulator.py +19 -17
  483. cirq/sim/sparse_simulator_test.py +41 -40
  484. cirq/sim/state_vector.py +15 -12
  485. cirq/sim/state_vector_simulation_state.py +31 -31
  486. cirq/sim/state_vector_simulation_state_test.py +16 -14
  487. cirq/sim/state_vector_simulator.py +17 -14
  488. cirq/sim/state_vector_simulator_test.py +2 -0
  489. cirq/sim/state_vector_test.py +6 -3
  490. cirq/study/flatten_expressions.py +16 -15
  491. cirq/study/flatten_expressions_test.py +13 -11
  492. cirq/study/resolver.py +18 -17
  493. cirq/study/resolver_test.py +22 -20
  494. cirq/study/result.py +17 -27
  495. cirq/study/result_test.py +2 -0
  496. cirq/study/sweepable.py +12 -10
  497. cirq/study/sweepable_test.py +3 -0
  498. cirq/study/sweeps.py +42 -61
  499. cirq/study/sweeps_test.py +33 -0
  500. cirq/testing/__init__.py +7 -11
  501. cirq/testing/_compat_test_data/module_a/__init__.py +1 -0
  502. cirq/testing/_compat_test_data/module_a/module_b/__init__.py +1 -0
  503. cirq/testing/_compat_test_data/module_a/sub/__init__.py +1 -0
  504. cirq/testing/circuit_compare.py +8 -17
  505. cirq/testing/circuit_compare_test.py +2 -0
  506. cirq/testing/consistent_act_on.py +13 -11
  507. cirq/testing/consistent_act_on_test.py +5 -3
  508. cirq/testing/consistent_channels.py +2 -0
  509. cirq/testing/consistent_channels_test.py +10 -8
  510. cirq/testing/consistent_controlled_gate_op.py +5 -5
  511. cirq/testing/consistent_controlled_gate_op_test.py +18 -18
  512. cirq/testing/consistent_decomposition.py +2 -2
  513. cirq/testing/consistent_decomposition_test.py +4 -2
  514. cirq/testing/consistent_pauli_expansion.py +2 -0
  515. cirq/testing/consistent_pauli_expansion_test.py +3 -1
  516. cirq/testing/consistent_phase_by.py +2 -0
  517. cirq/testing/consistent_phase_by_test.py +3 -1
  518. cirq/testing/consistent_protocols.py +14 -20
  519. cirq/testing/consistent_protocols_test.py +13 -11
  520. cirq/testing/consistent_qasm.py +6 -4
  521. cirq/testing/consistent_qasm_test.py +7 -7
  522. cirq/testing/consistent_resolve_parameters.py +2 -0
  523. cirq/testing/consistent_specified_has_unitary.py +2 -2
  524. cirq/testing/consistent_specified_has_unitary_test.py +6 -4
  525. cirq/testing/consistent_unitary.py +1 -0
  526. cirq/testing/consistent_unitary_test.py +4 -2
  527. cirq/testing/deprecation.py +5 -2
  528. cirq/testing/deprecation_test.py +5 -2
  529. cirq/testing/devices.py +7 -4
  530. cirq/testing/devices_test.py +7 -4
  531. cirq/testing/equals_tester.py +4 -2
  532. cirq/testing/equals_tester_test.py +21 -17
  533. cirq/testing/equivalent_basis_map.py +6 -4
  534. cirq/testing/equivalent_basis_map_test.py +6 -4
  535. cirq/testing/equivalent_repr_eval.py +6 -4
  536. cirq/testing/equivalent_repr_eval_test.py +5 -3
  537. cirq/testing/gate_features.py +2 -0
  538. cirq/testing/gate_features_test.py +7 -5
  539. cirq/testing/json.py +19 -15
  540. cirq/testing/json_test.py +5 -3
  541. cirq/testing/lin_alg_utils.py +10 -11
  542. cirq/testing/lin_alg_utils_test.py +14 -12
  543. cirq/testing/logs.py +7 -6
  544. cirq/testing/logs_test.py +9 -7
  545. cirq/testing/no_identifier_qubit.py +4 -2
  546. cirq/testing/no_identifier_qubit_test.py +5 -3
  547. cirq/testing/op_tree.py +2 -0
  548. cirq/testing/op_tree_test.py +4 -1
  549. cirq/testing/order_tester.py +2 -0
  550. cirq/testing/order_tester_test.py +8 -6
  551. cirq/testing/pytest_utils.py +2 -0
  552. cirq/testing/pytest_utils_test.py +4 -2
  553. cirq/testing/random_circuit.py +21 -20
  554. cirq/testing/random_circuit_test.py +12 -9
  555. cirq/testing/repr_pretty_tester.py +1 -0
  556. cirq/testing/repr_pretty_tester_test.py +5 -3
  557. cirq/testing/routing_devices.py +4 -1
  558. cirq/testing/routing_devices_test.py +9 -6
  559. cirq/testing/sample_circuits.py +4 -1
  560. cirq/testing/sample_circuits_test.py +3 -1
  561. cirq/testing/sample_gates.py +3 -0
  562. cirq/testing/sample_gates_test.py +5 -2
  563. cirq/transformers/__init__.py +11 -4
  564. cirq/transformers/align.py +9 -7
  565. cirq/transformers/align_test.py +2 -0
  566. cirq/transformers/analytical_decompositions/__init__.py +3 -6
  567. cirq/transformers/analytical_decompositions/clifford_decomposition.py +18 -16
  568. cirq/transformers/analytical_decompositions/clifford_decomposition_test.py +2 -0
  569. cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +19 -16
  570. cirq/transformers/analytical_decompositions/controlled_gate_decomposition_test.py +2 -0
  571. cirq/transformers/analytical_decompositions/cphase_to_fsim.py +11 -9
  572. cirq/transformers/analytical_decompositions/cphase_to_fsim_test.py +5 -3
  573. cirq/transformers/analytical_decompositions/pauli_string_decomposition.py +5 -3
  574. cirq/transformers/analytical_decompositions/pauli_string_decomposition_test.py +5 -3
  575. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +141 -44
  576. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +35 -1
  577. cirq/transformers/analytical_decompositions/single_qubit_decompositions.py +8 -7
  578. cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py +2 -0
  579. cirq/transformers/analytical_decompositions/single_to_two_qubit_isometry.py +7 -4
  580. cirq/transformers/analytical_decompositions/single_to_two_qubit_isometry_test.py +3 -0
  581. cirq/transformers/analytical_decompositions/three_qubit_decomposition.py +11 -19
  582. cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +8 -33
  583. cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py +9 -11
  584. cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +2 -0
  585. cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +91 -27
  586. cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +36 -7
  587. cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py +20 -21
  588. cirq/transformers/analytical_decompositions/two_qubit_to_fsim_test.py +8 -6
  589. cirq/transformers/analytical_decompositions/two_qubit_to_ms.py +13 -15
  590. cirq/transformers/analytical_decompositions/two_qubit_to_ms_test.py +3 -1
  591. cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap.py +39 -41
  592. cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py +2 -0
  593. cirq/transformers/drop_empty_moments.py +5 -3
  594. cirq/transformers/drop_empty_moments_test.py +4 -2
  595. cirq/transformers/drop_negligible_operations.py +7 -5
  596. cirq/transformers/drop_negligible_operations_test.py +2 -0
  597. cirq/transformers/dynamical_decoupling.py +49 -42
  598. cirq/transformers/dynamical_decoupling_test.py +223 -205
  599. cirq/transformers/eject_phased_paulis.py +28 -26
  600. cirq/transformers/eject_phased_paulis_test.py +12 -9
  601. cirq/transformers/eject_z.py +12 -12
  602. cirq/transformers/eject_z_test.py +2 -2
  603. cirq/transformers/expand_composite.py +6 -4
  604. cirq/transformers/expand_composite_test.py +3 -1
  605. cirq/transformers/gauge_compiling/__init__.py +3 -1
  606. cirq/transformers/gauge_compiling/cphase_gauge.py +2 -0
  607. cirq/transformers/gauge_compiling/cphase_gauge_test.py +2 -0
  608. cirq/transformers/gauge_compiling/cz_gauge.py +2 -0
  609. cirq/transformers/gauge_compiling/cz_gauge_test.py +1 -0
  610. cirq/transformers/gauge_compiling/gauge_compiling.py +45 -41
  611. cirq/transformers/gauge_compiling/gauge_compiling_test.py +2 -0
  612. cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +1 -0
  613. cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py +5 -1
  614. cirq/transformers/gauge_compiling/iswap_gauge.py +2 -0
  615. cirq/transformers/gauge_compiling/iswap_gauge_test.py +1 -0
  616. cirq/transformers/gauge_compiling/spin_inversion_gauge.py +2 -0
  617. cirq/transformers/gauge_compiling/spin_inversion_gauge_test.py +2 -0
  618. cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +7 -6
  619. cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py +2 -0
  620. cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py +2 -0
  621. cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py +2 -0
  622. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +6 -3
  623. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py +3 -0
  624. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +12 -9
  625. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation_test.py +9 -7
  626. cirq/transformers/insertion_sort.py +8 -6
  627. cirq/transformers/insertion_sort_test.py +3 -1
  628. cirq/transformers/measurement_transformers.py +29 -29
  629. cirq/transformers/measurement_transformers_test.py +2 -0
  630. cirq/transformers/merge_k_qubit_gates.py +12 -10
  631. cirq/transformers/merge_k_qubit_gates_test.py +18 -18
  632. cirq/transformers/merge_single_qubit_gates.py +197 -20
  633. cirq/transformers/merge_single_qubit_gates_test.py +177 -5
  634. cirq/transformers/noise_adding.py +5 -3
  635. cirq/transformers/noise_adding_test.py +2 -0
  636. cirq/transformers/optimize_for_target_gateset.py +19 -17
  637. cirq/transformers/optimize_for_target_gateset_test.py +11 -8
  638. cirq/transformers/qubit_management_transformers.py +13 -11
  639. cirq/transformers/qubit_management_transformers_test.py +5 -3
  640. cirq/transformers/randomized_measurements.py +16 -14
  641. cirq/transformers/randomized_measurements_test.py +10 -4
  642. cirq/transformers/routing/initial_mapper.py +6 -4
  643. cirq/transformers/routing/initial_mapper_test.py +2 -0
  644. cirq/transformers/routing/line_initial_mapper.py +16 -14
  645. cirq/transformers/routing/line_initial_mapper_test.py +9 -7
  646. cirq/transformers/routing/mapping_manager.py +10 -10
  647. cirq/transformers/routing/mapping_manager_test.py +2 -0
  648. cirq/transformers/routing/route_circuit_cqc.py +33 -31
  649. cirq/transformers/routing/route_circuit_cqc_test.py +15 -13
  650. cirq/transformers/routing/visualize_routed_circuit.py +8 -7
  651. cirq/transformers/routing/visualize_routed_circuit_test.py +4 -2
  652. cirq/transformers/stratify.py +17 -15
  653. cirq/transformers/stratify_test.py +3 -0
  654. cirq/transformers/symbolize.py +103 -0
  655. cirq/transformers/symbolize_test.py +62 -0
  656. cirq/transformers/synchronize_terminal_measurements.py +10 -10
  657. cirq/transformers/synchronize_terminal_measurements_test.py +12 -10
  658. cirq/transformers/tag_transformers.py +97 -0
  659. cirq/transformers/tag_transformers_test.py +103 -0
  660. cirq/transformers/target_gatesets/compilation_target_gateset.py +21 -19
  661. cirq/transformers/target_gatesets/compilation_target_gateset_test.py +20 -16
  662. cirq/transformers/target_gatesets/cz_gateset.py +7 -5
  663. cirq/transformers/target_gatesets/cz_gateset_test.py +21 -19
  664. cirq/transformers/target_gatesets/sqrt_iswap_gateset.py +9 -7
  665. cirq/transformers/target_gatesets/sqrt_iswap_gateset_test.py +25 -25
  666. cirq/transformers/transformer_api.py +34 -47
  667. cirq/transformers/transformer_api_test.py +9 -8
  668. cirq/transformers/transformer_primitives.py +39 -49
  669. cirq/transformers/transformer_primitives_test.py +10 -17
  670. cirq/value/abc_alt.py +6 -4
  671. cirq/value/abc_alt_test.py +5 -3
  672. cirq/value/angle.py +11 -12
  673. cirq/value/angle_test.py +5 -3
  674. cirq/value/classical_data.py +27 -27
  675. cirq/value/classical_data_test.py +11 -8
  676. cirq/value/condition.py +26 -24
  677. cirq/value/condition_test.py +2 -0
  678. cirq/value/digits.py +14 -11
  679. cirq/value/digits_test.py +2 -0
  680. cirq/value/duration.py +23 -20
  681. cirq/value/duration_test.py +2 -0
  682. cirq/value/linear_dict.py +25 -30
  683. cirq/value/linear_dict_test.py +10 -8
  684. cirq/value/measurement_key.py +12 -12
  685. cirq/value/measurement_key_test.py +2 -0
  686. cirq/value/periodic_value.py +4 -4
  687. cirq/value/periodic_value_test.py +11 -7
  688. cirq/value/probability.py +3 -1
  689. cirq/value/probability_test.py +4 -2
  690. cirq/value/product_state.py +15 -13
  691. cirq/value/product_state_test.py +4 -1
  692. cirq/value/random_state.py +2 -0
  693. cirq/value/random_state_test.py +5 -3
  694. cirq/value/timestamp.py +11 -7
  695. cirq/value/timestamp_test.py +14 -12
  696. cirq/value/type_alias.py +4 -4
  697. cirq/value/value_equality_attr.py +8 -9
  698. cirq/value/value_equality_attr_test.py +14 -11
  699. cirq/vis/density_matrix.py +3 -3
  700. cirq/vis/density_matrix_test.py +20 -17
  701. cirq/vis/heatmap.py +24 -37
  702. cirq/vis/heatmap_test.py +3 -0
  703. cirq/vis/histogram.py +9 -6
  704. cirq/vis/histogram_test.py +5 -2
  705. cirq/vis/state_histogram.py +10 -8
  706. cirq/vis/state_histogram_test.py +7 -5
  707. cirq/vis/vis_utils.py +4 -1
  708. cirq/vis/vis_utils_test.py +4 -1
  709. cirq/work/collector.py +12 -18
  710. cirq/work/collector_test.py +15 -10
  711. cirq/work/observable_grouping.py +6 -7
  712. cirq/work/observable_grouping_test.py +10 -9
  713. cirq/work/observable_measurement.py +47 -45
  714. cirq/work/observable_measurement_data.py +22 -17
  715. cirq/work/observable_measurement_data_test.py +4 -1
  716. cirq/work/observable_measurement_test.py +48 -29
  717. cirq/work/observable_readout_calibration.py +5 -2
  718. cirq/work/observable_readout_calibration_test.py +5 -2
  719. cirq/work/observable_settings.py +13 -22
  720. cirq/work/observable_settings_test.py +9 -7
  721. cirq/work/pauli_sum_collector.py +12 -10
  722. cirq/work/pauli_sum_collector_test.py +9 -9
  723. cirq/work/sampler.py +42 -43
  724. cirq/work/sampler_test.py +31 -24
  725. cirq/work/zeros_sampler.py +6 -4
  726. cirq/work/zeros_sampler_test.py +7 -5
  727. {cirq_core-1.5.0.dev20250409225226.dist-info → cirq_core-1.6.0.dist-info}/METADATA +7 -8
  728. cirq_core-1.6.0.dist-info/RECORD +1241 -0
  729. {cirq_core-1.5.0.dev20250409225226.dist-info → cirq_core-1.6.0.dist-info}/WHEEL +1 -1
  730. cirq_core-1.5.0.dev20250409225226.dist-info/RECORD +0 -1216
  731. {cirq_core-1.5.0.dev20250409225226.dist-info → cirq_core-1.6.0.dist-info}/licenses/LICENSE +0 -0
  732. {cirq_core-1.5.0.dev20250409225226.dist-info → cirq_core-1.6.0.dist-info}/top_level.txt +0 -0
cirq/circuits/circuit.py CHANGED
@@ -33,19 +33,13 @@ from typing import (
33
33
  Any,
34
34
  Callable,
35
35
  cast,
36
- Dict,
37
- FrozenSet,
36
+ Hashable,
38
37
  Iterable,
39
38
  Iterator,
40
- List,
41
39
  Mapping,
42
40
  MutableSequence,
43
- Optional,
44
41
  overload,
45
42
  Sequence,
46
- Set,
47
- Tuple,
48
- Type,
49
43
  TYPE_CHECKING,
50
44
  TypeVar,
51
45
  Union,
@@ -93,7 +87,7 @@ document(
93
87
  """,
94
88
  )
95
89
 
96
- _INT_TYPE = Union[int, np.integer]
90
+ _INT_TYPE = int | np.integer
97
91
 
98
92
 
99
93
  class Alignment(enum.Enum):
@@ -148,7 +142,9 @@ class AbstractCircuit(abc.ABC):
148
142
  """
149
143
 
150
144
  @classmethod
151
- def from_moments(cls: Type[CIRCUIT_TYPE], *moments: Optional[cirq.OP_TREE]) -> CIRCUIT_TYPE:
145
+ def from_moments(
146
+ cls: type[CIRCUIT_TYPE], *moments: cirq.OP_TREE | None, tags: Sequence[Hashable] = ()
147
+ ) -> CIRCUIT_TYPE:
152
148
  """Create a circuit from moment op trees.
153
149
 
154
150
  Args:
@@ -162,11 +158,15 @@ class AbstractCircuit(abc.ABC):
162
158
  which is then included in the new circuit. Note that in this
163
159
  case we have the normal restriction that operations in a
164
160
  moment must be applied to disjoint sets of qubits.
161
+ tags: A sequence of any type of object that is useful to attach metadata
162
+ to this circuit as long as the type is hashable. If you wish the
163
+ resulting circuit to be eventually serialized into JSON, you should
164
+ also restrict the tags to be JSON serializable.
165
165
  """
166
- return cls._from_moments(cls._make_moments(moments))
166
+ return cls._from_moments(cls._make_moments(moments), tags=tags)
167
167
 
168
168
  @staticmethod
169
- def _make_moments(moments: Iterable[Optional[cirq.OP_TREE]]) -> Iterator[cirq.Moment]:
169
+ def _make_moments(moments: Iterable[cirq.OP_TREE | None]) -> Iterator[cirq.Moment]:
170
170
  for m in moments:
171
171
  if isinstance(m, Moment):
172
172
  yield m
@@ -177,7 +177,9 @@ class AbstractCircuit(abc.ABC):
177
177
 
178
178
  @classmethod
179
179
  @abc.abstractmethod
180
- def _from_moments(cls: Type[CIRCUIT_TYPE], moments: Iterable[cirq.Moment]) -> CIRCUIT_TYPE:
180
+ def _from_moments(
181
+ cls: type[CIRCUIT_TYPE], moments: Iterable[cirq.Moment], tags: Sequence[Hashable]
182
+ ) -> CIRCUIT_TYPE:
181
183
  """Create a circuit from moments.
182
184
 
183
185
  This must be implemented by subclasses. It provides a more efficient way
@@ -208,6 +210,20 @@ class AbstractCircuit(abc.ABC):
208
210
  copy: If True and 'self' is a Circuit, returns a copy that circuit.
209
211
  """
210
212
 
213
+ @property
214
+ @abc.abstractmethod
215
+ def tags(self) -> tuple[Hashable, ...]:
216
+ """Returns a tuple of the Circuit's tags."""
217
+
218
+ @abc.abstractmethod
219
+ def with_tags(self, *new_tags: Hashable) -> Self:
220
+ """Creates a new tagged Circuit with `self.tags` and `new_tags` combined."""
221
+
222
+ @property
223
+ def untagged(self) -> Self:
224
+ """Returns the underlying Circuit without any tags."""
225
+ return self._from_moments(self.moments, tags=()) if self.tags else self
226
+
211
227
  def __bool__(self) -> bool:
212
228
  return bool(self.moments)
213
229
 
@@ -217,14 +233,16 @@ class AbstractCircuit(abc.ABC):
217
233
  return other is self or (
218
234
  len(self.moments) == len(other.moments)
219
235
  and all(m0 == m1 for m0, m1 in zip(self.moments, other.moments))
236
+ and self.tags == other.tags
220
237
  )
221
238
 
222
239
  def _approx_eq_(self, other: Any, atol: float) -> bool:
223
240
  """See `cirq.protocols.SupportsApproximateEquality`."""
224
241
  if not isinstance(other, AbstractCircuit):
225
242
  return NotImplemented
226
- return other is self or cirq.protocols.approx_eq(
227
- tuple(self.moments), tuple(other.moments), atol=atol
243
+ return other is self or (
244
+ self.tags == other.tags
245
+ and cirq.protocols.approx_eq(tuple(self.moments), tuple(other.moments), atol=atol)
228
246
  )
229
247
 
230
248
  def __ne__(self, other) -> bool:
@@ -240,17 +258,16 @@ class AbstractCircuit(abc.ABC):
240
258
  """See `cirq.SupportsDecompose`."""
241
259
  return self.all_operations()
242
260
 
243
- # pylint: disable=function-redefined
244
261
  @overload
245
262
  def __getitem__(self, key: int) -> cirq.Moment:
246
263
  pass
247
264
 
248
265
  @overload
249
- def __getitem__(self, key: Tuple[int, cirq.Qid]) -> cirq.Operation:
266
+ def __getitem__(self, key: tuple[int, cirq.Qid]) -> cirq.Operation:
250
267
  pass
251
268
 
252
269
  @overload
253
- def __getitem__(self, key: Tuple[int, Iterable[cirq.Qid]]) -> cirq.Moment:
270
+ def __getitem__(self, key: tuple[int, Iterable[cirq.Qid]]) -> cirq.Moment:
254
271
  pass
255
272
 
256
273
  @overload
@@ -258,16 +275,16 @@ class AbstractCircuit(abc.ABC):
258
275
  pass
259
276
 
260
277
  @overload
261
- def __getitem__(self, key: Tuple[slice, cirq.Qid]) -> Self:
278
+ def __getitem__(self, key: tuple[slice, cirq.Qid]) -> Self:
262
279
  pass
263
280
 
264
281
  @overload
265
- def __getitem__(self, key: Tuple[slice, Iterable[cirq.Qid]]) -> Self:
282
+ def __getitem__(self, key: tuple[slice, Iterable[cirq.Qid]]) -> Self:
266
283
  pass
267
284
 
268
285
  def __getitem__(self, key):
269
286
  if isinstance(key, slice):
270
- return self._from_moments(self.moments[key])
287
+ return self._from_moments(self.moments[key], tags=self.tags)
271
288
  if hasattr(key, '__index__'):
272
289
  return self.moments[key]
273
290
  if isinstance(key, tuple):
@@ -280,12 +297,12 @@ class AbstractCircuit(abc.ABC):
280
297
  return selected_moments[qubit_idx]
281
298
  if isinstance(qubit_idx, ops.Qid):
282
299
  qubit_idx = [qubit_idx]
283
- return self._from_moments(moment[qubit_idx] for moment in selected_moments)
300
+ return self._from_moments(
301
+ (moment[qubit_idx] for moment in selected_moments), tags=self.tags
302
+ )
284
303
 
285
304
  raise TypeError('__getitem__ called with key not of type slice, int, or tuple.')
286
305
 
287
- # pylint: enable=function-redefined
288
-
289
306
  def __str__(self) -> str:
290
307
  return self.to_text_diagram()
291
308
 
@@ -293,7 +310,9 @@ class AbstractCircuit(abc.ABC):
293
310
  args = []
294
311
  if self.moments:
295
312
  args.append(_list_repr_with_indented_item_lines(self.moments))
296
- return f'{", ".join(args)}'
313
+ moments_repr = f'{", ".join(args)}'
314
+ tag_repr = ','.join(_compat.proper_repr(t) for t in self.tags)
315
+ return f'{moments_repr}, tags=[{tag_repr}]' if self.tags else moments_repr
297
316
 
298
317
  def __repr__(self) -> str:
299
318
  cls_name = self.__class__.__name__
@@ -318,7 +337,7 @@ class AbstractCircuit(abc.ABC):
318
337
 
319
338
  def _first_moment_operating_on(
320
339
  self, qubits: Iterable[cirq.Qid], indices: Iterable[int]
321
- ) -> Optional[int]:
340
+ ) -> int | None:
322
341
  qubits = frozenset(qubits)
323
342
  for m in indices:
324
343
  if self._has_op_at(m, qubits):
@@ -329,8 +348,8 @@ class AbstractCircuit(abc.ABC):
329
348
  self,
330
349
  qubits: Iterable[cirq.Qid],
331
350
  start_moment_index: int = 0,
332
- max_distance: Optional[int] = None,
333
- ) -> Optional[int]:
351
+ max_distance: int | None = None,
352
+ ) -> int | None:
334
353
  """Finds the index of the next moment that touches the given qubits.
335
354
 
336
355
  Args:
@@ -360,7 +379,7 @@ class AbstractCircuit(abc.ABC):
360
379
 
361
380
  def next_moments_operating_on(
362
381
  self, qubits: Iterable[cirq.Qid], start_moment_index: int = 0
363
- ) -> Dict[cirq.Qid, int]:
382
+ ) -> dict[cirq.Qid, int]:
364
383
  """Finds the index of the next moment that touches each qubit.
365
384
 
366
385
  Args:
@@ -383,9 +402,9 @@ class AbstractCircuit(abc.ABC):
383
402
  def prev_moment_operating_on(
384
403
  self,
385
404
  qubits: Sequence[cirq.Qid],
386
- end_moment_index: Optional[int] = None,
387
- max_distance: Optional[int] = None,
388
- ) -> Optional[int]:
405
+ end_moment_index: int | None = None,
406
+ max_distance: int | None = None,
407
+ ) -> int | None:
389
408
  """Finds the index of the previous moment that touches the given qubits.
390
409
 
391
410
  Args:
@@ -427,10 +446,10 @@ class AbstractCircuit(abc.ABC):
427
446
 
428
447
  def reachable_frontier_from(
429
448
  self,
430
- start_frontier: Dict[cirq.Qid, int],
449
+ start_frontier: dict[cirq.Qid, int],
431
450
  *,
432
451
  is_blocker: Callable[[cirq.Operation], bool] = lambda op: False,
433
- ) -> Dict[cirq.Qid, int]:
452
+ ) -> dict[cirq.Qid, int]:
434
453
  """Determines how far can be reached into a circuit under certain rules.
435
454
 
436
455
  The location L = (qubit, moment_index) is *reachable* if and only if the
@@ -566,7 +585,7 @@ class AbstractCircuit(abc.ABC):
566
585
  where i is the moment index, q is the qubit, and end_frontier is the
567
586
  result of this method.
568
587
  """
569
- active: Set[cirq.Qid] = set()
588
+ active: set[cirq.Qid] = set()
570
589
  end_frontier = {}
571
590
  queue = BucketPriorityQueue[ops.Operation](drop_duplicate_entries=True)
572
591
 
@@ -610,10 +629,10 @@ class AbstractCircuit(abc.ABC):
610
629
 
611
630
  def findall_operations_between(
612
631
  self,
613
- start_frontier: Dict[cirq.Qid, int],
614
- end_frontier: Dict[cirq.Qid, int],
632
+ start_frontier: dict[cirq.Qid, int],
633
+ end_frontier: dict[cirq.Qid, int],
615
634
  omit_crossing_operations: bool = False,
616
- ) -> List[Tuple[int, cirq.Operation]]:
635
+ ) -> list[tuple[int, cirq.Operation]]:
617
636
  """Finds operations between the two given frontiers.
618
637
 
619
638
  If a qubit is in `start_frontier` but not `end_frontier`, its end index
@@ -658,9 +677,9 @@ class AbstractCircuit(abc.ABC):
658
677
 
659
678
  def findall_operations_until_blocked(
660
679
  self,
661
- start_frontier: Dict[cirq.Qid, int],
680
+ start_frontier: dict[cirq.Qid, int],
662
681
  is_blocker: Callable[[cirq.Operation], bool] = lambda op: False,
663
- ) -> List[Tuple[int, cirq.Operation]]:
682
+ ) -> list[tuple[int, cirq.Operation]]:
664
683
  """Finds all operations until a blocking operation is hit.
665
684
 
666
685
  An operation is considered blocking if both of the following hold:
@@ -746,11 +765,11 @@ class AbstractCircuit(abc.ABC):
746
765
  and the second item is the operation itself.
747
766
 
748
767
  """
749
- op_list: List[Tuple[int, ops.Operation]] = []
768
+ op_list: list[tuple[int, ops.Operation]] = []
750
769
  if not start_frontier:
751
770
  return op_list
752
771
  start_index = min(start_frontier.values())
753
- blocked_qubits: Set[cirq.Qid] = set()
772
+ blocked_qubits: set[cirq.Qid] = set()
754
773
  for index, moment in enumerate(self[start_index:], start_index):
755
774
  active_qubits = set(q for q, s in start_frontier.items() if s <= index)
756
775
  for op in moment.operations:
@@ -762,7 +781,7 @@ class AbstractCircuit(abc.ABC):
762
781
  break
763
782
  return op_list
764
783
 
765
- def operation_at(self, qubit: cirq.Qid, moment_index: int) -> Optional[cirq.Operation]:
784
+ def operation_at(self, qubit: cirq.Qid, moment_index: int) -> cirq.Operation | None:
766
785
  """Finds the operation on a qubit within a moment, if any.
767
786
 
768
787
  Args:
@@ -780,7 +799,7 @@ class AbstractCircuit(abc.ABC):
780
799
 
781
800
  def findall_operations(
782
801
  self, predicate: Callable[[cirq.Operation], bool]
783
- ) -> Iterable[Tuple[int, cirq.Operation]]:
802
+ ) -> Iterable[tuple[int, cirq.Operation]]:
784
803
  """Find the locations of all operations that satisfy a given condition.
785
804
 
786
805
  This returns an iterator of (index, operation) tuples where each
@@ -800,8 +819,8 @@ class AbstractCircuit(abc.ABC):
800
819
  yield index, op
801
820
 
802
821
  def findall_operations_with_gate_type(
803
- self, gate_type: Type[_TGate]
804
- ) -> Iterable[Tuple[int, cirq.GateOperation, _TGate]]:
822
+ self, gate_type: type[_TGate]
823
+ ) -> Iterable[tuple[int, cirq.GateOperation, _TGate]]:
805
824
  """Find the locations of all gate operations of a given type.
806
825
 
807
826
  Args:
@@ -817,7 +836,7 @@ class AbstractCircuit(abc.ABC):
817
836
  gate_op = cast(ops.GateOperation, op)
818
837
  yield index, gate_op, cast(_TGate, gate_op.gate)
819
838
 
820
- def has_measurements(self):
839
+ def has_measurements(self) -> bool:
821
840
  """Returns whether or not this circuit has measurements.
822
841
 
823
842
  Returns: True if `cirq.is_measurement(self)` is True otherwise False.
@@ -922,10 +941,10 @@ class AbstractCircuit(abc.ABC):
922
941
  qubits
923
942
  )
924
943
 
925
- def all_qubits(self) -> FrozenSet[cirq.Qid]:
944
+ def all_qubits(self) -> frozenset[cirq.Qid]:
926
945
  """Returns the qubits acted upon by Operations in this circuit.
927
946
 
928
- Returns: FrozenSet of `cirq.Qid` objects acted on by all operations
947
+ Returns: frozenset of `cirq.Qid` objects acted on by all operations
929
948
  in this circuit.
930
949
  """
931
950
  return frozenset(q for m in self.moments for q in m.qubits)
@@ -952,11 +971,13 @@ class AbstractCircuit(abc.ABC):
952
971
  """Apply func to expand each op into a circuit, then zip up the circuits."""
953
972
  return Circuit.zip(*[Circuit(func(op)) for op in moment])
954
973
 
955
- return self._from_moments(m for moment in self for m in map_moment(moment))
974
+ return self._from_moments(
975
+ (m for moment in self for m in map_moment(moment)), tags=self.tags
976
+ )
956
977
 
957
978
  def qid_shape(
958
979
  self, qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT
959
- ) -> Tuple[int, ...]:
980
+ ) -> tuple[int, ...]:
960
981
  """Get the qubit shapes of all qubits in this circuit.
961
982
 
962
983
  Returns: A tuple containing the dimensions (shape) of all qudits
@@ -965,56 +986,60 @@ class AbstractCircuit(abc.ABC):
965
986
  qids = ops.QubitOrder.as_qubit_order(qubit_order).order_for(self.all_qubits())
966
987
  return protocols.qid_shape(qids)
967
988
 
968
- def all_measurement_key_objs(self) -> FrozenSet[cirq.MeasurementKey]:
989
+ def all_measurement_key_objs(self) -> frozenset[cirq.MeasurementKey]:
969
990
  return frozenset(
970
991
  key for op in self.all_operations() for key in protocols.measurement_key_objs(op)
971
992
  )
972
993
 
973
- def _measurement_key_objs_(self) -> FrozenSet[cirq.MeasurementKey]:
994
+ def _measurement_key_objs_(self) -> frozenset[cirq.MeasurementKey]:
974
995
  """Returns the set of all measurement keys in this circuit.
975
996
 
976
- Returns: FrozenSet of `cirq.MeasurementKey` objects that are
997
+ Returns: frozenset of `cirq.MeasurementKey` objects that are
977
998
  in this circuit.
978
999
  """
979
1000
  return self.all_measurement_key_objs()
980
1001
 
981
- def all_measurement_key_names(self) -> FrozenSet[str]:
1002
+ def all_measurement_key_names(self) -> frozenset[str]:
982
1003
  """Returns the set of all measurement key names in this circuit.
983
1004
 
984
- Returns: FrozenSet of strings that are the measurement key
1005
+ Returns: frozenset of strings that are the measurement key
985
1006
  names in this circuit.
986
1007
  """
987
1008
  return frozenset(
988
1009
  key for op in self.all_operations() for key in protocols.measurement_key_names(op)
989
1010
  )
990
1011
 
991
- def _measurement_key_names_(self) -> FrozenSet[str]:
1012
+ def _measurement_key_names_(self) -> frozenset[str]:
992
1013
  return self.all_measurement_key_names()
993
1014
 
994
1015
  def _with_measurement_key_mapping_(self, key_map: Mapping[str, str]):
995
1016
  return self._from_moments(
996
- protocols.with_measurement_key_mapping(moment, key_map) for moment in self.moments
1017
+ (protocols.with_measurement_key_mapping(moment, key_map) for moment in self.moments),
1018
+ tags=self.tags,
997
1019
  )
998
1020
 
999
- def _with_key_path_(self, path: Tuple[str, ...]):
1000
- return self._from_moments(protocols.with_key_path(moment, path) for moment in self.moments)
1021
+ def _with_key_path_(self, path: tuple[str, ...]):
1022
+ return self._from_moments(
1023
+ (protocols.with_key_path(moment, path) for moment in self.moments), tags=self.tags
1024
+ )
1001
1025
 
1002
- def _with_key_path_prefix_(self, prefix: Tuple[str, ...]):
1026
+ def _with_key_path_prefix_(self, prefix: tuple[str, ...]):
1003
1027
  return self._from_moments(
1004
- protocols.with_key_path_prefix(moment, prefix) for moment in self.moments
1028
+ (protocols.with_key_path_prefix(moment, prefix) for moment in self.moments),
1029
+ tags=self.tags,
1005
1030
  )
1006
1031
 
1007
1032
  def _with_rescoped_keys_(
1008
- self, path: Tuple[str, ...], bindable_keys: FrozenSet[cirq.MeasurementKey]
1033
+ self, path: tuple[str, ...], bindable_keys: frozenset[cirq.MeasurementKey]
1009
1034
  ):
1010
1035
  moments = []
1011
1036
  for moment in self.moments:
1012
1037
  new_moment = protocols.with_rescoped_keys(moment, path, bindable_keys)
1013
1038
  moments.append(new_moment)
1014
1039
  bindable_keys |= protocols.measurement_key_objs(new_moment)
1015
- return self._from_moments(moments)
1040
+ return self._from_moments(moments, tags=self.tags)
1016
1041
 
1017
- def _qid_shape_(self) -> Tuple[int, ...]:
1042
+ def _qid_shape_(self) -> tuple[int, ...]:
1018
1043
  return self.qid_shape()
1019
1044
 
1020
1045
  def _has_unitary_(self) -> bool:
@@ -1029,7 +1054,7 @@ class AbstractCircuit(abc.ABC):
1029
1054
  )
1030
1055
  return all(protocols.has_unitary(e) for e in unitary_ops)
1031
1056
 
1032
- def _unitary_(self) -> Union[np.ndarray, NotImplementedType]:
1057
+ def _unitary_(self) -> np.ndarray | NotImplementedType:
1033
1058
  """Converts the circuit into a unitary matrix, if possible.
1034
1059
 
1035
1060
  If the circuit contains any non-terminal measurements, the conversion
@@ -1047,7 +1072,7 @@ class AbstractCircuit(abc.ABC):
1047
1072
  qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1048
1073
  qubits_that_should_be_present: Iterable[cirq.Qid] = (),
1049
1074
  ignore_terminal_measurements: bool = True,
1050
- dtype: Type[np.complexfloating] = np.complex128,
1075
+ dtype: type[np.complexfloating] = np.complex128,
1051
1076
  ) -> np.ndarray:
1052
1077
  """Converts the circuit into a unitary matrix, if possible.
1053
1078
 
@@ -1124,7 +1149,7 @@ class AbstractCircuit(abc.ABC):
1124
1149
  initial_state: cirq.STATE_VECTOR_LIKE = 0,
1125
1150
  qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1126
1151
  ignore_terminal_measurements: bool = False,
1127
- dtype: Type[np.complexfloating] = np.complex128,
1152
+ dtype: type[np.complexfloating] = np.complex128,
1128
1153
  param_resolver: cirq.ParamResolverOrSimilarType = None,
1129
1154
  seed: cirq.RANDOM_STATE_OR_SEED_LIKE = None,
1130
1155
  ) -> np.ndarray:
@@ -1182,8 +1207,8 @@ class AbstractCircuit(abc.ABC):
1182
1207
  *,
1183
1208
  use_unicode_characters: bool = True,
1184
1209
  transpose: bool = False,
1185
- include_tags: bool = True,
1186
- precision: Optional[int] = 3,
1210
+ include_tags: bool | Iterable[type] = True,
1211
+ precision: int | None = 3,
1187
1212
  qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1188
1213
  ) -> str:
1189
1214
  """Returns text containing a diagram describing the circuit.
@@ -1192,7 +1217,10 @@ class AbstractCircuit(abc.ABC):
1192
1217
  use_unicode_characters: Determines if unicode characters are
1193
1218
  allowed (as opposed to ascii-only diagrams).
1194
1219
  transpose: Arranges qubit wires vertically instead of horizontally.
1195
- include_tags: Whether tags on TaggedOperations should be printed
1220
+ include_tags: Controls which tags attached to operations are
1221
+ included. ``True`` includes all tags, ``False`` includes none,
1222
+ or a collection of tag classes may be specified to include only
1223
+ those tags.
1196
1224
  precision: Number of digits to display in text diagram
1197
1225
  qubit_order: Determines how qubits are ordered in the diagram.
1198
1226
 
@@ -1217,15 +1245,15 @@ class AbstractCircuit(abc.ABC):
1217
1245
  self,
1218
1246
  *,
1219
1247
  use_unicode_characters: bool = True,
1220
- qubit_namer: Optional[Callable[[cirq.Qid], str]] = None,
1248
+ qubit_namer: Callable[[cirq.Qid], str] | None = None,
1221
1249
  transpose: bool = False,
1222
- include_tags: bool = True,
1250
+ include_tags: bool | Iterable[type] = True,
1223
1251
  draw_moment_groups: bool = True,
1224
- precision: Optional[int] = 3,
1252
+ precision: int | None = 3,
1225
1253
  qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1226
- get_circuit_diagram_info: Optional[
1227
- Callable[[cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo]
1228
- ] = None,
1254
+ get_circuit_diagram_info: (
1255
+ Callable[[cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo] | None
1256
+ ) = None,
1229
1257
  ) -> cirq.TextDiagramDrawer:
1230
1258
  """Returns a TextDiagramDrawer with the circuit drawn into it.
1231
1259
 
@@ -1234,7 +1262,10 @@ class AbstractCircuit(abc.ABC):
1234
1262
  allowed (as opposed to ascii-only diagrams).
1235
1263
  qubit_namer: Names qubits in diagram. Defaults to using _circuit_diagram_info_ or str.
1236
1264
  transpose: Arranges qubit wires vertically instead of horizontally.
1237
- include_tags: Whether to include tags in the operation.
1265
+ include_tags: Controls which tags attached to operations are
1266
+ included. ``True`` includes all tags, ``False`` includes none,
1267
+ or a collection of tag classes may be specified to include only
1268
+ those tags.
1238
1269
  draw_moment_groups: Whether to draw moment symbol or not
1239
1270
  precision: Number of digits to use when representing numbers.
1240
1271
  qubit_order: Determines how qubits are ordered in the diagram.
@@ -1276,7 +1307,7 @@ class AbstractCircuit(abc.ABC):
1276
1307
  diagram.write(0, max(label_map.values(), default=0) + 1, 'global phase:')
1277
1308
  first_annotation_row += 1
1278
1309
 
1279
- moment_groups: List[Tuple[int, int]] = []
1310
+ moment_groups: list[tuple[int, int]] = []
1280
1311
  for moment in self.moments:
1281
1312
  _draw_moment_in_diagram(
1282
1313
  moment=moment,
@@ -1304,24 +1335,35 @@ class AbstractCircuit(abc.ABC):
1304
1335
  return diagram
1305
1336
 
1306
1337
  def _is_parameterized_(self) -> bool:
1307
- return any(protocols.is_parameterized(op) for op in self.all_operations())
1338
+ return any(protocols.is_parameterized(op) for op in self.all_operations()) or any(
1339
+ protocols.is_parameterized(tag) for tag in self.tags
1340
+ )
1308
1341
 
1309
1342
  def _parameter_names_(self) -> AbstractSet[str]:
1310
- return {name for op in self.all_operations() for name in protocols.parameter_names(op)}
1343
+ op_params = {name for op in self.all_operations() for name in protocols.parameter_names(op)}
1344
+ tag_params = {name for tag in self.tags for name in protocols.parameter_names(tag)}
1345
+ return op_params | tag_params
1311
1346
 
1312
1347
  def _resolve_parameters_(self, resolver: cirq.ParamResolver, recursive: bool) -> Self:
1313
1348
  changed = False
1314
- resolved_moments: List[cirq.Moment] = []
1349
+ resolved_moments: list[cirq.Moment] = []
1350
+ resolved_tags: list[Hashable] = []
1315
1351
  for moment in self:
1316
1352
  resolved_moment = protocols.resolve_parameters(moment, resolver, recursive)
1317
1353
  if resolved_moment is not moment:
1318
1354
  changed = True
1319
1355
  resolved_moments.append(resolved_moment)
1320
- if not changed:
1356
+ for tag in self.tags:
1357
+ resolved_tag = protocols.resolve_parameters(tag, resolver, recursive)
1358
+ if resolved_tag is not tag:
1359
+ changed = True
1360
+ resolved_tags.append(resolved_tag)
1361
+ if changed:
1362
+ return self._from_moments(resolved_moments, tags=resolved_tags)
1363
+ else:
1321
1364
  return self # pragma: no cover
1322
- return self._from_moments(resolved_moments)
1323
1365
 
1324
- def _qasm_(self, args: Optional[cirq.QasmArgs] = None) -> str:
1366
+ def _qasm_(self, args: cirq.QasmArgs | None = None) -> str:
1325
1367
  if args is None:
1326
1368
  output = self._to_qasm_output()
1327
1369
  else:
@@ -1330,7 +1372,7 @@ class AbstractCircuit(abc.ABC):
1330
1372
 
1331
1373
  def _to_qasm_output(
1332
1374
  self,
1333
- header: Optional[str] = None,
1375
+ header: str | None = None,
1334
1376
  precision: int = 10,
1335
1377
  qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1336
1378
  version: str = '2.0',
@@ -1359,7 +1401,7 @@ class AbstractCircuit(abc.ABC):
1359
1401
 
1360
1402
  def to_qasm(
1361
1403
  self,
1362
- header: Optional[str] = None,
1404
+ header: str | None = None,
1363
1405
  precision: int = 10,
1364
1406
  qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1365
1407
  version: str = '2.0',
@@ -1380,8 +1422,8 @@ class AbstractCircuit(abc.ABC):
1380
1422
 
1381
1423
  def save_qasm(
1382
1424
  self,
1383
- file_path: Union[str, bytes, int],
1384
- header: Optional[str] = None,
1425
+ file_path: str | bytes | int,
1426
+ header: str | None = None,
1385
1427
  precision: int = 10,
1386
1428
  qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1387
1429
  ) -> None:
@@ -1398,14 +1440,16 @@ class AbstractCircuit(abc.ABC):
1398
1440
  self._to_qasm_output(header, precision, qubit_order).save(file_path)
1399
1441
 
1400
1442
  def _json_dict_(self):
1401
- return protocols.obj_to_dict_helper(self, ['moments'])
1443
+ attribute_names = ['moments', 'tags'] if self.tags else ['moments']
1444
+ ret = protocols.obj_to_dict_helper(self, attribute_names)
1445
+ return ret
1402
1446
 
1403
1447
  @classmethod
1404
- def _from_json_dict_(cls, moments, **kwargs):
1405
- return cls(moments, strategy=InsertStrategy.EARLIEST)
1448
+ def _from_json_dict_(cls, moments, tags=(), **kwargs):
1449
+ return cls(moments, tags=tags, strategy=InsertStrategy.EARLIEST)
1406
1450
 
1407
1451
  def zip(
1408
- *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
1452
+ *circuits: cirq.AbstractCircuit, align: cirq.Alignment | str = Alignment.LEFT
1409
1453
  ) -> cirq.AbstractCircuit:
1410
1454
  """Combines operations from circuits in a moment-by-moment fashion.
1411
1455
 
@@ -1466,7 +1510,7 @@ class AbstractCircuit(abc.ABC):
1466
1510
  if isinstance(align, str):
1467
1511
  align = Alignment[align.upper()]
1468
1512
 
1469
- result = cirq.Circuit()
1513
+ result = cirq.Circuit(tags=circuits[0].tags if circuits else ())
1470
1514
  for k in range(n):
1471
1515
  try:
1472
1516
  if align == Alignment.LEFT:
@@ -1481,7 +1525,7 @@ class AbstractCircuit(abc.ABC):
1481
1525
  return result
1482
1526
 
1483
1527
  def concat_ragged(
1484
- *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
1528
+ *circuits: cirq.AbstractCircuit, align: cirq.Alignment | str = Alignment.LEFT
1485
1529
  ) -> cirq.AbstractCircuit:
1486
1530
  """Concatenates circuits, overlapping them if possible due to ragged edges.
1487
1531
 
@@ -1535,9 +1579,9 @@ class AbstractCircuit(abc.ABC):
1535
1579
  for k in range(1, len(circuits)):
1536
1580
  offset, n_acc = _concat_ragged_helper(offset, n_acc, buffer, circuits[k].moments, align)
1537
1581
 
1538
- return cirq.Circuit(buffer[offset : offset + n_acc])
1582
+ return cirq.Circuit(buffer[offset : offset + n_acc], tags=circuits[0].tags)
1539
1583
 
1540
- def get_independent_qubit_sets(self) -> List[Set[cirq.Qid]]:
1584
+ def get_independent_qubit_sets(self) -> list[set[cirq.Qid]]:
1541
1585
  """Divide circuit's qubits into independent qubit sets.
1542
1586
 
1543
1587
  Independent qubit sets are the qubit sets such that there are
@@ -1614,9 +1658,12 @@ class AbstractCircuit(abc.ABC):
1614
1658
  # the qubits from one factor belong to a specific independent qubit set.
1615
1659
  # This makes it possible to create independent circuits based on these
1616
1660
  # moments.
1617
- return (self._from_moments(m[qubits] for m in self.moments) for qubits in qubit_factors)
1661
+ return (
1662
+ self._from_moments([m[qubits] for m in self.moments], tags=self.tags)
1663
+ for qubits in qubit_factors
1664
+ )
1618
1665
 
1619
- def _control_keys_(self) -> FrozenSet[cirq.MeasurementKey]:
1666
+ def _control_keys_(self) -> frozenset[cirq.MeasurementKey]:
1620
1667
  controls = frozenset(k for op in self.all_operations() for k in protocols.control_keys(op))
1621
1668
  return controls - protocols.measurement_key_objs(self)
1622
1669
 
@@ -1626,7 +1673,7 @@ def _overlap_collision_time(
1626
1673
  ) -> int:
1627
1674
  # Tracks the first used moment index for each qubit in c2.
1628
1675
  # Tracks the complementary last used moment index for each qubit in c1.
1629
- seen_times: Dict[cirq.Qid, int] = {}
1676
+ seen_times: dict[cirq.Qid, int] = {}
1630
1677
 
1631
1678
  # Start scanning from end of first and start of second.
1632
1679
  if align == Alignment.LEFT:
@@ -1667,7 +1714,7 @@ def _concat_ragged_helper(
1667
1714
  buf: MutableSequence[cirq.Moment],
1668
1715
  c2: Sequence[cirq.Moment],
1669
1716
  align: cirq.Alignment,
1670
- ) -> Tuple[int, int]:
1717
+ ) -> tuple[int, int]:
1671
1718
  n2 = len(c2)
1672
1719
  shift = _overlap_collision_time(buf[c1_offset : c1_offset + n1], c2, align)
1673
1720
  c2_offset = c1_offset + n1 - shift
@@ -1757,7 +1804,10 @@ class Circuit(AbstractCircuit):
1757
1804
  """
1758
1805
 
1759
1806
  def __init__(
1760
- self, *contents: cirq.OP_TREE, strategy: cirq.InsertStrategy = InsertStrategy.EARLIEST
1807
+ self,
1808
+ *contents: cirq.OP_TREE,
1809
+ strategy: cirq.InsertStrategy = InsertStrategy.EARLIEST,
1810
+ tags: Sequence[Hashable] = (),
1761
1811
  ) -> None:
1762
1812
  """Initializes a circuit.
1763
1813
 
@@ -1771,18 +1821,23 @@ class Circuit(AbstractCircuit):
1771
1821
  from `contents`, this determines how the operations are packed
1772
1822
  together. This option does not affect later insertions into the
1773
1823
  circuit.
1824
+ tags: A sequence of any type of object that is useful to attach metadata
1825
+ to this circuit as long as the type is hashable. If you wish the
1826
+ resulting circuit to be eventually serialized into JSON, you should
1827
+ also restrict the tags to be JSON serializable.
1774
1828
  """
1775
- self._placement_cache: Optional[_PlacementCache] = _PlacementCache()
1776
- self._moments: List[cirq.Moment] = []
1829
+ self._placement_cache: _PlacementCache | None = _PlacementCache()
1830
+ self._moments: list[cirq.Moment] = []
1831
+ self._tags = tuple(tags)
1777
1832
 
1778
1833
  # Implementation note: the following cached properties are set lazily and then
1779
1834
  # invalidated and reset to None in `self._mutated()`, which is called any time
1780
1835
  # `self._moments` is changed.
1781
- self._all_qubits: Optional[FrozenSet[cirq.Qid]] = None
1782
- self._frozen: Optional[cirq.FrozenCircuit] = None
1783
- self._is_measurement: Optional[bool] = None
1784
- self._is_parameterized: Optional[bool] = None
1785
- self._parameter_names: Optional[AbstractSet[str]] = None
1836
+ self._all_qubits: frozenset[cirq.Qid] | None = None
1837
+ self._frozen: cirq.FrozenCircuit | None = None
1838
+ self._is_measurement: bool | None = None
1839
+ self._is_parameterized: bool | None = None
1840
+ self._parameter_names: AbstractSet[str] | None = None
1786
1841
  if not contents:
1787
1842
  return
1788
1843
  flattened_contents = tuple(ops.flatten_to_ops_or_moments(contents))
@@ -1807,10 +1862,11 @@ class Circuit(AbstractCircuit):
1807
1862
  self._placement_cache = None
1808
1863
 
1809
1864
  @classmethod
1810
- def _from_moments(cls, moments: Iterable[cirq.Moment]) -> Circuit:
1865
+ def _from_moments(cls, moments: Iterable[cirq.Moment], tags: Sequence[Hashable]) -> Circuit:
1811
1866
  new_circuit = Circuit()
1812
1867
  new_circuit._moments[:] = moments
1813
1868
  new_circuit._placement_cache = None
1869
+ new_circuit._tags = tuple(tags)
1814
1870
  return new_circuit
1815
1871
 
1816
1872
  def _load_contents_with_earliest_strategy(self, contents: cirq.OP_TREE):
@@ -1837,8 +1893,8 @@ class Circuit(AbstractCircuit):
1837
1893
 
1838
1894
  # We also maintain the dict from moment index to moments/ops that go into it, for use when
1839
1895
  # building the actual moments at the end.
1840
- op_lists_by_index: Dict[int, List[cirq.Operation]] = defaultdict(list)
1841
- moments_by_index: Dict[int, cirq.Moment] = {}
1896
+ op_lists_by_index: dict[int, list[cirq.Operation]] = defaultdict(list)
1897
+ moments_by_index: dict[int, cirq.Moment] = {}
1842
1898
 
1843
1899
  # "mop" means current moment-or-operation
1844
1900
  for mop in ops.flatten_to_ops_or_moments(contents):
@@ -1869,13 +1925,13 @@ class Circuit(AbstractCircuit):
1869
1925
  from cirq.circuits.frozen_circuit import FrozenCircuit
1870
1926
 
1871
1927
  if self._frozen is None:
1872
- self._frozen = FrozenCircuit._from_moments(self._moments)
1928
+ self._frozen = FrozenCircuit._from_moments(self._moments, tags=self.tags)
1873
1929
  return self._frozen
1874
1930
 
1875
1931
  def unfreeze(self, copy: bool = True) -> cirq.Circuit:
1876
1932
  return self.copy() if copy else self
1877
1933
 
1878
- def all_qubits(self) -> FrozenSet[cirq.Qid]:
1934
+ def all_qubits(self) -> frozenset[cirq.Qid]:
1879
1935
  if self._all_qubits is None:
1880
1936
  self._all_qubits = super().all_qubits()
1881
1937
  return self._all_qubits
@@ -1898,11 +1954,11 @@ class Circuit(AbstractCircuit):
1898
1954
  def copy(self) -> Circuit:
1899
1955
  """Return a copy of this circuit."""
1900
1956
  copied_circuit = Circuit()
1901
- copied_circuit._moments = self._moments[:]
1957
+ copied_circuit._moments[:] = self._moments
1902
1958
  copied_circuit._placement_cache = None
1959
+ copied_circuit._tags = self.tags
1903
1960
  return copied_circuit
1904
1961
 
1905
- # pylint: disable=function-redefined
1906
1962
  @overload
1907
1963
  def __setitem__(self, key: int, value: cirq.Moment):
1908
1964
  pass
@@ -1923,9 +1979,7 @@ class Circuit(AbstractCircuit):
1923
1979
  self._moments[key] = value
1924
1980
  self._mutated()
1925
1981
 
1926
- # pylint: enable=function-redefined
1927
-
1928
- def __delitem__(self, key: Union[int, slice]):
1982
+ def __delitem__(self, key: int | slice):
1929
1983
  del self._moments[key]
1930
1984
  self._mutated()
1931
1985
 
@@ -1962,7 +2016,7 @@ class Circuit(AbstractCircuit):
1962
2016
  def __mul__(self, repetitions: _INT_TYPE):
1963
2017
  if not isinstance(repetitions, (int, np.integer)):
1964
2018
  return NotImplemented
1965
- return Circuit(self._moments * int(repetitions))
2019
+ return Circuit(self._moments * int(repetitions), tags=self.tags)
1966
2020
 
1967
2021
  def __rmul__(self, repetitions: _INT_TYPE):
1968
2022
  if not isinstance(repetitions, (int, np.integer)):
@@ -1988,26 +2042,26 @@ class Circuit(AbstractCircuit):
1988
2042
  return NotImplemented
1989
2043
  inv_moments.append(inv_moment)
1990
2044
 
1991
- return cirq.Circuit(inv_moments)
2045
+ return cirq.Circuit(inv_moments, tags=self.tags)
1992
2046
 
1993
2047
  __hash__ = None # type: ignore
1994
2048
 
1995
2049
  def concat_ragged(
1996
- *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
2050
+ *circuits: cirq.AbstractCircuit, align: cirq.Alignment | str = Alignment.LEFT
1997
2051
  ) -> cirq.Circuit:
1998
2052
  return AbstractCircuit.concat_ragged(*circuits, align=align).unfreeze(copy=False)
1999
2053
 
2000
2054
  concat_ragged.__doc__ = AbstractCircuit.concat_ragged.__doc__
2001
2055
 
2002
2056
  def zip(
2003
- *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
2057
+ *circuits: cirq.AbstractCircuit, align: cirq.Alignment | str = Alignment.LEFT
2004
2058
  ) -> cirq.Circuit:
2005
2059
  return AbstractCircuit.zip(*circuits, align=align).unfreeze(copy=False)
2006
2060
 
2007
2061
  zip.__doc__ = AbstractCircuit.zip.__doc__
2008
2062
 
2009
2063
  def transform_qubits(
2010
- self, qubit_map: Union[Dict[cirq.Qid, cirq.Qid], Callable[[cirq.Qid], cirq.Qid]]
2064
+ self, qubit_map: dict[cirq.Qid, cirq.Qid] | Callable[[cirq.Qid], cirq.Qid]
2011
2065
  ) -> cirq.Circuit:
2012
2066
  """Returns the same circuit, but with different qubits.
2013
2067
 
@@ -2055,7 +2109,7 @@ class Circuit(AbstractCircuit):
2055
2109
  return Circuit(op_list)
2056
2110
 
2057
2111
  def earliest_available_moment(
2058
- self, op: cirq.Operation, *, end_moment_index: Optional[int] = None
2112
+ self, op: cirq.Operation, *, end_moment_index: int | None = None
2059
2113
  ) -> int:
2060
2114
  """Finds the index of the earliest (i.e. left most) moment which can accommodate `op`.
2061
2115
 
@@ -2148,7 +2202,7 @@ class Circuit(AbstractCircuit):
2148
2202
  and not all(
2149
2203
  (strategy is InsertStrategy.EARLIEST and self._can_add_op_at(k, op))
2150
2204
  or (k > 0 and self._can_add_op_at(k - 1, op))
2151
- for op in cast(List[cirq.Operation], batch)
2205
+ for op in cast(list[cirq.Operation], batch)
2152
2206
  )
2153
2207
  ):
2154
2208
  self._moments.insert(k, Moment())
@@ -2227,10 +2281,10 @@ class Circuit(AbstractCircuit):
2227
2281
 
2228
2282
  def _push_frontier(
2229
2283
  self,
2230
- early_frontier: Dict[cirq.Qid, int],
2231
- late_frontier: Dict[cirq.Qid, int],
2232
- update_qubits: Optional[Iterable[cirq.Qid]] = None,
2233
- ) -> Tuple[int, int]:
2284
+ early_frontier: dict[cirq.Qid, int],
2285
+ late_frontier: dict[cirq.Qid, int],
2286
+ update_qubits: Iterable[cirq.Qid] | None = None,
2287
+ ) -> tuple[int, int]:
2234
2288
  """Inserts moments to separate two frontiers.
2235
2289
 
2236
2290
  After insertion n_new moments, the following holds:
@@ -2292,15 +2346,15 @@ class Circuit(AbstractCircuit):
2292
2346
  raise ValueError('operations and insertion_indices must have the same length.')
2293
2347
  self._moments += [Moment() for _ in range(1 + max(insertion_indices) - len(self))]
2294
2348
  self._mutated()
2295
- moment_to_ops: Dict[int, List[cirq.Operation]] = defaultdict(list)
2349
+ moment_to_ops: dict[int, list[cirq.Operation]] = defaultdict(list)
2296
2350
  for op_index, moment_index in enumerate(insertion_indices):
2297
2351
  moment_to_ops[moment_index].append(operations[op_index])
2298
2352
  for moment_index, new_ops in moment_to_ops.items():
2299
2353
  self._moments[moment_index] = self._moments[moment_index].with_operations(*new_ops)
2300
2354
 
2301
2355
  def insert_at_frontier(
2302
- self, operations: cirq.OP_TREE, start: int, frontier: Optional[Dict[cirq.Qid, int]] = None
2303
- ) -> Dict[cirq.Qid, int]:
2356
+ self, operations: cirq.OP_TREE, start: int, frontier: dict[cirq.Qid, int] | None = None
2357
+ ) -> dict[cirq.Qid, int]:
2304
2358
  """Inserts operations inline at frontier.
2305
2359
 
2306
2360
  Args:
@@ -2334,7 +2388,7 @@ class Circuit(AbstractCircuit):
2334
2388
 
2335
2389
  return frontier
2336
2390
 
2337
- def batch_remove(self, removals: Iterable[Tuple[int, cirq.Operation]]) -> None:
2391
+ def batch_remove(self, removals: Iterable[tuple[int, cirq.Operation]]) -> None:
2338
2392
  """Removes several operations from a circuit.
2339
2393
 
2340
2394
  Args:
@@ -2358,7 +2412,7 @@ class Circuit(AbstractCircuit):
2358
2412
  self._mutated()
2359
2413
 
2360
2414
  def batch_replace(
2361
- self, replacements: Iterable[Tuple[int, cirq.Operation, cirq.Operation]]
2415
+ self, replacements: Iterable[tuple[int, cirq.Operation, cirq.Operation]]
2362
2416
  ) -> None:
2363
2417
  """Replaces several operations in a circuit with new operations.
2364
2418
 
@@ -2382,7 +2436,7 @@ class Circuit(AbstractCircuit):
2382
2436
  self._moments = copy._moments
2383
2437
  self._mutated()
2384
2438
 
2385
- def batch_insert_into(self, insert_intos: Iterable[Tuple[int, cirq.OP_TREE]]) -> None:
2439
+ def batch_insert_into(self, insert_intos: Iterable[tuple[int, cirq.OP_TREE]]) -> None:
2386
2440
  """Inserts operations into empty spaces in existing moments.
2387
2441
 
2388
2442
  If any of the insertions fails (due to colliding with an existing
@@ -2403,7 +2457,7 @@ class Circuit(AbstractCircuit):
2403
2457
  self._moments = copy._moments
2404
2458
  self._mutated()
2405
2459
 
2406
- def batch_insert(self, insertions: Iterable[Tuple[int, cirq.OP_TREE]]) -> None:
2460
+ def batch_insert(self, insertions: Iterable[tuple[int, cirq.OP_TREE]]) -> None:
2407
2461
  """Applies a batched insert operation to the circuit.
2408
2462
 
2409
2463
  Transparently handles the fact that earlier insertions may shift
@@ -2453,7 +2507,9 @@ class Circuit(AbstractCircuit):
2453
2507
  """
2454
2508
  self.insert(len(self._moments), moment_or_operation_tree, strategy)
2455
2509
 
2456
- def clear_operations_touching(self, qubits: Iterable[cirq.Qid], moment_indices: Iterable[int]):
2510
+ def clear_operations_touching(
2511
+ self, qubits: Iterable[cirq.Qid], moment_indices: Iterable[int]
2512
+ ) -> None:
2457
2513
  """Clears operations that are touching given qubits at given moments.
2458
2514
 
2459
2515
  Args:
@@ -2471,6 +2527,18 @@ class Circuit(AbstractCircuit):
2471
2527
  def moments(self) -> Sequence[cirq.Moment]:
2472
2528
  return self._moments
2473
2529
 
2530
+ @property
2531
+ def tags(self) -> tuple[Hashable, ...]:
2532
+ return self._tags
2533
+
2534
+ def with_tags(self, *new_tags: Hashable) -> cirq.Circuit:
2535
+ """Creates a new tagged `Circuit` with `self.tags` and `new_tags` combined."""
2536
+ if not new_tags:
2537
+ return self
2538
+ new_circuit = Circuit(tags=self.tags + new_tags)
2539
+ new_circuit._moments[:] = self._moments
2540
+ return new_circuit
2541
+
2474
2542
  def with_noise(self, noise: cirq.NOISE_MODEL_LIKE) -> cirq.Circuit:
2475
2543
  """Make a noisy version of the circuit.
2476
2544
 
@@ -2485,7 +2553,7 @@ class Circuit(AbstractCircuit):
2485
2553
  """
2486
2554
  noise_model = devices.NoiseModel.from_noise_model_like(noise)
2487
2555
  qubits = sorted(self.all_qubits())
2488
- c_noisy = Circuit()
2556
+ c_noisy = Circuit(tags=self.tags)
2489
2557
  for op_tree in noise_model.noisy_moments(self, qubits):
2490
2558
  # Keep moments aligned
2491
2559
  c_noisy += Circuit(op_tree)
@@ -2495,8 +2563,8 @@ class Circuit(AbstractCircuit):
2495
2563
  def _pick_inserted_ops_moment_indices(
2496
2564
  operations: Sequence[cirq.Operation],
2497
2565
  start: int = 0,
2498
- frontier: Optional[Dict[cirq.Qid, int]] = None,
2499
- ) -> Tuple[Sequence[int], Dict[cirq.Qid, int]]:
2566
+ frontier: dict[cirq.Qid, int] | None = None,
2567
+ ) -> tuple[Sequence[int], dict[cirq.Qid, int]]:
2500
2568
  """Greedily assigns operations to moments.
2501
2569
 
2502
2570
  Args:
@@ -2541,13 +2609,13 @@ def _draw_moment_annotations(
2541
2609
  moment: cirq.Moment,
2542
2610
  col: int,
2543
2611
  use_unicode_characters: bool,
2544
- label_map: Dict[cirq.LabelEntity, int],
2612
+ label_map: dict[cirq.LabelEntity, int],
2545
2613
  out_diagram: cirq.TextDiagramDrawer,
2546
- precision: Optional[int],
2614
+ precision: int | None,
2547
2615
  get_circuit_diagram_info: Callable[
2548
2616
  [cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo
2549
2617
  ],
2550
- include_tags: bool,
2618
+ include_tags: bool | Iterable[type],
2551
2619
  first_annotation_row: int,
2552
2620
  transpose: bool,
2553
2621
  ):
@@ -2572,14 +2640,14 @@ def _draw_moment_in_diagram(
2572
2640
  *,
2573
2641
  moment: cirq.Moment,
2574
2642
  use_unicode_characters: bool,
2575
- label_map: Dict[cirq.LabelEntity, int],
2643
+ label_map: dict[cirq.LabelEntity, int],
2576
2644
  out_diagram: cirq.TextDiagramDrawer,
2577
- precision: Optional[int],
2578
- moment_groups: List[Tuple[int, int]],
2579
- get_circuit_diagram_info: Optional[
2580
- Callable[[cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo]
2581
- ],
2582
- include_tags: bool,
2645
+ precision: int | None,
2646
+ moment_groups: list[tuple[int, int]],
2647
+ get_circuit_diagram_info: (
2648
+ Callable[[cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo] | None
2649
+ ),
2650
+ include_tags: bool | Iterable[type],
2583
2651
  first_annotation_row: int,
2584
2652
  transpose: bool,
2585
2653
  ):
@@ -2650,8 +2718,16 @@ def _draw_moment_in_diagram(
2650
2718
  desc = _formatted_phase(global_phase, use_unicode_characters, precision)
2651
2719
  if desc:
2652
2720
  y = max(label_map.values(), default=0) + 1
2653
- if tags and include_tags:
2654
- desc = desc + f"[{', '.join(map(str, tags))}]"
2721
+ visible_tags = protocols.CircuitDiagramInfoArgs(
2722
+ known_qubits=None,
2723
+ known_qubit_count=None,
2724
+ use_unicode_characters=True,
2725
+ precision=None,
2726
+ label_map=None,
2727
+ include_tags=include_tags,
2728
+ ).tags_to_include(tags)
2729
+ if visible_tags:
2730
+ desc = desc + f"[{', '.join(map(str, visible_tags))}]"
2655
2731
  out_diagram.write(x0, y, desc)
2656
2732
 
2657
2733
  if not non_global_ops:
@@ -2662,7 +2738,7 @@ def _draw_moment_in_diagram(
2662
2738
  moment_groups.append((x0, max_x))
2663
2739
 
2664
2740
 
2665
- def _get_global_phase_and_tags_for_op(op: cirq.Operation) -> Tuple[Optional[complex], List[Any]]:
2741
+ def _get_global_phase_and_tags_for_op(op: cirq.Operation) -> tuple[complex | None, list[Any]]:
2666
2742
  if isinstance(op.gate, ops.GlobalPhaseGate):
2667
2743
  return complex(op.gate.coefficient), list(op.tags)
2668
2744
  elif isinstance(op.untagged, CircuitOperation):
@@ -2671,9 +2747,9 @@ def _get_global_phase_and_tags_for_op(op: cirq.Operation) -> Tuple[Optional[comp
2671
2747
  return None, []
2672
2748
 
2673
2749
 
2674
- def _get_global_phase_and_tags_for_ops(op_list: Any) -> Tuple[Optional[complex], List[Any]]:
2675
- global_phase: Optional[complex] = None
2676
- tags: List[Any] = []
2750
+ def _get_global_phase_and_tags_for_ops(op_list: Any) -> tuple[complex | None, list[Any]]:
2751
+ global_phase: complex | None = None
2752
+ tags: list[Any] = []
2677
2753
  for op in op_list:
2678
2754
  op_phase, op_tags = _get_global_phase_and_tags_for_op(op)
2679
2755
  if op_phase:
@@ -2685,7 +2761,7 @@ def _get_global_phase_and_tags_for_ops(op_list: Any) -> Tuple[Optional[complex],
2685
2761
  return global_phase, tags
2686
2762
 
2687
2763
 
2688
- def _formatted_phase(coefficient: complex, unicode: bool, precision: Optional[int]) -> str:
2764
+ def _formatted_phase(coefficient: complex, unicode: bool, precision: int | None) -> str:
2689
2765
  h = math.atan2(coefficient.imag, coefficient.real) / math.pi
2690
2766
  unit = 'π' if unicode else 'pi'
2691
2767
  if h == 1:
@@ -2694,7 +2770,7 @@ def _formatted_phase(coefficient: complex, unicode: bool, precision: Optional[in
2694
2770
 
2695
2771
 
2696
2772
  def _draw_moment_groups_in_diagram(
2697
- moment_groups: List[Tuple[int, int]],
2773
+ moment_groups: list[tuple[int, int]],
2698
2774
  use_unicode_characters: bool,
2699
2775
  out_diagram: cirq.TextDiagramDrawer,
2700
2776
  ):
@@ -2728,8 +2804,8 @@ def _draw_moment_groups_in_diagram(
2728
2804
  def _apply_unitary_circuit(
2729
2805
  circuit: cirq.AbstractCircuit,
2730
2806
  state: np.ndarray,
2731
- qubits: Tuple[cirq.Qid, ...],
2732
- dtype: Type[np.complexfloating],
2807
+ qubits: tuple[cirq.Qid, ...],
2808
+ dtype: type[np.complexfloating],
2733
2809
  ) -> np.ndarray:
2734
2810
  """Applies a circuit's unitary effect to the given vector or matrix.
2735
2811
 
@@ -2792,14 +2868,14 @@ _TKey = TypeVar('_TKey')
2792
2868
  @overload
2793
2869
  def _group_until_different(
2794
2870
  items: Iterable[_TIn], key: Callable[[_TIn], _TKey]
2795
- ) -> Iterable[Tuple[_TKey, List[_TIn]]]:
2871
+ ) -> Iterable[tuple[_TKey, list[_TIn]]]:
2796
2872
  pass
2797
2873
 
2798
2874
 
2799
2875
  @overload
2800
2876
  def _group_until_different(
2801
2877
  items: Iterable[_TIn], key: Callable[[_TIn], _TKey], val: Callable[[_TIn], _TOut]
2802
- ) -> Iterable[Tuple[_TKey, List[_TOut]]]:
2878
+ ) -> Iterable[tuple[_TKey, list[_TOut]]]:
2803
2879
  pass
2804
2880
 
2805
2881
 
@@ -2829,7 +2905,7 @@ def _group_until_different(items: Iterable[_TIn], key: Callable[[_TIn], _TKey],
2829
2905
  return ((k, [val(i) for i in v]) for (k, v) in itertools.groupby(items, key))
2830
2906
 
2831
2907
 
2832
- def _group_into_moment_compatible(inputs: Sequence[_MOMENT_OR_OP]) -> Iterator[List[_MOMENT_OR_OP]]:
2908
+ def _group_into_moment_compatible(inputs: Sequence[_MOMENT_OR_OP]) -> Iterator[list[_MOMENT_OR_OP]]:
2833
2909
  """Groups sequential ops into those that can coexist in a single moment.
2834
2910
 
2835
2911
  This function will go through the input sequence in order, emitting lists of sequential
@@ -2844,8 +2920,8 @@ def _group_into_moment_compatible(inputs: Sequence[_MOMENT_OR_OP]) -> Iterator[L
2844
2920
  [X(a), X(a), X(b)] -> [[X(a)], [X(a), X(b)]]
2845
2921
  [X(a), Moment(X(b)), X(c)] -> [[X(a)], [Moment(X(b))], [X(c)]]
2846
2922
  """
2847
- batch: List[_MOMENT_OR_OP] = []
2848
- batch_qubits: Set[cirq.Qid] = set()
2923
+ batch: list[_MOMENT_OR_OP] = []
2924
+ batch_qubits: set[cirq.Qid] = set()
2849
2925
  for mop in inputs:
2850
2926
  is_moment = isinstance(mop, cirq.Moment)
2851
2927
  if (is_moment and batch) or not batch_qubits.isdisjoint(mop.qubits):
@@ -2863,10 +2939,10 @@ def _group_into_moment_compatible(inputs: Sequence[_MOMENT_OR_OP]) -> Iterator[L
2863
2939
 
2864
2940
  def get_earliest_accommodating_moment_index(
2865
2941
  moment_or_operation: _MOMENT_OR_OP,
2866
- qubit_indices: Dict[cirq.Qid, int],
2867
- mkey_indices: Dict[cirq.MeasurementKey, int],
2868
- ckey_indices: Dict[cirq.MeasurementKey, int],
2869
- length: Optional[int] = None,
2942
+ qubit_indices: dict[cirq.Qid, int],
2943
+ mkey_indices: dict[cirq.MeasurementKey, int],
2944
+ ckey_indices: dict[cirq.MeasurementKey, int],
2945
+ length: int | None = None,
2870
2946
  ) -> int:
2871
2947
  """Get the index of the earliest moment that can accommodate the given moment or operation.
2872
2948
 
@@ -2951,9 +3027,9 @@ class _PlacementCache:
2951
3027
 
2952
3028
  def __init__(self) -> None:
2953
3029
  # These are dicts from the qubit/key to the greatest moment index that has it.
2954
- self._qubit_indices: Dict[cirq.Qid, int] = {}
2955
- self._mkey_indices: Dict[cirq.MeasurementKey, int] = {}
2956
- self._ckey_indices: Dict[cirq.MeasurementKey, int] = {}
3030
+ self._qubit_indices: dict[cirq.Qid, int] = {}
3031
+ self._mkey_indices: dict[cirq.MeasurementKey, int] = {}
3032
+ self._ckey_indices: dict[cirq.MeasurementKey, int] = {}
2957
3033
 
2958
3034
  # For keeping track of length of the circuit thus far.
2959
3035
  self._length = 0