cirq-core 1.6.0.dev20250520234959__py3-none-any.whl → 1.6.0.dev20250521215048__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 (73) hide show
  1. cirq/_version.py +1 -1
  2. cirq/_version_test.py +1 -1
  3. cirq/circuits/circuit_operation_test.py +1 -2
  4. cirq/contrib/acquaintance/devices.py +2 -2
  5. cirq/contrib/acquaintance/inspection_utils.py +2 -2
  6. cirq/contrib/qasm_import/_lexer.py +1 -2
  7. cirq/contrib/routing/router.py +3 -3
  8. cirq/devices/device.py +4 -4
  9. cirq/experiments/fidelity_estimation.py +4 -4
  10. cirq/experiments/t1_decay_experiment.py +2 -2
  11. cirq/experiments/xeb_fitting.py +1 -1
  12. cirq/interop/quirk/cells/measurement_cells.py +2 -2
  13. cirq/linalg/combinators.py +3 -3
  14. cirq/linalg/predicates.py +2 -2
  15. cirq/linalg/tolerance.py +2 -2
  16. cirq/ops/arithmetic_operation_test.py +2 -4
  17. cirq/ops/common_gate_families.py +7 -7
  18. cirq/ops/controlled_gate.py +11 -0
  19. cirq/ops/controlled_gate_test.py +36 -2
  20. cirq/ops/controlled_operation_test.py +2 -2
  21. cirq/ops/linear_combinations_test.py +8 -9
  22. cirq/ops/qid_util.py +2 -2
  23. cirq/ops/qubit_order_or_list.py +2 -2
  24. cirq/protocols/act_on_protocol.py +4 -4
  25. cirq/protocols/commutes_protocol.py +5 -7
  26. cirq/protocols/control_key_protocol.py +4 -4
  27. cirq/protocols/decompose_protocol_test.py +1 -2
  28. cirq/protocols/has_stabilizer_effect_protocol.py +5 -5
  29. cirq/protocols/has_unitary_protocol.py +5 -5
  30. cirq/protocols/pauli_expansion_protocol.py +3 -3
  31. cirq/protocols/pow_protocol.py +5 -5
  32. cirq/protocols/trace_distance_bound.py +4 -4
  33. cirq/protocols/unitary_protocol.py +7 -7
  34. cirq/protocols/unitary_protocol_test.py +1 -3
  35. cirq/qis/entropy.py +2 -2
  36. cirq/sim/clifford/clifford_tableau_simulation_state.py +4 -4
  37. cirq/sim/clifford/stabilizer_ch_form_simulation_state.py +5 -5
  38. cirq/sim/clifford/stabilizer_simulation_state.py +5 -5
  39. cirq/sim/density_matrix_simulator_test.py +45 -46
  40. cirq/sim/sparse_simulator_test.py +38 -39
  41. cirq/sim/state_vector_simulation_state_test.py +2 -2
  42. cirq/sim/state_vector_test.py +3 -3
  43. cirq/study/sweepable.py +5 -5
  44. cirq/testing/consistent_act_on.py +5 -7
  45. cirq/testing/consistent_controlled_gate_op.py +3 -5
  46. cirq/testing/deprecation.py +2 -2
  47. cirq/testing/lin_alg_utils.py +2 -2
  48. cirq/transformers/align.py +3 -3
  49. cirq/transformers/drop_empty_moments.py +2 -2
  50. cirq/transformers/drop_negligible_operations.py +2 -2
  51. cirq/transformers/expand_composite.py +2 -2
  52. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +3 -3
  53. cirq/transformers/merge_k_qubit_gates.py +5 -5
  54. cirq/transformers/merge_single_qubit_gates.py +5 -5
  55. cirq/transformers/optimize_for_target_gateset.py +6 -6
  56. cirq/transformers/optimize_for_target_gateset_test.py +3 -3
  57. cirq/transformers/tag_transformers.py +5 -5
  58. cirq/transformers/target_gatesets/cz_gateset_test.py +2 -2
  59. cirq/transformers/target_gatesets/sqrt_iswap_gateset_test.py +1 -3
  60. cirq/transformers/transformer_api_test.py +7 -8
  61. cirq/value/abc_alt.py +2 -2
  62. cirq/value/abc_alt_test.py +3 -3
  63. cirq/value/angle.py +7 -7
  64. cirq/value/type_alias.py +3 -5
  65. cirq/vis/density_matrix.py +2 -4
  66. cirq/vis/histogram.py +6 -6
  67. cirq/vis/state_histogram.py +7 -7
  68. cirq/work/observable_readout_calibration.py +2 -2
  69. {cirq_core-1.6.0.dev20250520234959.dist-info → cirq_core-1.6.0.dev20250521215048.dist-info}/METADATA +1 -1
  70. {cirq_core-1.6.0.dev20250520234959.dist-info → cirq_core-1.6.0.dev20250521215048.dist-info}/RECORD +73 -73
  71. {cirq_core-1.6.0.dev20250520234959.dist-info → cirq_core-1.6.0.dev20250521215048.dist-info}/WHEEL +0 -0
  72. {cirq_core-1.6.0.dev20250520234959.dist-info → cirq_core-1.6.0.dev20250521215048.dist-info}/licenses/LICENSE +0 -0
  73. {cirq_core-1.6.0.dev20250520234959.dist-info → cirq_core-1.6.0.dev20250521215048.dist-info}/top_level.txt +0 -0
cirq/_version.py CHANGED
@@ -28,4 +28,4 @@ if sys.version_info < (3, 11, 0): # pragma: no cover
28
28
  'of cirq (e.g. "python -m pip install cirq==1.5.0")'
29
29
  )
30
30
 
31
- __version__ = "1.6.0.dev20250520234959"
31
+ __version__ = "1.6.0.dev20250521215048"
cirq/_version_test.py CHANGED
@@ -3,4 +3,4 @@ import cirq
3
3
 
4
4
 
5
5
  def test_version() -> None:
6
- assert cirq.__version__ == "1.6.0.dev20250520234959"
6
+ assert cirq.__version__ == "1.6.0.dev20250521215048"
@@ -15,7 +15,6 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import unittest.mock as mock
18
- from typing import Optional
19
18
 
20
19
  import numpy as np
21
20
  import pytest
@@ -294,7 +293,7 @@ def test_repeat(add_measurements: bool, use_default_ids_for_initial_rep: bool) -
294
293
  _ = op_base.repeat(initial_repetitions)
295
294
  initial_repetitions = abs(initial_repetitions)
296
295
 
297
- op_with_reps: Optional[cirq.CircuitOperation] = None
296
+ op_with_reps: cirq.CircuitOperation | None = None
298
297
  rep_ids = []
299
298
  if use_default_ids_for_initial_rep:
300
299
  rep_ids = ['0', '1', '2']
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import abc
18
- from typing import TYPE_CHECKING, Union
18
+ from typing import TYPE_CHECKING
19
19
 
20
20
  from cirq import circuits, devices, ops
21
21
  from cirq.contrib.acquaintance.bipartite import BipartiteSwapNetworkGate
@@ -42,7 +42,7 @@ class AcquaintanceDevice(devices.Device, metaclass=abc.ABCMeta):
42
42
  )
43
43
 
44
44
 
45
- def get_acquaintance_size(obj: Union[circuits.Circuit, ops.Operation]) -> int:
45
+ def get_acquaintance_size(obj: circuits.Circuit | ops.Operation) -> int:
46
46
  """The maximum number of qubits to be acquainted with each other."""
47
47
  if isinstance(obj, circuits.Circuit):
48
48
  return max(tuple(get_acquaintance_size(op) for op in obj.all_operations()) or (0,))
@@ -14,7 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import FrozenSet, Iterator, Sequence, Set, TYPE_CHECKING
17
+ from typing import Iterator, Sequence, TYPE_CHECKING
18
18
 
19
19
  from cirq import devices
20
20
  from cirq.contrib import circuitdag
@@ -66,7 +66,7 @@ def get_acquaintance_dag(strategy: cirq.Circuit, initial_mapping: LogicalMapping
66
66
 
67
67
  def get_logical_acquaintance_opportunities(
68
68
  strategy: cirq.Circuit, initial_mapping: LogicalMapping
69
- ) -> Set[FrozenSet[LogicalIndex]]:
69
+ ) -> set[frozenset[LogicalIndex]]:
70
70
  acquaintance_dag = get_acquaintance_dag(strategy, initial_mapping)
71
71
  logical_acquaintance_opportunities = set()
72
72
  for op in acquaintance_dag.all_operations():
@@ -15,7 +15,6 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import re
18
- from typing import Optional
19
18
 
20
19
  import ply.lex as lex
21
20
 
@@ -117,5 +116,5 @@ class QasmLexer:
117
116
  def input(self, qasm):
118
117
  self.lex.input(qasm)
119
118
 
120
- def token(self) -> Optional[lex.Token]:
119
+ def token(self) -> lex.Token | None:
121
120
  return self.lex.token()
@@ -14,7 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import Callable, Optional, TYPE_CHECKING
17
+ from typing import Callable, TYPE_CHECKING
18
18
 
19
19
  from cirq import circuits, protocols
20
20
  from cirq.contrib.routing.greedy import route_circuit_greedily
@@ -31,8 +31,8 @@ def route_circuit(
31
31
  circuit: circuits.Circuit,
32
32
  device_graph: nx.Graph,
33
33
  *,
34
- algo_name: Optional[str] = None,
35
- router: Optional[Callable[..., SwapNetwork]] = None,
34
+ algo_name: str | None = None,
35
+ router: Callable[..., SwapNetwork] | None = None,
36
36
  **kwargs,
37
37
  ) -> SwapNetwork:
38
38
  """Routes a circuit on a given device.
cirq/devices/device.py CHANGED
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import abc
18
- from typing import FrozenSet, Iterable, Optional, TYPE_CHECKING
18
+ from typing import Iterable, TYPE_CHECKING
19
19
 
20
20
  import networkx as nx
21
21
 
@@ -59,7 +59,7 @@ class Device(metaclass=abc.ABCMeta):
59
59
  """
60
60
 
61
61
  @property
62
- def metadata(self) -> Optional[DeviceMetadata]:
62
+ def metadata(self) -> DeviceMetadata | None:
63
63
  """Returns the associated Metadata with the device if applicable.
64
64
 
65
65
  Returns:
@@ -116,11 +116,11 @@ class DeviceMetadata:
116
116
  directional coupling, undirected edges indicate bi-directional
117
117
  coupling.
118
118
  """
119
- self._qubits_set: FrozenSet[cirq.Qid] = frozenset(qubits)
119
+ self._qubits_set: frozenset[cirq.Qid] = frozenset(qubits)
120
120
  self._nx_graph = nx_graph
121
121
 
122
122
  @property
123
- def qubit_set(self) -> FrozenSet[cirq.Qid]:
123
+ def qubit_set(self) -> frozenset[cirq.Qid]:
124
124
  """Returns the set of qubits on the device.
125
125
 
126
126
  Returns:
@@ -16,7 +16,7 @@
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- from typing import Callable, Mapping, Optional, Sequence, TYPE_CHECKING
19
+ from typing import Callable, Mapping, Sequence, TYPE_CHECKING
20
20
 
21
21
  import numpy as np
22
22
 
@@ -141,7 +141,7 @@ def xeb_fidelity(
141
141
  circuit: cirq.Circuit,
142
142
  bitstrings: Sequence[int],
143
143
  qubit_order: QubitOrderOrList = QubitOrder.DEFAULT,
144
- amplitudes: Optional[Mapping[int, complex]] = None,
144
+ amplitudes: Mapping[int, complex] | None = None,
145
145
  estimator: Callable[[int, Sequence[float]], float] = linear_xeb_fidelity_from_probabilities,
146
146
  ) -> float:
147
147
  """Estimates XEB fidelity from one circuit using user-supplied estimator.
@@ -206,7 +206,7 @@ def linear_xeb_fidelity(
206
206
  circuit: cirq.Circuit,
207
207
  bitstrings: Sequence[int],
208
208
  qubit_order: QubitOrderOrList = QubitOrder.DEFAULT,
209
- amplitudes: Optional[Mapping[int, complex]] = None,
209
+ amplitudes: Mapping[int, complex] | None = None,
210
210
  ) -> float:
211
211
  """Estimates XEB fidelity from one circuit using linear estimator."""
212
212
  return xeb_fidelity(
@@ -222,7 +222,7 @@ def log_xeb_fidelity(
222
222
  circuit: cirq.Circuit,
223
223
  bitstrings: Sequence[int],
224
224
  qubit_order: QubitOrderOrList = QubitOrder.DEFAULT,
225
- amplitudes: Optional[Mapping[int, complex]] = None,
225
+ amplitudes: Mapping[int, complex] | None = None,
226
226
  ) -> float:
227
227
  """Estimates XEB fidelity from one circuit using logarithmic estimator."""
228
228
  return xeb_fidelity(
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import warnings
18
- from typing import Any, cast, Optional, Sequence, TYPE_CHECKING
18
+ from typing import Any, cast, Sequence, TYPE_CHECKING
19
19
 
20
20
  import numpy as np
21
21
  import pandas as pd
@@ -146,7 +146,7 @@ class T1DecayResult:
146
146
  return np.nan
147
147
 
148
148
  def plot(
149
- self, ax: Optional[plt.Axes] = None, include_fit: bool = False, **plot_kwargs: Any
149
+ self, ax: plt.Axes | None = None, include_fit: bool = False, **plot_kwargs: Any
150
150
  ) -> plt.Axes:
151
151
  """Plots the excited state probability vs the amount of delay.
152
152
 
@@ -506,7 +506,7 @@ def characterize_phased_fsim_parameters_with_xeb(
506
506
  )
507
507
  return XEBCharacterizationResult(
508
508
  optimization_results={pair: optimization_result},
509
- final_params={pair: final_params}, # type: ignore[dict-item]
509
+ final_params={pair: final_params},
510
510
  fidelities_df=fidelities_df,
511
511
  )
512
512
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import cast, Iterable, Iterator, Optional, TYPE_CHECKING
17
+ from typing import cast, Iterable, Iterator, TYPE_CHECKING
18
18
 
19
19
  from cirq import ops
20
20
  from cirq.interop.quirk.cells.cell import CellMaker, ExplicitOperationsCell
@@ -30,7 +30,7 @@ def generate_all_measurement_cell_makers() -> Iterator[CellMaker]:
30
30
  yield _measurement("XDetector", basis_change=ops.Y**0.5)
31
31
 
32
32
 
33
- def _measurement(identifier: str, basis_change: Optional[cirq.Gate] = None) -> CellMaker:
33
+ def _measurement(identifier: str, basis_change: cirq.Gate | None = None) -> CellMaker:
34
34
  return CellMaker(
35
35
  identifier=identifier,
36
36
  size=1,
@@ -17,7 +17,7 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  import functools
20
- from typing import TYPE_CHECKING, Union
20
+ from typing import TYPE_CHECKING
21
21
 
22
22
  import numpy as np
23
23
 
@@ -27,7 +27,7 @@ if TYPE_CHECKING:
27
27
  from numpy.typing import ArrayLike, DTypeLike
28
28
 
29
29
 
30
- def kron(*factors: Union[np.ndarray, complex], shape_len: int = 2) -> np.ndarray:
30
+ def kron(*factors: np.ndarray | complex, shape_len: int = 2) -> np.ndarray:
31
31
  """Computes the kronecker product of a sequence of values.
32
32
 
33
33
  A *args version of lambda args: functools.reduce(np.kron, args).
@@ -58,7 +58,7 @@ document(
58
58
  )
59
59
 
60
60
 
61
- def kron_with_controls(*factors: Union[np.ndarray, complex]) -> np.ndarray:
61
+ def kron_with_controls(*factors: np.ndarray | complex) -> np.ndarray:
62
62
  """Computes the kronecker product of a sequence of values and control tags.
63
63
 
64
64
  Use `cirq.CONTROL_TAG` to represent controls. Any entry of the output
cirq/linalg/predicates.py CHANGED
@@ -17,7 +17,7 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  from types import EllipsisType
20
- from typing import cast, Sequence, Union
20
+ from typing import cast, Sequence
21
21
 
22
22
  import numpy as np
23
23
 
@@ -302,7 +302,7 @@ def slice_for_qubits_equal_to(
302
302
  out_size = (
303
303
  cast(int, num_qubits) if out_size_specified else max(target_qubit_axes, default=-1) + 1
304
304
  )
305
- result = cast(list[Union[slice, int, EllipsisType]], [slice(None)] * out_size)
305
+ result = cast(list[slice | int | EllipsisType], [slice(None)] * out_size)
306
306
  if not out_size_specified:
307
307
  result.append(Ellipsis)
308
308
  if qid_shape is None:
cirq/linalg/tolerance.py CHANGED
@@ -17,7 +17,7 @@ tolerances."""
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
- from typing import Iterable, TYPE_CHECKING, Union
20
+ from typing import Iterable, TYPE_CHECKING
21
21
 
22
22
  import numpy as np
23
23
 
@@ -36,7 +36,7 @@ def all_near_zero(a: ArrayLike, *, atol: float = 1e-8) -> bool:
36
36
 
37
37
 
38
38
  def all_near_zero_mod(
39
- a: Union[float, Iterable[float], np.ndarray], period: float, *, atol: float = 1e-8
39
+ a: float | Iterable[float] | np.ndarray, period: float, *, atol: float = 1e-8
40
40
  ) -> bool:
41
41
  """Checks if the tensor's elements are all near multiples of the period.
42
42
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import Sequence, Union
17
+ from typing import Sequence
18
18
 
19
19
  import numpy as np
20
20
  import pytest
@@ -89,9 +89,7 @@ def test_the_tests() -> None:
89
89
  def test_arithmetic_gate_apply_unitary() -> None:
90
90
  class Add(cirq.ArithmeticGate):
91
91
  def __init__(
92
- self,
93
- target_register: Union[int, Sequence[int]],
94
- input_register: Union[int, Sequence[int]],
92
+ self, target_register: int | Sequence[int], input_register: int | Sequence[int]
95
93
  ):
96
94
  self.target_register = target_register
97
95
  self.input_register = input_register
@@ -16,7 +16,7 @@
16
16
 
17
17
  from __future__ import annotations
18
18
 
19
- from typing import Any, cast, Optional, Type, Union
19
+ from typing import Any, cast
20
20
 
21
21
  from cirq import protocols
22
22
  from cirq.ops import eigen_gate, gateset, parallel_gate, raw_types
@@ -25,7 +25,7 @@ from cirq.ops import eigen_gate, gateset, parallel_gate, raw_types
25
25
  class AnyUnitaryGateFamily(gateset.GateFamily):
26
26
  """GateFamily which accepts any N-Qubit unitary gate."""
27
27
 
28
- def __init__(self, num_qubits: Optional[int] = None) -> None:
28
+ def __init__(self, num_qubits: int | None = None) -> None:
29
29
  """Init AnyUnitaryGateFamily
30
30
 
31
31
  Args:
@@ -65,7 +65,7 @@ class AnyUnitaryGateFamily(gateset.GateFamily):
65
65
  class AnyIntegerPowerGateFamily(gateset.GateFamily):
66
66
  """GateFamily which accepts instances of a given `cirq.EigenGate`, raised to integer power."""
67
67
 
68
- def __init__(self, gate: Type[eigen_gate.EigenGate]) -> None:
68
+ def __init__(self, gate: type[eigen_gate.EigenGate]) -> None:
69
69
  """Init AnyIntegerPowerGateFamily
70
70
 
71
71
  Args:
@@ -129,11 +129,11 @@ class ParallelGateFamily(gateset.GateFamily):
129
129
 
130
130
  def __init__(
131
131
  self,
132
- gate: Union[Type[raw_types.Gate], raw_types.Gate],
132
+ gate: type[raw_types.Gate] | raw_types.Gate,
133
133
  *,
134
- name: Optional[str] = None,
135
- description: Optional[str] = None,
136
- max_parallel_allowed: Optional[int] = None,
134
+ name: str | None = None,
135
+ description: str | None = None,
136
+ max_parallel_allowed: int | None = None,
137
137
  ) -> None:
138
138
  """Inits ParallelGateFamily
139
139
 
@@ -146,6 +146,17 @@ class ControlledGate(raw_types.Gate):
146
146
  self, qubits: tuple[cirq.Qid, ...], context: cirq.DecompositionContext | None = None
147
147
  ) -> None | NotImplementedType | cirq.OP_TREE:
148
148
  control_qubits = list(qubits[: self.num_controls()])
149
+ controlled_sub_gate = self.sub_gate.controlled(
150
+ self.num_controls(), self.control_values, self.control_qid_shape
151
+ )
152
+ # Prefer the subgate controlled version if available
153
+ if self != controlled_sub_gate:
154
+ # Prevent 2-cycle from appearing in the recursive decomposition
155
+ # TODO: Remove after #7241 is resolved
156
+ if not isinstance(controlled_sub_gate, ControlledGate) or not isinstance(
157
+ controlled_sub_gate.sub_gate, common_gates.CZPowGate
158
+ ):
159
+ return controlled_sub_gate.on(*qubits)
149
160
  if (
150
161
  protocols.has_unitary(self.sub_gate)
151
162
  and protocols.num_qubits(self.sub_gate) == 1
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  from types import EllipsisType, NotImplementedType
18
- from typing import Any, cast, Sequence, Union
18
+ from typing import Any, cast, Sequence
19
19
 
20
20
  import numpy as np
21
21
  import pytest
@@ -47,7 +47,7 @@ class GateAllocatingNewSpaceForResult(cirq.testing.SingleQubitGate):
47
47
  def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs) -> np.ndarray | NotImplementedType:
48
48
  assert len(args.axes) == 1
49
49
  a = args.axes[0]
50
- seed = cast(tuple[Union[int, slice, EllipsisType], ...], (slice(None),))
50
+ seed = cast(tuple[int | slice | EllipsisType, ...], (slice(None),))
51
51
  zero = seed * a + (0, Ellipsis)
52
52
  one = seed * a + (1, Ellipsis)
53
53
  result = np.zeros(args.target_tensor.shape, args.target_tensor.dtype)
@@ -496,6 +496,40 @@ def _test_controlled_gate_is_consistent(
496
496
  np.testing.assert_allclose(cirq.unitary(cgate), cirq.unitary(circuit), atol=1e-13)
497
497
 
498
498
 
499
+ @pytest.mark.parametrize(
500
+ 'sub_gate, expected_decomposition',
501
+ [
502
+ (cirq.X, [cirq.CX]),
503
+ (cirq.CX, [cirq.CCX]),
504
+ (cirq.XPowGate(), [cirq.CXPowGate()]),
505
+ (cirq.CXPowGate(), [cirq.CCXPowGate()]),
506
+ (cirq.Z, [cirq.CZ]),
507
+ (cirq.CZ, [cirq.CCZ]),
508
+ (cirq.ZPowGate(), [cirq.CZPowGate()]),
509
+ (cirq.CZPowGate(), [cirq.CCZPowGate()]),
510
+ ],
511
+ )
512
+ def test_controlled_gate_decomposition_uses_canonical_version(
513
+ sub_gate: cirq.Gate, expected_decomposition: list[cirq.Gate]
514
+ ):
515
+ cgate = cirq.ControlledGate(sub_gate, num_controls=1)
516
+ qubits = cirq.LineQubit.range(1 + sub_gate.num_qubits())
517
+ dec = cirq.decompose_once(cgate.on(*qubits))
518
+ assert dec == [gate.on(*qubits) for gate in expected_decomposition]
519
+
520
+
521
+ @pytest.mark.parametrize(
522
+ 'sub_gate, expected_decomposition', [(cirq.Z, [cirq.CZ]), (cirq.ZPowGate(), [cirq.CZPowGate()])]
523
+ )
524
+ def test_controlled_gate_full_decomposition(
525
+ sub_gate: cirq.Gate, expected_decomposition: list[cirq.Gate]
526
+ ):
527
+ cgate = cirq.ControlledGate(sub_gate, num_controls=1)
528
+ qubits = cirq.LineQubit.range(1 + sub_gate.num_qubits())
529
+ dec = cirq.decompose(cgate.on(*qubits))
530
+ assert dec == [gate.on(*qubits) for gate in expected_decomposition]
531
+
532
+
499
533
  def test_pow_inverse():
500
534
  assert cirq.inverse(CRestricted, None) is None
501
535
  assert cirq.pow(CRestricted, 1.5, None) is None
@@ -17,7 +17,7 @@ from __future__ import annotations
17
17
  import itertools
18
18
  import re
19
19
  from types import EllipsisType, NotImplementedType
20
- from typing import cast, Union
20
+ from typing import cast
21
21
 
22
22
  import numpy as np
23
23
  import pytest
@@ -50,7 +50,7 @@ class GateAllocatingNewSpaceForResult(cirq.testing.SingleQubitGate):
50
50
  def _apply_unitary_(self, args: cirq.ApplyUnitaryArgs) -> np.ndarray | NotImplementedType:
51
51
  assert len(args.axes) == 1
52
52
  a = args.axes[0]
53
- seed = cast(tuple[Union[int, slice, EllipsisType], ...], (slice(None),))
53
+ seed = cast(tuple[int | slice | EllipsisType, ...], (slice(None),))
54
54
  zero = seed * a + (0, Ellipsis)
55
55
  one = seed * a + (1, Ellipsis)
56
56
  result = np.zeros(args.target_tensor.shape, args.target_tensor.dtype)
@@ -15,7 +15,6 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import collections
18
- from typing import Union
19
18
 
20
19
  import numpy as np
21
20
  import pytest
@@ -230,12 +229,12 @@ def test_parameterized_linear_combination_of_gates(
230
229
 
231
230
 
232
231
  def get_matrix(
233
- operator: Union[
234
- cirq.Gate,
235
- cirq.GateOperation,
236
- cirq.LinearCombinationOfGates,
237
- cirq.LinearCombinationOfOperations,
238
- ],
232
+ operator: (
233
+ cirq.Gate
234
+ | cirq.GateOperation
235
+ | cirq.LinearCombinationOfGates
236
+ | cirq.LinearCombinationOfOperations
237
+ ),
239
238
  ) -> np.ndarray:
240
239
  if isinstance(operator, (cirq.LinearCombinationOfGates, cirq.LinearCombinationOfOperations)):
241
240
  return operator.matrix()
@@ -243,8 +242,8 @@ def get_matrix(
243
242
 
244
243
 
245
244
  def assert_linear_combinations_are_equal(
246
- actual: Union[cirq.LinearCombinationOfGates, cirq.LinearCombinationOfOperations],
247
- expected: Union[cirq.LinearCombinationOfGates, cirq.LinearCombinationOfOperations],
245
+ actual: cirq.LinearCombinationOfGates | cirq.LinearCombinationOfOperations,
246
+ expected: cirq.LinearCombinationOfGates | cirq.LinearCombinationOfOperations,
248
247
  ) -> None:
249
248
  if not actual and not expected:
250
249
  assert len(actual) == 0
cirq/ops/qid_util.py CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from typing import overload, TYPE_CHECKING, Union
17
+ from typing import overload, TYPE_CHECKING
18
18
 
19
19
  if TYPE_CHECKING:
20
20
  import cirq
@@ -32,7 +32,7 @@ def q(__row: int, __col: int) -> cirq.GridQubit: ...
32
32
  def q(__name: str) -> cirq.NamedQubit: ...
33
33
 
34
34
 
35
- def q(*args: Union[int, str]) -> Union[cirq.LineQubit, cirq.GridQubit, cirq.NamedQubit]:
35
+ def q(*args: int | str) -> cirq.LineQubit | cirq.GridQubit | cirq.NamedQubit:
36
36
  """Constructs a qubit id of the appropriate type based on args.
37
37
 
38
38
  This is shorthand for constructing qubit ids of common types:
@@ -20,12 +20,12 @@ mypy.
20
20
 
21
21
  from __future__ import annotations
22
22
 
23
- from typing import Iterable, Union
23
+ from typing import Iterable
24
24
 
25
25
  from cirq._doc import document
26
26
  from cirq.ops import qubit_order, raw_types
27
27
 
28
- QubitOrderOrList = Union[qubit_order.QubitOrder, Iterable[raw_types.Qid]]
28
+ QubitOrderOrList = qubit_order.QubitOrder | Iterable[raw_types.Qid]
29
29
  document(
30
30
  QubitOrderOrList,
31
31
  """Specifies a qubit ordering.
@@ -15,7 +15,7 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  from types import NotImplementedType
18
- from typing import Any, Optional, Sequence, TYPE_CHECKING, Union
18
+ from typing import Any, Sequence, TYPE_CHECKING
19
19
 
20
20
  from typing_extensions import Protocol
21
21
 
@@ -30,7 +30,7 @@ class SupportsActOn(Protocol):
30
30
  """An object that explicitly specifies how to act on simulator states."""
31
31
 
32
32
  @doc_private
33
- def _act_on_(self, sim_state: cirq.SimulationStateBase) -> Union[NotImplementedType, bool]:
33
+ def _act_on_(self, sim_state: cirq.SimulationStateBase) -> NotImplementedType | bool:
34
34
  """Applies an action to the given argument, if it is a supported type.
35
35
 
36
36
  For example, unitary operations can implement an `_act_on_` method that
@@ -62,7 +62,7 @@ class SupportsActOnQubits(Protocol):
62
62
  @doc_private
63
63
  def _act_on_(
64
64
  self, sim_state: cirq.SimulationStateBase, qubits: Sequence[cirq.Qid]
65
- ) -> Union[NotImplementedType, bool]:
65
+ ) -> NotImplementedType | bool:
66
66
  """Applies an action to the given argument, if it is a supported type.
67
67
 
68
68
  For example, unitary operations can implement an `_act_on_` method that
@@ -91,7 +91,7 @@ class SupportsActOnQubits(Protocol):
91
91
  def act_on(
92
92
  action: Any,
93
93
  sim_state: cirq.SimulationStateBase,
94
- qubits: Optional[Sequence[cirq.Qid]] = None,
94
+ qubits: Sequence[cirq.Qid] | None = None,
95
95
  *,
96
96
  allow_decompose: bool = True,
97
97
  ):
@@ -17,7 +17,7 @@
17
17
  from __future__ import annotations
18
18
 
19
19
  from types import NotImplementedType
20
- from typing import Any, overload, TypeVar, Union
20
+ from typing import Any, overload, TypeVar
21
21
 
22
22
  import numpy as np
23
23
  from typing_extensions import Protocol
@@ -38,7 +38,7 @@ class SupportsCommutes(Protocol):
38
38
  """An object that can determine commutation relationships vs others."""
39
39
 
40
40
  @doc_private
41
- def _commutes_(self, other: Any, *, atol: float) -> Union[None, bool, NotImplementedType]:
41
+ def _commutes_(self, other: Any, *, atol: float) -> None | bool | NotImplementedType:
42
42
  r"""Determines if this object commutes with the other object.
43
43
 
44
44
  Can return None to indicate the commutation relationship is
@@ -81,9 +81,7 @@ def commutes(v1: Any, v2: Any, *, atol: float = 1e-8) -> bool: ...
81
81
 
82
82
 
83
83
  @overload
84
- def commutes(
85
- v1: Any, v2: Any, *, atol: float = 1e-8, default: TDefault
86
- ) -> Union[bool, TDefault]: ...
84
+ def commutes(v1: Any, v2: Any, *, atol: float = 1e-8, default: TDefault) -> bool | TDefault: ...
87
85
 
88
86
 
89
87
  def commutes(
@@ -162,7 +160,7 @@ def definitely_commutes(v1: Any, v2: Any, *, atol: float = 1e-8) -> bool:
162
160
 
163
161
  def _strat_commutes_from_commutes(
164
162
  v1: Any, v2: Any, *, atol: float = 1e-8
165
- ) -> Union[bool, NotImplementedType, None]:
163
+ ) -> bool | NotImplementedType | None:
166
164
  """Attempts to determine commutativity via the objects' _commutes_
167
165
  method."""
168
166
 
@@ -179,7 +177,7 @@ def _strat_commutes_from_commutes(
179
177
 
180
178
  def _strat_commutes_from_matrix(
181
179
  v1: Any, v2: Any, *, atol: float
182
- ) -> Union[bool, NotImplementedType, None]:
180
+ ) -> bool | NotImplementedType | None:
183
181
  """Attempts to determine commutativity of matrices."""
184
182
  if not isinstance(v1, np.ndarray) or not isinstance(v2, np.ndarray):
185
183
  return NotImplemented
@@ -16,7 +16,7 @@
16
16
  from __future__ import annotations
17
17
 
18
18
  from types import NotImplementedType
19
- from typing import Any, FrozenSet, TYPE_CHECKING, Union
19
+ from typing import Any, TYPE_CHECKING
20
20
 
21
21
  from typing_extensions import Protocol
22
22
 
@@ -37,7 +37,7 @@ class SupportsControlKey(Protocol):
37
37
  """
38
38
 
39
39
  @doc_private
40
- def _control_keys_(self) -> Union[FrozenSet[cirq.MeasurementKey], NotImplementedType, None]:
40
+ def _control_keys_(self) -> frozenset[cirq.MeasurementKey] | NotImplementedType | None:
41
41
  """Return the keys for controls referenced by the receiving object.
42
42
 
43
43
  Returns:
@@ -46,7 +46,7 @@ class SupportsControlKey(Protocol):
46
46
  """
47
47
 
48
48
 
49
- def control_keys(val: Any) -> FrozenSet[cirq.MeasurementKey]:
49
+ def control_keys(val: Any) -> frozenset[cirq.MeasurementKey]:
50
50
  """Gets the keys that the value is classically controlled by.
51
51
 
52
52
  Args:
@@ -64,7 +64,7 @@ def control_keys(val: Any) -> FrozenSet[cirq.MeasurementKey]:
64
64
  return frozenset()
65
65
 
66
66
 
67
- def measurement_keys_touched(val: Any) -> FrozenSet[cirq.MeasurementKey]:
67
+ def measurement_keys_touched(val: Any) -> frozenset[cirq.MeasurementKey]:
68
68
  """Returns all the measurement keys used by the value.
69
69
 
70
70
  This would be the case if the value is or contains a measurement gate, or
@@ -15,7 +15,6 @@
15
15
  from __future__ import annotations
16
16
 
17
17
  import itertools
18
- from typing import Optional
19
18
  from unittest import mock
20
19
 
21
20
  import pytest
@@ -231,7 +230,7 @@ def test_decompose_intercept() -> None:
231
230
 
232
231
  # Accepts a context, when provided.
233
232
  def _intercept_with_context(
234
- op: cirq.Operation, context: Optional[cirq.DecompositionContext] = None
233
+ op: cirq.Operation, context: cirq.DecompositionContext | None = None
235
234
  ):
236
235
  assert context is not None
237
236
  if op.gate == cirq.SWAP: