cirq-core 1.4.0.dev20240529225110__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 +31 -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.0.dev20240529225110.dist-info → cirq_core-1.5.0.dist-info}/RECORD +586 -552
  584. {cirq_core-1.4.0.dev20240529225110.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.0.dev20240529225110.dist-info/METADATA +0 -50
  589. {cirq_core-1.4.0.dev20240529225110.dist-info → cirq_core-1.5.0.dist-info}/LICENSE +0 -0
  590. {cirq_core-1.4.0.dev20240529225110.dist-info → cirq_core-1.5.0.dist-info}/top_level.txt +0 -0
@@ -12,14 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  """Wraps Parallel Two Qubit XEB into a few convenience methods."""
15
- from typing import Optional, Sequence, Dict
16
- import itertools
17
15
  import io
16
+ import itertools
17
+ from typing import Dict, Optional, Sequence
18
18
 
19
19
  import matplotlib.pyplot as plt
20
-
21
- import numpy as np
22
20
  import networkx as nx
21
+ import numpy as np
23
22
  import pandas as pd
24
23
  import pytest
25
24
 
@@ -148,10 +147,10 @@ _TEST_RESULT = cirq.experiments.TwoQubitXEBResult(
148
147
  @pytest.mark.parametrize(
149
148
  'q0,q1,pauli',
150
149
  [
151
- (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 1 / 8),
152
- (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 1 / 4),
153
- (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.8 + 3 / 40),
154
- (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 5 / 8),
150
+ (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 0.09374999999999997),
151
+ (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 0.18749999999999994),
152
+ (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.65625),
153
+ (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 0.46875),
155
154
  ],
156
155
  )
157
156
  def test_pauli_error(q0: cirq.GridQubit, q1: cirq.GridQubit, pauli: float):
@@ -174,10 +173,10 @@ class MockParallelRandomizedBenchmarkingResult(ParallelRandomizedBenchmarkingRes
174
173
  @pytest.mark.parametrize(
175
174
  'q0,q1,pauli',
176
175
  [
177
- (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 1 / 8 - 0.03),
178
- (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 1 / 4 - 0.09),
179
- (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.8 + 3 / 40 - 0.08),
180
- (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 5 / 8 - 0.13),
176
+ (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 0.09374999999999997 - 0.03),
177
+ (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 0.18749999999999994 - 0.09),
178
+ (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.65625 - 0.08),
179
+ (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 0.46875 - 0.13),
181
180
  ],
182
181
  )
183
182
  def test_inferred_pauli_error(q0: cirq.GridQubit, q1: cirq.GridQubit, pauli: float):
@@ -191,10 +190,10 @@ def test_inferred_pauli_error(q0: cirq.GridQubit, q1: cirq.GridQubit, pauli: flo
191
190
  @pytest.mark.parametrize(
192
191
  'q0,q1,xeb',
193
192
  [
194
- (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 0.076),
195
- (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 0.128),
196
- (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.636),
197
- (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 0.396),
193
+ (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 0.050999999999999934),
194
+ (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 0.07799999999999996),
195
+ (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.46099999999999997),
196
+ (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 0.2709999999999999),
198
197
  ],
199
198
  )
200
199
  def test_inferred_xeb_error(q0: cirq.GridQubit, q1: cirq.GridQubit, xeb: float):
@@ -224,10 +223,10 @@ def test_inferred_single_qubit_pauli():
224
223
  @pytest.mark.parametrize(
225
224
  'q0,q1,pauli',
226
225
  [
227
- (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 1 / 8),
228
- (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 1 / 4),
229
- (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.8 + 3 / 40),
230
- (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 5 / 8),
226
+ (cirq.GridQubit(4, 4), cirq.GridQubit(5, 4), 0.09374999999999997),
227
+ (cirq.GridQubit(5, 3), cirq.GridQubit(6, 3), 0.18749999999999994),
228
+ (cirq.GridQubit(4, 3), cirq.GridQubit(5, 3), 0.65625),
229
+ (cirq.GridQubit(6, 3), cirq.GridQubit(6, 4), 0.46875),
231
230
  ],
232
231
  )
233
232
  def test_inferred_two_qubit_pauli(q0: cirq.GridQubit, q1: cirq.GridQubit, pauli: float):
@@ -261,26 +260,43 @@ def test_inferred_plots(ax, target_error, kind):
261
260
 
262
261
 
263
262
  @pytest.mark.parametrize(
264
- 'sampler,qubits',
263
+ 'sampler,qubits,pairs',
265
264
  [
266
265
  (
267
266
  cirq.DensityMatrixSimulator(
268
267
  seed=0, noise=cirq.ConstantQubitNoiseModel(cirq.amplitude_damp(0.1))
269
268
  ),
270
269
  cirq.GridQubit.rect(3, 2, 4, 3),
270
+ None,
271
+ ),
272
+ (
273
+ cirq.DensityMatrixSimulator(
274
+ seed=0, noise=cirq.ConstantQubitNoiseModel(cirq.amplitude_damp(0.1))
275
+ ),
276
+ None,
277
+ [
278
+ (cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)),
279
+ (cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)),
280
+ ],
271
281
  ),
272
282
  (
273
283
  DensityMatrixSimulatorWithProcessor(
274
284
  seed=0, noise=cirq.ConstantQubitNoiseModel(cirq.amplitude_damp(0.1))
275
285
  ),
276
286
  None,
287
+ None,
277
288
  ),
278
289
  ],
279
290
  )
280
- def test_run_rb_and_xeb(sampler: cirq.Sampler, qubits: Optional[Sequence[cirq.GridQubit]]):
291
+ def test_run_rb_and_xeb(
292
+ sampler: cirq.Sampler,
293
+ qubits: Optional[Sequence[cirq.GridQubit]],
294
+ pairs: Optional[Sequence[tuple[cirq.GridQubit, cirq.GridQubit]]],
295
+ ):
281
296
  res = cirq.experiments.run_rb_and_xeb(
282
297
  sampler=sampler,
283
298
  qubits=qubits,
299
+ pairs=pairs,
284
300
  repetitions=100,
285
301
  num_clifford_range=tuple(np.arange(3, 10, 1)),
286
302
  xeb_combinations=1,
@@ -13,20 +13,23 @@
13
13
  # limitations under the License.
14
14
  """Estimation of fidelity associated with experimental circuit executions."""
15
15
  import dataclasses
16
- from abc import abstractmethod, ABC
16
+ from abc import ABC, abstractmethod
17
17
  from typing import Dict, Iterable, List, Optional, Sequence, Tuple, TYPE_CHECKING
18
18
 
19
19
  import numpy as np
20
20
  import pandas as pd
21
21
  import sympy
22
- from cirq import circuits, ops, protocols, _import
22
+
23
+ from cirq import _import, circuits, ops, protocols
23
24
  from cirq.experiments.xeb_simulation import simulate_2q_xeb_circuits
24
25
 
25
26
  if TYPE_CHECKING:
26
- import cirq
27
27
  import multiprocessing
28
+
28
29
  import scipy.optimize
29
30
 
31
+ import cirq
32
+
30
33
  # We initialize these lazily, otherwise they slow global import speed.
31
34
  optimize = _import.LazyLoader("optimize", globals(), "scipy.optimize")
32
35
  stats = _import.LazyLoader("stats", globals(), "scipy.stats")
@@ -95,6 +98,11 @@ def benchmark_2q_xeb_fidelities(
95
98
  df['e_u'] = np.sum(pure_probs**2, axis=1)
96
99
  df['u_u'] = np.sum(pure_probs, axis=1) / D
97
100
  df['m_u'] = np.sum(pure_probs * sampled_probs, axis=1)
101
+ # Var[m_u] = Var[sum p(x) * p_sampled(x)]
102
+ # = sum p(x)^2 Var[p_sampled(x)]
103
+ # = sum p(x)^2 p(x) (1 - p(x))
104
+ # = sum p(x)^3 (1 - p(x))
105
+ df['var_m_u'] = np.sum(pure_probs**3 * (1 - pure_probs), axis=1)
98
106
  df['y'] = df['m_u'] - df['u_u']
99
107
  df['x'] = df['e_u'] - df['u_u']
100
108
  df['numerator'] = df['x'] * df['y']
@@ -103,7 +111,11 @@ def benchmark_2q_xeb_fidelities(
103
111
  def per_cycle_depth(df):
104
112
  """This function is applied per cycle_depth in the following groupby aggregation."""
105
113
  fid_lsq = df['numerator'].sum() / df['denominator'].sum()
106
- ret = {'fidelity': fid_lsq}
114
+ # Note: both df['denominator'] and df['x'] are constants.
115
+ # Var[f] = Var[df['numerator']] / (sum df['denominator'])^2
116
+ # = sum (df['x']^2 * df['var_m_u']) / (sum df['denominator'])^2
117
+ var_fid = (df['var_m_u'] * df['x'] ** 2).sum() / df['denominator'].sum() ** 2
118
+ ret = {'fidelity': fid_lsq, 'fidelity_variance': var_fid}
107
119
 
108
120
  def _try_keep(k):
109
121
  """If all the values for a key `k` are the same in this group, we can keep it."""
@@ -126,7 +138,7 @@ def benchmark_2q_xeb_fidelities(
126
138
  else:
127
139
  groupby_names = ['cycle_depth']
128
140
 
129
- return df.groupby(groupby_names).apply(per_cycle_depth).reset_index()
141
+ return df.groupby(groupby_names).apply(per_cycle_depth, include_groups=False).reset_index()
130
142
 
131
143
 
132
144
  class XEBCharacterizationOptions(ABC):
@@ -146,6 +158,69 @@ class XEBCharacterizationOptions(ABC):
146
158
  """Return an initial Nelder-Mead simplex and the names for each parameter."""
147
159
 
148
160
 
161
+ def _try_defaults_from_unitary(gate: 'cirq.Gate') -> Optional[Dict[str, 'cirq.TParamVal']]:
162
+ r"""Try to figure out the PhasedFSim angles from the unitary of the gate.
163
+
164
+ The unitary of a PhasedFSimGate has the form:
165
+ $$
166
+ \begin{bmatrix}
167
+ 1 & 0 & 0 & 0 \\
168
+ 0 & e^{-i \gamma - i \zeta} \cos(\theta) & -i e^{-i \gamma + i\chi} \sin(\theta) & 0 \\
169
+ 0 & -i e^{-i \gamma - i \chi} \sin(\theta) & e^{-i \gamma + i \zeta} \cos(\theta) & 0 \\
170
+ 0 & 0 & 0 & e^{-2i \gamma - i \phi}
171
+ \end{bmatrix}
172
+ $$
173
+ That's the information about the five angles $\theta, \phi, \gamma, \zeta, \chi$ is encoded in
174
+ the submatrix unitary[1:3, 1:3] and the element u[3][3]. With some algebra, we can isolate each
175
+ of the angles as an argument of a combination of those elements (and potentially other angles).
176
+
177
+ Args:
178
+ A cirq gate.
179
+
180
+ Returns:
181
+ A dictionary mapping angles to values or None if the gate doesn't have a unitary or if it
182
+ can't be represented by a PhasedFSimGate.
183
+ """
184
+ u = protocols.unitary(gate, default=None)
185
+ if u is None:
186
+ return None
187
+
188
+ gamma = np.angle(u[1, 1] * u[2, 2] - u[1, 2] * u[2, 1]) / -2
189
+ phi = -np.angle(u[3, 3]) - 2 * gamma
190
+ phased_cos_theta_2 = u[1, 1] * u[2, 2]
191
+ if phased_cos_theta_2 == 0:
192
+ # The zeta phase is multiplied with cos(theta),
193
+ # so if cos(theta) is zero then any value is possible.
194
+ zeta = 0
195
+ else:
196
+ zeta = np.angle(u[2, 2] / u[1, 1]) / 2
197
+
198
+ phased_sin_theta_2 = u[1, 2] * u[2, 1]
199
+ if phased_sin_theta_2 == 0:
200
+ # The chi phase is multiplied with sin(theta),
201
+ # so if sin(theta) is zero then any value is possible.
202
+ chi = 0
203
+ else:
204
+ chi = np.angle(u[1, 2] / u[2, 1]) / 2
205
+
206
+ theta = np.angle(np.exp(1j * (gamma + zeta)) * u[1, 1] - np.exp(1j * (gamma - chi)) * u[1, 2])
207
+
208
+ if np.allclose(
209
+ u,
210
+ protocols.unitary(
211
+ ops.PhasedFSimGate(theta=theta, phi=phi, chi=chi, zeta=zeta, gamma=gamma)
212
+ ),
213
+ ):
214
+ return {
215
+ 'theta_default': theta,
216
+ 'phi_default': phi,
217
+ 'gamma_default': gamma,
218
+ 'zeta_default': zeta,
219
+ 'chi_default': chi,
220
+ }
221
+ return None
222
+
223
+
149
224
  def phased_fsim_angles_from_gate(gate: 'cirq.Gate') -> Dict[str, 'cirq.TParamVal']:
150
225
  """For a given gate, return a dictionary mapping '{angle}_default' to its noiseless value
151
226
  for the five PhasedFSim angles."""
@@ -175,6 +250,11 @@ def phased_fsim_angles_from_gate(gate: 'cirq.Gate') -> Dict[str, 'cirq.TParamVal
175
250
  'phi_default': gate.phi,
176
251
  }
177
252
 
253
+ # Handle all gates that can be represented using an FSimGate.
254
+ from_unitary = _try_defaults_from_unitary(gate)
255
+ if from_unitary is not None:
256
+ return from_unitary
257
+
178
258
  raise ValueError(f"Unknown default angles for {gate}.")
179
259
 
180
260
 
@@ -317,16 +397,21 @@ def SqrtISwapXEBOptions(*args, **kwargs):
317
397
 
318
398
 
319
399
  def parameterize_circuit(
320
- circuit: 'cirq.Circuit', options: XEBCharacterizationOptions
400
+ circuit: 'cirq.Circuit',
401
+ options: XEBCharacterizationOptions,
402
+ target_gatefamily: Optional[ops.GateFamily] = None,
321
403
  ) -> 'cirq.Circuit':
322
404
  """Parameterize PhasedFSim-like gates in a given circuit according to
323
405
  `phased_fsim_options`.
324
406
  """
407
+ if isinstance(target_gatefamily, ops.GateFamily):
408
+ should_parameterize = lambda op: op in target_gatefamily or options.should_parameterize(op)
409
+ else:
410
+ should_parameterize = options.should_parameterize
325
411
  gate = options.get_parameterized_gate()
326
412
  return circuits.Circuit(
327
413
  circuits.Moment(
328
- gate.on(*op.qubits) if options.should_parameterize(op) else op
329
- for op in moment.operations
414
+ gate.on(*op.qubits) if should_parameterize(op) else op for op in moment.operations
330
415
  )
331
416
  for moment in circuit.moments
332
417
  )
@@ -572,6 +657,7 @@ def _fit_exponential_decay(
572
657
  fidelities,
573
658
  p0=(a_0, layer_fid_0),
574
659
  bounds=((0, 0), (1, 1)),
660
+ maxfev=1000,
575
661
  )
576
662
  except ValueError: # pragma: no cover
577
663
  return 0, 0, np.inf, np.inf
@@ -580,15 +666,6 @@ def _fit_exponential_decay(
580
666
  return a, layer_fid, a_std, layer_fid_std
581
667
 
582
668
 
583
- def _one_unique(df, name, default):
584
- """Helper function to assert that there's one unique value in a column and return it."""
585
- if name not in df.columns:
586
- return default
587
- vals = df[name].unique()
588
- assert len(vals) == 1, name
589
- return vals[0]
590
-
591
-
592
669
  def fit_exponential_decays(fidelities_df: pd.DataFrame) -> pd.DataFrame:
593
670
  """Fit exponential decay curves to a fidelities DataFrame.
594
671
 
@@ -608,13 +685,16 @@ def fit_exponential_decays(fidelities_df: pd.DataFrame) -> pd.DataFrame:
608
685
  a, layer_fid, a_std, layer_fid_std = _fit_exponential_decay(
609
686
  f1['cycle_depth'], f1['fidelity']
610
687
  )
688
+ fidelity_variance = 0
689
+ if 'fidelity_variance' in f1:
690
+ fidelity_variance = f1['fidelity_variance'].values
611
691
  record = {
612
692
  'a': a,
613
693
  'layer_fid': layer_fid,
614
694
  'cycle_depths': f1['cycle_depth'].values,
615
695
  'fidelities': f1['fidelity'].values,
616
696
  'a_std': a_std,
617
- 'layer_fid_std': layer_fid_std,
697
+ 'layer_fid_std': np.sqrt(layer_fid_std**2 + fidelity_variance),
618
698
  }
619
699
  return pd.Series(record)
620
700
 
@@ -622,7 +702,7 @@ def fit_exponential_decays(fidelities_df: pd.DataFrame) -> pd.DataFrame:
622
702
  groupby = ['layer_i', 'pair_i', 'pair']
623
703
  else:
624
704
  groupby = ['pair']
625
- return fidelities_df.groupby(groupby).apply(_per_pair)
705
+ return fidelities_df.groupby(groupby).apply(_per_pair, include_groups=False)
626
706
 
627
707
 
628
708
  def before_and_after_characterization(
@@ -11,9 +11,12 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
14
17
  import itertools
15
18
  import multiprocessing
16
- from typing import Iterable
19
+ from typing import Iterable, Iterator
17
20
 
18
21
  import networkx as nx
19
22
  import numpy as np
@@ -23,18 +26,28 @@ import pytest
23
26
  import cirq
24
27
  import cirq.experiments.random_quantum_circuit_generation as rqcg
25
28
  from cirq.experiments.xeb_fitting import (
29
+ _fit_exponential_decay,
30
+ before_and_after_characterization,
26
31
  benchmark_2q_xeb_fidelities,
27
- parameterize_circuit,
28
- SqrtISwapXEBOptions,
29
32
  characterize_phased_fsim_parameters_with_xeb,
30
33
  characterize_phased_fsim_parameters_with_xeb_by_pair,
31
- _fit_exponential_decay,
32
34
  fit_exponential_decays,
33
- before_and_after_characterization,
35
+ parameterize_circuit,
36
+ phased_fsim_angles_from_gate,
37
+ SqrtISwapXEBOptions,
34
38
  XEBPhasedFSimCharacterizationOptions,
35
39
  )
36
40
  from cirq.experiments.xeb_sampling import sample_2q_xeb_circuits
37
41
 
42
+ _POOL_NUM_PROCESSES = min(4, multiprocessing.cpu_count())
43
+
44
+
45
+ @pytest.fixture
46
+ def pool() -> Iterator[multiprocessing.pool.Pool]:
47
+ ctx = multiprocessing.get_context()
48
+ with ctx.Pool(_POOL_NUM_PROCESSES) as pool:
49
+ yield pool
50
+
38
51
 
39
52
  @pytest.fixture(scope='module')
40
53
  def circuits_cycle_depths_sampled_df():
@@ -203,7 +216,7 @@ def test_get_initial_simplex():
203
216
  assert simplex.shape[1] == len(names)
204
217
 
205
218
 
206
- def test_characterize_phased_fsim_parameters_with_xeb():
219
+ def test_characterize_phased_fsim_parameters_with_xeb(pool):
207
220
  q0, q1 = cirq.LineQubit.range(2)
208
221
  rs = np.random.RandomState(52)
209
222
  circuits = [
@@ -228,17 +241,16 @@ def test_characterize_phased_fsim_parameters_with_xeb():
228
241
  characterize_phi=False,
229
242
  )
230
243
  p_circuits = [parameterize_circuit(circuit, options) for circuit in circuits]
231
- with multiprocessing.Pool() as pool:
232
- result = characterize_phased_fsim_parameters_with_xeb(
233
- sampled_df=sampled_df,
234
- parameterized_circuits=p_circuits,
235
- cycle_depths=cycle_depths,
236
- options=options,
237
- # speed up with looser tolerances:
238
- fatol=1e-2,
239
- xatol=1e-2,
240
- pool=pool,
241
- )
244
+ result = characterize_phased_fsim_parameters_with_xeb(
245
+ sampled_df=sampled_df,
246
+ parameterized_circuits=p_circuits,
247
+ cycle_depths=cycle_depths,
248
+ options=options,
249
+ # speed up with looser tolerances:
250
+ fatol=1e-2,
251
+ xatol=1e-2,
252
+ pool=pool,
253
+ )
242
254
  opt_res = result.optimization_results[(q0, q1)]
243
255
  assert np.abs(opt_res.x[0] + np.pi / 4) < 0.1
244
256
  assert np.abs(opt_res.fun) < 0.1 # noiseless simulator
@@ -248,7 +260,7 @@ def test_characterize_phased_fsim_parameters_with_xeb():
248
260
 
249
261
 
250
262
  @pytest.mark.parametrize('use_pool', (True, False))
251
- def test_parallel_full_workflow(use_pool):
263
+ def test_parallel_full_workflow(request, use_pool):
252
264
  circuits = rqcg.generate_library_of_2q_circuits(
253
265
  n_library_circuits=5,
254
266
  two_qubit_gate=cirq.ISWAP**0.5,
@@ -268,10 +280,8 @@ def test_parallel_full_workflow(use_pool):
268
280
  combinations_by_layer=combs,
269
281
  )
270
282
 
271
- if use_pool:
272
- pool = multiprocessing.Pool()
273
- else:
274
- pool = None
283
+ # avoid starting worker pool if it is not needed
284
+ pool = request.getfixturevalue("pool") if use_pool else None
275
285
 
276
286
  fids_df_0 = benchmark_2q_xeb_fidelities(
277
287
  sampled_df=sampled_df, circuits=circuits, cycle_depths=cycle_depths, pool=pool
@@ -292,8 +302,6 @@ def test_parallel_full_workflow(use_pool):
292
302
  xatol=5e-2,
293
303
  pool=pool,
294
304
  )
295
- if pool is not None:
296
- pool.terminate()
297
305
 
298
306
  assert len(result.optimization_results) == graph.number_of_edges()
299
307
  for opt_res in result.optimization_results.values():
@@ -354,7 +362,7 @@ def test_options_with_defaults_from_gate():
354
362
  assert options.zeta_default == 0.0
355
363
 
356
364
  with pytest.raises(ValueError):
357
- _ = XEBPhasedFSimCharacterizationOptions().with_defaults_from_gate(cirq.CZ)
365
+ _ = XEBPhasedFSimCharacterizationOptions().with_defaults_from_gate(cirq.XX)
358
366
 
359
367
 
360
368
  def test_options_defaults_set():
@@ -395,3 +403,34 @@ def test_options_defaults_set():
395
403
  phi_default=0.0,
396
404
  )
397
405
  assert o3.defaults_set() is True
406
+
407
+
408
+ def _random_angles(n, seed):
409
+ rng = np.random.default_rng(seed)
410
+ r = 2 * rng.random((n, 5)) - 1
411
+ return np.pi * r
412
+
413
+
414
+ @pytest.mark.parametrize(
415
+ 'gate',
416
+ [
417
+ cirq.CZ,
418
+ cirq.SQRT_ISWAP,
419
+ cirq.SQRT_ISWAP_INV,
420
+ cirq.ISWAP,
421
+ cirq.ISWAP_INV,
422
+ cirq.cphase(0.1),
423
+ cirq.CZ**0.2,
424
+ ]
425
+ + [cirq.PhasedFSimGate(*r) for r in _random_angles(10, 0)],
426
+ )
427
+ def test_phased_fsim_angles_from_gate(gate):
428
+ angles = phased_fsim_angles_from_gate(gate)
429
+ angles = {k.removesuffix('_default'): v for k, v in angles.items()}
430
+ phasedfsim = cirq.PhasedFSimGate(**angles)
431
+ np.testing.assert_allclose(cirq.unitary(phasedfsim), cirq.unitary(gate), atol=1e-9)
432
+
433
+
434
+ def test_phased_fsim_angles_from_gate_unsupporet_gate():
435
+ with pytest.raises(ValueError, match='Unknown default angles'):
436
+ _ = phased_fsim_angles_from_gate(cirq.testing.TwoQubitGate())
@@ -12,30 +12,28 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  """Estimation of fidelity associated with experimental circuit executions."""
15
- import concurrent
16
15
  import os
17
16
  import time
18
17
  import uuid
19
- from concurrent.futures.thread import ThreadPoolExecutor
20
18
  from dataclasses import dataclass
21
19
  from typing import (
20
+ Any,
22
21
  Callable,
22
+ ContextManager,
23
+ Dict,
23
24
  List,
24
25
  Optional,
25
26
  Sequence,
27
+ Set,
26
28
  Tuple,
27
29
  TYPE_CHECKING,
28
- Set,
29
- ContextManager,
30
- Dict,
31
- Any,
32
30
  )
33
31
 
34
32
  import numpy as np
35
33
  import pandas as pd
36
34
  import tqdm
37
35
 
38
- from cirq import ops, devices, value, protocols
36
+ from cirq import devices, ops, protocols, value
39
37
  from cirq.circuits import Circuit, Moment
40
38
  from cirq.experiments.random_quantum_circuit_generation import CircuitLibraryCombination
41
39
 
@@ -276,18 +274,16 @@ def _execute_sample_2q_xeb_tasks_in_batches(
276
274
  run_batch = _SampleInBatches(
277
275
  sampler=sampler, repetitions=repetitions, combinations_by_layer=combinations_by_layer
278
276
  )
279
- with ThreadPoolExecutor(max_workers=2) as pool:
280
- futures = [pool.submit(run_batch, task_batch) for task_batch in batched_tasks]
281
277
 
282
- records = []
283
- with progress_bar(total=len(batched_tasks) * batch_size) as progress:
284
- for future in concurrent.futures.as_completed(futures):
285
- new_records = future.result()
286
- if dataset_directory is not None:
287
- os.makedirs(f'{dataset_directory}', exist_ok=True)
288
- protocols.to_json(new_records, f'{dataset_directory}/xeb.{uuid.uuid4()}.json')
289
- records.extend(new_records)
290
- progress.update(batch_size)
278
+ records = []
279
+ with progress_bar(total=len(batched_tasks) * batch_size) as progress:
280
+ for task in batched_tasks:
281
+ new_records = run_batch(task)
282
+ if dataset_directory is not None:
283
+ os.makedirs(f'{dataset_directory}', exist_ok=True)
284
+ protocols.to_json(new_records, f'{dataset_directory}/xeb.{uuid.uuid4()}.json')
285
+ records.extend(new_records)
286
+ progress.update(batch_size)
291
287
  return records
292
288
 
293
289
 
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
  """Estimation of fidelity associated with experimental circuit executions."""
15
15
  from dataclasses import dataclass
16
- from typing import List, Optional, Sequence, TYPE_CHECKING, Dict, Any
16
+ from typing import Any, Dict, List, Optional, Sequence, TYPE_CHECKING
17
17
 
18
18
  import numpy as np
19
19
  import pandas as pd
@@ -21,9 +21,10 @@ import pandas as pd
21
21
  from cirq import sim, value
22
22
 
23
23
  if TYPE_CHECKING:
24
- import cirq
25
24
  import multiprocessing
26
25
 
26
+ import cirq
27
+
27
28
 
28
29
  @dataclass(frozen=True)
29
30
  class _Simulate2qXEBTask:
@@ -107,7 +108,7 @@ def simulate_2q_xeb_circuits(
107
108
  # Need an actual object; not np.random or else multiprocessing will
108
109
  # fail to pickle the closure object:
109
110
  # https://github.com/quantumlib/Cirq/issues/3717
110
- simulator = sim.Simulator(seed=np.random.RandomState())
111
+ simulator = sim.Simulator(seed=np.random.RandomState(), dtype=np.complex128)
111
112
  _simulate_2q_xeb_circuit = _Simulate_2q_XEB_Circuit(simulator=simulator)
112
113
 
113
114
  tasks = tuple(
@@ -11,9 +11,11 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
14
17
  import multiprocessing
15
- from typing import Dict, Any, Optional
16
- from typing import Sequence
18
+ from typing import Any, Dict, Iterator, Optional, Sequence
17
19
 
18
20
  import numpy as np
19
21
  import pandas as pd
@@ -23,8 +25,17 @@ import cirq
23
25
  import cirq.experiments.random_quantum_circuit_generation as rqcg
24
26
  from cirq.experiments.xeb_simulation import simulate_2q_xeb_circuits
25
27
 
28
+ _POOL_NUM_PROCESSES = min(4, multiprocessing.cpu_count())
29
+
30
+
31
+ @pytest.fixture
32
+ def pool() -> Iterator[multiprocessing.pool.Pool]:
33
+ ctx = multiprocessing.get_context()
34
+ with ctx.Pool(_POOL_NUM_PROCESSES) as pool:
35
+ yield pool
26
36
 
27
- def test_simulate_2q_xeb_circuits():
37
+
38
+ def test_simulate_2q_xeb_circuits(pool):
28
39
  q0, q1 = cirq.LineQubit.range(2)
29
40
  circuits = [
30
41
  rqcg.random_rotations_between_two_qubit_circuit(
@@ -42,8 +53,7 @@ def test_simulate_2q_xeb_circuits():
42
53
  assert len(row['pure_probs']) == 4
43
54
  assert np.isclose(np.sum(row['pure_probs']), 1)
44
55
 
45
- with multiprocessing.Pool() as pool:
46
- df2 = simulate_2q_xeb_circuits(circuits, cycle_depths, pool=pool)
56
+ df2 = simulate_2q_xeb_circuits(circuits, cycle_depths, pool=pool)
47
57
 
48
58
  pd.testing.assert_frame_equal(df, df2)
49
59
 
@@ -76,7 +86,7 @@ def _ref_simulate_2q_xeb_circuit(task: Dict[str, Any]):
76
86
  tcircuit = circuit[:circuit_depth]
77
87
  tcircuit = cirq.resolve_parameters_once(tcircuit, param_resolver=param_resolver)
78
88
 
79
- pure_sim = cirq.Simulator()
89
+ pure_sim = cirq.Simulator(dtype=np.complex128)
80
90
  psi = pure_sim.simulate(tcircuit)
81
91
  psi_vector = psi.final_state_vector
82
92
  pure_probs = cirq.state_vector_to_probabilities(psi_vector)
@@ -118,8 +128,8 @@ def _ref_simulate_2q_xeb_circuits(
118
128
  return pd.DataFrame(records).set_index(['circuit_i', 'cycle_depth']).sort_index()
119
129
 
120
130
 
121
- @pytest.mark.parametrize('multiprocess', (True, False))
122
- def test_incremental_simulate(multiprocess):
131
+ @pytest.mark.parametrize('use_pool', (True, False))
132
+ def test_incremental_simulate(request, use_pool):
123
133
  q0, q1 = cirq.LineQubit.range(2)
124
134
  circuits = [
125
135
  rqcg.random_rotations_between_two_qubit_circuit(
@@ -129,16 +139,12 @@ def test_incremental_simulate(multiprocess):
129
139
  ]
130
140
  cycle_depths = np.arange(3, 100, 9, dtype=np.int64)
131
141
 
132
- if multiprocess:
133
- pool = multiprocessing.Pool()
134
- else:
135
- pool = None
142
+ # avoid starting worker pool if it is not needed
143
+ pool = request.getfixturevalue("pool") if use_pool else None
136
144
 
137
145
  df_ref = _ref_simulate_2q_xeb_circuits(circuits=circuits, cycle_depths=cycle_depths, pool=pool)
138
146
 
139
147
  df = simulate_2q_xeb_circuits(circuits=circuits, cycle_depths=cycle_depths, pool=pool)
140
- if pool is not None:
141
- pool.terminate()
142
148
  pd.testing.assert_frame_equal(df_ref, df)
143
149
 
144
150
  # Use below for approximate equality, if e.g. you're using qsim: