cirq-core 1.7.0.dev20250825174419__py3-none-any.whl → 1.7.0.dev20251203004401__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (426) hide show
  1. cirq/__init__.py +1 -0
  2. cirq/_compat.py +3 -2
  3. cirq/_compat_test.py +16 -15
  4. cirq/_doc.py +4 -3
  5. cirq/_import.py +2 -1
  6. cirq/_version.py +1 -1
  7. cirq/_version_test.py +1 -1
  8. cirq/circuits/_bucket_priority_queue.py +2 -1
  9. cirq/circuits/circuit.py +19 -17
  10. cirq/circuits/circuit_operation.py +2 -1
  11. cirq/circuits/circuit_operation_test.py +19 -0
  12. cirq/circuits/circuit_test.py +31 -12
  13. cirq/circuits/frozen_circuit.py +3 -2
  14. cirq/circuits/moment.py +3 -15
  15. cirq/circuits/optimization_pass.py +2 -1
  16. cirq/circuits/qasm_output.py +39 -10
  17. cirq/circuits/qasm_output_test.py +51 -2
  18. cirq/circuits/text_diagram_drawer.py +2 -1
  19. cirq/contrib/acquaintance/bipartite.py +2 -1
  20. cirq/contrib/acquaintance/devices.py +1 -1
  21. cirq/contrib/acquaintance/executor.py +4 -5
  22. cirq/contrib/acquaintance/executor_test.py +2 -1
  23. cirq/contrib/acquaintance/gates.py +2 -1
  24. cirq/contrib/acquaintance/gates_test.py +1 -1
  25. cirq/contrib/acquaintance/inspection_utils.py +2 -1
  26. cirq/contrib/acquaintance/mutation_utils.py +2 -1
  27. cirq/contrib/acquaintance/optimizers.py +2 -1
  28. cirq/contrib/acquaintance/permutation.py +2 -1
  29. cirq/contrib/acquaintance/permutation_test.py +1 -1
  30. cirq/contrib/acquaintance/shift.py +2 -1
  31. cirq/contrib/acquaintance/shift_swap_network.py +2 -1
  32. cirq/contrib/acquaintance/strategies/complete.py +3 -2
  33. cirq/contrib/acquaintance/strategies/cubic.py +2 -1
  34. cirq/contrib/acquaintance/strategies/quartic_paired.py +2 -1
  35. cirq/contrib/acquaintance/strategies/quartic_paired_test.py +1 -1
  36. cirq/contrib/acquaintance/testing.py +2 -1
  37. cirq/contrib/acquaintance/topological_sort.py +2 -1
  38. cirq/contrib/bayesian_network/bayesian_network_gate.py +3 -2
  39. cirq/contrib/circuitdag/circuit_dag.py +4 -2
  40. cirq/contrib/custom_simulators/custom_state_simulator.py +2 -1
  41. cirq/contrib/custom_simulators/custom_state_simulator_test.py +1 -1
  42. cirq/contrib/graph_device/graph_device.py +2 -1
  43. cirq/contrib/graph_device/graph_device_test.py +2 -1
  44. cirq/contrib/graph_device/hypergraph.py +2 -1
  45. cirq/contrib/graph_device/uniform_graph_device.py +2 -1
  46. cirq/contrib/json.py +14 -2
  47. cirq/contrib/json_test_data/BayesianNetworkGate.json +10 -0
  48. cirq/contrib/json_test_data/BayesianNetworkGate.repr +3 -0
  49. cirq/contrib/json_test_data/QuantumVolumeResult.json +169 -0
  50. cirq/contrib/json_test_data/QuantumVolumeResult.repr +22 -0
  51. cirq/contrib/json_test_data/SwapPermutationGate.json +3 -0
  52. cirq/contrib/json_test_data/SwapPermutationGate.repr +1 -0
  53. cirq/contrib/json_test_data/spec.py +0 -2
  54. cirq/contrib/noise_models/noise_models.py +2 -1
  55. cirq/contrib/paulistring/clifford_optimize.py +20 -2
  56. cirq/contrib/paulistring/optimize.py +1 -1
  57. cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation.py +146 -35
  58. cirq/contrib/paulistring/pauli_string_measurement_with_readout_mitigation_test.py +81 -178
  59. cirq/contrib/paulistring/recombine.py +5 -2
  60. cirq/contrib/paulistring/separate.py +1 -1
  61. cirq/contrib/qasm_import/_lexer.py +6 -1
  62. cirq/contrib/qasm_import/_lexer_test.py +1 -1
  63. cirq/contrib/qasm_import/_parser.py +24 -8
  64. cirq/contrib/qasm_import/_parser_test.py +44 -6
  65. cirq/contrib/qcircuit/qcircuit_pdf_test.py +6 -9
  66. cirq/contrib/quantikz/__init__.py +21 -0
  67. cirq/contrib/quantikz/circuit_to_latex_quantikz.py +680 -0
  68. cirq/contrib/quantikz/circuit_to_latex_quantikz_test.py +253 -0
  69. cirq/contrib/quantikz/circuit_to_latex_render.py +424 -0
  70. cirq/contrib/quantikz/circuit_to_latex_render_test.py +44 -0
  71. cirq/contrib/quantum_volume/quantum_volume.py +2 -1
  72. cirq/contrib/quimb/density_matrix.py +1 -1
  73. cirq/contrib/quimb/grid_circuits.py +2 -1
  74. cirq/contrib/quimb/grid_circuits_test.py +1 -1
  75. cirq/contrib/quimb/mps_simulator.py +4 -3
  76. cirq/contrib/quimb/state_vector.py +2 -1
  77. cirq/contrib/quirk/export_to_quirk.py +2 -1
  78. cirq/contrib/quirk/linearize_circuit.py +1 -1
  79. cirq/contrib/quirk/quirk_gate.py +2 -1
  80. cirq/contrib/routing/device.py +1 -1
  81. cirq/contrib/routing/greedy.py +2 -1
  82. cirq/contrib/routing/initialization.py +2 -1
  83. cirq/contrib/routing/router.py +2 -1
  84. cirq/contrib/routing/swap_network.py +2 -1
  85. cirq/contrib/routing/utils.py +2 -1
  86. cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking.py +7 -5
  87. cirq/contrib/shuffle_circuits/shuffle_circuits_with_readout_benchmarking_test.py +6 -6
  88. cirq/devices/device.py +2 -1
  89. cirq/devices/grid_device_metadata.py +2 -1
  90. cirq/devices/grid_qubit.py +7 -6
  91. cirq/devices/insertion_noise_model.py +2 -1
  92. cirq/devices/line_qubit.py +2 -1
  93. cirq/devices/named_topologies.py +2 -1
  94. cirq/devices/noise_model.py +2 -1
  95. cirq/devices/noise_model_test.py +1 -1
  96. cirq/devices/noise_properties.py +2 -1
  97. cirq/devices/superconducting_qubits_noise_properties_test.py +2 -1
  98. cirq/devices/thermal_noise_model.py +2 -1
  99. cirq/experiments/__init__.py +2 -0
  100. cirq/experiments/benchmarking/parallel_xeb.py +2 -1
  101. cirq/experiments/benchmarking/parallel_xeb_test.py +1 -1
  102. cirq/experiments/fidelity_estimation.py +2 -1
  103. cirq/experiments/fidelity_estimation_test.py +1 -1
  104. cirq/experiments/ghz_2d.py +150 -0
  105. cirq/experiments/ghz_2d_test.py +155 -0
  106. cirq/experiments/n_qubit_tomography.py +2 -1
  107. cirq/experiments/n_qubit_tomography_test.py +1 -1
  108. cirq/experiments/purity_estimation.py +1 -1
  109. cirq/experiments/qubit_characterizations.py +33 -4
  110. cirq/experiments/qubit_characterizations_test.py +16 -0
  111. cirq/experiments/random_quantum_circuit_generation.py +2 -1
  112. cirq/experiments/random_quantum_circuit_generation_test.py +2 -1
  113. cirq/experiments/readout_confusion_matrix.py +2 -1
  114. cirq/experiments/readout_confusion_matrix_test.py +1 -1
  115. cirq/experiments/single_qubit_readout_calibration.py +2 -1
  116. cirq/experiments/single_qubit_readout_calibration_test.py +1 -1
  117. cirq/experiments/t1_decay_experiment.py +2 -1
  118. cirq/experiments/two_qubit_xeb.py +2 -1
  119. cirq/experiments/two_qubit_xeb_test.py +1 -1
  120. cirq/experiments/xeb_fitting.py +2 -1
  121. cirq/experiments/xeb_fitting_test.py +1 -1
  122. cirq/experiments/xeb_sampling.py +5 -3
  123. cirq/experiments/xeb_sampling_test.py +1 -1
  124. cirq/experiments/xeb_simulation.py +2 -1
  125. cirq/experiments/xeb_simulation_test.py +2 -1
  126. cirq/experiments/z_phase_calibration.py +2 -1
  127. cirq/experiments/z_phase_calibration_test.py +18 -3
  128. cirq/interop/quirk/cells/__init__.py +1 -2
  129. cirq/interop/quirk/cells/all_cells.py +2 -1
  130. cirq/interop/quirk/cells/arithmetic_cells.py +2 -1
  131. cirq/interop/quirk/cells/cell.py +2 -1
  132. cirq/interop/quirk/cells/composite_cell.py +2 -1
  133. cirq/interop/quirk/cells/composite_cell_test.py +1 -1
  134. cirq/interop/quirk/cells/control_cells.py +2 -1
  135. cirq/interop/quirk/cells/frequency_space_cells.py +1 -1
  136. cirq/interop/quirk/cells/ignored_cells.py +1 -1
  137. cirq/interop/quirk/cells/input_cells.py +2 -1
  138. cirq/interop/quirk/cells/input_rotation_cells.py +2 -1
  139. cirq/interop/quirk/cells/measurement_cells.py +2 -1
  140. cirq/interop/quirk/cells/parse.py +2 -11
  141. cirq/interop/quirk/cells/qubit_permutation_cells.py +2 -1
  142. cirq/interop/quirk/cells/scalar_cells.py +2 -1
  143. cirq/interop/quirk/cells/single_qubit_rotation_cells.py +2 -1
  144. cirq/interop/quirk/cells/swap_cell.py +2 -1
  145. cirq/interop/quirk/cells/unsupported_cells.py +1 -1
  146. cirq/interop/quirk/url_to_circuit.py +2 -1
  147. cirq/json_resolver_cache.py +0 -2
  148. cirq/linalg/decompositions.py +6 -2
  149. cirq/linalg/decompositions_test.py +1 -0
  150. cirq/linalg/diagonalize.py +1 -1
  151. cirq/linalg/predicates.py +2 -1
  152. cirq/linalg/tolerance.py +2 -1
  153. cirq/linalg/transformations.py +3 -2
  154. cirq/ops/arithmetic_operation.py +4 -3
  155. cirq/ops/arithmetic_operation_test.py +1 -1
  156. cirq/ops/boolean_hamiltonian.py +4 -3
  157. cirq/ops/classically_controlled_operation.py +11 -11
  158. cirq/ops/classically_controlled_operation_test.py +26 -2
  159. cirq/ops/clifford_gate.py +3 -2
  160. cirq/ops/clifford_gate_test.py +1 -2
  161. cirq/ops/common_channels.py +2 -1
  162. cirq/ops/common_gates.py +3 -2
  163. cirq/ops/control_values.py +2 -1
  164. cirq/ops/controlled_gate.py +3 -2
  165. cirq/ops/controlled_gate_test.py +2 -1
  166. cirq/ops/controlled_operation.py +3 -2
  167. cirq/ops/controlled_operation_test.py +2 -1
  168. cirq/ops/dense_pauli_string.py +44 -81
  169. cirq/ops/dense_pauli_string_test.py +21 -0
  170. cirq/ops/diagonal_gate.py +3 -2
  171. cirq/ops/eigen_gate.py +9 -7
  172. cirq/ops/fourier_transform.py +3 -2
  173. cirq/ops/fourier_transform_test.py +2 -4
  174. cirq/ops/fsim_gate.py +3 -2
  175. cirq/ops/gate_operation.py +23 -12
  176. cirq/ops/gateset.py +22 -2
  177. cirq/ops/global_phase_op.py +3 -2
  178. cirq/ops/greedy_qubit_manager.py +2 -1
  179. cirq/ops/identity.py +2 -1
  180. cirq/ops/kraus_channel.py +2 -1
  181. cirq/ops/linear_combinations.py +12 -17
  182. cirq/ops/linear_combinations_test.py +23 -1
  183. cirq/ops/matrix_gates.py +2 -1
  184. cirq/ops/measure_util.py +8 -6
  185. cirq/ops/measurement_gate.py +2 -1
  186. cirq/ops/mixed_unitary_channel.py +2 -1
  187. cirq/ops/named_qubit.py +2 -2
  188. cirq/ops/op_tree.py +2 -1
  189. cirq/ops/parallel_gate.py +3 -2
  190. cirq/ops/parity_gates.py +2 -1
  191. cirq/ops/parity_gates_test.py +35 -0
  192. cirq/ops/pauli_interaction_gate.py +2 -1
  193. cirq/ops/pauli_measurement_gate.py +2 -1
  194. cirq/ops/pauli_string.py +37 -57
  195. cirq/ops/pauli_string_phasor.py +6 -5
  196. cirq/ops/pauli_string_raw_types.py +2 -1
  197. cirq/ops/pauli_string_test.py +49 -6
  198. cirq/ops/pauli_sum_exponential.py +2 -1
  199. cirq/ops/permutation_gate.py +2 -1
  200. cirq/ops/phased_iswap_gate.py +3 -2
  201. cirq/ops/phased_x_gate.py +5 -4
  202. cirq/ops/phased_x_z_gate.py +12 -5
  203. cirq/ops/projector.py +2 -1
  204. cirq/ops/qubit_manager.py +2 -1
  205. cirq/ops/qubit_order.py +2 -1
  206. cirq/ops/qubit_order_or_list.py +1 -1
  207. cirq/ops/random_gate_channel.py +3 -2
  208. cirq/ops/raw_types.py +33 -16
  209. cirq/ops/raw_types_test.py +4 -3
  210. cirq/ops/state_preparation_channel.py +2 -1
  211. cirq/ops/three_qubit_gates.py +3 -2
  212. cirq/ops/two_qubit_diagonal_gate.py +3 -2
  213. cirq/ops/uniform_superposition_gate.py +2 -1
  214. cirq/ops/wait_gate.py +10 -4
  215. cirq/protocols/act_on_protocol.py +2 -1
  216. cirq/protocols/act_on_protocol_test.py +2 -1
  217. cirq/protocols/apply_channel_protocol.py +2 -1
  218. cirq/protocols/apply_mixture_protocol.py +2 -1
  219. cirq/protocols/apply_mixture_protocol_test.py +2 -1
  220. cirq/protocols/apply_unitary_protocol.py +2 -1
  221. cirq/protocols/apply_unitary_protocol_test.py +2 -0
  222. cirq/protocols/approximate_equality_protocol.py +2 -1
  223. cirq/protocols/circuit_diagram_info_protocol.py +2 -1
  224. cirq/protocols/control_key_protocol.py +7 -0
  225. cirq/protocols/decompose_protocol.py +2 -12
  226. cirq/protocols/has_stabilizer_effect_protocol.py +1 -1
  227. cirq/protocols/has_stabilizer_effect_protocol_test.py +11 -9
  228. cirq/protocols/has_unitary_protocol_test.py +3 -3
  229. cirq/protocols/hash_from_pickle_test.py +2 -2
  230. cirq/protocols/inverse_protocol.py +2 -1
  231. cirq/protocols/json_serialization.py +5 -4
  232. cirq/protocols/json_serialization_test.py +31 -31
  233. cirq/protocols/kraus_protocol.py +4 -3
  234. cirq/protocols/kraus_protocol_test.py +7 -7
  235. cirq/protocols/measurement_key_protocol.py +32 -8
  236. cirq/protocols/mixture_protocol.py +3 -2
  237. cirq/protocols/mixture_protocol_test.py +7 -7
  238. cirq/protocols/mul_protocol_test.py +4 -4
  239. cirq/protocols/phase_protocol.py +13 -4
  240. cirq/protocols/pow_protocol.py +2 -1
  241. cirq/protocols/pow_protocol_test.py +5 -5
  242. cirq/protocols/qasm.py +2 -1
  243. cirq/protocols/qid_shape_protocol.py +2 -1
  244. cirq/protocols/resolve_parameters.py +17 -15
  245. cirq/protocols/trace_distance_bound.py +2 -1
  246. cirq/protocols/unitary_protocol.py +21 -21
  247. cirq/protocols/unitary_protocol_test.py +31 -19
  248. cirq/qis/channels.py +1 -1
  249. cirq/qis/channels_test.py +1 -1
  250. cirq/qis/clifford_tableau.py +16 -15
  251. cirq/qis/clifford_tableau_test.py +17 -17
  252. cirq/qis/entropy.py +3 -3
  253. cirq/qis/entropy_test.py +1 -1
  254. cirq/qis/quantum_state_representation.py +2 -1
  255. cirq/qis/states.py +7 -2
  256. cirq/qis/states_test.py +54 -54
  257. cirq/sim/classical_simulator.py +25 -14
  258. cirq/sim/classical_simulator_test.py +85 -30
  259. cirq/sim/clifford/clifford_simulator.py +7 -6
  260. cirq/sim/clifford/clifford_simulator_test.py +51 -50
  261. cirq/sim/clifford/clifford_tableau_simulation_state.py +2 -1
  262. cirq/sim/clifford/stabilizer_ch_form_simulation_state.py +2 -1
  263. cirq/sim/clifford/stabilizer_sampler.py +1 -1
  264. cirq/sim/clifford/stabilizer_simulation_state.py +2 -1
  265. cirq/sim/clifford/stabilizer_state_ch_form.py +16 -15
  266. cirq/sim/clifford/stabilizer_state_ch_form_test.py +0 -1
  267. cirq/sim/density_matrix_simulation_state.py +7 -6
  268. cirq/sim/density_matrix_simulator.py +3 -2
  269. cirq/sim/density_matrix_simulator_test.py +94 -84
  270. cirq/sim/density_matrix_utils.py +2 -1
  271. cirq/sim/density_matrix_utils_test.py +1 -1
  272. cirq/sim/mux.py +35 -8
  273. cirq/sim/mux_test.py +39 -26
  274. cirq/sim/simulation_product_state.py +2 -1
  275. cirq/sim/simulation_product_state_test.py +8 -7
  276. cirq/sim/simulation_state.py +6 -5
  277. cirq/sim/simulation_state_base.py +3 -2
  278. cirq/sim/simulation_state_test.py +7 -6
  279. cirq/sim/simulation_utils.py +2 -1
  280. cirq/sim/simulator.py +4 -3
  281. cirq/sim/simulator_base.py +2 -1
  282. cirq/sim/simulator_base_test.py +51 -36
  283. cirq/sim/simulator_test.py +41 -36
  284. cirq/sim/sparse_simulator.py +3 -2
  285. cirq/sim/sparse_simulator_test.py +92 -82
  286. cirq/sim/state_vector.py +5 -6
  287. cirq/sim/state_vector_simulation_state.py +10 -9
  288. cirq/sim/state_vector_simulator.py +2 -1
  289. cirq/sim/state_vector_simulator_test.py +9 -9
  290. cirq/sim/state_vector_test.py +40 -39
  291. cirq/study/__init__.py +1 -0
  292. cirq/study/flatten_expressions.py +2 -1
  293. cirq/study/resolver.py +31 -18
  294. cirq/study/resolver_test.py +1 -1
  295. cirq/study/result.py +2 -1
  296. cirq/study/result_test.py +20 -20
  297. cirq/study/sweepable.py +2 -1
  298. cirq/study/sweepable_test.py +20 -20
  299. cirq/study/sweeps.py +26 -1
  300. cirq/study/sweeps_test.py +67 -43
  301. cirq/testing/_compat_test_data/__init__.py +3 -3
  302. cirq/testing/circuit_compare.py +2 -1
  303. cirq/testing/circuit_compare_test.py +16 -14
  304. cirq/testing/consistent_act_on_test.py +1 -1
  305. cirq/testing/consistent_channels.py +2 -2
  306. cirq/testing/consistent_controlled_gate_op.py +2 -2
  307. cirq/testing/consistent_controlled_gate_op_test.py +2 -1
  308. cirq/testing/consistent_decomposition.py +4 -2
  309. cirq/testing/consistent_phase_by.py +1 -1
  310. cirq/testing/consistent_protocols.py +2 -1
  311. cirq/testing/consistent_protocols_test.py +3 -3
  312. cirq/testing/consistent_qasm.py +4 -3
  313. cirq/testing/consistent_qasm_test.py +3 -3
  314. cirq/testing/consistent_resolve_parameters.py +1 -1
  315. cirq/testing/consistent_unitary.py +1 -1
  316. cirq/testing/consistent_unitary_test.py +1 -1
  317. cirq/testing/deprecation.py +1 -1
  318. cirq/testing/devices.py +3 -2
  319. cirq/testing/equals_tester.py +4 -3
  320. cirq/testing/equivalent_basis_map.py +4 -2
  321. cirq/testing/json.py +3 -2
  322. cirq/testing/lin_alg_utils.py +1 -1
  323. cirq/testing/logs.py +1 -1
  324. cirq/testing/op_tree.py +1 -1
  325. cirq/testing/order_tester.py +2 -2
  326. cirq/testing/pytest_utils.py +2 -1
  327. cirq/testing/random_circuit.py +2 -1
  328. cirq/testing/random_circuit_test.py +2 -1
  329. cirq/testing/repr_pretty_tester.py +3 -3
  330. cirq/transformers/__init__.py +1 -0
  331. cirq/transformers/_connected_component.py +231 -0
  332. cirq/transformers/_connected_component_test.py +200 -0
  333. cirq/transformers/align_test.py +13 -13
  334. cirq/transformers/analytical_decompositions/clifford_decomposition.py +8 -7
  335. cirq/transformers/analytical_decompositions/clifford_decomposition_test.py +5 -5
  336. cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +11 -10
  337. cirq/transformers/analytical_decompositions/controlled_gate_decomposition_test.py +6 -6
  338. cirq/transformers/analytical_decompositions/cphase_to_fsim.py +3 -2
  339. cirq/transformers/analytical_decompositions/cphase_to_fsim_test.py +11 -10
  340. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +8 -7
  341. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +17 -20
  342. cirq/transformers/analytical_decompositions/single_qubit_decompositions_test.py +33 -27
  343. cirq/transformers/analytical_decompositions/single_to_two_qubit_isometry_test.py +1 -1
  344. cirq/transformers/analytical_decompositions/three_qubit_decomposition.py +1 -1
  345. cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +12 -11
  346. cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +5 -2
  347. cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +3 -3
  348. cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py +2 -1
  349. cirq/transformers/analytical_decompositions/two_qubit_to_ms.py +2 -1
  350. cirq/transformers/analytical_decompositions/two_qubit_to_ms_test.py +2 -2
  351. cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap.py +2 -1
  352. cirq/transformers/analytical_decompositions/two_qubit_to_sqrt_iswap_test.py +32 -30
  353. cirq/transformers/drop_negligible_operations_test.py +7 -7
  354. cirq/transformers/dynamical_decoupling.py +185 -112
  355. cirq/transformers/dynamical_decoupling_test.py +195 -201
  356. cirq/transformers/eject_phased_paulis.py +2 -1
  357. cirq/transformers/eject_phased_paulis_test.py +3 -2
  358. cirq/transformers/eject_z.py +5 -3
  359. cirq/transformers/eject_z_test.py +23 -25
  360. cirq/transformers/expand_composite.py +3 -2
  361. cirq/transformers/expand_composite_test.py +14 -14
  362. cirq/transformers/gauge_compiling/__init__.py +13 -0
  363. cirq/transformers/gauge_compiling/gauge_compiling.py +3 -2
  364. cirq/transformers/gauge_compiling/gauge_compiling_test.py +14 -12
  365. cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +3 -3
  366. cirq/transformers/gauge_compiling/idle_moments_gauge.py +225 -0
  367. cirq/transformers/gauge_compiling/idle_moments_gauge_test.py +193 -0
  368. cirq/transformers/gauge_compiling/multi_moment_cphase_gauge.py +242 -0
  369. cirq/transformers/gauge_compiling/multi_moment_cphase_gauge_test.py +243 -0
  370. cirq/transformers/gauge_compiling/multi_moment_gauge_compiling.py +151 -0
  371. cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +2 -1
  372. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +1 -1
  373. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils_test.py +6 -6
  374. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +3 -2
  375. cirq/transformers/measurement_transformers.py +2 -1
  376. cirq/transformers/measurement_transformers_test.py +45 -39
  377. cirq/transformers/merge_k_qubit_gates.py +2 -1
  378. cirq/transformers/merge_k_qubit_gates_test.py +1 -1
  379. cirq/transformers/merge_single_qubit_gates.py +9 -5
  380. cirq/transformers/merge_single_qubit_gates_test.py +22 -22
  381. cirq/transformers/noise_adding_test.py +2 -2
  382. cirq/transformers/optimize_for_target_gateset.py +2 -1
  383. cirq/transformers/optimize_for_target_gateset_test.py +11 -9
  384. cirq/transformers/qubit_management_transformers_test.py +6 -2
  385. cirq/transformers/routing/mapping_manager.py +2 -1
  386. cirq/transformers/routing/route_circuit_cqc.py +2 -1
  387. cirq/transformers/stratify.py +2 -1
  388. cirq/transformers/symbolize.py +2 -1
  389. cirq/transformers/tag_transformers.py +2 -1
  390. cirq/transformers/target_gatesets/compilation_target_gateset.py +2 -1
  391. cirq/transformers/target_gatesets/cz_gateset.py +2 -1
  392. cirq/transformers/target_gatesets/cz_gateset_test.py +1 -1
  393. cirq/transformers/target_gatesets/sqrt_iswap_gateset.py +2 -1
  394. cirq/transformers/transformer_api.py +2 -1
  395. cirq/transformers/transformer_primitives.py +271 -145
  396. cirq/transformers/transformer_primitives_test.py +185 -1
  397. cirq/value/abc_alt.py +2 -1
  398. cirq/value/classical_data.py +2 -1
  399. cirq/value/condition.py +2 -1
  400. cirq/value/digits.py +9 -2
  401. cirq/value/duration.py +6 -5
  402. cirq/value/linear_dict.py +4 -9
  403. cirq/value/measurement_key.py +2 -1
  404. cirq/value/periodic_value.py +3 -2
  405. cirq/value/product_state.py +2 -1
  406. cirq/value/value_equality_attr.py +2 -1
  407. cirq/vis/density_matrix.py +1 -1
  408. cirq/vis/heatmap.py +2 -1
  409. cirq/vis/histogram.py +2 -1
  410. cirq/vis/state_histogram.py +2 -1
  411. cirq/work/collector.py +2 -1
  412. cirq/work/observable_grouping.py +2 -1
  413. cirq/work/observable_measurement.py +2 -1
  414. cirq/work/observable_measurement_data.py +2 -1
  415. cirq/work/observable_measurement_test.py +1 -1
  416. cirq/work/observable_readout_calibration.py +2 -1
  417. cirq/work/observable_readout_calibration_test.py +1 -1
  418. cirq/work/observable_settings.py +2 -1
  419. cirq/work/sampler.py +2 -1
  420. cirq/work/sampler_test.py +1 -1
  421. {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/METADATA +5 -6
  422. {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/RECORD +425 -406
  423. cirq/contrib/json_test.py +0 -33
  424. {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/WHEEL +0 -0
  425. {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/licenses/LICENSE +0 -0
  426. {cirq_core-1.7.0.dev20250825174419.dist-info → cirq_core-1.7.0.dev20251203004401.dist-info}/top_level.txt +0 -0
@@ -32,10 +32,10 @@ class Fixed(cirq.Operation):
32
32
  return self.unitary
33
33
 
34
34
  @property
35
- def qubits(self):
36
- return cirq.LineQubit.range(self.unitary.shape[0].bit_length() - 1)
35
+ def qubits(self) -> tuple[cirq.Qid, ...]:
36
+ return tuple(cirq.LineQubit.range(self.unitary.shape[0].bit_length() - 1))
37
37
 
38
- def with_qubits(self, *new_qubits):
38
+ def with_qubits(self, *new_qubits) -> Fixed:
39
39
  raise NotImplementedError()
40
40
 
41
41
  def _qasm_(self, args: cirq.QasmArgs):
@@ -21,7 +21,7 @@ import sympy
21
21
  import cirq
22
22
 
23
23
 
24
- def assert_consistent_resolve_parameters(val: Any):
24
+ def assert_consistent_resolve_parameters(val: Any) -> None:
25
25
  names = cirq.parameter_names(val)
26
26
  symbols = cirq.parameter_symbols(val)
27
27
 
@@ -21,7 +21,7 @@ import numpy as np
21
21
  import cirq
22
22
 
23
23
 
24
- def assert_unitary_is_consistent(val: Any, ignoring_global_phase: bool = False):
24
+ def assert_unitary_is_consistent(val: Any, ignoring_global_phase: bool = False) -> None:
25
25
  if not isinstance(val, (cirq.Operation, cirq.Gate)):
26
26
  return
27
27
 
@@ -95,4 +95,4 @@ def test_failed_decomposition() -> None:
95
95
  with pytest.raises(ValueError):
96
96
  cirq.testing.assert_unitary_is_consistent(FailsOnDecompostion())
97
97
 
98
- _ = cirq.testing.assert_unitary_is_consistent(cirq.Circuit())
98
+ cirq.testing.assert_unitary_is_consistent(cirq.Circuit())
@@ -17,7 +17,7 @@ from __future__ import annotations
17
17
  import contextlib
18
18
  import logging
19
19
  import os
20
- from typing import Iterator
20
+ from collections.abc import Iterator
21
21
 
22
22
  from cirq._compat import ALLOW_DEPRECATION_IN_TEST
23
23
 
cirq/testing/devices.py CHANGED
@@ -15,7 +15,8 @@
15
15
  """Provides test devices that can validate circuits."""
16
16
  from __future__ import annotations
17
17
 
18
- from typing import AbstractSet, cast
18
+ from collections.abc import Set
19
+ from typing import cast
19
20
 
20
21
  from cirq import devices, ops
21
22
 
@@ -38,7 +39,7 @@ class ValidatingTestDevice(devices.Device):
38
39
 
39
40
  def __init__(
40
41
  self,
41
- qubits: AbstractSet[ops.Qid],
42
+ qubits: Set[ops.Qid],
42
43
  name: str = "ValidatingTestDevice",
43
44
  allowed_gates: tuple[type, ...] = (ops.Gate,),
44
45
  allowed_qubit_types: tuple[type, ...] = (devices.GridQubit,),
@@ -24,7 +24,8 @@ from __future__ import annotations
24
24
 
25
25
  import collections
26
26
  import itertools
27
- from typing import Any, Callable
27
+ from collections.abc import Callable
28
+ from typing import Any
28
29
 
29
30
 
30
31
  class EqualsTester:
@@ -93,7 +94,7 @@ class EqualsTester:
93
94
  "Common problem: returning NotImplementedError instead of NotImplemented. "
94
95
  )
95
96
 
96
- def add_equality_group(self, *group_items: Any):
97
+ def add_equality_group(self, *group_items: Any) -> None:
97
98
  """Tries to add a disjoint equivalence group to the equality tester.
98
99
 
99
100
  This methods asserts that items within the group must all be equal to
@@ -114,7 +115,7 @@ class EqualsTester:
114
115
  # Remember this group, to enable disjoint checks vs later groups.
115
116
  self._groups.append(group_items)
116
117
 
117
- def make_equality_group(self, *factories: Callable[[], Any]):
118
+ def make_equality_group(self, *factories: Callable[[], Any]) -> None:
118
119
  """Tries to add a disjoint equivalence group to the equality tester.
119
120
 
120
121
  Uses the factory methods to produce two different objects with the same
@@ -14,7 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import Sequence
17
+ from collections.abc import Sequence
18
18
 
19
19
  import numpy as np
20
20
 
@@ -22,7 +22,9 @@ import cirq
22
22
  from cirq import circuits
23
23
 
24
24
 
25
- def assert_equivalent_computational_basis_map(maps: dict[int, int], circuit: circuits.Circuit):
25
+ def assert_equivalent_computational_basis_map(
26
+ maps: dict[int, int], circuit: circuits.Circuit
27
+ ) -> None:
26
28
  """Ensure equivalence of basis state mapping.
27
29
 
28
30
  Args:
cirq/testing/json.py CHANGED
@@ -18,9 +18,10 @@ import dataclasses
18
18
  import inspect
19
19
  import io
20
20
  import pathlib
21
+ from collections.abc import Iterator
21
22
  from dataclasses import dataclass
22
23
  from types import ModuleType
23
- from typing import Iterator, TYPE_CHECKING
24
+ from typing import TYPE_CHECKING
24
25
 
25
26
  import numpy as np
26
27
  import pandas as pd
@@ -149,7 +150,7 @@ def spec_for(module_name: str) -> ModuleJsonTestSpec:
149
150
  return getattr(test_module, "TestSpec")
150
151
 
151
152
 
152
- def assert_json_roundtrip_works(obj, text_should_be=None, resolvers=None):
153
+ def assert_json_roundtrip_works(obj, text_should_be=None, resolvers=None) -> None:
153
154
  """Tests that the given object can serialized and de-serialized
154
155
 
155
156
  Args:
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
28
28
 
29
29
  def random_superposition(
30
30
  dim: int, *, random_state: cirq.RANDOM_STATE_OR_SEED_LIKE = None
31
- ) -> np.ndarray:
31
+ ) -> np.ndarray[tuple[int], np.dtype[np.complex128]]:
32
32
  """Returns a random unit-length vector from the uniform distribution.
33
33
 
34
34
  Args:
cirq/testing/logs.py CHANGED
@@ -18,7 +18,7 @@ from __future__ import annotations
18
18
 
19
19
  import contextlib
20
20
  import logging
21
- from typing import Iterator
21
+ from collections.abc import Iterator
22
22
 
23
23
 
24
24
  @contextlib.contextmanager
cirq/testing/op_tree.py CHANGED
@@ -17,7 +17,7 @@ from __future__ import annotations
17
17
  from cirq import ops
18
18
 
19
19
 
20
- def assert_equivalent_op_tree(x: ops.OP_TREE, y: ops.OP_TREE):
20
+ def assert_equivalent_op_tree(x: ops.OP_TREE, y: ops.OP_TREE) -> None:
21
21
  """Ensures that the two OP_TREEs are equivalent.
22
22
 
23
23
  Args:
@@ -78,7 +78,7 @@ class OrderTester:
78
78
  f"That rule is being violated by this value: {item!r}"
79
79
  ) from ex
80
80
 
81
- def add_ascending(self, *items: Any):
81
+ def add_ascending(self, *items: Any) -> None:
82
82
  """Tries to add a sequence of ascending items to the order tester.
83
83
 
84
84
  This methods asserts that items must all be ascending
@@ -98,7 +98,7 @@ class OrderTester:
98
98
  for item in items:
99
99
  self.add_ascending_equivalence_group(item)
100
100
 
101
- def add_ascending_equivalence_group(self, *group_items: Any):
101
+ def add_ascending_equivalence_group(self, *group_items: Any) -> None:
102
102
  """Tries to add an ascending equivalence group to the order tester.
103
103
 
104
104
  Asserts that the group items are equal to each other, but strictly
@@ -18,7 +18,8 @@ from __future__ import annotations
18
18
 
19
19
  import functools
20
20
  import warnings
21
- from typing import Any, Callable
21
+ from collections.abc import Callable
22
+ from typing import Any
22
23
 
23
24
 
24
25
  def retry_once_after_timeout(testfunc: Callable) -> Callable:
@@ -14,7 +14,8 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import Sequence, TYPE_CHECKING
17
+ from collections.abc import Sequence
18
+ from typing import TYPE_CHECKING
18
19
 
19
20
  from cirq import circuits, ops, value
20
21
  from cirq._doc import document
@@ -15,7 +15,8 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import random
18
- from typing import cast, Sequence
18
+ from collections.abc import Sequence
19
+ from typing import cast
19
20
 
20
21
  import numpy as np
21
22
  import pytest
@@ -34,11 +34,11 @@ class FakePrinter:
34
34
  def __init__(self):
35
35
  self.text_pretty = ""
36
36
 
37
- def text(self, to_print):
37
+ def text(self, to_print) -> None:
38
38
  self.text_pretty += to_print
39
39
 
40
40
 
41
- def assert_repr_pretty(val: Any, text: str, cycle: bool = False):
41
+ def assert_repr_pretty(val: Any, text: str, cycle: bool = False) -> None:
42
42
  """Assert that the given object has a `_repr_pretty_` method that produces the given text.
43
43
 
44
44
  Args:
@@ -57,7 +57,7 @@ def assert_repr_pretty(val: Any, text: str, cycle: bool = False):
57
57
  assert p.text_pretty == text, f"{p.text_pretty} != {text}"
58
58
 
59
59
 
60
- def assert_repr_pretty_contains(val: Any, substr: str, cycle: bool = False):
60
+ def assert_repr_pretty_contains(val: Any, substr: str, cycle: bool = False) -> None:
61
61
  """Assert that the given object has a `_repr_pretty_` output that contains the given text.
62
62
 
63
63
  Args:
@@ -150,6 +150,7 @@ from cirq.transformers.gauge_compiling import (
150
150
  SpinInversionGaugeTransformer as SpinInversionGaugeTransformer,
151
151
  SqrtCZGaugeTransformer as SqrtCZGaugeTransformer,
152
152
  SqrtISWAPGaugeTransformer as SqrtISWAPGaugeTransformer,
153
+ CPhaseGaugeTransformerMM as CPhaseGaugeTransformerMM,
153
154
  )
154
155
 
155
156
  from cirq.transformers.randomized_measurements import (
@@ -0,0 +1,231 @@
1
+ # Copyright 2025 The Cirq Developers
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Defines a connected component of operations, to be used in merge transformers."""
16
+
17
+ from __future__ import annotations
18
+
19
+ from collections.abc import Callable, Sequence
20
+ from typing import cast, TYPE_CHECKING
21
+
22
+ from scipy.cluster.hierarchy import DisjointSet
23
+
24
+ from cirq import ops, protocols
25
+
26
+ if TYPE_CHECKING:
27
+ import cirq
28
+
29
+
30
+ class Component:
31
+ """Internal representation for a connected component of operations."""
32
+
33
+ # Circuit moment containing the component
34
+ moment_id: int
35
+ # Union of all op qubits in the component
36
+ qubits: frozenset[cirq.Qid]
37
+ # Union of all measurement keys in the component
38
+ mkeys: frozenset[cirq.MeasurementKey]
39
+ # Union of all control keys in the component
40
+ ckeys: frozenset[cirq.MeasurementKey]
41
+ # Initial operation in the component
42
+ op: cirq.Operation
43
+
44
+ # True if the component can be merged with other components
45
+ is_mergeable: bool
46
+
47
+ def __init__(self, op: cirq.Operation, moment_id: int, is_mergeable=True):
48
+ """Initializes a singleton component."""
49
+ self.op = op
50
+ self.is_mergeable = is_mergeable
51
+ self.moment_id = moment_id
52
+ self.qubits = frozenset(op.qubits)
53
+ self.mkeys = protocols.measurement_key_objs(op)
54
+ self.ckeys = protocols.control_keys(op)
55
+
56
+
57
+ class ComponentWithOps(Component):
58
+ """Component that keeps track of operations."""
59
+
60
+ # List of all operations in the component
61
+ ops: list[cirq.Operation]
62
+
63
+ def __init__(self, op: cirq.Operation, moment_id: int, is_mergeable=True):
64
+ super().__init__(op, moment_id, is_mergeable)
65
+ self.ops = [op]
66
+
67
+
68
+ class ComponentWithCircuitOp(Component):
69
+ """Component that keeps track of operations as a CircuitOperation."""
70
+
71
+ # CircuitOperation containing all the operations in the component,
72
+ # or a single Operation if the component is a singleton
73
+ circuit_op: cirq.Operation
74
+
75
+ def __init__(self, op: cirq.Operation, moment_id: int, is_mergeable=True):
76
+ super().__init__(op, moment_id, is_mergeable)
77
+ self.circuit_op = op
78
+
79
+
80
+ class ComponentSet:
81
+ """Represents a set of mergeable components of operations."""
82
+
83
+ _comp_type: type[Component]
84
+
85
+ _disjoint_set: DisjointSet
86
+
87
+ # Callable to decide if a component is mergeable
88
+ _is_mergeable: Callable[[cirq.Operation], bool]
89
+
90
+ # List of components in creation order
91
+ _components: list[Component]
92
+
93
+ def __init__(self, is_mergeable: Callable[[cirq.Operation], bool]):
94
+ self._is_mergeable = is_mergeable
95
+ self._disjoint_set = DisjointSet()
96
+ self._components = []
97
+ self._comp_type = Component
98
+
99
+ def new_component(self, op: cirq.Operation, moment_id: int, is_mergeable=True) -> Component:
100
+ """Creates a new component and adds it to the set."""
101
+ c = self._comp_type(op, moment_id, is_mergeable and self._is_mergeable(op))
102
+ self._disjoint_set.add(c)
103
+ self._components.append(c)
104
+ return c
105
+
106
+ def components(self) -> list[Component]:
107
+ """Returns the initial components in creation order."""
108
+ return self._components
109
+
110
+ def find(self, x: Component) -> Component:
111
+ """Finds the representative for a merged component."""
112
+ return self._disjoint_set[x]
113
+
114
+ def merge(self, x: Component, y: Component, merge_left=True) -> Component | None:
115
+ """Attempts to merge two components.
116
+
117
+ If merge_left is True, y is merged into x, and the representative will keep
118
+ y's moment. If merge_left is False, x is merged into y, and the representative
119
+ will keep y's moment.
120
+
121
+ Args:
122
+ x: First component to merge.
123
+ y: Second component to merge.
124
+ merge_left: True to keep x's moment for the merged component, False to
125
+ keep y's moment for the merged component.
126
+
127
+ Returns:
128
+ None, if the components can't be merged.
129
+ Otherwise the new component representative.
130
+ """
131
+ x = self._disjoint_set[x]
132
+ y = self._disjoint_set[y]
133
+
134
+ if not x.is_mergeable or not y.is_mergeable:
135
+ return None
136
+
137
+ if not self._disjoint_set.merge(x, y):
138
+ return x
139
+
140
+ root = self._disjoint_set[x]
141
+ root.moment_id = x.moment_id if merge_left else y.moment_id
142
+ root.qubits = x.qubits.union(y.qubits)
143
+ root.mkeys = x.mkeys.union(y.mkeys)
144
+ root.ckeys = x.ckeys.union(y.ckeys)
145
+
146
+ return root
147
+
148
+
149
+ class ComponentWithOpsSet(ComponentSet):
150
+ """Represents a set of mergeable components, where each component tracks operations."""
151
+
152
+ # Callable that returns if two components can be merged based on their operations
153
+ _can_merge: Callable[[Sequence[cirq.Operation], Sequence[cirq.Operation]], bool]
154
+
155
+ def __init__(
156
+ self,
157
+ is_mergeable: Callable[[cirq.Operation], bool],
158
+ can_merge: Callable[[Sequence[cirq.Operation], Sequence[cirq.Operation]], bool],
159
+ ):
160
+ super().__init__(is_mergeable)
161
+ self._can_merge = can_merge
162
+ self._comp_type = ComponentWithOps
163
+
164
+ def merge(self, x: Component, y: Component, merge_left=True) -> Component | None:
165
+ """Attempts to merge two components.
166
+
167
+ Returns:
168
+ None if can_merge is False or the merge doesn't succeed, otherwise the
169
+ new representative. The representative will have ops = x.ops + y.ops.
170
+ """
171
+ x = cast(ComponentWithOps, self._disjoint_set[x])
172
+ y = cast(ComponentWithOps, self._disjoint_set[y])
173
+
174
+ if x is y:
175
+ return x
176
+
177
+ if not x.is_mergeable or not y.is_mergeable or not self._can_merge(x.ops, y.ops):
178
+ return None
179
+
180
+ root = cast(ComponentWithOps, super().merge(x, y, merge_left))
181
+ root.ops = x.ops + y.ops
182
+ # Clear the ops list in the non-representative component to avoid duplication
183
+ other = y if x is root else x
184
+ other.ops = []
185
+ return root
186
+
187
+
188
+ class ComponentWithCircuitOpSet(ComponentSet):
189
+ """Represents a set of mergeable components, with operations as a CircuitOperation."""
190
+
191
+ # Callable that merges CircuitOperations from two components
192
+ _merge_func: Callable[[ops.Operation, ops.Operation], ops.Operation | None]
193
+
194
+ def __init__(
195
+ self,
196
+ is_mergeable: Callable[[cirq.Operation], bool],
197
+ merge_func: Callable[[ops.Operation, ops.Operation], ops.Operation | None],
198
+ ):
199
+ super().__init__(is_mergeable)
200
+ self._merge_func = merge_func
201
+ self._comp_type = ComponentWithCircuitOp
202
+
203
+ def merge(self, x: Component, y: Component, merge_left=True) -> Component | None:
204
+ """Attempts to merge two components.
205
+
206
+ Returns:
207
+ None if merge_func returns None or the merge doesn't succeed,
208
+ otherwise the new representative.
209
+ """
210
+ x = cast(ComponentWithCircuitOp, self._disjoint_set[x])
211
+ y = cast(ComponentWithCircuitOp, self._disjoint_set[y])
212
+
213
+ if x is y:
214
+ return x
215
+
216
+ if not x.is_mergeable or not y.is_mergeable:
217
+ return None
218
+
219
+ new_op = self._merge_func(x.circuit_op, y.circuit_op)
220
+ if not new_op:
221
+ return None
222
+
223
+ root = cast(ComponentWithCircuitOp, super().merge(x, y, merge_left))
224
+
225
+ root.circuit_op = new_op
226
+ # The merge_func can be arbitrary, so we need to recompute the component properties
227
+ root.qubits = frozenset(new_op.qubits)
228
+ root.mkeys = protocols.measurement_key_objs(new_op)
229
+ root.ckeys = protocols.control_keys(new_op)
230
+
231
+ return root
@@ -0,0 +1,200 @@
1
+ # Copyright 2025 The Cirq Developers
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import pytest
18
+
19
+ import cirq
20
+ from cirq.transformers._connected_component import (
21
+ ComponentSet,
22
+ ComponentWithCircuitOpSet,
23
+ ComponentWithOpsSet,
24
+ )
25
+
26
+
27
+ def _always_mergeable(_: cirq.Operation) -> bool:
28
+ return True
29
+
30
+
31
+ def _never_mergeable(_: cirq.Operation) -> bool:
32
+ return False
33
+
34
+
35
+ def _always_can_merge(_ops1: list[cirq.Operation], _ops2: list[cirq.Operation]) -> bool:
36
+ return True
37
+
38
+
39
+ def _never_can_merge(_ops1: list[cirq.Operation], _ops2: list[cirq.Operation]) -> bool:
40
+ return False
41
+
42
+
43
+ def _merge_as_first_operation(op1: cirq.Operation, _op2: cirq.Operation) -> cirq.Operation:
44
+ return op1
45
+
46
+
47
+ def _merge_never_successful(op1: cirq.Operation, _op2: cirq.Operation) -> None:
48
+ return None
49
+
50
+
51
+ def test_find_returns_itself_for_singleton():
52
+ cset = ComponentSet(_always_mergeable)
53
+
54
+ q = cirq.NamedQubit('x')
55
+ c = cset.new_component(op=cirq.X(q), moment_id=0)
56
+ assert cset.find(c) is c
57
+
58
+
59
+ def test_merge_components():
60
+ cset = ComponentSet(_always_mergeable)
61
+
62
+ q = cirq.NamedQubit('x')
63
+ c = [cset.new_component(op=cirq.X(q), moment_id=i) for i in range(5)]
64
+ cset.merge(c[1], c[0])
65
+ cset.merge(c[2], c[1])
66
+ cset.merge(c[4], c[3])
67
+ cset.merge(c[3], c[0])
68
+
69
+ for i in range(5):
70
+ assert cset.find(c[i]) is cset.find(c[0])
71
+
72
+
73
+ def test_merge_same_component():
74
+ cset = ComponentSet(_always_mergeable)
75
+
76
+ q = cirq.NamedQubit('x')
77
+ c = [cset.new_component(op=cirq.X(q), moment_id=i) for i in range(3)]
78
+ cset.merge(c[1], c[0])
79
+ cset.merge(c[2], c[1])
80
+
81
+ root = cset.find(c[0])
82
+
83
+ assert cset.merge(c[0], c[2]) is root
84
+
85
+
86
+ def test_merge_returns_None_if_one_component_is_not_mergeable():
87
+ cset = ComponentSet(_always_mergeable)
88
+
89
+ q = cirq.NamedQubit('x')
90
+ c0 = cset.new_component(op=cirq.X(q), moment_id=0, is_mergeable=True)
91
+ c1 = cset.new_component(op=cirq.X(q), moment_id=1, is_mergeable=False)
92
+ assert cset.merge(c0, c1) is None
93
+
94
+
95
+ def test_cset_merge_returns_None_if_is_mergeable_is_false():
96
+ q = cirq.NamedQubit('x')
97
+ cset = ComponentSet(is_mergeable=_never_mergeable)
98
+
99
+ c0 = cset.new_component(op=cirq.X(q), moment_id=0, is_mergeable=True)
100
+ c1 = cset.new_component(op=cirq.X(q), moment_id=1, is_mergeable=True)
101
+ assert cset.merge(c0, c1) is None
102
+
103
+
104
+ @pytest.mark.parametrize("merge_left,expected_moment_id", [(True, 0), (False, 1)])
105
+ def test_merge_qubits_with_merge_left(merge_left: bool, expected_moment_id: int) -> None:
106
+ cset = ComponentSet(_always_mergeable)
107
+
108
+ q0 = cirq.NamedQubit('x')
109
+ q1 = cirq.NamedQubit('y')
110
+ c0 = cset.new_component(op=cirq.X(q0), moment_id=0)
111
+ c1 = cset.new_component(op=cirq.X(q1), moment_id=1)
112
+ c2 = cset.new_component(op=cirq.X(q1), moment_id=2)
113
+ cset.merge(c1, c2)
114
+ cset.merge(c0, c1, merge_left=merge_left)
115
+ assert cset.find(c1).qubits == frozenset([q0, q1])
116
+ assert cset.find(c1).moment_id == expected_moment_id
117
+
118
+
119
+ def test_component_with_ops_merge():
120
+ cset = ComponentWithOpsSet(_always_mergeable, _always_can_merge)
121
+
122
+ q = cirq.LineQubit.range(3)
123
+ ops = [cirq.X(q[i]) for i in range(3)]
124
+ c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
125
+ cset.merge(c[0], c[1])
126
+ cset.merge(c[1], c[2])
127
+ assert cset.find(c[0]).ops == ops
128
+ # check merge of indirectly merged components does not make a difference
129
+ assert cset.merge(c[0], c[2]).ops == ops
130
+
131
+
132
+ def test_component_with_ops_merge_when_merge_fails():
133
+ cset = ComponentWithOpsSet(_always_mergeable, _never_can_merge)
134
+
135
+ q = cirq.LineQubit.range(3)
136
+ ops = [cirq.X(q[i]) for i in range(3)]
137
+ c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
138
+
139
+ cset.merge(c[0], c[1])
140
+ cset.merge(c[1], c[2])
141
+ # No merge happened
142
+ for i in range(3):
143
+ assert cset.find(c[i]) is c[i]
144
+
145
+
146
+ def test_component_with_ops_merge_when_is_mergeable_is_false():
147
+ cset = ComponentWithOpsSet(_never_mergeable, _always_can_merge)
148
+
149
+ q = cirq.LineQubit.range(3)
150
+ ops = [cirq.X(q[i]) for i in range(3)]
151
+ c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
152
+
153
+ cset.merge(c[0], c[1])
154
+ cset.merge(c[1], c[2])
155
+ # No merge happened
156
+ for i in range(3):
157
+ assert cset.find(c[i]) is c[i]
158
+
159
+
160
+ def test_component_with_circuit_op_merge():
161
+ cset = ComponentWithCircuitOpSet(_always_mergeable, _merge_as_first_operation)
162
+
163
+ q = cirq.LineQubit.range(3)
164
+ ops = [cirq.X(q[i]) for i in range(3)]
165
+ c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
166
+
167
+ cset.merge(c[0], c[1])
168
+ cset.merge(c[1], c[2])
169
+ for i in range(3):
170
+ assert cset.find(c[i]).circuit_op == ops[0]
171
+ # check merge of indirectly merged components does not make a difference
172
+ assert cset.merge(c[0], c[2]) is cset.find(c[1])
173
+
174
+
175
+ def test_component_with_circuit_op_merge_func_is_none():
176
+ cset = ComponentWithCircuitOpSet(_always_mergeable, _merge_never_successful)
177
+
178
+ q = cirq.LineQubit.range(3)
179
+ ops = [cirq.X(q[i]) for i in range(3)]
180
+ c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
181
+
182
+ cset.merge(c[0], c[1])
183
+ cset.merge(c[1], c[2])
184
+ # No merge happened
185
+ for i in range(3):
186
+ assert cset.find(c[i]) is c[i]
187
+
188
+
189
+ def test_component_with_circuit_op_merge_when_is_mergeable_is_false():
190
+ cset = ComponentWithCircuitOpSet(_never_mergeable, _merge_as_first_operation)
191
+
192
+ q = cirq.LineQubit.range(3)
193
+ ops = [cirq.X(q[i]) for i in range(3)]
194
+ c = [cset.new_component(op=ops[i], moment_id=i) for i in range(3)]
195
+
196
+ cset.merge(c[0], c[1])
197
+ cset.merge(c[1], c[2])
198
+ # No merge happened
199
+ for i in range(3):
200
+ assert cset.find(c[i]) is c[i]