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
cirq/circuits/circuit.py CHANGED
@@ -19,12 +19,15 @@ Operations. Each Operation is a Gate that acts on some Qubits, for a given
19
19
  Moment the Operations must all act on distinct Qubits.
20
20
  """
21
21
 
22
+ from __future__ import annotations
23
+
22
24
  import abc
23
25
  import enum
24
26
  import html
25
27
  import itertools
26
28
  import math
27
29
  from collections import defaultdict
30
+ from types import NotImplementedType
28
31
  from typing import (
29
32
  AbstractSet,
30
33
  Any,
@@ -47,10 +50,10 @@ from typing import (
47
50
  TypeVar,
48
51
  Union,
49
52
  )
50
- from typing_extensions import Self
51
53
 
52
54
  import networkx
53
55
  import numpy as np
56
+ from typing_extensions import Self
54
57
 
55
58
  import cirq._version
56
59
  from cirq import _compat, devices, ops, protocols, qis
@@ -62,13 +65,13 @@ from cirq.circuits.moment import Moment
62
65
  from cirq.circuits.qasm_output import QasmOutput
63
66
  from cirq.circuits.text_diagram_drawer import TextDiagramDrawer
64
67
  from cirq.protocols import circuit_diagram_info_protocol
65
- from cirq.type_workarounds import NotImplementedType
66
68
 
67
69
  if TYPE_CHECKING:
68
70
  import cirq
69
71
 
70
72
 
71
73
  _TGate = TypeVar('_TGate', bound='cirq.Gate')
74
+ _MOMENT_OR_OP = Union['cirq.Moment', 'cirq.Operation']
72
75
 
73
76
  CIRCUIT_TYPE = TypeVar('CIRCUIT_TYPE', bound='AbstractCircuit')
74
77
  document(
@@ -145,34 +148,36 @@ class AbstractCircuit(abc.ABC):
145
148
  """
146
149
 
147
150
  @classmethod
148
- def from_moments(cls: Type[CIRCUIT_TYPE], *moments: 'cirq.OP_TREE') -> CIRCUIT_TYPE:
151
+ def from_moments(cls: Type[CIRCUIT_TYPE], *moments: Optional[cirq.OP_TREE]) -> CIRCUIT_TYPE:
149
152
  """Create a circuit from moment op trees.
150
153
 
151
154
  Args:
152
- *moments: Op tree for each moment. If an op tree is a moment, it
153
- will be included directly in the new circuit. If an op tree is
154
- a circuit, it will be frozen, wrapped in a CircuitOperation, and
155
- included in its own moment in the new circuit. Otherwise, the
156
- op tree will be passed to `cirq.Moment` to create a new moment
157
- which is then included in the new circuit. Note that in the
158
- latter case we have the normal restriction that operations in a
159
- moment must be applied to disjoint sets of qubits.
155
+ *moments: Op trees for each moment, which can be one of the following:
156
+ - Moment: will be included directly in the new circuit.
157
+ - AbstractCircuit: will be frozen, wrapped in a CircuitOperation,
158
+ and included in its own moment in the new circuit.
159
+ - None: will be skipped and omitted from the circuit. This can be
160
+ used to include or skip a moment based on a conditional, for example.
161
+ - Other OP_TREE: will be passed to `cirq.Moment` to create a new moment
162
+ which is then included in the new circuit. Note that in this
163
+ case we have the normal restriction that operations in a
164
+ moment must be applied to disjoint sets of qubits.
160
165
  """
161
166
  return cls._from_moments(cls._make_moments(moments))
162
167
 
163
168
  @staticmethod
164
- def _make_moments(moments: Iterable['cirq.OP_TREE']) -> Iterator['cirq.Moment']:
169
+ def _make_moments(moments: Iterable[Optional[cirq.OP_TREE]]) -> Iterator[cirq.Moment]:
165
170
  for m in moments:
166
171
  if isinstance(m, Moment):
167
172
  yield m
168
173
  elif isinstance(m, AbstractCircuit):
169
174
  yield Moment(m.freeze().to_op())
170
- else:
175
+ elif m is not None:
171
176
  yield Moment(m)
172
177
 
173
178
  @classmethod
174
179
  @abc.abstractmethod
175
- def _from_moments(cls: Type[CIRCUIT_TYPE], moments: Iterable['cirq.Moment']) -> CIRCUIT_TYPE:
180
+ def _from_moments(cls: Type[CIRCUIT_TYPE], moments: Iterable[cirq.Moment]) -> CIRCUIT_TYPE:
176
181
  """Create a circuit from moments.
177
182
 
178
183
  This must be implemented by subclasses. It provides a more efficient way
@@ -185,18 +190,18 @@ class AbstractCircuit(abc.ABC):
185
190
 
186
191
  @property
187
192
  @abc.abstractmethod
188
- def moments(self) -> Sequence['cirq.Moment']:
193
+ def moments(self) -> Sequence[cirq.Moment]:
189
194
  pass
190
195
 
191
196
  @abc.abstractmethod
192
- def freeze(self) -> 'cirq.FrozenCircuit':
197
+ def freeze(self) -> cirq.FrozenCircuit:
193
198
  """Creates a FrozenCircuit from this circuit.
194
199
 
195
200
  If 'self' is a FrozenCircuit, the original object is returned.
196
201
  """
197
202
 
198
203
  @abc.abstractmethod
199
- def unfreeze(self, copy: bool = True) -> 'cirq.Circuit':
204
+ def unfreeze(self, copy: bool = True) -> cirq.Circuit:
200
205
  """Creates a Circuit from this circuit.
201
206
 
202
207
  Args:
@@ -214,7 +219,7 @@ class AbstractCircuit(abc.ABC):
214
219
  and all(m0 == m1 for m0, m1 in zip(self.moments, other.moments))
215
220
  )
216
221
 
217
- def _approx_eq_(self, other: Any, atol: Union[int, float]) -> bool:
222
+ def _approx_eq_(self, other: Any, atol: float) -> bool:
218
223
  """See `cirq.protocols.SupportsApproximateEquality`."""
219
224
  if not isinstance(other, AbstractCircuit):
220
225
  return NotImplemented
@@ -228,24 +233,24 @@ class AbstractCircuit(abc.ABC):
228
233
  def __len__(self) -> int:
229
234
  return len(self.moments)
230
235
 
231
- def __iter__(self) -> Iterator['cirq.Moment']:
236
+ def __iter__(self) -> Iterator[cirq.Moment]:
232
237
  return iter(self.moments)
233
238
 
234
- def _decompose_(self) -> 'cirq.OP_TREE':
239
+ def _decompose_(self) -> cirq.OP_TREE:
235
240
  """See `cirq.SupportsDecompose`."""
236
241
  return self.all_operations()
237
242
 
238
243
  # pylint: disable=function-redefined
239
244
  @overload
240
- def __getitem__(self, key: int) -> 'cirq.Moment':
245
+ def __getitem__(self, key: int) -> cirq.Moment:
241
246
  pass
242
247
 
243
248
  @overload
244
- def __getitem__(self, key: Tuple[int, 'cirq.Qid']) -> 'cirq.Operation':
249
+ def __getitem__(self, key: Tuple[int, cirq.Qid]) -> cirq.Operation:
245
250
  pass
246
251
 
247
252
  @overload
248
- def __getitem__(self, key: Tuple[int, Iterable['cirq.Qid']]) -> 'cirq.Moment':
253
+ def __getitem__(self, key: Tuple[int, Iterable[cirq.Qid]]) -> cirq.Moment:
249
254
  pass
250
255
 
251
256
  @overload
@@ -253,11 +258,11 @@ class AbstractCircuit(abc.ABC):
253
258
  pass
254
259
 
255
260
  @overload
256
- def __getitem__(self, key: Tuple[slice, 'cirq.Qid']) -> Self:
261
+ def __getitem__(self, key: Tuple[slice, cirq.Qid]) -> Self:
257
262
  pass
258
263
 
259
264
  @overload
260
- def __getitem__(self, key: Tuple[slice, Iterable['cirq.Qid']]) -> Self:
265
+ def __getitem__(self, key: Tuple[slice, Iterable[cirq.Qid]]) -> Self:
261
266
  pass
262
267
 
263
268
  def __getitem__(self, key):
@@ -312,7 +317,7 @@ class AbstractCircuit(abc.ABC):
312
317
  )
313
318
 
314
319
  def _first_moment_operating_on(
315
- self, qubits: Iterable['cirq.Qid'], indices: Iterable[int]
320
+ self, qubits: Iterable[cirq.Qid], indices: Iterable[int]
316
321
  ) -> Optional[int]:
317
322
  qubits = frozenset(qubits)
318
323
  for m in indices:
@@ -322,7 +327,7 @@ class AbstractCircuit(abc.ABC):
322
327
 
323
328
  def next_moment_operating_on(
324
329
  self,
325
- qubits: Iterable['cirq.Qid'],
330
+ qubits: Iterable[cirq.Qid],
326
331
  start_moment_index: int = 0,
327
332
  max_distance: Optional[int] = None,
328
333
  ) -> Optional[int]:
@@ -354,8 +359,8 @@ class AbstractCircuit(abc.ABC):
354
359
  )
355
360
 
356
361
  def next_moments_operating_on(
357
- self, qubits: Iterable['cirq.Qid'], start_moment_index: int = 0
358
- ) -> Dict['cirq.Qid', int]:
362
+ self, qubits: Iterable[cirq.Qid], start_moment_index: int = 0
363
+ ) -> Dict[cirq.Qid, int]:
359
364
  """Finds the index of the next moment that touches each qubit.
360
365
 
361
366
  Args:
@@ -377,7 +382,7 @@ class AbstractCircuit(abc.ABC):
377
382
 
378
383
  def prev_moment_operating_on(
379
384
  self,
380
- qubits: Sequence['cirq.Qid'],
385
+ qubits: Sequence[cirq.Qid],
381
386
  end_moment_index: Optional[int] = None,
382
387
  max_distance: Optional[int] = None,
383
388
  ) -> Optional[int]:
@@ -422,10 +427,10 @@ class AbstractCircuit(abc.ABC):
422
427
 
423
428
  def reachable_frontier_from(
424
429
  self,
425
- start_frontier: Dict['cirq.Qid', int],
430
+ start_frontier: Dict[cirq.Qid, int],
426
431
  *,
427
- is_blocker: Callable[['cirq.Operation'], bool] = lambda op: False,
428
- ) -> Dict['cirq.Qid', int]:
432
+ is_blocker: Callable[[cirq.Operation], bool] = lambda op: False,
433
+ ) -> Dict[cirq.Qid, int]:
429
434
  """Determines how far can be reached into a circuit under certain rules.
430
435
 
431
436
  The location L = (qubit, moment_index) is *reachable* if and only if the
@@ -561,11 +566,11 @@ class AbstractCircuit(abc.ABC):
561
566
  where i is the moment index, q is the qubit, and end_frontier is the
562
567
  result of this method.
563
568
  """
564
- active: Set['cirq.Qid'] = set()
569
+ active: Set[cirq.Qid] = set()
565
570
  end_frontier = {}
566
571
  queue = BucketPriorityQueue[ops.Operation](drop_duplicate_entries=True)
567
572
 
568
- def enqueue_next(qubit: 'cirq.Qid', moment: int) -> None:
573
+ def enqueue_next(qubit: cirq.Qid, moment: int) -> None:
569
574
  next_moment = self.next_moment_operating_on([qubit], moment)
570
575
  if next_moment is None:
571
576
  end_frontier[qubit] = max(len(self), start_frontier[qubit])
@@ -605,10 +610,10 @@ class AbstractCircuit(abc.ABC):
605
610
 
606
611
  def findall_operations_between(
607
612
  self,
608
- start_frontier: Dict['cirq.Qid', int],
609
- end_frontier: Dict['cirq.Qid', int],
613
+ start_frontier: Dict[cirq.Qid, int],
614
+ end_frontier: Dict[cirq.Qid, int],
610
615
  omit_crossing_operations: bool = False,
611
- ) -> List[Tuple[int, 'cirq.Operation']]:
616
+ ) -> List[Tuple[int, cirq.Operation]]:
612
617
  """Finds operations between the two given frontiers.
613
618
 
614
619
  If a qubit is in `start_frontier` but not `end_frontier`, its end index
@@ -653,9 +658,9 @@ class AbstractCircuit(abc.ABC):
653
658
 
654
659
  def findall_operations_until_blocked(
655
660
  self,
656
- start_frontier: Dict['cirq.Qid', int],
657
- is_blocker: Callable[['cirq.Operation'], bool] = lambda op: False,
658
- ) -> List[Tuple[int, 'cirq.Operation']]:
661
+ start_frontier: Dict[cirq.Qid, int],
662
+ is_blocker: Callable[[cirq.Operation], bool] = lambda op: False,
663
+ ) -> List[Tuple[int, cirq.Operation]]:
659
664
  """Finds all operations until a blocking operation is hit.
660
665
 
661
666
  An operation is considered blocking if both of the following hold:
@@ -757,7 +762,7 @@ class AbstractCircuit(abc.ABC):
757
762
  break
758
763
  return op_list
759
764
 
760
- def operation_at(self, qubit: 'cirq.Qid', moment_index: int) -> Optional['cirq.Operation']:
765
+ def operation_at(self, qubit: cirq.Qid, moment_index: int) -> Optional[cirq.Operation]:
761
766
  """Finds the operation on a qubit within a moment, if any.
762
767
 
763
768
  Args:
@@ -774,8 +779,8 @@ class AbstractCircuit(abc.ABC):
774
779
  return self.moments[moment_index].operation_at(qubit)
775
780
 
776
781
  def findall_operations(
777
- self, predicate: Callable[['cirq.Operation'], bool]
778
- ) -> Iterable[Tuple[int, 'cirq.Operation']]:
782
+ self, predicate: Callable[[cirq.Operation], bool]
783
+ ) -> Iterable[Tuple[int, cirq.Operation]]:
779
784
  """Find the locations of all operations that satisfy a given condition.
780
785
 
781
786
  This returns an iterator of (index, operation) tuples where each
@@ -796,7 +801,7 @@ class AbstractCircuit(abc.ABC):
796
801
 
797
802
  def findall_operations_with_gate_type(
798
803
  self, gate_type: Type[_TGate]
799
- ) -> Iterable[Tuple[int, 'cirq.GateOperation', _TGate]]:
804
+ ) -> Iterable[Tuple[int, cirq.GateOperation, _TGate]]:
800
805
  """Find the locations of all gate operations of a given type.
801
806
 
802
807
  Args:
@@ -829,7 +834,7 @@ class AbstractCircuit(abc.ABC):
829
834
  """
830
835
  return self.are_all_matches_terminal(protocols.is_measurement)
831
836
 
832
- def are_all_matches_terminal(self, predicate: Callable[['cirq.Operation'], bool]) -> bool:
837
+ def are_all_matches_terminal(self, predicate: Callable[[cirq.Operation], bool]) -> bool:
833
838
  """Check whether all of the ops that satisfy a predicate are terminal.
834
839
 
835
840
  This method will transparently descend into any CircuitOperations this
@@ -874,7 +879,7 @@ class AbstractCircuit(abc.ABC):
874
879
  """
875
880
  return self.are_any_matches_terminal(protocols.is_measurement)
876
881
 
877
- def are_any_matches_terminal(self, predicate: Callable[['cirq.Operation'], bool]) -> bool:
882
+ def are_any_matches_terminal(self, predicate: Callable[[cirq.Operation], bool]) -> bool:
878
883
  """Check whether any of the ops that satisfy a predicate are terminal.
879
884
 
880
885
  This method will transparently descend into any CircuitOperations this
@@ -912,12 +917,12 @@ class AbstractCircuit(abc.ABC):
912
917
  return True
913
918
  return False
914
919
 
915
- def _has_op_at(self, moment_index: int, qubits: Iterable['cirq.Qid']) -> bool:
920
+ def _has_op_at(self, moment_index: int, qubits: Iterable[cirq.Qid]) -> bool:
916
921
  return 0 <= moment_index < len(self.moments) and self.moments[moment_index].operates_on(
917
922
  qubits
918
923
  )
919
924
 
920
- def all_qubits(self) -> FrozenSet['cirq.Qid']:
925
+ def all_qubits(self) -> FrozenSet[cirq.Qid]:
921
926
  """Returns the qubits acted upon by Operations in this circuit.
922
927
 
923
928
  Returns: FrozenSet of `cirq.Qid` objects acted on by all operations
@@ -925,14 +930,14 @@ class AbstractCircuit(abc.ABC):
925
930
  """
926
931
  return frozenset(q for m in self.moments for q in m.qubits)
927
932
 
928
- def all_operations(self) -> Iterator['cirq.Operation']:
933
+ def all_operations(self) -> Iterator[cirq.Operation]:
929
934
  """Returns an iterator over the operations in the circuit.
930
935
 
931
936
  Returns: Iterator over `cirq.Operation` elements found in this circuit.
932
937
  """
933
938
  return (op for moment in self for op in moment.operations)
934
939
 
935
- def map_operations(self, func: Callable[['cirq.Operation'], 'cirq.OP_TREE']) -> Self:
940
+ def map_operations(self, func: Callable[[cirq.Operation], cirq.OP_TREE]) -> Self:
936
941
  """Applies the given function to all operations in this circuit.
937
942
 
938
943
  Args:
@@ -943,14 +948,14 @@ class AbstractCircuit(abc.ABC):
943
948
  each operation `op` replaced with `func(op)`.
944
949
  """
945
950
 
946
- def map_moment(moment: 'cirq.Moment') -> 'cirq.Circuit':
951
+ def map_moment(moment: cirq.Moment) -> cirq.Circuit:
947
952
  """Apply func to expand each op into a circuit, then zip up the circuits."""
948
953
  return Circuit.zip(*[Circuit(func(op)) for op in moment])
949
954
 
950
955
  return self._from_moments(m for moment in self for m in map_moment(moment))
951
956
 
952
957
  def qid_shape(
953
- self, qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT
958
+ self, qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT
954
959
  ) -> Tuple[int, ...]:
955
960
  """Get the qubit shapes of all qubits in this circuit.
956
961
 
@@ -960,12 +965,12 @@ class AbstractCircuit(abc.ABC):
960
965
  qids = ops.QubitOrder.as_qubit_order(qubit_order).order_for(self.all_qubits())
961
966
  return protocols.qid_shape(qids)
962
967
 
963
- def all_measurement_key_objs(self) -> FrozenSet['cirq.MeasurementKey']:
968
+ def all_measurement_key_objs(self) -> FrozenSet[cirq.MeasurementKey]:
964
969
  return frozenset(
965
970
  key for op in self.all_operations() for key in protocols.measurement_key_objs(op)
966
971
  )
967
972
 
968
- def _measurement_key_objs_(self) -> FrozenSet['cirq.MeasurementKey']:
973
+ def _measurement_key_objs_(self) -> FrozenSet[cirq.MeasurementKey]:
969
974
  """Returns the set of all measurement keys in this circuit.
970
975
 
971
976
  Returns: FrozenSet of `cirq.MeasurementKey` objects that are
@@ -1000,7 +1005,7 @@ class AbstractCircuit(abc.ABC):
1000
1005
  )
1001
1006
 
1002
1007
  def _with_rescoped_keys_(
1003
- self, path: Tuple[str, ...], bindable_keys: FrozenSet['cirq.MeasurementKey']
1008
+ self, path: Tuple[str, ...], bindable_keys: FrozenSet[cirq.MeasurementKey]
1004
1009
  ):
1005
1010
  moments = []
1006
1011
  for moment in self.moments:
@@ -1039,8 +1044,8 @@ class AbstractCircuit(abc.ABC):
1039
1044
 
1040
1045
  def unitary(
1041
1046
  self,
1042
- qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
1043
- qubits_that_should_be_present: Iterable['cirq.Qid'] = (),
1047
+ qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1048
+ qubits_that_should_be_present: Iterable[cirq.Qid] = (),
1044
1049
  ignore_terminal_measurements: bool = True,
1045
1050
  dtype: Type[np.complexfloating] = np.complex128,
1046
1051
  ) -> np.ndarray:
@@ -1116,12 +1121,12 @@ class AbstractCircuit(abc.ABC):
1116
1121
  def final_state_vector(
1117
1122
  self,
1118
1123
  *,
1119
- initial_state: 'cirq.STATE_VECTOR_LIKE' = 0,
1120
- qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
1124
+ initial_state: cirq.STATE_VECTOR_LIKE = 0,
1125
+ qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1121
1126
  ignore_terminal_measurements: bool = False,
1122
1127
  dtype: Type[np.complexfloating] = np.complex128,
1123
- param_resolver: 'cirq.ParamResolverOrSimilarType' = None,
1124
- seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None,
1128
+ param_resolver: cirq.ParamResolverOrSimilarType = None,
1129
+ seed: cirq.RANDOM_STATE_OR_SEED_LIKE = None,
1125
1130
  ) -> np.ndarray:
1126
1131
  """Returns the state vector resulting from acting operations on a state.
1127
1132
 
@@ -1179,7 +1184,7 @@ class AbstractCircuit(abc.ABC):
1179
1184
  transpose: bool = False,
1180
1185
  include_tags: bool = True,
1181
1186
  precision: Optional[int] = 3,
1182
- qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
1187
+ qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1183
1188
  ) -> str:
1184
1189
  """Returns text containing a diagram describing the circuit.
1185
1190
 
@@ -1212,16 +1217,16 @@ class AbstractCircuit(abc.ABC):
1212
1217
  self,
1213
1218
  *,
1214
1219
  use_unicode_characters: bool = True,
1215
- qubit_namer: Optional[Callable[['cirq.Qid'], str]] = None,
1220
+ qubit_namer: Optional[Callable[[cirq.Qid], str]] = None,
1216
1221
  transpose: bool = False,
1217
1222
  include_tags: bool = True,
1218
1223
  draw_moment_groups: bool = True,
1219
1224
  precision: Optional[int] = 3,
1220
- qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
1225
+ qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1221
1226
  get_circuit_diagram_info: Optional[
1222
- Callable[['cirq.Operation', 'cirq.CircuitDiagramInfoArgs'], 'cirq.CircuitDiagramInfo']
1227
+ Callable[[cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo]
1223
1228
  ] = None,
1224
- ) -> 'cirq.TextDiagramDrawer':
1229
+ ) -> cirq.TextDiagramDrawer:
1225
1230
  """Returns a TextDiagramDrawer with the circuit drawn into it.
1226
1231
 
1227
1232
  Args:
@@ -1304,27 +1309,32 @@ class AbstractCircuit(abc.ABC):
1304
1309
  def _parameter_names_(self) -> AbstractSet[str]:
1305
1310
  return {name for op in self.all_operations() for name in protocols.parameter_names(op)}
1306
1311
 
1307
- def _resolve_parameters_(self, resolver: 'cirq.ParamResolver', recursive: bool) -> Self:
1312
+ def _resolve_parameters_(self, resolver: cirq.ParamResolver, recursive: bool) -> Self:
1308
1313
  changed = False
1309
- resolved_moments: List['cirq.Moment'] = []
1314
+ resolved_moments: List[cirq.Moment] = []
1310
1315
  for moment in self:
1311
1316
  resolved_moment = protocols.resolve_parameters(moment, resolver, recursive)
1312
1317
  if resolved_moment is not moment:
1313
1318
  changed = True
1314
1319
  resolved_moments.append(resolved_moment)
1315
1320
  if not changed:
1316
- return self
1321
+ return self # pragma: no cover
1317
1322
  return self._from_moments(resolved_moments)
1318
1323
 
1319
- def _qasm_(self) -> str:
1320
- return self.to_qasm()
1324
+ def _qasm_(self, args: Optional[cirq.QasmArgs] = None) -> str:
1325
+ if args is None:
1326
+ output = self._to_qasm_output()
1327
+ else:
1328
+ output = self._to_qasm_output(precision=args.precision, version=args.version)
1329
+ return str(output)
1321
1330
 
1322
1331
  def _to_qasm_output(
1323
1332
  self,
1324
1333
  header: Optional[str] = None,
1325
1334
  precision: int = 10,
1326
- qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
1327
- ) -> 'cirq.QasmOutput':
1335
+ qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1336
+ version: str = '2.0',
1337
+ ) -> cirq.QasmOutput:
1328
1338
  """Returns a QASM object equivalent to the circuit.
1329
1339
 
1330
1340
  Args:
@@ -1333,6 +1343,8 @@ class AbstractCircuit(abc.ABC):
1333
1343
  precision: Number of digits to use when representing numbers.
1334
1344
  qubit_order: Determines how qubits are ordered in the QASM
1335
1345
  register.
1346
+ version: Version of OpenQASM to render as output. Defaults
1347
+ to OpenQASM 2.0. For OpenQASM 3.0, set this to '3.0'.
1336
1348
  """
1337
1349
  if header is None:
1338
1350
  header = f'Generated from Cirq v{cirq._version.__version__}'
@@ -1342,14 +1354,15 @@ class AbstractCircuit(abc.ABC):
1342
1354
  qubits=qubits,
1343
1355
  header=header,
1344
1356
  precision=precision,
1345
- version='2.0',
1357
+ version=version,
1346
1358
  )
1347
1359
 
1348
1360
  def to_qasm(
1349
1361
  self,
1350
1362
  header: Optional[str] = None,
1351
1363
  precision: int = 10,
1352
- qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
1364
+ qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1365
+ version: str = '2.0',
1353
1366
  ) -> str:
1354
1367
  """Returns QASM equivalent to the circuit.
1355
1368
 
@@ -1359,16 +1372,18 @@ class AbstractCircuit(abc.ABC):
1359
1372
  precision: Number of digits to use when representing numbers.
1360
1373
  qubit_order: Determines how qubits are ordered in the QASM
1361
1374
  register.
1375
+ version: Version of OpenQASM to output. Defaults to OpenQASM 2.0.
1376
+ Specify '3.0' if OpenQASM 3.0 is desired.
1362
1377
  """
1363
1378
 
1364
- return str(self._to_qasm_output(header, precision, qubit_order))
1379
+ return str(self._to_qasm_output(header, precision, qubit_order, version))
1365
1380
 
1366
1381
  def save_qasm(
1367
1382
  self,
1368
1383
  file_path: Union[str, bytes, int],
1369
1384
  header: Optional[str] = None,
1370
1385
  precision: int = 10,
1371
- qubit_order: 'cirq.QubitOrderOrList' = ops.QubitOrder.DEFAULT,
1386
+ qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT,
1372
1387
  ) -> None:
1373
1388
  """Save a QASM file equivalent to the circuit.
1374
1389
 
@@ -1390,8 +1405,8 @@ class AbstractCircuit(abc.ABC):
1390
1405
  return cls(moments, strategy=InsertStrategy.EARLIEST)
1391
1406
 
1392
1407
  def zip(
1393
- *circuits: 'cirq.AbstractCircuit', align: Union['cirq.Alignment', str] = Alignment.LEFT
1394
- ) -> 'cirq.AbstractCircuit':
1408
+ *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
1409
+ ) -> cirq.AbstractCircuit:
1395
1410
  """Combines operations from circuits in a moment-by-moment fashion.
1396
1411
 
1397
1412
  Moment k of the resulting circuit will have all operations from moment
@@ -1466,8 +1481,8 @@ class AbstractCircuit(abc.ABC):
1466
1481
  return result
1467
1482
 
1468
1483
  def concat_ragged(
1469
- *circuits: 'cirq.AbstractCircuit', align: Union['cirq.Alignment', str] = Alignment.LEFT
1470
- ) -> 'cirq.AbstractCircuit':
1484
+ *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
1485
+ ) -> cirq.AbstractCircuit:
1471
1486
  """Concatenates circuits, overlapping them if possible due to ragged edges.
1472
1487
 
1473
1488
  Starts with the first circuit (index 0), then iterates over the other
@@ -1510,7 +1525,7 @@ class AbstractCircuit(abc.ABC):
1510
1525
 
1511
1526
  # Allocate a buffer large enough to append and prepend all the circuits.
1512
1527
  pad_len = sum(len(c) for c in circuits) - n_acc
1513
- buffer: MutableSequence['cirq.Moment'] = [cirq.Moment()] * (pad_len * 2 + n_acc)
1528
+ buffer: MutableSequence[cirq.Moment] = [cirq.Moment()] * (pad_len * 2 + n_acc)
1514
1529
 
1515
1530
  # Put the initial circuit in the center of the buffer.
1516
1531
  offset = pad_len
@@ -1522,7 +1537,7 @@ class AbstractCircuit(abc.ABC):
1522
1537
 
1523
1538
  return cirq.Circuit(buffer[offset : offset + n_acc])
1524
1539
 
1525
- def get_independent_qubit_sets(self) -> List[Set['cirq.Qid']]:
1540
+ def get_independent_qubit_sets(self) -> List[Set[cirq.Qid]]:
1526
1541
  """Divide circuit's qubits into independent qubit sets.
1527
1542
 
1528
1543
  Independent qubit sets are the qubit sets such that there are
@@ -1601,17 +1616,17 @@ class AbstractCircuit(abc.ABC):
1601
1616
  # moments.
1602
1617
  return (self._from_moments(m[qubits] for m in self.moments) for qubits in qubit_factors)
1603
1618
 
1604
- def _control_keys_(self) -> FrozenSet['cirq.MeasurementKey']:
1619
+ def _control_keys_(self) -> FrozenSet[cirq.MeasurementKey]:
1605
1620
  controls = frozenset(k for op in self.all_operations() for k in protocols.control_keys(op))
1606
1621
  return controls - protocols.measurement_key_objs(self)
1607
1622
 
1608
1623
 
1609
1624
  def _overlap_collision_time(
1610
- c1: Sequence['cirq.Moment'], c2: Sequence['cirq.Moment'], align: 'cirq.Alignment'
1625
+ c1: Sequence[cirq.Moment], c2: Sequence[cirq.Moment], align: cirq.Alignment
1611
1626
  ) -> int:
1612
1627
  # Tracks the first used moment index for each qubit in c2.
1613
1628
  # Tracks the complementary last used moment index for each qubit in c1.
1614
- seen_times: Dict['cirq.Qid', int] = {}
1629
+ seen_times: Dict[cirq.Qid, int] = {}
1615
1630
 
1616
1631
  # Start scanning from end of first and start of second.
1617
1632
  if align == Alignment.LEFT:
@@ -1649,9 +1664,9 @@ def _overlap_collision_time(
1649
1664
  def _concat_ragged_helper(
1650
1665
  c1_offset: int,
1651
1666
  n1: int,
1652
- buf: MutableSequence['cirq.Moment'],
1653
- c2: Sequence['cirq.Moment'],
1654
- align: 'cirq.Alignment',
1667
+ buf: MutableSequence[cirq.Moment],
1668
+ c2: Sequence[cirq.Moment],
1669
+ align: cirq.Alignment,
1655
1670
  ) -> Tuple[int, int]:
1656
1671
  n2 = len(c2)
1657
1672
  shift = _overlap_collision_time(buf[c1_offset : c1_offset + n1], c2, align)
@@ -1742,7 +1757,7 @@ class Circuit(AbstractCircuit):
1742
1757
  """
1743
1758
 
1744
1759
  def __init__(
1745
- self, *contents: 'cirq.OP_TREE', strategy: 'cirq.InsertStrategy' = InsertStrategy.EARLIEST
1760
+ self, *contents: cirq.OP_TREE, strategy: cirq.InsertStrategy = InsertStrategy.EARLIEST
1746
1761
  ) -> None:
1747
1762
  """Initializes a circuit.
1748
1763
 
@@ -1757,19 +1772,22 @@ class Circuit(AbstractCircuit):
1757
1772
  together. This option does not affect later insertions into the
1758
1773
  circuit.
1759
1774
  """
1760
- self._moments: List['cirq.Moment'] = []
1775
+ self._placement_cache: Optional[_PlacementCache] = _PlacementCache()
1776
+ self._moments: List[cirq.Moment] = []
1761
1777
 
1762
1778
  # Implementation note: the following cached properties are set lazily and then
1763
1779
  # invalidated and reset to None in `self._mutated()`, which is called any time
1764
1780
  # `self._moments` is changed.
1765
- self._all_qubits: Optional[FrozenSet['cirq.Qid']] = None
1766
- self._frozen: Optional['cirq.FrozenCircuit'] = None
1781
+ self._all_qubits: Optional[FrozenSet[cirq.Qid]] = None
1782
+ self._frozen: Optional[cirq.FrozenCircuit] = None
1767
1783
  self._is_measurement: Optional[bool] = None
1768
1784
  self._is_parameterized: Optional[bool] = None
1769
1785
  self._parameter_names: Optional[AbstractSet[str]] = None
1770
-
1786
+ if not contents:
1787
+ return
1771
1788
  flattened_contents = tuple(ops.flatten_to_ops_or_moments(contents))
1772
1789
  if all(isinstance(c, Moment) for c in flattened_contents):
1790
+ self._placement_cache = None
1773
1791
  self._moments[:] = cast(Iterable[Moment], flattened_contents)
1774
1792
  return
1775
1793
  with _compat.block_overlapping_deprecation('.*'):
@@ -1778,21 +1796,24 @@ class Circuit(AbstractCircuit):
1778
1796
  else:
1779
1797
  self.append(flattened_contents, strategy=strategy)
1780
1798
 
1781
- def _mutated(self) -> None:
1799
+ def _mutated(self, *, preserve_placement_cache=False) -> None:
1782
1800
  """Clear cached properties in response to this circuit being mutated."""
1783
1801
  self._all_qubits = None
1784
1802
  self._frozen = None
1785
1803
  self._is_measurement = None
1786
1804
  self._is_parameterized = None
1787
1805
  self._parameter_names = None
1806
+ if not preserve_placement_cache:
1807
+ self._placement_cache = None
1788
1808
 
1789
1809
  @classmethod
1790
- def _from_moments(cls, moments: Iterable['cirq.Moment']) -> 'Circuit':
1810
+ def _from_moments(cls, moments: Iterable[cirq.Moment]) -> Circuit:
1791
1811
  new_circuit = Circuit()
1792
1812
  new_circuit._moments[:] = moments
1813
+ new_circuit._placement_cache = None
1793
1814
  return new_circuit
1794
1815
 
1795
- def _load_contents_with_earliest_strategy(self, contents: 'cirq.OP_TREE'):
1816
+ def _load_contents_with_earliest_strategy(self, contents: cirq.OP_TREE):
1796
1817
  """Optimized algorithm to load contents quickly.
1797
1818
 
1798
1819
  The default algorithm appends operations one-at-a-time, letting them
@@ -1811,27 +1832,18 @@ class Circuit(AbstractCircuit):
1811
1832
  Non-moment entries will be inserted according to the EARLIEST
1812
1833
  insertion strategy.
1813
1834
  """
1814
- # These are dicts from the qubit/key to the greatest moment index that has it.
1815
- qubit_indices: Dict['cirq.Qid', int] = {}
1816
- mkey_indices: Dict['cirq.MeasurementKey', int] = {}
1817
- ckey_indices: Dict['cirq.MeasurementKey', int] = {}
1835
+ # PlacementCache holds dicts from the qubit/key to the greatest moment index that has it.
1836
+ placement_cache = cast(_PlacementCache, self._placement_cache)
1818
1837
 
1819
1838
  # We also maintain the dict from moment index to moments/ops that go into it, for use when
1820
1839
  # building the actual moments at the end.
1821
- op_lists_by_index: Dict[int, List['cirq.Operation']] = defaultdict(list)
1822
- moments_by_index: Dict[int, 'cirq.Moment'] = {}
1823
-
1824
- # For keeping track of length of the circuit thus far.
1825
- length = 0
1840
+ op_lists_by_index: Dict[int, List[cirq.Operation]] = defaultdict(list)
1841
+ moments_by_index: Dict[int, cirq.Moment] = {}
1826
1842
 
1827
1843
  # "mop" means current moment-or-operation
1828
1844
  for mop in ops.flatten_to_ops_or_moments(contents):
1829
1845
  # Identify the index of the moment to place this `mop` into.
1830
- placement_index = get_earliest_accommodating_moment_index(
1831
- mop, qubit_indices, mkey_indices, ckey_indices, length
1832
- )
1833
- length = max(length, placement_index + 1) # update the length of the circuit thus far
1834
-
1846
+ placement_index = placement_cache.append(mop)
1835
1847
  if isinstance(mop, Moment):
1836
1848
  moments_by_index[placement_index] = mop
1837
1849
  else:
@@ -1839,16 +1851,16 @@ class Circuit(AbstractCircuit):
1839
1851
 
1840
1852
  # Finally, once everything is placed, we can construct and append the actual moments for
1841
1853
  # each index.
1842
- for i in range(length):
1854
+ for i in range(placement_cache._length):
1843
1855
  if i in moments_by_index:
1844
1856
  self._moments.append(moments_by_index[i].with_operations(op_lists_by_index[i]))
1845
1857
  else:
1846
1858
  self._moments.append(Moment(op_lists_by_index[i]))
1847
1859
 
1848
- def __copy__(self) -> 'cirq.Circuit':
1860
+ def __copy__(self) -> cirq.Circuit:
1849
1861
  return self.copy()
1850
1862
 
1851
- def freeze(self) -> 'cirq.FrozenCircuit':
1863
+ def freeze(self) -> cirq.FrozenCircuit:
1852
1864
  """Gets a frozen version of this circuit.
1853
1865
 
1854
1866
  Repeated calls to `.freeze()` will return the same FrozenCircuit
@@ -1857,13 +1869,13 @@ class Circuit(AbstractCircuit):
1857
1869
  from cirq.circuits.frozen_circuit import FrozenCircuit
1858
1870
 
1859
1871
  if self._frozen is None:
1860
- self._frozen = FrozenCircuit.from_moments(*self._moments)
1872
+ self._frozen = FrozenCircuit._from_moments(self._moments)
1861
1873
  return self._frozen
1862
1874
 
1863
- def unfreeze(self, copy: bool = True) -> 'cirq.Circuit':
1875
+ def unfreeze(self, copy: bool = True) -> cirq.Circuit:
1864
1876
  return self.copy() if copy else self
1865
1877
 
1866
- def all_qubits(self) -> FrozenSet['cirq.Qid']:
1878
+ def all_qubits(self) -> FrozenSet[cirq.Qid]:
1867
1879
  if self._all_qubits is None:
1868
1880
  self._all_qubits = super().all_qubits()
1869
1881
  return self._all_qubits
@@ -1883,19 +1895,20 @@ class Circuit(AbstractCircuit):
1883
1895
  self._parameter_names = super()._parameter_names_()
1884
1896
  return self._parameter_names
1885
1897
 
1886
- def copy(self) -> 'Circuit':
1898
+ def copy(self) -> Circuit:
1887
1899
  """Return a copy of this circuit."""
1888
1900
  copied_circuit = Circuit()
1889
1901
  copied_circuit._moments = self._moments[:]
1902
+ copied_circuit._placement_cache = None
1890
1903
  return copied_circuit
1891
1904
 
1892
1905
  # pylint: disable=function-redefined
1893
1906
  @overload
1894
- def __setitem__(self, key: int, value: 'cirq.Moment'):
1907
+ def __setitem__(self, key: int, value: cirq.Moment):
1895
1908
  pass
1896
1909
 
1897
1910
  @overload
1898
- def __setitem__(self, key: slice, value: Iterable['cirq.Moment']):
1911
+ def __setitem__(self, key: slice, value: Iterable[cirq.Moment]):
1899
1912
  pass
1900
1913
 
1901
1914
  def __setitem__(self, key, value):
@@ -1956,7 +1969,7 @@ class Circuit(AbstractCircuit):
1956
1969
  return NotImplemented
1957
1970
  return self * int(repetitions)
1958
1971
 
1959
- def __pow__(self, exponent: int) -> 'cirq.Circuit':
1972
+ def __pow__(self, exponent: int) -> cirq.Circuit:
1960
1973
  """A circuit raised to a power, only valid for exponent -1, the inverse.
1961
1974
 
1962
1975
  This will fail if anything other than -1 is passed to the Circuit by
@@ -1980,24 +1993,42 @@ class Circuit(AbstractCircuit):
1980
1993
  __hash__ = None # type: ignore
1981
1994
 
1982
1995
  def concat_ragged(
1983
- *circuits: 'cirq.AbstractCircuit', align: Union['cirq.Alignment', str] = Alignment.LEFT
1984
- ) -> 'cirq.Circuit':
1996
+ *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
1997
+ ) -> cirq.Circuit:
1985
1998
  return AbstractCircuit.concat_ragged(*circuits, align=align).unfreeze(copy=False)
1986
1999
 
1987
2000
  concat_ragged.__doc__ = AbstractCircuit.concat_ragged.__doc__
1988
2001
 
1989
2002
  def zip(
1990
- *circuits: 'cirq.AbstractCircuit', align: Union['cirq.Alignment', str] = Alignment.LEFT
1991
- ) -> 'cirq.Circuit':
2003
+ *circuits: cirq.AbstractCircuit, align: Union[cirq.Alignment, str] = Alignment.LEFT
2004
+ ) -> cirq.Circuit:
1992
2005
  return AbstractCircuit.zip(*circuits, align=align).unfreeze(copy=False)
1993
2006
 
1994
2007
  zip.__doc__ = AbstractCircuit.zip.__doc__
1995
2008
 
1996
2009
  def transform_qubits(
1997
- self, qubit_map: Union[Dict['cirq.Qid', 'cirq.Qid'], Callable[['cirq.Qid'], 'cirq.Qid']]
1998
- ) -> 'cirq.Circuit':
2010
+ self, qubit_map: Union[Dict[cirq.Qid, cirq.Qid], Callable[[cirq.Qid], cirq.Qid]]
2011
+ ) -> cirq.Circuit:
1999
2012
  """Returns the same circuit, but with different qubits.
2000
2013
 
2014
+ This function will return a new `Circuit` with the same gates but
2015
+ with qubits mapped according to the argument.
2016
+
2017
+ For example, the following will translate LineQubits to GridQubits:
2018
+
2019
+ >>> grid_qubits = cirq.GridQubit.square(2)
2020
+ >>> line_qubits = cirq.LineQubit.range(4)
2021
+ >>> circuit = cirq.Circuit([cirq.H(q) for q in line_qubits])
2022
+ >>> circuit.transform_qubits(lambda q : grid_qubits[q.x])
2023
+ cirq.Circuit([
2024
+ cirq.Moment(
2025
+ cirq.H(cirq.GridQubit(0, 0)),
2026
+ cirq.H(cirq.GridQubit(0, 1)),
2027
+ cirq.H(cirq.GridQubit(1, 0)),
2028
+ cirq.H(cirq.GridQubit(1, 1)),
2029
+ ),
2030
+ ])
2031
+
2001
2032
  Args:
2002
2033
  qubit_map: A function or a dict mapping each current qubit into a desired
2003
2034
  new qubit.
@@ -2012,7 +2043,7 @@ class Circuit(AbstractCircuit):
2012
2043
  if callable(qubit_map):
2013
2044
  transform = qubit_map
2014
2045
  elif isinstance(qubit_map, dict):
2015
- transform = lambda q: qubit_map.get(q, q) # type: ignore
2046
+ transform = lambda q: qubit_map.get(q, q)
2016
2047
  else:
2017
2048
  raise TypeError('qubit_map must be a function or dict mapping qubits to qubits.')
2018
2049
 
@@ -2024,7 +2055,7 @@ class Circuit(AbstractCircuit):
2024
2055
  return Circuit(op_list)
2025
2056
 
2026
2057
  def earliest_available_moment(
2027
- self, op: 'cirq.Operation', *, end_moment_index: Optional[int] = None
2058
+ self, op: cirq.Operation, *, end_moment_index: Optional[int] = None
2028
2059
  ) -> int:
2029
2060
  """Finds the index of the earliest (i.e. left most) moment which can accommodate `op`.
2030
2061
 
@@ -2067,50 +2098,7 @@ class Circuit(AbstractCircuit):
2067
2098
  last_available = k
2068
2099
  return last_available
2069
2100
 
2070
- def _pick_or_create_inserted_op_moment_index(
2071
- self, splitter_index: int, op: 'cirq.Operation', strategy: 'cirq.InsertStrategy'
2072
- ) -> int:
2073
- """Determines and prepares where an insertion will occur.
2074
-
2075
- Args:
2076
- splitter_index: The index to insert at.
2077
- op: The operation that will be inserted.
2078
- strategy: The insertion strategy.
2079
-
2080
- Returns:
2081
- The index of the (possibly new) moment where the insertion should
2082
- occur.
2083
-
2084
- Raises:
2085
- ValueError: Unrecognized append strategy.
2086
- """
2087
-
2088
- if strategy is InsertStrategy.NEW or strategy is InsertStrategy.NEW_THEN_INLINE:
2089
- self._moments.insert(splitter_index, Moment())
2090
- self._mutated()
2091
- return splitter_index
2092
-
2093
- if strategy is InsertStrategy.INLINE:
2094
- if 0 <= splitter_index - 1 < len(self._moments) and self._can_add_op_at(
2095
- splitter_index - 1, op
2096
- ):
2097
- return splitter_index - 1
2098
-
2099
- return self._pick_or_create_inserted_op_moment_index(
2100
- splitter_index, op, InsertStrategy.NEW
2101
- )
2102
-
2103
- if strategy is InsertStrategy.EARLIEST:
2104
- if self._can_add_op_at(splitter_index, op):
2105
- return self.earliest_available_moment(op, end_moment_index=splitter_index)
2106
-
2107
- return self._pick_or_create_inserted_op_moment_index(
2108
- splitter_index, op, InsertStrategy.INLINE
2109
- )
2110
-
2111
- raise ValueError(f'Unrecognized append strategy: {strategy}')
2112
-
2113
- def _can_add_op_at(self, moment_index: int, operation: 'cirq.Operation') -> bool:
2101
+ def _can_add_op_at(self, moment_index: int, operation: cirq.Operation) -> bool:
2114
2102
  if not 0 <= moment_index < len(self._moments):
2115
2103
  return True
2116
2104
 
@@ -2119,8 +2107,8 @@ class Circuit(AbstractCircuit):
2119
2107
  def insert(
2120
2108
  self,
2121
2109
  index: int,
2122
- moment_or_operation_tree: Union['cirq.Operation', 'cirq.OP_TREE'],
2123
- strategy: 'cirq.InsertStrategy' = InsertStrategy.EARLIEST,
2110
+ moment_or_operation_tree: cirq.OP_TREE,
2111
+ strategy: cirq.InsertStrategy = InsertStrategy.EARLIEST,
2124
2112
  ) -> int:
2125
2113
  """Inserts operations into the circuit.
2126
2114
 
@@ -2142,23 +2130,61 @@ class Circuit(AbstractCircuit):
2142
2130
  """
2143
2131
  # limit index to 0..len(self._moments), also deal with indices smaller 0
2144
2132
  k = max(min(index if index >= 0 else len(self._moments) + index, len(self._moments)), 0)
2145
- for moment_or_op in list(ops.flatten_to_ops_or_moments(moment_or_operation_tree)):
2146
- if isinstance(moment_or_op, Moment):
2147
- self._moments.insert(k, moment_or_op)
2148
- k += 1
2149
- else:
2150
- op = moment_or_op
2151
- p = self._pick_or_create_inserted_op_moment_index(k, op, strategy)
2152
- while p >= len(self._moments):
2153
- self._moments.append(Moment())
2154
- self._moments[p] = self._moments[p].with_operation(op)
2155
- k = max(k, p + 1)
2133
+ if strategy != InsertStrategy.EARLIEST or k != len(self._moments):
2134
+ self._placement_cache = None
2135
+ mops = list(ops.flatten_to_ops_or_moments(moment_or_operation_tree))
2136
+ if self._placement_cache:
2137
+ batches = [mops] # Any grouping would work here; this just happens to be the fastest.
2138
+ elif strategy is InsertStrategy.NEW:
2139
+ batches = [[mop] for mop in mops] # Each op goes into its own moment.
2140
+ else:
2141
+ batches = list(_group_into_moment_compatible(mops))
2142
+ for batch in batches:
2143
+ # Insert a moment if inline/earliest and _any_ op in the batch requires it.
2144
+ if (
2145
+ not self._placement_cache
2146
+ and not isinstance(batch[0], Moment)
2147
+ and strategy in (InsertStrategy.INLINE, InsertStrategy.EARLIEST)
2148
+ and not all(
2149
+ (strategy is InsertStrategy.EARLIEST and self._can_add_op_at(k, op))
2150
+ or (k > 0 and self._can_add_op_at(k - 1, op))
2151
+ for op in cast(List[cirq.Operation], batch)
2152
+ )
2153
+ ):
2154
+ self._moments.insert(k, Moment())
2155
+ if strategy is InsertStrategy.INLINE:
2156
+ k += 1
2157
+ max_p = 0
2158
+ for moment_or_op in batch:
2159
+ # Determine Placement
2160
+ if self._placement_cache:
2161
+ p = self._placement_cache.append(moment_or_op)
2162
+ elif isinstance(moment_or_op, Moment):
2163
+ p = k
2164
+ elif strategy in (InsertStrategy.NEW, InsertStrategy.NEW_THEN_INLINE):
2165
+ self._moments.insert(k, Moment())
2166
+ p = k
2167
+ elif strategy is InsertStrategy.INLINE:
2168
+ p = k - 1
2169
+ else: # InsertStrategy.EARLIEST:
2170
+ p = self.earliest_available_moment(moment_or_op, end_moment_index=k)
2171
+ # Place
2172
+ if isinstance(moment_or_op, Moment):
2173
+ self._moments.insert(p, moment_or_op)
2174
+ elif p == len(self._moments):
2175
+ self._moments.append(Moment(moment_or_op))
2176
+ else:
2177
+ self._moments[p] = self._moments[p].with_operation(moment_or_op)
2178
+ # Iterate
2179
+ max_p = max(p, max_p)
2156
2180
  if strategy is InsertStrategy.NEW_THEN_INLINE:
2157
2181
  strategy = InsertStrategy.INLINE
2158
- self._mutated()
2182
+ k += 1
2183
+ k = max(k, max_p + 1)
2184
+ self._mutated(preserve_placement_cache=True)
2159
2185
  return k
2160
2186
 
2161
- def insert_into_range(self, operations: 'cirq.OP_TREE', start: int, end: int) -> int:
2187
+ def insert_into_range(self, operations: cirq.OP_TREE, start: int, end: int) -> int:
2162
2188
  """Writes operations inline into an area of the circuit.
2163
2189
 
2164
2190
  Args:
@@ -2201,9 +2227,9 @@ class Circuit(AbstractCircuit):
2201
2227
 
2202
2228
  def _push_frontier(
2203
2229
  self,
2204
- early_frontier: Dict['cirq.Qid', int],
2205
- late_frontier: Dict['cirq.Qid', int],
2206
- update_qubits: Optional[Iterable['cirq.Qid']] = None,
2230
+ early_frontier: Dict[cirq.Qid, int],
2231
+ late_frontier: Dict[cirq.Qid, int],
2232
+ update_qubits: Optional[Iterable[cirq.Qid]] = None,
2207
2233
  ) -> Tuple[int, int]:
2208
2234
  """Inserts moments to separate two frontiers.
2209
2235
 
@@ -2246,7 +2272,7 @@ class Circuit(AbstractCircuit):
2246
2272
  return (0, 0)
2247
2273
 
2248
2274
  def _insert_operations(
2249
- self, operations: Sequence['cirq.Operation'], insertion_indices: Sequence[int]
2275
+ self, operations: Sequence[cirq.Operation], insertion_indices: Sequence[int]
2250
2276
  ) -> None:
2251
2277
  """Inserts operations at the specified moments. Appends new moments if
2252
2278
  necessary.
@@ -2266,18 +2292,15 @@ class Circuit(AbstractCircuit):
2266
2292
  raise ValueError('operations and insertion_indices must have the same length.')
2267
2293
  self._moments += [Moment() for _ in range(1 + max(insertion_indices) - len(self))]
2268
2294
  self._mutated()
2269
- moment_to_ops: Dict[int, List['cirq.Operation']] = defaultdict(list)
2295
+ moment_to_ops: Dict[int, List[cirq.Operation]] = defaultdict(list)
2270
2296
  for op_index, moment_index in enumerate(insertion_indices):
2271
2297
  moment_to_ops[moment_index].append(operations[op_index])
2272
2298
  for moment_index, new_ops in moment_to_ops.items():
2273
2299
  self._moments[moment_index] = self._moments[moment_index].with_operations(*new_ops)
2274
2300
 
2275
2301
  def insert_at_frontier(
2276
- self,
2277
- operations: 'cirq.OP_TREE',
2278
- start: int,
2279
- frontier: Optional[Dict['cirq.Qid', int]] = None,
2280
- ) -> Dict['cirq.Qid', int]:
2302
+ self, operations: cirq.OP_TREE, start: int, frontier: Optional[Dict[cirq.Qid, int]] = None
2303
+ ) -> Dict[cirq.Qid, int]:
2281
2304
  """Inserts operations inline at frontier.
2282
2305
 
2283
2306
  Args:
@@ -2293,7 +2316,7 @@ class Circuit(AbstractCircuit):
2293
2316
  frontier = defaultdict(lambda: 0)
2294
2317
  flat_ops = tuple(ops.flatten_to_ops(operations))
2295
2318
  if not flat_ops:
2296
- return frontier
2319
+ return frontier # pragma: no cover
2297
2320
  qubits = set(q for op in flat_ops for q in op.qubits)
2298
2321
  if any(frontier[q] > start for q in qubits):
2299
2322
  raise ValueError(
@@ -2311,7 +2334,7 @@ class Circuit(AbstractCircuit):
2311
2334
 
2312
2335
  return frontier
2313
2336
 
2314
- def batch_remove(self, removals: Iterable[Tuple[int, 'cirq.Operation']]) -> None:
2337
+ def batch_remove(self, removals: Iterable[Tuple[int, cirq.Operation]]) -> None:
2315
2338
  """Removes several operations from a circuit.
2316
2339
 
2317
2340
  Args:
@@ -2335,7 +2358,7 @@ class Circuit(AbstractCircuit):
2335
2358
  self._mutated()
2336
2359
 
2337
2360
  def batch_replace(
2338
- self, replacements: Iterable[Tuple[int, 'cirq.Operation', 'cirq.Operation']]
2361
+ self, replacements: Iterable[Tuple[int, cirq.Operation, cirq.Operation]]
2339
2362
  ) -> None:
2340
2363
  """Replaces several operations in a circuit with new operations.
2341
2364
 
@@ -2359,7 +2382,7 @@ class Circuit(AbstractCircuit):
2359
2382
  self._moments = copy._moments
2360
2383
  self._mutated()
2361
2384
 
2362
- def batch_insert_into(self, insert_intos: Iterable[Tuple[int, 'cirq.OP_TREE']]) -> None:
2385
+ def batch_insert_into(self, insert_intos: Iterable[Tuple[int, cirq.OP_TREE]]) -> None:
2363
2386
  """Inserts operations into empty spaces in existing moments.
2364
2387
 
2365
2388
  If any of the insertions fails (due to colliding with an existing
@@ -2380,7 +2403,7 @@ class Circuit(AbstractCircuit):
2380
2403
  self._moments = copy._moments
2381
2404
  self._mutated()
2382
2405
 
2383
- def batch_insert(self, insertions: Iterable[Tuple[int, 'cirq.OP_TREE']]) -> None:
2406
+ def batch_insert(self, insertions: Iterable[Tuple[int, cirq.OP_TREE]]) -> None:
2384
2407
  """Applies a batched insert operation to the circuit.
2385
2408
 
2386
2409
  Transparently handles the fact that earlier insertions may shift
@@ -2417,8 +2440,8 @@ class Circuit(AbstractCircuit):
2417
2440
 
2418
2441
  def append(
2419
2442
  self,
2420
- moment_or_operation_tree: Union['cirq.Moment', 'cirq.OP_TREE'],
2421
- strategy: 'cirq.InsertStrategy' = InsertStrategy.EARLIEST,
2443
+ moment_or_operation_tree: cirq.OP_TREE,
2444
+ strategy: cirq.InsertStrategy = InsertStrategy.EARLIEST,
2422
2445
  ) -> None:
2423
2446
  """Appends operations onto the end of the circuit.
2424
2447
 
@@ -2430,9 +2453,7 @@ class Circuit(AbstractCircuit):
2430
2453
  """
2431
2454
  self.insert(len(self._moments), moment_or_operation_tree, strategy)
2432
2455
 
2433
- def clear_operations_touching(
2434
- self, qubits: Iterable['cirq.Qid'], moment_indices: Iterable[int]
2435
- ):
2456
+ def clear_operations_touching(self, qubits: Iterable[cirq.Qid], moment_indices: Iterable[int]):
2436
2457
  """Clears operations that are touching given qubits at given moments.
2437
2458
 
2438
2459
  Args:
@@ -2447,10 +2468,10 @@ class Circuit(AbstractCircuit):
2447
2468
  self._mutated()
2448
2469
 
2449
2470
  @property
2450
- def moments(self) -> Sequence['cirq.Moment']:
2471
+ def moments(self) -> Sequence[cirq.Moment]:
2451
2472
  return self._moments
2452
2473
 
2453
- def with_noise(self, noise: 'cirq.NOISE_MODEL_LIKE') -> 'cirq.Circuit':
2474
+ def with_noise(self, noise: cirq.NOISE_MODEL_LIKE) -> cirq.Circuit:
2454
2475
  """Make a noisy version of the circuit.
2455
2476
 
2456
2477
  Args:
@@ -2472,10 +2493,10 @@ class Circuit(AbstractCircuit):
2472
2493
 
2473
2494
 
2474
2495
  def _pick_inserted_ops_moment_indices(
2475
- operations: Sequence['cirq.Operation'],
2496
+ operations: Sequence[cirq.Operation],
2476
2497
  start: int = 0,
2477
- frontier: Optional[Dict['cirq.Qid', int]] = None,
2478
- ) -> Tuple[Sequence[int], Dict['cirq.Qid', int]]:
2498
+ frontier: Optional[Dict[cirq.Qid, int]] = None,
2499
+ ) -> Tuple[Sequence[int], Dict[cirq.Qid, int]]:
2479
2500
  """Greedily assigns operations to moments.
2480
2501
 
2481
2502
  Args:
@@ -2501,7 +2522,7 @@ def _pick_inserted_ops_moment_indices(
2501
2522
  return moment_indices, frontier
2502
2523
 
2503
2524
 
2504
- def _get_moment_annotations(moment: 'cirq.Moment') -> Iterator['cirq.Operation']:
2525
+ def _get_moment_annotations(moment: cirq.Moment) -> Iterator[cirq.Operation]:
2505
2526
  for op in moment.operations:
2506
2527
  if op.qubits:
2507
2528
  continue
@@ -2517,14 +2538,14 @@ def _get_moment_annotations(moment: 'cirq.Moment') -> Iterator['cirq.Operation']
2517
2538
 
2518
2539
  def _draw_moment_annotations(
2519
2540
  *,
2520
- moment: 'cirq.Moment',
2541
+ moment: cirq.Moment,
2521
2542
  col: int,
2522
2543
  use_unicode_characters: bool,
2523
- label_map: Dict['cirq.LabelEntity', int],
2524
- out_diagram: 'cirq.TextDiagramDrawer',
2544
+ label_map: Dict[cirq.LabelEntity, int],
2545
+ out_diagram: cirq.TextDiagramDrawer,
2525
2546
  precision: Optional[int],
2526
2547
  get_circuit_diagram_info: Callable[
2527
- ['cirq.Operation', 'cirq.CircuitDiagramInfoArgs'], 'cirq.CircuitDiagramInfo'
2548
+ [cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo
2528
2549
  ],
2529
2550
  include_tags: bool,
2530
2551
  first_annotation_row: int,
@@ -2549,14 +2570,14 @@ def _draw_moment_annotations(
2549
2570
 
2550
2571
  def _draw_moment_in_diagram(
2551
2572
  *,
2552
- moment: 'cirq.Moment',
2573
+ moment: cirq.Moment,
2553
2574
  use_unicode_characters: bool,
2554
- label_map: Dict['cirq.LabelEntity', int],
2555
- out_diagram: 'cirq.TextDiagramDrawer',
2575
+ label_map: Dict[cirq.LabelEntity, int],
2576
+ out_diagram: cirq.TextDiagramDrawer,
2556
2577
  precision: Optional[int],
2557
2578
  moment_groups: List[Tuple[int, int]],
2558
2579
  get_circuit_diagram_info: Optional[
2559
- Callable[['cirq.Operation', 'cirq.CircuitDiagramInfoArgs'], 'cirq.CircuitDiagramInfo']
2580
+ Callable[[cirq.Operation, cirq.CircuitDiagramInfoArgs], cirq.CircuitDiagramInfo]
2560
2581
  ],
2561
2582
  include_tags: bool,
2562
2583
  first_annotation_row: int,
@@ -2641,7 +2662,7 @@ def _draw_moment_in_diagram(
2641
2662
  moment_groups.append((x0, max_x))
2642
2663
 
2643
2664
 
2644
- def _get_global_phase_and_tags_for_op(op: 'cirq.Operation') -> Tuple[Optional[complex], List[Any]]:
2665
+ def _get_global_phase_and_tags_for_op(op: cirq.Operation) -> Tuple[Optional[complex], List[Any]]:
2645
2666
  if isinstance(op.gate, ops.GlobalPhaseGate):
2646
2667
  return complex(op.gate.coefficient), list(op.tags)
2647
2668
  elif isinstance(op.untagged, CircuitOperation):
@@ -2675,7 +2696,7 @@ def _formatted_phase(coefficient: complex, unicode: bool, precision: Optional[in
2675
2696
  def _draw_moment_groups_in_diagram(
2676
2697
  moment_groups: List[Tuple[int, int]],
2677
2698
  use_unicode_characters: bool,
2678
- out_diagram: 'cirq.TextDiagramDrawer',
2699
+ out_diagram: cirq.TextDiagramDrawer,
2679
2700
  ):
2680
2701
  out_diagram.insert_empty_rows(0)
2681
2702
  h = out_diagram.height()
@@ -2705,9 +2726,9 @@ def _draw_moment_groups_in_diagram(
2705
2726
 
2706
2727
 
2707
2728
  def _apply_unitary_circuit(
2708
- circuit: 'cirq.AbstractCircuit',
2729
+ circuit: cirq.AbstractCircuit,
2709
2730
  state: np.ndarray,
2710
- qubits: Tuple['cirq.Qid', ...],
2731
+ qubits: Tuple[cirq.Qid, ...],
2711
2732
  dtype: Type[np.complexfloating],
2712
2733
  ) -> np.ndarray:
2713
2734
  """Applies a circuit's unitary effect to the given vector or matrix.
@@ -2751,7 +2772,7 @@ def _apply_unitary_circuit(
2751
2772
  return result
2752
2773
 
2753
2774
 
2754
- def _decompose_measurement_inversions(op: 'cirq.Operation') -> 'cirq.OP_TREE':
2775
+ def _decompose_measurement_inversions(op: cirq.Operation) -> cirq.OP_TREE:
2755
2776
  if isinstance(op.gate, ops.MeasurementGate):
2756
2777
  return [ops.X(q) for q, b in zip(op.qubits, op.gate.invert_mask) if b]
2757
2778
  return NotImplemented
@@ -2808,11 +2829,43 @@ def _group_until_different(items: Iterable[_TIn], key: Callable[[_TIn], _TKey],
2808
2829
  return ((k, [val(i) for i in v]) for (k, v) in itertools.groupby(items, key))
2809
2830
 
2810
2831
 
2832
+ def _group_into_moment_compatible(inputs: Sequence[_MOMENT_OR_OP]) -> Iterator[List[_MOMENT_OR_OP]]:
2833
+ """Groups sequential ops into those that can coexist in a single moment.
2834
+
2835
+ This function will go through the input sequence in order, emitting lists of sequential
2836
+ operations that can go into a single moment. It does not try to rearrange the elements or try
2837
+ to move them to open slots in earlier moments; it simply processes them in order and outputs
2838
+ them. i.e. the output, if flattened, will equal the input.
2839
+
2840
+ Actual Moments in the input will always be emitted by themselves as a single-element list.
2841
+
2842
+ Examples:
2843
+ [X(a), X(b), X(a)] -> [[X(a), X(b)], [X(a)]]
2844
+ [X(a), X(a), X(b)] -> [[X(a)], [X(a), X(b)]]
2845
+ [X(a), Moment(X(b)), X(c)] -> [[X(a)], [Moment(X(b))], [X(c)]]
2846
+ """
2847
+ batch: List[_MOMENT_OR_OP] = []
2848
+ batch_qubits: Set[cirq.Qid] = set()
2849
+ for mop in inputs:
2850
+ is_moment = isinstance(mop, cirq.Moment)
2851
+ if (is_moment and batch) or not batch_qubits.isdisjoint(mop.qubits):
2852
+ yield batch
2853
+ batch = []
2854
+ batch_qubits.clear()
2855
+ if is_moment:
2856
+ yield [mop]
2857
+ continue
2858
+ batch.append(mop)
2859
+ batch_qubits.update(mop.qubits)
2860
+ if batch:
2861
+ yield batch
2862
+
2863
+
2811
2864
  def get_earliest_accommodating_moment_index(
2812
- moment_or_operation: Union['cirq.Moment', 'cirq.Operation'],
2813
- qubit_indices: Dict['cirq.Qid', int],
2814
- mkey_indices: Dict['cirq.MeasurementKey', int],
2815
- ckey_indices: Dict['cirq.MeasurementKey', int],
2865
+ 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],
2816
2869
  length: Optional[int] = None,
2817
2870
  ) -> int:
2818
2871
  """Get the index of the earliest moment that can accommodate the given moment or operation.
@@ -2823,7 +2876,7 @@ def get_earliest_accommodating_moment_index(
2823
2876
  Args:
2824
2877
  moment_or_operation: The moment operation in question.
2825
2878
  qubit_indices: A dictionary mapping qubits to the latest moments that address them.
2826
- mkey_indices: A dictionary mapping measureent keys to the latest moments that address them.
2879
+ mkey_indices: A dictionary mapping measurement keys to the latest moments that address them.
2827
2880
  ckey_indices: A dictionary mapping control keys to the latest moments that address them.
2828
2881
  length: The length of the circuit that we are trying to insert a moment or operation into.
2829
2882
  Should probably be equal to the maximum of the values in `qubit_indices`,
@@ -2867,7 +2920,7 @@ def get_earliest_accommodating_moment_index(
2867
2920
  if mop_ckeys:
2868
2921
  last_conflict = max(last_conflict, *[mkey_indices.get(key, -1) for key in mop_ckeys])
2869
2922
 
2870
- # The index of the moment to place this moment or operaton ("mop") into.
2923
+ # The index of the moment to place this moment or operation ("mop") into.
2871
2924
  mop_index = last_conflict + 1
2872
2925
 
2873
2926
  # Update our dicts with data from this `mop` placement. Note `mop_index` will always be greater
@@ -2880,3 +2933,52 @@ def get_earliest_accommodating_moment_index(
2880
2933
  ckey_indices[key] = mop_index
2881
2934
 
2882
2935
  return mop_index
2936
+
2937
+
2938
+ class _PlacementCache:
2939
+ """Maintains qubit and cbit indices for quick op placement.
2940
+
2941
+ Here, we keep track of the greatest moment that contains each qubit,
2942
+ measurement key, and control key, and append operations to the moment after
2943
+ the maximum of these. This avoids having to iterate backwards, checking
2944
+ each moment one at a time.
2945
+
2946
+ It is only valid for `append` operations, and if any other insert strategy
2947
+ is used, or if any operation is added to the circuit without notifying the
2948
+ cache, then the cache must be invalidated for the circuit or rebuilt from
2949
+ scratch. Future improvements may ease this restriction.
2950
+ """
2951
+
2952
+ def __init__(self) -> None:
2953
+ # 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] = {}
2957
+
2958
+ # For keeping track of length of the circuit thus far.
2959
+ self._length = 0
2960
+
2961
+ def append(self, moment_or_operation: _MOMENT_OR_OP) -> int:
2962
+ """Find placement for moment/operation and update cache.
2963
+
2964
+ Determines the placement index of the provided operation, assuming
2965
+ EARLIEST (append) strategy, and assuming that the internal cache
2966
+ correctly represents the circuit. It then updates the cache and returns
2967
+ the placement index.
2968
+
2969
+ Args:
2970
+ moment_or_operation: The moment or operation to append.
2971
+
2972
+ Returns:
2973
+ The index at which the moment/operation should be placed.
2974
+ """
2975
+ # Identify the index of the moment to place this into.
2976
+ index = get_earliest_accommodating_moment_index(
2977
+ moment_or_operation,
2978
+ self._qubit_indices,
2979
+ self._mkey_indices,
2980
+ self._ckey_indices,
2981
+ self._length,
2982
+ )
2983
+ self._length = max(self._length, index + 1)
2984
+ return index