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
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Any, Sequence, TYPE_CHECKING, Union
15
+ from typing import Any, Optional, Sequence, TYPE_CHECKING, Union
16
16
 
17
17
  from typing_extensions import Protocol
18
18
 
@@ -89,7 +89,7 @@ class SupportsActOnQubits(Protocol):
89
89
  def act_on(
90
90
  action: Any,
91
91
  sim_state: 'cirq.SimulationStateBase',
92
- qubits: Sequence['cirq.Qid'] = None,
92
+ qubits: Optional[Sequence['cirq.Qid']] = None,
93
93
  *,
94
94
  allow_decompose: bool = True,
95
95
  ):
@@ -149,7 +149,7 @@ def act_on(
149
149
 
150
150
  arg_fallback = getattr(sim_state, '_act_on_fallback_', None)
151
151
  if arg_fallback is not None:
152
- qubits = action.qubits if isinstance(action, ops.Operation) else qubits
152
+ qubits = action.qubits if is_op else qubits
153
153
  result = arg_fallback(action, qubits=qubits, allow_decompose=allow_decompose)
154
154
  if result is True:
155
155
  return
@@ -12,12 +12,12 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  from typing import Any, Sequence, Tuple
15
+ from typing_extensions import Self
15
16
 
16
17
  import numpy as np
17
18
  import pytest
18
19
 
19
20
  import cirq
20
- from cirq.ops.raw_types import TSelf
21
21
 
22
22
 
23
23
  class DummyQuantumState(cirq.QuantumStateRepresentation):
@@ -62,10 +62,10 @@ def test_act_on_fallback_errors():
62
62
  def test_act_on_errors():
63
63
  class Op(cirq.Operation):
64
64
  @property
65
- def qubits(self) -> Tuple['cirq.Qid', ...]:
65
+ def qubits(self) -> Tuple['cirq.Qid', ...]: # type: ignore[empty-body]
66
66
  pass
67
67
 
68
- def with_qubits(self: TSelf, *new_qubits: 'cirq.Qid') -> TSelf:
68
+ def with_qubits(self, *new_qubits: 'cirq.Qid') -> Self: # type: ignore[empty-body]
69
69
  pass
70
70
 
71
71
  def _act_on_(self, sim_state):
@@ -79,10 +79,10 @@ def test_act_on_errors():
79
79
  def test_qubits_not_allowed_for_operations():
80
80
  class Op(cirq.Operation):
81
81
  @property
82
- def qubits(self) -> Tuple['cirq.Qid', ...]:
82
+ def qubits(self) -> Tuple['cirq.Qid', ...]: # type: ignore[empty-body]
83
83
  pass
84
84
 
85
- def with_qubits(self: TSelf, *new_qubits: 'cirq.Qid') -> TSelf:
85
+ def with_qubits(self, *new_qubits: 'cirq.Qid') -> Self: # type: ignore[empty-body]
86
86
  pass
87
87
 
88
88
  state = DummySimulationState()
@@ -11,6 +11,7 @@
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
+
14
15
  """A protocol for implementing high performance channel evolutions."""
15
16
 
16
17
  from typing import Any, Iterable, Optional, Sequence, TypeVar, Tuple, Union
@@ -142,7 +143,7 @@ class SupportsApplyChannel(Protocol):
142
143
  `args.target_tensor` and the given buffers.
143
144
 
144
145
  Returns:
145
- If the receiving object is not able to apply a chanel, None
146
+ If the receiving object is not able to apply a channel, None
146
147
  or NotImplemented should be returned.
147
148
 
148
149
  If the receiving object is able to work inline, it should directly
@@ -224,13 +225,13 @@ def apply_channel(
224
225
  raise ValueError(
225
226
  'Invalid target_tensor shape or selected axes. '
226
227
  'The selected left and right shape of target_tensor '
227
- 'are not equal. Got {!r} and {!r}.'.format(left_shape, right_shape)
228
+ f'are not equal. Got {left_shape!r} and {right_shape!r}.'
228
229
  )
229
230
  if val_qid_shape != left_shape:
230
231
  raise ValueError(
231
232
  'Invalid channel qid shape is not equal to the '
232
233
  'selected left and right shape of target_tensor. '
233
- 'Got {!r} but expected {!r}.'.format(val_qid_shape, left_shape)
234
+ f'Got {val_qid_shape!r} but expected {left_shape!r}.'
234
235
  )
235
236
 
236
237
  # Check if the specialized method is present.
@@ -240,9 +241,9 @@ def apply_channel(
240
241
 
241
242
  def err_str(buf_num_str):
242
243
  return (
243
- "Object of type '{}' returned a result object equal to "
244
- "auxiliary_buffer{}. This type violates the contract "
245
- "that appears in apply_channel's documentation.".format(type(val), buf_num_str)
244
+ f"Object of type '{type(val)}' returned a result object equal to "
245
+ f"auxiliary_buffer{buf_num_str}. This type violates the contract "
246
+ "that appears in apply_channel's documentation."
246
247
  )
247
248
 
248
249
  assert result is not args.auxiliary_buffer0, err_str('0')
@@ -263,9 +264,9 @@ def apply_channel(
263
264
  if default is not RaiseTypeErrorIfNotProvided:
264
265
  return default
265
266
  raise TypeError(
266
- "object of type '{}' has no _apply_channel_, _apply_unitary_, "
267
+ f"object of type '{type(val)}' has no _apply_channel_, _apply_unitary_, "
267
268
  "_unitary_, or _kraus_ methods (or they returned None or "
268
- "NotImplemented).".format(type(val))
269
+ "NotImplemented)."
269
270
  )
270
271
 
271
272
 
@@ -11,6 +11,7 @@
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
+
14
15
  """A protocol for implementing high performance mixture evolutions."""
15
16
 
16
17
  from typing import Any, cast, Iterable, Optional, Tuple, TypeVar, Union
@@ -245,9 +246,9 @@ def apply_mixture(
245
246
 
246
247
  def err_str(buf_num_str):
247
248
  return (
248
- "Object of type '{}' returned a result object equal to "
249
- "auxiliary_buffer{}. This type violates the contract "
250
- "that appears in apply_mixture's documentation.".format(type(val), buf_num_str)
249
+ f"Object of type '{type(val)}' returned a result object equal to "
250
+ f"auxiliary_buffer{buf_num_str}. This type violates the contract "
251
+ "that appears in apply_mixture's documentation."
251
252
  )
252
253
 
253
254
  assert result is not args.auxiliary_buffer0, err_str('0')
@@ -269,9 +270,8 @@ def apply_mixture(
269
270
  if default is not RaiseTypeErrorIfNotProvided:
270
271
  return default
271
272
  raise TypeError(
272
- "object of type '{}' has no _apply_mixture_, _apply_unitary_, "
273
- "_unitary_, or _mixture_ methods (or they returned None or "
274
- "NotImplemented).".format(type(val))
273
+ f"object of type '{type(val)}' has no _apply_mixture_, _apply_unitary_, "
274
+ "_unitary_, or _mixture_ methods (or they returned None or NotImplemented)."
275
275
  )
276
276
 
277
277
 
@@ -287,7 +287,7 @@ def _validate_input(val: Any, args: 'ApplyMixtureArgs') -> Tuple[Any, 'ApplyMixt
287
287
  raise ValueError(
288
288
  'Invalid mixture qid shape is not equal to the '
289
289
  'selected left and right shape of target_tensor. '
290
- 'Got {!r} but expected {!r}.'.format(val_qid_shape, left_shape)
290
+ f'Got {val_qid_shape!r} but expected {left_shape!r}.'
291
291
  )
292
292
 
293
293
  if args.right_axes is not None:
@@ -298,7 +298,7 @@ def _validate_input(val: Any, args: 'ApplyMixtureArgs') -> Tuple[Any, 'ApplyMixt
298
298
  raise ValueError(
299
299
  'Invalid target_tensor shape or selected axes. '
300
300
  'The selected left and right shape of '
301
- 'target_tensor are not equal. Got {!r} and {!r}.'.format(left_shape, right_shape)
301
+ f'target_tensor are not equal. Got {left_shape!r} and {right_shape!r}.'
302
302
  )
303
303
 
304
304
  return val, args, is_density_matrix
@@ -33,7 +33,7 @@ def assert_apply_mixture_returns(
33
33
  left_axes: Iterable[int],
34
34
  right_axes: Optional[Iterable[int]],
35
35
  assert_result_is_out_buf: bool = False,
36
- expected_result: np.ndarray = None,
36
+ expected_result: Optional[np.ndarray] = None,
37
37
  ):
38
38
  out_buf, buf0, buf1 = make_buffers(rho.shape, rho.dtype)
39
39
  result = cirq.apply_mixture(
@@ -11,7 +11,9 @@
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
+
14
15
  """A protocol for implementing high performance unitary left-multiplies."""
16
+
15
17
  import warnings
16
18
  from typing import Any, cast, Iterable, Optional, Sequence, Tuple, TYPE_CHECKING, TypeVar, Union
17
19
 
@@ -133,6 +135,33 @@ class ApplyUnitaryArgs:
133
135
  state = qis.one_hot(index=(0,) * num_qubits, shape=qid_shape, dtype=np.complex128)
134
136
  return ApplyUnitaryArgs(state, np.empty_like(state), range(num_qubits))
135
137
 
138
+ @classmethod
139
+ def for_unitary(
140
+ cls, num_qubits: Optional[int] = None, *, qid_shape: Optional[Tuple[int, ...]] = None
141
+ ) -> 'ApplyUnitaryArgs':
142
+ """A default instance corresponding to an identity matrix.
143
+
144
+ Specify exactly one argument.
145
+
146
+ Args:
147
+ num_qubits: The number of qubits to make space for in the state.
148
+ qid_shape: A tuple representing the number of quantum levels of each
149
+ qubit the identity matrix applies to. `qid_shape` is (2, 2, 2) for
150
+ a three-qubit identity operation tensor.
151
+
152
+ Raises:
153
+ TypeError: If exactly neither `num_qubits` or `qid_shape` is provided or
154
+ both are provided.
155
+ """
156
+ if (num_qubits is None) == (qid_shape is None):
157
+ raise TypeError('Specify exactly one of num_qubits or qid_shape.')
158
+ if num_qubits is not None:
159
+ qid_shape = (2,) * num_qubits
160
+ qid_shape = cast(Tuple[int, ...], qid_shape) # Satisfy mypy
161
+ num_qubits = len(qid_shape)
162
+ state = qis.eye_tensor(qid_shape, dtype=np.complex128)
163
+ return ApplyUnitaryArgs(state, np.empty_like(state), range(num_qubits))
164
+
136
165
  def with_axes_transposed_to_start(self) -> 'ApplyUnitaryArgs':
137
166
  """Returns a transposed view of the same arguments.
138
167
 
@@ -376,8 +405,8 @@ def apply_unitary(
376
405
  "cirq.apply_unitary failed. "
377
406
  "Value doesn't have a (non-parameterized) unitary effect.\n"
378
407
  "\n"
379
- "type: {}\n"
380
- "value: {!r}\n"
408
+ f"type: {type(unitary_value)}\n"
409
+ f"value: {unitary_value!r}\n"
381
410
  "\n"
382
411
  "The value failed to satisfy any of the following criteria:\n"
383
412
  "- An `_apply_unitary_(self, args) method that returned a value "
@@ -386,7 +415,6 @@ def apply_unitary(
386
415
  "besides None or NotImplemented.\n"
387
416
  "- A `_decompose_(self)` method that returned a "
388
417
  "list of unitary operations.\n"
389
- "".format(type(unitary_value), unitary_value)
390
418
  )
391
419
 
392
420
 
@@ -409,19 +437,7 @@ def _strat_apply_unitary_from_apply_unitary(
409
437
  return _incorporate_result_into_target(args, sub_args, sub_result)
410
438
 
411
439
 
412
- def _strat_apply_unitary_from_unitary(
413
- unitary_value: Any, args: ApplyUnitaryArgs
414
- ) -> Optional[np.ndarray]:
415
- # Check for magic method.
416
- method = getattr(unitary_value, '_unitary_', None)
417
- if method is None:
418
- return NotImplemented
419
-
420
- # Attempt to get the unitary matrix.
421
- matrix = method()
422
- if matrix is NotImplemented or matrix is None:
423
- return matrix
424
-
440
+ def _apply_unitary_from_matrix(matrix: np.ndarray, unitary_value: Any, args: ApplyUnitaryArgs):
425
441
  if args.slices is None:
426
442
  val_qid_shape = qid_shape_protocol.qid_shape(unitary_value, default=(2,) * len(args.axes))
427
443
  slices = tuple(slice(0, size) for size in val_qid_shape)
@@ -450,11 +466,42 @@ def _strat_apply_unitary_from_unitary(
450
466
  return _incorporate_result_into_target(args, sub_args, sub_result)
451
467
 
452
468
 
469
+ def _strat_apply_unitary_from_unitary(
470
+ unitary_value: Any, args: ApplyUnitaryArgs
471
+ ) -> Optional[np.ndarray]:
472
+ # Check for magic method.
473
+ method = getattr(unitary_value, '_unitary_', None)
474
+ if method is None:
475
+ return NotImplemented
476
+
477
+ # Attempt to get the unitary matrix.
478
+ matrix = method()
479
+ if matrix is NotImplemented or matrix is None:
480
+ return matrix
481
+
482
+ return _apply_unitary_from_matrix(matrix, unitary_value, args)
483
+
484
+
453
485
  def _strat_apply_unitary_from_decompose(val: Any, args: ApplyUnitaryArgs) -> Optional[np.ndarray]:
454
486
  operations, qubits, _ = _try_decompose_into_operations_and_qubits(val)
455
487
  if operations is None:
456
488
  return NotImplemented
457
- return apply_unitaries(operations, qubits, args, None)
489
+ all_qubits = frozenset([q for op in operations for q in op.qubits])
490
+ ancilla = tuple(sorted(all_qubits.difference(qubits)))
491
+ if not len(ancilla):
492
+ return apply_unitaries(operations, qubits, args, None)
493
+ ordered_qubits = ancilla + tuple(qubits)
494
+ all_qid_shapes = qid_shape_protocol.qid_shape(ordered_qubits)
495
+ result = apply_unitaries(
496
+ operations, ordered_qubits, ApplyUnitaryArgs.for_unitary(qid_shape=all_qid_shapes), None
497
+ )
498
+ if result is None or result is NotImplemented:
499
+ return result
500
+ result = result.reshape((np.prod(all_qid_shapes, dtype=np.int64), -1))
501
+ val_qid_shape = qid_shape_protocol.qid_shape(qubits)
502
+ state_vec_length = np.prod(val_qid_shape, dtype=np.int64)
503
+ result = result[:state_vec_length, :state_vec_length]
504
+ return _apply_unitary_from_matrix(result, val, args)
458
505
 
459
506
 
460
507
  def apply_unitaries(
@@ -528,8 +575,8 @@ def apply_unitaries(
528
575
  "There was a non-unitary value in the `unitary_values` "
529
576
  "list.\n"
530
577
  "\n"
531
- "non-unitary value type: {}\n"
532
- "non-unitary value: {!r}".format(type(op), op)
578
+ f"non-unitary value type: {type(op)}\n"
579
+ f"non-unitary value: {op!r}"
533
580
  )
534
581
  return default
535
582
 
@@ -717,3 +717,53 @@ def test_cast_to_complex():
717
717
  np.ComplexWarning, match='Casting complex values to real discards the imaginary part'
718
718
  ):
719
719
  cirq.apply_unitary(y0, args)
720
+
721
+
722
+ class NotDecomposableGate(cirq.Gate):
723
+ def num_qubits(self):
724
+ return 1
725
+
726
+
727
+ class DecomposableGate(cirq.Gate):
728
+ def __init__(self, sub_gate: cirq.Gate, allocate_ancilla: bool) -> None:
729
+ super().__init__()
730
+ self._sub_gate = sub_gate
731
+ self._allocate_ancilla = allocate_ancilla
732
+
733
+ def num_qubits(self):
734
+ return 1
735
+
736
+ def _decompose_(self, qubits):
737
+ if self._allocate_ancilla:
738
+ yield cirq.Z(cirq.NamedQubit('DecomposableGateQubit'))
739
+ yield self._sub_gate(qubits[0])
740
+
741
+
742
+ def test_strat_apply_unitary_from_decompose():
743
+ state = np.eye(2, dtype=np.complex128)
744
+ args = cirq.ApplyUnitaryArgs(
745
+ target_tensor=state, available_buffer=np.zeros_like(state), axes=(0,)
746
+ )
747
+ np.testing.assert_allclose(
748
+ cirq.apply_unitaries(
749
+ [DecomposableGate(cirq.X, False)(cirq.LineQubit(0))], [cirq.LineQubit(0)], args
750
+ ),
751
+ [[0, 1], [1, 0]],
752
+ )
753
+
754
+ with pytest.raises(TypeError):
755
+ _ = cirq.apply_unitaries(
756
+ [DecomposableGate(NotDecomposableGate(), True)(cirq.LineQubit(0))],
757
+ [cirq.LineQubit(0)],
758
+ args,
759
+ )
760
+
761
+
762
+ def test_unitary_construction():
763
+ with pytest.raises(TypeError):
764
+ _ = cirq.ApplyUnitaryArgs.for_unitary()
765
+
766
+ np.testing.assert_allclose(
767
+ cirq.ApplyUnitaryArgs.for_unitary(num_qubits=3).target_tensor,
768
+ cirq.eye_tensor((2,) * 3, dtype=np.complex128),
769
+ )
@@ -11,6 +11,7 @@
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
+
14
15
  import re
15
16
  from fractions import Fraction
16
17
  from typing import (
@@ -73,7 +74,7 @@ class CircuitDiagramInfo:
73
74
  could be mistaken for an identity wire). Defaults to True.
74
75
 
75
76
  Raises:
76
- ValueError: If `wire_symbols` is a string, and not an interable
77
+ ValueError: If `wire_symbols` is a string, and not an iterable
77
78
  of strings.
78
79
  """
79
80
  if isinstance(wire_symbols, str):
@@ -118,13 +119,10 @@ class CircuitDiagramInfo:
118
119
  else:
119
120
  ks = (0,)
120
121
  for k in ks:
121
- result[k] += '^' + exponent
122
+ result[k] += f"^{exponent}"
122
123
  return result
123
124
 
124
- def _formatted_exponent(
125
- self: 'cirq.CircuitDiagramInfo', args: 'cirq.CircuitDiagramInfoArgs'
126
- ) -> Optional[str]:
127
-
125
+ def _formatted_exponent(self, args: 'cirq.CircuitDiagramInfoArgs') -> Optional[str]:
128
126
  if protocols.is_parameterized(self.exponent):
129
127
  name = str(self.exponent)
130
128
  return f'({name})' if _is_exposed_formula(name) else name
@@ -273,7 +271,7 @@ class CircuitDiagramInfoArgs:
273
271
  if radians == 0:
274
272
  return '0'
275
273
  if radians == -np.pi:
276
- return '-' + unit
274
+ return f"-{unit}"
277
275
  if self.precision is not None and not isinstance(radians, sympy.Basic):
278
276
  quantity = self.format_real(radians / np.pi)
279
277
  return quantity + unit
@@ -433,8 +431,8 @@ def circuit_diagram_info(
433
431
  if getter is None:
434
432
  raise TypeError(f"object of type '{type(val)}' has no _circuit_diagram_info_ method.")
435
433
  raise TypeError(
436
- "object of type '{}' does have a _circuit_diagram_info_ "
437
- "method, but it returned NotImplemented.".format(type(val))
434
+ f"object of type '{type(val)}' does have a _circuit_diagram_info_ "
435
+ "method, but it returned NotImplemented."
438
436
  )
439
437
 
440
438