cirq-core 1.1.0.dev20221220224914__py3-none-any.whl → 1.2.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.
Files changed (228) hide show
  1. cirq/__init__.py +8 -0
  2. cirq/_compat.py +29 -4
  3. cirq/_compat_test.py +24 -26
  4. cirq/_version.py +32 -1
  5. cirq/_version_test.py +1 -1
  6. cirq/circuits/_block_diagram_drawer_test.py +4 -3
  7. cirq/circuits/circuit.py +109 -63
  8. cirq/circuits/circuit_operation.py +2 -3
  9. cirq/circuits/circuit_operation_test.py +4 -4
  10. cirq/circuits/circuit_test.py +11 -0
  11. cirq/circuits/frozen_circuit.py +13 -1
  12. cirq/circuits/frozen_circuit_test.py +5 -1
  13. cirq/circuits/moment.py +39 -14
  14. cirq/circuits/moment_test.py +7 -0
  15. cirq/circuits/text_diagram_drawer.py +1 -1
  16. cirq/circuits/text_diagram_drawer_test.py +3 -7
  17. cirq/contrib/acquaintance/bipartite.py +1 -1
  18. cirq/contrib/acquaintance/devices.py +2 -2
  19. cirq/contrib/acquaintance/executor.py +5 -2
  20. cirq/contrib/acquaintance/gates.py +3 -2
  21. cirq/contrib/acquaintance/permutation.py +13 -2
  22. cirq/contrib/acquaintance/testing.py +3 -5
  23. cirq/contrib/paulistring/recombine.py +3 -6
  24. cirq/contrib/qasm_import/_parser.py +17 -21
  25. cirq/contrib/qasm_import/_parser_test.py +30 -45
  26. cirq/contrib/qcircuit/qcircuit_test.py +3 -7
  27. cirq/contrib/quantum_volume/quantum_volume.py +3 -3
  28. cirq/contrib/quimb/mps_simulator.py +1 -1
  29. cirq/contrib/quimb/state_vector.py +2 -0
  30. cirq/contrib/quirk/quirk_gate.py +1 -0
  31. cirq/contrib/svg/svg.py +4 -7
  32. cirq/contrib/svg/svg_test.py +29 -1
  33. cirq/devices/grid_qubit.py +26 -28
  34. cirq/devices/grid_qubit_test.py +21 -5
  35. cirq/devices/line_qubit.py +10 -12
  36. cirq/devices/line_qubit_test.py +9 -2
  37. cirq/devices/named_topologies.py +1 -1
  38. cirq/devices/noise_model.py +4 -1
  39. cirq/devices/superconducting_qubits_noise_properties.py +1 -3
  40. cirq/experiments/n_qubit_tomography.py +1 -1
  41. cirq/experiments/qubit_characterizations.py +2 -2
  42. cirq/experiments/single_qubit_readout_calibration.py +1 -1
  43. cirq/experiments/t2_decay_experiment.py +1 -1
  44. cirq/experiments/xeb_simulation_test.py +2 -2
  45. cirq/interop/quirk/cells/testing.py +1 -1
  46. cirq/json_resolver_cache.py +1 -0
  47. cirq/linalg/__init__.py +2 -0
  48. cirq/linalg/decompositions_test.py +4 -4
  49. cirq/linalg/diagonalize_test.py +5 -6
  50. cirq/linalg/transformations.py +72 -9
  51. cirq/linalg/transformations_test.py +23 -7
  52. cirq/ops/__init__.py +4 -0
  53. cirq/ops/arithmetic_operation.py +4 -6
  54. cirq/ops/classically_controlled_operation.py +10 -3
  55. cirq/ops/clifford_gate.py +1 -7
  56. cirq/ops/common_channels.py +21 -15
  57. cirq/ops/common_gate_families.py +2 -3
  58. cirq/ops/common_gates.py +48 -11
  59. cirq/ops/common_gates_test.py +4 -0
  60. cirq/ops/controlled_gate.py +44 -18
  61. cirq/ops/controlled_operation.py +13 -5
  62. cirq/ops/dense_pauli_string.py +14 -19
  63. cirq/ops/diagonal_gate.py +3 -4
  64. cirq/ops/eigen_gate.py +8 -10
  65. cirq/ops/eigen_gate_test.py +6 -0
  66. cirq/ops/gate_operation.py +11 -6
  67. cirq/ops/gate_operation_test.py +11 -2
  68. cirq/ops/gateset.py +2 -1
  69. cirq/ops/gateset_test.py +38 -5
  70. cirq/ops/global_phase_op.py +28 -2
  71. cirq/ops/global_phase_op_test.py +21 -0
  72. cirq/ops/identity.py +1 -1
  73. cirq/ops/kraus_channel_test.py +2 -2
  74. cirq/ops/linear_combinations.py +7 -6
  75. cirq/ops/linear_combinations_test.py +26 -10
  76. cirq/ops/matrix_gates.py +8 -4
  77. cirq/ops/matrix_gates_test.py +25 -3
  78. cirq/ops/measure_util.py +13 -5
  79. cirq/ops/measure_util_test.py +8 -2
  80. cirq/ops/measurement_gate.py +1 -1
  81. cirq/ops/measurement_gate_test.py +9 -4
  82. cirq/ops/mixed_unitary_channel_test.py +4 -4
  83. cirq/ops/named_qubit.py +2 -4
  84. cirq/ops/parity_gates.py +5 -1
  85. cirq/ops/parity_gates_test.py +6 -0
  86. cirq/ops/pauli_gates.py +9 -9
  87. cirq/ops/pauli_string.py +4 -2
  88. cirq/ops/pauli_string_raw_types.py +4 -11
  89. cirq/ops/pauli_string_test.py +13 -13
  90. cirq/ops/pauli_sum_exponential.py +6 -1
  91. cirq/ops/qubit_manager.py +97 -0
  92. cirq/ops/qubit_manager_test.py +66 -0
  93. cirq/ops/raw_types.py +75 -33
  94. cirq/ops/raw_types_test.py +34 -0
  95. cirq/ops/three_qubit_gates.py +16 -10
  96. cirq/ops/three_qubit_gates_test.py +4 -2
  97. cirq/ops/two_qubit_diagonal_gate.py +3 -3
  98. cirq/ops/wait_gate.py +1 -1
  99. cirq/protocols/__init__.py +1 -0
  100. cirq/protocols/act_on_protocol.py +3 -3
  101. cirq/protocols/act_on_protocol_test.py +5 -5
  102. cirq/protocols/apply_channel_protocol.py +9 -8
  103. cirq/protocols/apply_mixture_protocol.py +8 -8
  104. cirq/protocols/apply_mixture_protocol_test.py +1 -1
  105. cirq/protocols/apply_unitary_protocol.py +66 -19
  106. cirq/protocols/apply_unitary_protocol_test.py +50 -0
  107. cirq/protocols/circuit_diagram_info_protocol.py +7 -9
  108. cirq/protocols/decompose_protocol.py +167 -125
  109. cirq/protocols/decompose_protocol_test.py +132 -2
  110. cirq/protocols/has_stabilizer_effect_protocol.py +2 -1
  111. cirq/protocols/inverse_protocol.py +2 -2
  112. cirq/protocols/json_serialization_test.py +3 -3
  113. cirq/protocols/json_test_data/Linspace.json +20 -7
  114. cirq/protocols/json_test_data/Linspace.repr +4 -1
  115. cirq/protocols/json_test_data/Points.json +19 -8
  116. cirq/protocols/json_test_data/Points.repr +4 -1
  117. cirq/protocols/json_test_data/Result.repr_inward +1 -1
  118. cirq/protocols/json_test_data/ResultDict.repr +1 -1
  119. cirq/protocols/json_test_data/ResultDict.repr_inward +1 -1
  120. cirq/protocols/json_test_data/TrialResult.repr_inward +1 -1
  121. cirq/protocols/json_test_data/XPowGate.json +13 -5
  122. cirq/protocols/json_test_data/XPowGate.repr +1 -1
  123. cirq/protocols/json_test_data/ZPowGate.json +13 -5
  124. cirq/protocols/json_test_data/ZPowGate.repr +1 -1
  125. cirq/protocols/json_test_data/ZipLongest.json +19 -0
  126. cirq/protocols/json_test_data/ZipLongest.repr +1 -0
  127. cirq/protocols/json_test_data/spec.py +1 -0
  128. cirq/protocols/kraus_protocol.py +3 -4
  129. cirq/protocols/measurement_key_protocol.py +3 -1
  130. cirq/protocols/mixture_protocol.py +3 -2
  131. cirq/protocols/phase_protocol.py +3 -3
  132. cirq/protocols/pow_protocol.py +1 -2
  133. cirq/protocols/qasm.py +4 -4
  134. cirq/protocols/qid_shape_protocol.py +8 -8
  135. cirq/protocols/resolve_parameters.py +8 -3
  136. cirq/protocols/resolve_parameters_test.py +3 -3
  137. cirq/protocols/unitary_protocol.py +19 -11
  138. cirq/protocols/unitary_protocol_test.py +37 -0
  139. cirq/qis/channels.py +1 -1
  140. cirq/qis/clifford_tableau.py +4 -5
  141. cirq/qis/quantum_state_representation.py +7 -9
  142. cirq/qis/states.py +21 -13
  143. cirq/qis/states_test.py +7 -0
  144. cirq/sim/clifford/clifford_simulator.py +3 -3
  145. cirq/sim/density_matrix_simulation_state.py +2 -1
  146. cirq/sim/density_matrix_simulator.py +1 -1
  147. cirq/sim/density_matrix_simulator_test.py +9 -5
  148. cirq/sim/density_matrix_utils.py +7 -32
  149. cirq/sim/mux.py +2 -2
  150. cirq/sim/simulation_state.py +58 -18
  151. cirq/sim/simulation_state_base.py +5 -2
  152. cirq/sim/simulation_state_test.py +121 -9
  153. cirq/sim/simulation_utils.py +59 -0
  154. cirq/sim/simulation_utils_test.py +32 -0
  155. cirq/sim/simulator.py +2 -1
  156. cirq/sim/simulator_base_test.py +3 -3
  157. cirq/sim/sparse_simulator.py +1 -1
  158. cirq/sim/sparse_simulator_test.py +5 -5
  159. cirq/sim/state_vector.py +7 -36
  160. cirq/sim/state_vector_simulation_state.py +18 -1
  161. cirq/sim/state_vector_simulator.py +3 -2
  162. cirq/sim/state_vector_simulator_test.py +24 -2
  163. cirq/sim/state_vector_test.py +46 -15
  164. cirq/study/__init__.py +1 -0
  165. cirq/study/flatten_expressions.py +2 -2
  166. cirq/study/resolver.py +2 -0
  167. cirq/study/resolver_test.py +1 -1
  168. cirq/study/result.py +1 -1
  169. cirq/study/sweeps.py +103 -9
  170. cirq/study/sweeps_test.py +64 -0
  171. cirq/testing/__init__.py +4 -0
  172. cirq/testing/circuit_compare.py +15 -18
  173. cirq/testing/consistent_act_on.py +4 -4
  174. cirq/testing/consistent_controlled_gate_op_test.py +1 -1
  175. cirq/testing/consistent_decomposition.py +11 -2
  176. cirq/testing/consistent_decomposition_test.py +8 -1
  177. cirq/testing/consistent_protocols.py +2 -0
  178. cirq/testing/consistent_protocols_test.py +8 -4
  179. cirq/testing/consistent_qasm.py +8 -15
  180. cirq/testing/consistent_specified_has_unitary.py +1 -1
  181. cirq/testing/consistent_unitary.py +85 -0
  182. cirq/testing/consistent_unitary_test.py +96 -0
  183. cirq/testing/equivalent_repr_eval.py +10 -10
  184. cirq/testing/json.py +3 -3
  185. cirq/testing/logs.py +1 -1
  186. cirq/testing/order_tester.py +4 -5
  187. cirq/testing/random_circuit.py +3 -5
  188. cirq/testing/sample_gates.py +79 -0
  189. cirq/testing/sample_gates_test.py +59 -0
  190. cirq/transformers/__init__.py +2 -0
  191. cirq/transformers/analytical_decompositions/__init__.py +8 -0
  192. cirq/transformers/analytical_decompositions/pauli_string_decomposition.py +130 -0
  193. cirq/transformers/analytical_decompositions/pauli_string_decomposition_test.py +58 -0
  194. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +230 -0
  195. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +112 -0
  196. cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +1 -3
  197. cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py +1 -1
  198. cirq/transformers/expand_composite.py +1 -1
  199. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +4 -4
  200. cirq/transformers/measurement_transformers.py +4 -4
  201. cirq/transformers/merge_single_qubit_gates.py +17 -4
  202. cirq/transformers/routing/route_circuit_cqc.py +2 -2
  203. cirq/transformers/stratify.py +125 -62
  204. cirq/transformers/stratify_test.py +20 -16
  205. cirq/transformers/transformer_api.py +1 -1
  206. cirq/transformers/transformer_primitives.py +3 -2
  207. cirq/transformers/transformer_primitives_test.py +11 -0
  208. cirq/value/abc_alt.py +3 -2
  209. cirq/value/abc_alt_test.py +1 -0
  210. cirq/value/classical_data.py +10 -10
  211. cirq/value/digits.py +2 -2
  212. cirq/value/linear_dict.py +18 -19
  213. cirq/value/product_state.py +7 -6
  214. cirq/value/value_equality_attr.py +2 -2
  215. cirq/vis/heatmap.py +1 -1
  216. cirq/vis/heatmap_test.py +2 -2
  217. cirq/work/collector.py +2 -2
  218. cirq/work/observable_measurement_data.py +5 -5
  219. cirq/work/observable_readout_calibration.py +3 -1
  220. cirq/work/observable_settings.py +1 -1
  221. cirq/work/pauli_sum_collector.py +9 -8
  222. cirq/work/sampler.py +2 -0
  223. cirq/work/zeros_sampler.py +2 -2
  224. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/METADATA +7 -15
  225. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/RECORD +228 -214
  226. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/WHEEL +1 -1
  227. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/LICENSE +0 -0
  228. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1156,6 +1156,22 @@ def test_pauli_sum_pow():
1156
1156
  for psum in [psum1, psum2, psum3, psum4]:
1157
1157
  assert cirq.approx_eq(psum**0, identity)
1158
1158
 
1159
+ # tests for exponents greater than two for both even and odd
1160
+ psum5 = cirq.Z(q0) * cirq.Z(q1) + cirq.Z(q2) + cirq.Z(q3)
1161
+ correctresult = psum5.copy()
1162
+ for e in range(1, 9):
1163
+ assert correctresult == psum5**e
1164
+ correctresult *= psum5
1165
+
1166
+ psum6 = cirq.X(q0) * cirq.Y(q1) + cirq.Z(q2) + cirq.X(q3)
1167
+ assert psum6 * psum6 * psum6 * psum6 * psum6 * psum6 * psum6 * psum6 == psum6**8
1168
+
1169
+ # test to ensure pow doesn't make any change to the original value
1170
+ psum7 = cirq.X(q0) * cirq.Y(q1) + cirq.Z(q2)
1171
+ psum7copy = psum7.copy()
1172
+ assert psum7**5 == psum7 * psum7 * psum7 * psum7 * psum7
1173
+ assert psum7copy == psum7
1174
+
1159
1175
 
1160
1176
  # Using the entries of table 1 of https://arxiv.org/abs/1804.09130 as golden values.
1161
1177
  @pytest.mark.parametrize(
@@ -1302,7 +1318,7 @@ def test_expectation_from_state_vector_two_qubit_states():
1302
1318
  psum1 = cirq.Z(q[0]) + 3.2 * cirq.Z(q[1])
1303
1319
  psum2 = -1 * cirq.X(q[0]) + 2 * cirq.X(q[1])
1304
1320
  wf1 = np.array([0, 1, 0, 0], dtype=complex)
1305
- for state in [wf1, wf1.reshape(2, 2)]:
1321
+ for state in [wf1, wf1.reshape((2, 2))]:
1306
1322
  np.testing.assert_allclose(
1307
1323
  psum1.expectation_from_state_vector(state, qubit_map=q_map), -2.2, atol=1e-7
1308
1324
  )
@@ -1311,7 +1327,7 @@ def test_expectation_from_state_vector_two_qubit_states():
1311
1327
  )
1312
1328
 
1313
1329
  wf2 = np.array([1, 1, 1, 1], dtype=complex) / 2
1314
- for state in [wf2, wf2.reshape(2, 2)]:
1330
+ for state in [wf2, wf2.reshape((2, 2))]:
1315
1331
  np.testing.assert_allclose(
1316
1332
  psum1.expectation_from_state_vector(state, qubit_map=q_map), 0, atol=1e-7
1317
1333
  )
@@ -1322,7 +1338,7 @@ def test_expectation_from_state_vector_two_qubit_states():
1322
1338
  psum3 = cirq.Z(q[0]) + cirq.X(q[1])
1323
1339
  wf3 = np.array([1, 1, 0, 0], dtype=complex) / np.sqrt(2)
1324
1340
  q_map_2 = {q0: 1, q1: 0}
1325
- for state in [wf3, wf3.reshape(2, 2)]:
1341
+ for state in [wf3, wf3.reshape((2, 2))]:
1326
1342
  np.testing.assert_allclose(
1327
1343
  psum3.expectation_from_state_vector(state, qubit_map=q_map), 2, atol=1e-7
1328
1344
  )
@@ -1336,7 +1352,7 @@ def test_expectation_from_density_matrix_invalid_input():
1336
1352
  psum = cirq.X(q0) + 2 * cirq.Y(q1) + 3 * cirq.Z(q3)
1337
1353
  q_map = {q0: 0, q1: 1, q3: 2}
1338
1354
  wf = np.array([1, 0, 0, 0, 0, 0, 0, 0], dtype=np.complex64)
1339
- rho = np.kron(wf.conjugate().T, wf).reshape(8, 8)
1355
+ rho = np.kron(wf.conjugate().T, wf).reshape((8, 8))
1340
1356
 
1341
1357
  im_psum = (1j + 1) * psum
1342
1358
  with pytest.raises(NotImplementedError, match='non-Hermitian'):
@@ -1443,24 +1459,24 @@ def test_expectation_from_density_matrix_two_qubit_states():
1443
1459
  psum1 = cirq.Z(q[0]) + 3.2 * cirq.Z(q[1])
1444
1460
  psum2 = -1 * cirq.X(q[0]) + 2 * cirq.X(q[1])
1445
1461
  wf1 = np.array([0, 1, 0, 0], dtype=complex)
1446
- rho1 = np.kron(wf1, wf1).reshape(4, 4)
1447
- for state in [rho1, rho1.reshape(2, 2, 2, 2)]:
1462
+ rho1 = np.kron(wf1, wf1).reshape((4, 4))
1463
+ for state in [rho1, rho1.reshape((2, 2, 2, 2))]:
1448
1464
  np.testing.assert_allclose(
1449
1465
  psum1.expectation_from_density_matrix(state, qubit_map=q_map), -2.2
1450
1466
  )
1451
1467
  np.testing.assert_allclose(psum2.expectation_from_density_matrix(state, qubit_map=q_map), 0)
1452
1468
 
1453
1469
  wf2 = np.array([1, 1, 1, 1], dtype=complex) / 2
1454
- rho2 = np.kron(wf2, wf2).reshape(4, 4)
1455
- for state in [rho2, rho2.reshape(2, 2, 2, 2)]:
1470
+ rho2 = np.kron(wf2, wf2).reshape((4, 4))
1471
+ for state in [rho2, rho2.reshape((2, 2, 2, 2))]:
1456
1472
  np.testing.assert_allclose(psum1.expectation_from_density_matrix(state, qubit_map=q_map), 0)
1457
1473
  np.testing.assert_allclose(psum2.expectation_from_density_matrix(state, qubit_map=q_map), 1)
1458
1474
 
1459
1475
  psum3 = cirq.Z(q[0]) + cirq.X(q[1])
1460
1476
  wf3 = np.array([1, 1, 0, 0], dtype=complex) / np.sqrt(2)
1461
- rho3 = np.kron(wf3, wf3).reshape(4, 4)
1477
+ rho3 = np.kron(wf3, wf3).reshape((4, 4))
1462
1478
  q_map_2 = {q0: 1, q1: 0}
1463
- for state in [rho3, rho3.reshape(2, 2, 2, 2)]:
1479
+ for state in [rho3, rho3.reshape((2, 2, 2, 2))]:
1464
1480
  np.testing.assert_allclose(psum3.expectation_from_density_matrix(state, qubit_map=q_map), 2)
1465
1481
  np.testing.assert_allclose(
1466
1482
  psum3.expectation_from_density_matrix(state, qubit_map=q_map_2), 0
cirq/ops/matrix_gates.py CHANGED
@@ -53,7 +53,7 @@ class MatrixGate(raw_types.Gate):
53
53
  self,
54
54
  matrix: np.ndarray,
55
55
  *,
56
- name: str = None,
56
+ name: Optional[str] = None,
57
57
  qid_shape: Optional[Iterable[int]] = None,
58
58
  unitary_check: bool = True,
59
59
  unitary_check_rtol: float = 1e-5,
@@ -114,11 +114,15 @@ class MatrixGate(raw_types.Gate):
114
114
  return MatrixGate(self._matrix, name=name, qid_shape=self._qid_shape, unitary_check=False)
115
115
 
116
116
  def _json_dict_(self) -> Dict[str, Any]:
117
- return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}
117
+ return {
118
+ 'matrix': self._matrix.tolist(),
119
+ 'qid_shape': self._qid_shape,
120
+ **({'name': self._name} if self._name is not None else {}),
121
+ }
118
122
 
119
123
  @classmethod
120
- def _from_json_dict_(cls, matrix, qid_shape, **kwargs):
121
- return cls(matrix=np.array(matrix), qid_shape=qid_shape)
124
+ def _from_json_dict_(cls, matrix, qid_shape, name=None, **kwargs):
125
+ return cls(matrix=np.array(matrix), qid_shape=qid_shape, name=name)
122
126
 
123
127
  def _qid_shape_(self) -> Tuple[int, ...]:
124
128
  return self._qid_shape
@@ -29,12 +29,12 @@ def test_single_qubit_init():
29
29
  m = np.array([[1, 1j], [1j, 1]]) * np.sqrt(0.5)
30
30
  x2 = cirq.MatrixGate(m)
31
31
  assert cirq.has_unitary(x2)
32
- assert np.alltrue(cirq.unitary(x2) == m)
32
+ assert np.all(cirq.unitary(x2) == m)
33
33
  assert cirq.qid_shape(x2) == (2,)
34
34
 
35
35
  x2 = cirq.MatrixGate(PLUS_ONE, qid_shape=(3,))
36
36
  assert cirq.has_unitary(x2)
37
- assert np.alltrue(cirq.unitary(x2) == PLUS_ONE)
37
+ assert np.all(cirq.unitary(x2) == PLUS_ONE)
38
38
  assert cirq.qid_shape(x2) == (3,)
39
39
 
40
40
  with pytest.raises(ValueError, match='Not a .*unitary matrix'):
@@ -97,7 +97,7 @@ def test_single_qubit_extrapolate():
97
97
  def test_two_qubit_init():
98
98
  x2 = cirq.MatrixGate(QFT2)
99
99
  assert cirq.has_unitary(x2)
100
- assert np.alltrue(cirq.unitary(x2) == QFT2)
100
+ assert np.all(cirq.unitary(x2) == QFT2)
101
101
 
102
102
 
103
103
  def test_two_qubit_eq():
@@ -388,3 +388,25 @@ def test_matrixgate_unitary_tolerance():
388
388
  # very low atol -> the check never converges
389
389
  with pytest.raises(ValueError):
390
390
  _ = cirq.MatrixGate(np.array([[0.707, 0.707], [-0.707, 0.707]]), unitary_check_rtol=1e-10)
391
+
392
+
393
+ def test_matrixgate_name_serialization():
394
+ # https://github.com/quantumlib/Cirq/issues/5999
395
+
396
+ # Test name serialization
397
+ gate1 = cirq.MatrixGate(np.eye(2), name='test_name')
398
+ gate_after_serialization1 = cirq.read_json(json_text=cirq.to_json(gate1))
399
+ assert gate1._name == 'test_name'
400
+ assert gate_after_serialization1._name == 'test_name'
401
+
402
+ # Test name backwards compatibility
403
+ gate2 = cirq.MatrixGate(np.eye(2))
404
+ gate_after_serialization2 = cirq.read_json(json_text=cirq.to_json(gate2))
405
+ assert gate2._name is None
406
+ assert gate_after_serialization2._name is None
407
+
408
+ # Test empty name
409
+ gate3 = cirq.MatrixGate(np.eye(2), name='')
410
+ gate_after_serialization3 = cirq.read_json(json_text=cirq.to_json(gate3))
411
+ assert gate3._name == ''
412
+ assert gate_after_serialization3._name == ''
cirq/ops/measure_util.py CHANGED
@@ -46,18 +46,22 @@ def measure_single_paulistring(
46
46
 
47
47
  Raises:
48
48
  ValueError: if the observable is not an instance of PauliString or if the coefficient
49
- is not +1.
49
+ is not +1 or -1.
50
50
  """
51
51
  if not isinstance(pauli_observable, pauli_string.PauliString):
52
52
  raise ValueError(
53
53
  f'Pauli observable {pauli_observable} should be an instance of cirq.PauliString.'
54
54
  )
55
- if pauli_observable.coefficient != 1:
56
- raise ValueError(f"Pauli observable {pauli_observable} must have a coefficient of +1.")
55
+ if abs(pauli_observable.coefficient) != 1:
56
+ raise ValueError(
57
+ f"Pauli observable {pauli_observable} must have a coefficient of +1 or -1."
58
+ )
57
59
 
58
60
  if key is None:
59
61
  key = _default_measurement_key(pauli_observable)
60
- return PauliMeasurementGate(pauli_observable.values(), key).on(*pauli_observable.keys())
62
+ return PauliMeasurementGate(pauli_observable.dense(list(pauli_observable.keys())), key).on(
63
+ *pauli_observable.keys()
64
+ )
61
65
 
62
66
 
63
67
  def measure_paulistring_terms(
@@ -115,7 +119,8 @@ def measure(
115
119
  ) -> raw_types.Operation:
116
120
  """Returns a single MeasurementGate applied to all the given qubits.
117
121
 
118
- The qubits are measured in the computational basis.
122
+ The qubits are measured in the computational basis. This can also be
123
+ used with the alias `cirq.M`.
119
124
 
120
125
  Args:
121
126
  *target: The qubits that the measurement gate should measure.
@@ -159,6 +164,9 @@ def measure(
159
164
  return MeasurementGate(len(targets), key, invert_mask, qid_shape, confusion_map).on(*targets)
160
165
 
161
166
 
167
+ M = measure
168
+
169
+
162
170
  @overload
163
171
  def measure_each(
164
172
  *qubits: raw_types.Qid, key_func: Callable[[raw_types.Qid], str] = str
@@ -95,6 +95,12 @@ def test_measure_single_paulistring():
95
95
  ps.values(), key='a'
96
96
  ).on(*ps.keys())
97
97
 
98
+ # Test with negative coefficient
99
+ ps_neg = -cirq.Y(cirq.LineQubit(0)) * cirq.Y(cirq.LineQubit(1))
100
+ assert cirq.measure_single_paulistring(ps_neg, key='1').gate == cirq.PauliMeasurementGate(
101
+ cirq.DensePauliString('YY', coefficient=-1), key='1'
102
+ )
103
+
98
104
  # Empty application
99
105
  with pytest.raises(ValueError, match='should be an instance of cirq.PauliString'):
100
106
  _ = cirq.measure_single_paulistring(cirq.I(q[0]) * cirq.I(q[1]))
@@ -103,9 +109,9 @@ def test_measure_single_paulistring():
103
109
  with pytest.raises(ValueError, match='should be an instance of cirq.PauliString'):
104
110
  _ = cirq.measure_single_paulistring(q)
105
111
 
106
- # Coefficient != +1
112
+ # Coefficient != +1 or -1
107
113
  with pytest.raises(ValueError, match='must have a coefficient'):
108
- _ = cirq.measure_single_paulistring(-ps)
114
+ _ = cirq.measure_single_paulistring(-2 * ps)
109
115
 
110
116
 
111
117
  def test_measure_paulistring_terms():
@@ -50,7 +50,7 @@ class MeasurementGate(raw_types.Gate):
50
50
  num_qubits: Optional[int] = None,
51
51
  key: Union[str, 'cirq.MeasurementKey'] = '',
52
52
  invert_mask: Tuple[bool, ...] = (),
53
- qid_shape: Tuple[int, ...] = None,
53
+ qid_shape: Optional[Tuple[int, ...]] = None,
54
54
  confusion_map: Optional[Dict[Tuple[int, ...], np.ndarray]] = None,
55
55
  ) -> None:
56
56
  """Inits MeasurementGate.
@@ -317,23 +317,28 @@ def test_op_repr():
317
317
  b,
318
318
  key='out',
319
319
  invert_mask=(False, True),
320
- confusion_map={(0,): np.array([[0, 1], [1, 0]], dtype=np.int64)},
320
+ confusion_map={(0,): np.array([[0, 1], [1, 0]], dtype=np.dtype('int64'))},
321
321
  )
322
322
  ) == (
323
323
  "cirq.measure(cirq.LineQubit(0), cirq.LineQubit(1), "
324
324
  "key=cirq.MeasurementKey(name='out'), "
325
325
  "invert_mask=(False, True), "
326
- "confusion_map={(0,): np.array([[0, 1], [1, 0]], dtype=np.int64)})"
326
+ "confusion_map={(0,): np.array([[0, 1], [1, 0]], dtype=np.dtype('int64'))})"
327
327
  )
328
328
 
329
329
 
330
330
  def test_repr():
331
331
  gate = cirq.MeasurementGate(
332
- 3, 'a', (True, False), (1, 2, 3), {(2,): np.array([[0, 1], [1, 0]], dtype=np.int64)}
332
+ 3,
333
+ 'a',
334
+ (True, False),
335
+ (1, 2, 3),
336
+ {(2,): np.array([[0, 1], [1, 0]], dtype=np.dtype('int64'))},
333
337
  )
334
338
  assert repr(gate) == (
335
339
  "cirq.MeasurementGate(3, cirq.MeasurementKey(name='a'), (True, False), "
336
- "qid_shape=(1, 2, 3), confusion_map={(2,): np.array([[0, 1], [1, 0]], dtype=np.int64)})"
340
+ "qid_shape=(1, 2, 3), "
341
+ "confusion_map={(2,): np.array([[0, 1], [1, 0]], dtype=np.dtype('int64'))})"
337
342
  )
338
343
 
339
344
 
@@ -95,16 +95,16 @@ def test_matrix_mixture_str():
95
95
 
96
96
  def test_matrix_mixture_repr():
97
97
  mix = [
98
- (0.5, np.array([[1, 0], [0, 1]], dtype=np.complex64)),
99
- (0.5, np.array([[0, 1], [1, 0]], dtype=np.complex64)),
98
+ (0.5, np.array([[1, 0], [0, 1]], dtype=np.dtype('complex64'))),
99
+ (0.5, np.array([[0, 1], [1, 0]], dtype=np.dtype('complex64'))),
100
100
  ]
101
101
  half_flip = cirq.MixedUnitaryChannel(mix, key='flip')
102
102
  assert (
103
103
  repr(half_flip)
104
104
  == """\
105
105
  cirq.MixedUnitaryChannel(mixture=[\
106
- (0.5, np.array([[(1+0j), 0j], [0j, (1+0j)]], dtype=np.complex64)), \
107
- (0.5, np.array([[0j, (1+0j)], [(1+0j), 0j]], dtype=np.complex64))], \
106
+ (0.5, np.array([[(1+0j), 0j], [0j, (1+0j)]], dtype=np.dtype('complex64'))), \
107
+ (0.5, np.array([[0j, (1+0j)], [(1+0j), 0j]], dtype=np.dtype('complex64')))], \
108
108
  key='flip')"""
109
109
  )
110
110
 
cirq/ops/named_qubit.py CHANGED
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  import functools
15
- from typing import Any, Dict, List, TYPE_CHECKING, TypeVar
15
+ from typing import Any, Dict, List, TYPE_CHECKING
16
16
 
17
17
  from cirq import protocols
18
18
  from cirq.ops import raw_types
@@ -21,10 +21,8 @@ from cirq.ops import raw_types
21
21
  if TYPE_CHECKING:
22
22
  import cirq
23
23
 
24
- TSelf = TypeVar('TSelf', bound='_BaseNamedQid')
25
24
 
26
-
27
- @functools.total_ordering # type: ignore
25
+ @functools.total_ordering
28
26
  class _BaseNamedQid(raw_types.Qid):
29
27
  """The base class for `NamedQid` and `NamedQubit`."""
30
28
 
cirq/ops/parity_gates.py CHANGED
@@ -15,6 +15,7 @@
15
15
  """Quantum gates that phase with respect to product-of-pauli observables."""
16
16
 
17
17
  from typing import Any, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
18
+ from typing_extensions import Self
18
19
 
19
20
  import numpy as np
20
21
 
@@ -295,6 +296,9 @@ class ZZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
295
296
 
296
297
  return args.target_tensor
297
298
 
299
+ def _phase_by_(self, phase_turns: float, qubit_index: int) -> "ZZPowGate":
300
+ return self
301
+
298
302
  def __str__(self) -> str:
299
303
  if self._exponent == 1:
300
304
  return 'ZZ'
@@ -328,7 +332,7 @@ class MSGate(XXPowGate):
328
332
  XXPowGate.__init__(self, exponent=rads * 2 / np.pi, global_shift=-0.5)
329
333
  self.rads = rads
330
334
 
331
- def _with_exponent(self: 'MSGate', exponent: value.TParamVal) -> 'MSGate':
335
+ def _with_exponent(self, exponent: value.TParamVal) -> Self:
332
336
  return type(self)(rads=exponent * np.pi / 2)
333
337
 
334
338
  def _circuit_diagram_info_(
@@ -195,6 +195,12 @@ def test_zz_pow():
195
195
  assert (cirq.ZZ**-1) ** 0.5 == cirq.ZZ**-0.5
196
196
 
197
197
 
198
+ def test_zz_phase_by():
199
+ assert cirq.phase_by(cirq.ZZ, 0.25, 0) == cirq.phase_by(cirq.ZZ, 0.25, 1) == cirq.ZZ
200
+ assert cirq.phase_by(cirq.ZZ**0.5, 0.25, 0) == cirq.ZZ**0.5
201
+ assert cirq.phase_by(cirq.ZZ**-0.5, 0.25, 1) == cirq.ZZ**-0.5
202
+
203
+
198
204
  def test_zz_str():
199
205
  assert str(cirq.ZZ) == 'ZZ'
200
206
  assert str(cirq.ZZ**0.5) == 'ZZ**0.5'
cirq/ops/pauli_gates.py CHANGED
@@ -112,10 +112,10 @@ class _PauliX(Pauli, common_gates.XPowGate):
112
112
  Pauli.__init__(self, index=0, name='X')
113
113
  common_gates.XPowGate.__init__(self, exponent=1.0)
114
114
 
115
- def __pow__(self: '_PauliX', exponent: 'cirq.TParamVal') -> common_gates.XPowGate:
115
+ def __pow__(self, exponent: 'cirq.TParamVal') -> common_gates.XPowGate:
116
116
  return common_gates.XPowGate(exponent=exponent) if exponent != 1 else _PauliX()
117
117
 
118
- def _with_exponent(self: '_PauliX', exponent: 'cirq.TParamVal') -> common_gates.XPowGate:
118
+ def _with_exponent(self, exponent: 'cirq.TParamVal') -> common_gates.XPowGate:
119
119
  return self.__pow__(exponent)
120
120
 
121
121
  @classmethod
@@ -125,7 +125,7 @@ class _PauliX(Pauli, common_gates.XPowGate):
125
125
  return Pauli._XYZ[0]
126
126
 
127
127
  @property
128
- def basis(self: '_PauliX') -> Dict[int, '_XEigenState']:
128
+ def basis(self) -> Dict[int, '_XEigenState']:
129
129
  from cirq.value.product_state import _XEigenState
130
130
 
131
131
  return {+1: _XEigenState(+1), -1: _XEigenState(-1)}
@@ -136,10 +136,10 @@ class _PauliY(Pauli, common_gates.YPowGate):
136
136
  Pauli.__init__(self, index=1, name='Y')
137
137
  common_gates.YPowGate.__init__(self, exponent=1.0)
138
138
 
139
- def __pow__(self: '_PauliY', exponent: 'cirq.TParamVal') -> common_gates.YPowGate:
139
+ def __pow__(self, exponent: 'cirq.TParamVal') -> common_gates.YPowGate:
140
140
  return common_gates.YPowGate(exponent=exponent) if exponent != 1 else _PauliY()
141
141
 
142
- def _with_exponent(self: '_PauliY', exponent: 'cirq.TParamVal') -> common_gates.YPowGate:
142
+ def _with_exponent(self, exponent: 'cirq.TParamVal') -> common_gates.YPowGate:
143
143
  return self.__pow__(exponent)
144
144
 
145
145
  @classmethod
@@ -149,7 +149,7 @@ class _PauliY(Pauli, common_gates.YPowGate):
149
149
  return Pauli._XYZ[1]
150
150
 
151
151
  @property
152
- def basis(self: '_PauliY') -> Dict[int, '_YEigenState']:
152
+ def basis(self) -> Dict[int, '_YEigenState']:
153
153
  from cirq.value.product_state import _YEigenState
154
154
 
155
155
  return {+1: _YEigenState(+1), -1: _YEigenState(-1)}
@@ -160,10 +160,10 @@ class _PauliZ(Pauli, common_gates.ZPowGate):
160
160
  Pauli.__init__(self, index=2, name='Z')
161
161
  common_gates.ZPowGate.__init__(self, exponent=1.0)
162
162
 
163
- def __pow__(self: '_PauliZ', exponent: 'cirq.TParamVal') -> common_gates.ZPowGate:
163
+ def __pow__(self, exponent: 'cirq.TParamVal') -> common_gates.ZPowGate:
164
164
  return common_gates.ZPowGate(exponent=exponent) if exponent != 1 else _PauliZ()
165
165
 
166
- def _with_exponent(self: '_PauliZ', exponent: 'cirq.TParamVal') -> common_gates.ZPowGate:
166
+ def _with_exponent(self, exponent: 'cirq.TParamVal') -> common_gates.ZPowGate:
167
167
  return self.__pow__(exponent)
168
168
 
169
169
  @classmethod
@@ -173,7 +173,7 @@ class _PauliZ(Pauli, common_gates.ZPowGate):
173
173
  return Pauli._XYZ[2]
174
174
 
175
175
  @property
176
- def basis(self: '_PauliZ') -> Dict[int, '_ZEigenState']:
176
+ def basis(self) -> Dict[int, '_ZEigenState']:
177
177
  from cirq.value.product_state import _ZEigenState
178
178
 
179
179
  return {+1: _ZEigenState(+1), -1: _ZEigenState(-1)}
cirq/ops/pauli_string.py CHANGED
@@ -242,12 +242,14 @@ class PauliString(raw_types.Operation, Generic[TKey]):
242
242
  def get(self, key: Any, default: TDefault) -> Union[pauli_gates.Pauli, TDefault]:
243
243
  pass
244
244
 
245
- def get(self, key: Any, default: TDefault = None) -> Union[pauli_gates.Pauli, TDefault, None]:
245
+ def get(
246
+ self, key: Any, default: Optional[TDefault] = None
247
+ ) -> Union[pauli_gates.Pauli, TDefault, None]:
246
248
  """Returns the `cirq.Pauli` operation acting on qubit `key` or `default` if none exists."""
247
249
  return self._qubit_pauli_map.get(key, default)
248
250
 
249
251
  @overload
250
- def __mul__( # type: ignore
252
+ def __mul__(
251
253
  self, other: 'cirq.PauliString[TKeyOther]'
252
254
  ) -> 'cirq.PauliString[Union[TKey, TKeyOther]]':
253
255
  pass
@@ -13,7 +13,8 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import abc
16
- from typing import Any, Dict, Sequence, Tuple, TypeVar, TYPE_CHECKING
16
+ from typing import Any, Dict, Sequence, Tuple, TYPE_CHECKING
17
+ from typing_extensions import Self
17
18
 
18
19
  from cirq import protocols
19
20
  from cirq.ops import pauli_string as ps, raw_types
@@ -21,10 +22,6 @@ from cirq.ops import pauli_string as ps, raw_types
21
22
  if TYPE_CHECKING:
22
23
  import cirq
23
24
 
24
- TSelf_PauliStringGateOperation = TypeVar(
25
- 'TSelf_PauliStringGateOperation', bound='PauliStringGateOperation'
26
- )
27
-
28
25
 
29
26
  class PauliStringGateOperation(raw_types.Operation, metaclass=abc.ABCMeta):
30
27
  def __init__(self, pauli_string: ps.PauliString) -> None:
@@ -38,16 +35,12 @@ class PauliStringGateOperation(raw_types.Operation, metaclass=abc.ABCMeta):
38
35
  if len(qubits) != len(self.pauli_string):
39
36
  raise ValueError('Incorrect number of qubits for gate')
40
37
 
41
- def with_qubits(
42
- self: TSelf_PauliStringGateOperation, *new_qubits: 'cirq.Qid'
43
- ) -> TSelf_PauliStringGateOperation:
38
+ def with_qubits(self, *new_qubits: 'cirq.Qid') -> Self:
44
39
  self.validate_args(new_qubits)
45
40
  return self.map_qubits(dict(zip(self.pauli_string.qubits, new_qubits)))
46
41
 
47
42
  @abc.abstractmethod
48
- def map_qubits(
49
- self: TSelf_PauliStringGateOperation, qubit_map: Dict[raw_types.Qid, raw_types.Qid]
50
- ) -> TSelf_PauliStringGateOperation:
43
+ def map_qubits(self, qubit_map: Dict[raw_types.Qid, raw_types.Qid]) -> Self:
51
44
  """Return an equivalent operation on new qubits with its Pauli string
52
45
  mapped to new qubits.
53
46
 
@@ -1030,17 +1030,17 @@ def test_expectation_from_state_vector_entangled_states():
1030
1030
  x0x1 = cirq.PauliString(x0x1_pauli_map)
1031
1031
  q_map = {q0: 0, q1: 1}
1032
1032
  wf1 = np.array([0, 1, 1, 0], dtype=complex) / np.sqrt(2)
1033
- for state in [wf1, wf1.reshape(2, 2)]:
1033
+ for state in [wf1, wf1.reshape((2, 2))]:
1034
1034
  np.testing.assert_allclose(z0z1.expectation_from_state_vector(state, q_map), -1)
1035
1035
  np.testing.assert_allclose(x0x1.expectation_from_state_vector(state, q_map), 1)
1036
1036
 
1037
1037
  wf2 = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2)
1038
- for state in [wf2, wf2.reshape(2, 2)]:
1038
+ for state in [wf2, wf2.reshape((2, 2))]:
1039
1039
  np.testing.assert_allclose(z0z1.expectation_from_state_vector(state, q_map), 1)
1040
1040
  np.testing.assert_allclose(x0x1.expectation_from_state_vector(state, q_map), 1)
1041
1041
 
1042
1042
  wf3 = np.array([1, 1, 1, 1], dtype=complex) / 2
1043
- for state in [wf3, wf3.reshape(2, 2)]:
1043
+ for state in [wf3, wf3.reshape((2, 2))]:
1044
1044
  np.testing.assert_allclose(z0z1.expectation_from_state_vector(state, q_map), 0)
1045
1045
  np.testing.assert_allclose(x0x1.expectation_from_state_vector(state, q_map), 1)
1046
1046
 
@@ -1049,7 +1049,7 @@ def test_expectation_from_state_vector_qubit_map():
1049
1049
  q0, q1, q2 = _make_qubits(3)
1050
1050
  z = cirq.PauliString({q0: cirq.Z})
1051
1051
  wf = np.array([0, 1, 0, 1, 0, 0, 0, 0], dtype=complex) / np.sqrt(2)
1052
- for state in [wf, wf.reshape(2, 2, 2)]:
1052
+ for state in [wf, wf.reshape((2, 2, 2))]:
1053
1053
  np.testing.assert_allclose(
1054
1054
  z.expectation_from_state_vector(state, {q0: 0, q1: 1, q2: 2}), 1, atol=1e-8
1055
1055
  )
@@ -1124,7 +1124,7 @@ def test_expectation_from_density_matrix_invalid_input():
1124
1124
  q0, q1, q2, q3 = _make_qubits(4)
1125
1125
  ps = cirq.PauliString({q0: cirq.X, q1: cirq.Y})
1126
1126
  wf = cirq.testing.random_superposition(4)
1127
- rho = np.kron(wf.conjugate().T, wf).reshape(4, 4)
1127
+ rho = np.kron(wf.conjugate().T, wf).reshape((4, 4))
1128
1128
  q_map = {q0: 0, q1: 1}
1129
1129
 
1130
1130
  im_ps = (1j + 1) * ps
@@ -1238,20 +1238,20 @@ def test_expectation_from_density_matrix_entangled_states():
1238
1238
  q_map = {q0: 0, q1: 1}
1239
1239
 
1240
1240
  wf1 = np.array([0, 1, 1, 0], dtype=complex) / np.sqrt(2)
1241
- rho1 = np.kron(wf1, wf1).reshape(4, 4)
1242
- for state in [rho1, rho1.reshape(2, 2, 2, 2)]:
1241
+ rho1 = np.kron(wf1, wf1).reshape((4, 4))
1242
+ for state in [rho1, rho1.reshape((2, 2, 2, 2))]:
1243
1243
  np.testing.assert_allclose(z0z1.expectation_from_density_matrix(state, q_map), -1)
1244
1244
  np.testing.assert_allclose(x0x1.expectation_from_density_matrix(state, q_map), 1)
1245
1245
 
1246
1246
  wf2 = np.array([1, 0, 0, 1], dtype=complex) / np.sqrt(2)
1247
- rho2 = np.kron(wf2, wf2).reshape(4, 4)
1248
- for state in [rho2, rho2.reshape(2, 2, 2, 2)]:
1247
+ rho2 = np.kron(wf2, wf2).reshape((4, 4))
1248
+ for state in [rho2, rho2.reshape((2, 2, 2, 2))]:
1249
1249
  np.testing.assert_allclose(z0z1.expectation_from_density_matrix(state, q_map), 1)
1250
1250
  np.testing.assert_allclose(x0x1.expectation_from_density_matrix(state, q_map), 1)
1251
1251
 
1252
1252
  wf3 = np.array([1, 1, 1, 1], dtype=complex) / 2
1253
- rho3 = np.kron(wf3, wf3).reshape(4, 4)
1254
- for state in [rho3, rho3.reshape(2, 2, 2, 2)]:
1253
+ rho3 = np.kron(wf3, wf3).reshape((4, 4))
1254
+ for state in [rho3, rho3.reshape((2, 2, 2, 2))]:
1255
1255
  np.testing.assert_allclose(z0z1.expectation_from_density_matrix(state, q_map), 0)
1256
1256
  np.testing.assert_allclose(x0x1.expectation_from_density_matrix(state, q_map), 1)
1257
1257
 
@@ -1260,9 +1260,9 @@ def test_expectation_from_density_matrix_qubit_map():
1260
1260
  q0, q1, q2 = _make_qubits(3)
1261
1261
  z = cirq.PauliString({q0: cirq.Z})
1262
1262
  wf = np.array([0, 1, 0, 1, 0, 0, 0, 0], dtype=complex) / np.sqrt(2)
1263
- rho = np.kron(wf, wf).reshape(8, 8)
1263
+ rho = np.kron(wf, wf).reshape((8, 8))
1264
1264
 
1265
- for state in [rho, rho.reshape(2, 2, 2, 2, 2, 2)]:
1265
+ for state in [rho, rho.reshape((2, 2, 2, 2, 2, 2))]:
1266
1266
  np.testing.assert_allclose(
1267
1267
  z.expectation_from_density_matrix(state, {q0: 0, q1: 1, q2: 2}), 1
1268
1268
  )
@@ -16,7 +16,7 @@ from typing import Any, Iterator, Tuple, Union, TYPE_CHECKING
16
16
  import numpy as np
17
17
  import sympy
18
18
 
19
- from cirq import linalg, protocols, value
19
+ from cirq import linalg, protocols, value, _compat
20
20
  from cirq.ops import linear_combinations, pauli_string_phasor
21
21
 
22
22
  if TYPE_CHECKING:
@@ -78,6 +78,10 @@ class PauliSumExponential:
78
78
  def with_qubits(self, *new_qubits: 'cirq.Qid') -> 'PauliSumExponential':
79
79
  return PauliSumExponential(self._pauli_sum.with_qubits(*new_qubits), self._exponent)
80
80
 
81
+ @_compat.cached_method
82
+ def _is_parameterized_(self) -> bool:
83
+ return protocols.is_parameterized(self._exponent)
84
+
81
85
  def _resolve_parameters_(
82
86
  self, resolver: 'cirq.ParamResolver', recursive: bool
83
87
  ) -> 'PauliSumExponential':
@@ -109,6 +113,7 @@ class PauliSumExponential:
109
113
  ret = np.kron(ret, protocols.unitary(pauli_string_exp))
110
114
  return ret
111
115
 
116
+ @_compat.cached_method
112
117
  def _has_unitary_(self) -> bool:
113
118
  return linalg.is_unitary(self.matrix())
114
119