cirq-core 1.6.0.dev20250709210336__py3-none-any.whl → 1.6.0.dev20250710201614__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.

cirq/_version.py CHANGED
@@ -29,4 +29,4 @@ if sys.version_info < (3, 11 - 1, 0): # pragma: no cover
29
29
  'of cirq (e.g. "python -m pip install cirq==1.5.0")'
30
30
  )
31
31
 
32
- __version__ = "1.6.0.dev20250709210336"
32
+ __version__ = "1.6.0.dev20250710201614"
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.dev20250709210336"
6
+ assert cirq.__version__ == "1.6.0.dev20250710201614"
cirq/circuits/circuit.py CHANGED
@@ -33,6 +33,7 @@ from typing import (
33
33
  Any,
34
34
  Callable,
35
35
  cast,
36
+ Hashable,
36
37
  Iterable,
37
38
  Iterator,
38
39
  Mapping,
@@ -141,7 +142,9 @@ class AbstractCircuit(abc.ABC):
141
142
  """
142
143
 
143
144
  @classmethod
144
- def from_moments(cls: type[CIRCUIT_TYPE], *moments: cirq.OP_TREE | None) -> CIRCUIT_TYPE:
145
+ def from_moments(
146
+ cls: type[CIRCUIT_TYPE], *moments: cirq.OP_TREE | None, tags: Sequence[Hashable] = ()
147
+ ) -> CIRCUIT_TYPE:
145
148
  """Create a circuit from moment op trees.
146
149
 
147
150
  Args:
@@ -155,8 +158,12 @@ class AbstractCircuit(abc.ABC):
155
158
  which is then included in the new circuit. Note that in this
156
159
  case we have the normal restriction that operations in a
157
160
  moment must be applied to disjoint sets of qubits.
161
+ tags: A sequence of any type of object that is useful to attach metadata
162
+ to this circuit as long as the type is hashable. If you wish the
163
+ resulting circuit to be eventually serialized into JSON, you should
164
+ also restrict the tags to be JSON serializable.
158
165
  """
159
- return cls._from_moments(cls._make_moments(moments))
166
+ return cls._from_moments(cls._make_moments(moments), tags=tags)
160
167
 
161
168
  @staticmethod
162
169
  def _make_moments(moments: Iterable[cirq.OP_TREE | None]) -> Iterator[cirq.Moment]:
@@ -170,7 +177,9 @@ class AbstractCircuit(abc.ABC):
170
177
 
171
178
  @classmethod
172
179
  @abc.abstractmethod
173
- def _from_moments(cls: type[CIRCUIT_TYPE], moments: Iterable[cirq.Moment]) -> CIRCUIT_TYPE:
180
+ def _from_moments(
181
+ cls: type[CIRCUIT_TYPE], moments: Iterable[cirq.Moment], tags: Sequence[Hashable]
182
+ ) -> CIRCUIT_TYPE:
174
183
  """Create a circuit from moments.
175
184
 
176
185
  This must be implemented by subclasses. It provides a more efficient way
@@ -201,6 +210,20 @@ class AbstractCircuit(abc.ABC):
201
210
  copy: If True and 'self' is a Circuit, returns a copy that circuit.
202
211
  """
203
212
 
213
+ @property
214
+ @abc.abstractmethod
215
+ def tags(self) -> tuple[Hashable, ...]:
216
+ """Returns a tuple of the Circuit's tags."""
217
+
218
+ @abc.abstractmethod
219
+ def with_tags(self, *new_tags: Hashable) -> Self:
220
+ """Creates a new tagged Circuit with `self.tags` and `new_tags` combined."""
221
+
222
+ @property
223
+ def untagged(self) -> Self:
224
+ """Returns the underlying Circuit without any tags."""
225
+ return self._from_moments(self.moments, tags=()) if self.tags else self
226
+
204
227
  def __bool__(self) -> bool:
205
228
  return bool(self.moments)
206
229
 
@@ -210,14 +233,16 @@ class AbstractCircuit(abc.ABC):
210
233
  return other is self or (
211
234
  len(self.moments) == len(other.moments)
212
235
  and all(m0 == m1 for m0, m1 in zip(self.moments, other.moments))
236
+ and self.tags == other.tags
213
237
  )
214
238
 
215
239
  def _approx_eq_(self, other: Any, atol: float) -> bool:
216
240
  """See `cirq.protocols.SupportsApproximateEquality`."""
217
241
  if not isinstance(other, AbstractCircuit):
218
242
  return NotImplemented
219
- return other is self or cirq.protocols.approx_eq(
220
- tuple(self.moments), tuple(other.moments), atol=atol
243
+ return other is self or (
244
+ self.tags == other.tags
245
+ and cirq.protocols.approx_eq(tuple(self.moments), tuple(other.moments), atol=atol)
221
246
  )
222
247
 
223
248
  def __ne__(self, other) -> bool:
@@ -259,7 +284,7 @@ class AbstractCircuit(abc.ABC):
259
284
 
260
285
  def __getitem__(self, key):
261
286
  if isinstance(key, slice):
262
- return self._from_moments(self.moments[key])
287
+ return self._from_moments(self.moments[key], tags=self.tags)
263
288
  if hasattr(key, '__index__'):
264
289
  return self.moments[key]
265
290
  if isinstance(key, tuple):
@@ -272,7 +297,9 @@ class AbstractCircuit(abc.ABC):
272
297
  return selected_moments[qubit_idx]
273
298
  if isinstance(qubit_idx, ops.Qid):
274
299
  qubit_idx = [qubit_idx]
275
- return self._from_moments(moment[qubit_idx] for moment in selected_moments)
300
+ return self._from_moments(
301
+ (moment[qubit_idx] for moment in selected_moments), tags=self.tags
302
+ )
276
303
 
277
304
  raise TypeError('__getitem__ called with key not of type slice, int, or tuple.')
278
305
 
@@ -283,7 +310,9 @@ class AbstractCircuit(abc.ABC):
283
310
  args = []
284
311
  if self.moments:
285
312
  args.append(_list_repr_with_indented_item_lines(self.moments))
286
- return f'{", ".join(args)}'
313
+ moments_repr = f'{", ".join(args)}'
314
+ tag_repr = ','.join(_compat.proper_repr(t) for t in self.tags)
315
+ return f'{moments_repr}, tags=[{tag_repr}]' if self.tags else moments_repr
287
316
 
288
317
  def __repr__(self) -> str:
289
318
  cls_name = self.__class__.__name__
@@ -942,7 +971,9 @@ class AbstractCircuit(abc.ABC):
942
971
  """Apply func to expand each op into a circuit, then zip up the circuits."""
943
972
  return Circuit.zip(*[Circuit(func(op)) for op in moment])
944
973
 
945
- return self._from_moments(m for moment in self for m in map_moment(moment))
974
+ return self._from_moments(
975
+ (m for moment in self for m in map_moment(moment)), tags=self.tags
976
+ )
946
977
 
947
978
  def qid_shape(
948
979
  self, qubit_order: cirq.QubitOrderOrList = ops.QubitOrder.DEFAULT
@@ -983,15 +1014,19 @@ class AbstractCircuit(abc.ABC):
983
1014
 
984
1015
  def _with_measurement_key_mapping_(self, key_map: Mapping[str, str]):
985
1016
  return self._from_moments(
986
- protocols.with_measurement_key_mapping(moment, key_map) for moment in self.moments
1017
+ (protocols.with_measurement_key_mapping(moment, key_map) for moment in self.moments),
1018
+ tags=self.tags,
987
1019
  )
988
1020
 
989
1021
  def _with_key_path_(self, path: tuple[str, ...]):
990
- return self._from_moments(protocols.with_key_path(moment, path) for moment in self.moments)
1022
+ return self._from_moments(
1023
+ (protocols.with_key_path(moment, path) for moment in self.moments), tags=self.tags
1024
+ )
991
1025
 
992
1026
  def _with_key_path_prefix_(self, prefix: tuple[str, ...]):
993
1027
  return self._from_moments(
994
- protocols.with_key_path_prefix(moment, prefix) for moment in self.moments
1028
+ (protocols.with_key_path_prefix(moment, prefix) for moment in self.moments),
1029
+ tags=self.tags,
995
1030
  )
996
1031
 
997
1032
  def _with_rescoped_keys_(
@@ -1002,7 +1037,7 @@ class AbstractCircuit(abc.ABC):
1002
1037
  new_moment = protocols.with_rescoped_keys(moment, path, bindable_keys)
1003
1038
  moments.append(new_moment)
1004
1039
  bindable_keys |= protocols.measurement_key_objs(new_moment)
1005
- return self._from_moments(moments)
1040
+ return self._from_moments(moments, tags=self.tags)
1006
1041
 
1007
1042
  def _qid_shape_(self) -> tuple[int, ...]:
1008
1043
  return self.qid_shape()
@@ -1300,22 +1335,33 @@ class AbstractCircuit(abc.ABC):
1300
1335
  return diagram
1301
1336
 
1302
1337
  def _is_parameterized_(self) -> bool:
1303
- return any(protocols.is_parameterized(op) for op in self.all_operations())
1338
+ return any(protocols.is_parameterized(op) for op in self.all_operations()) or any(
1339
+ protocols.is_parameterized(tag) for tag in self.tags
1340
+ )
1304
1341
 
1305
1342
  def _parameter_names_(self) -> AbstractSet[str]:
1306
- return {name for op in self.all_operations() for name in protocols.parameter_names(op)}
1343
+ op_params = {name for op in self.all_operations() for name in protocols.parameter_names(op)}
1344
+ tag_params = {name for tag in self.tags for name in protocols.parameter_names(tag)}
1345
+ return op_params | tag_params
1307
1346
 
1308
1347
  def _resolve_parameters_(self, resolver: cirq.ParamResolver, recursive: bool) -> Self:
1309
1348
  changed = False
1310
1349
  resolved_moments: list[cirq.Moment] = []
1350
+ resolved_tags: list[Hashable] = []
1311
1351
  for moment in self:
1312
1352
  resolved_moment = protocols.resolve_parameters(moment, resolver, recursive)
1313
1353
  if resolved_moment is not moment:
1314
1354
  changed = True
1315
1355
  resolved_moments.append(resolved_moment)
1316
- if not changed:
1356
+ for tag in self.tags:
1357
+ resolved_tag = protocols.resolve_parameters(tag, resolver, recursive)
1358
+ if resolved_tag is not tag:
1359
+ changed = True
1360
+ resolved_tags.append(resolved_tag)
1361
+ if changed:
1362
+ return self._from_moments(resolved_moments, tags=resolved_tags)
1363
+ else:
1317
1364
  return self # pragma: no cover
1318
- return self._from_moments(resolved_moments)
1319
1365
 
1320
1366
  def _qasm_(self, args: cirq.QasmArgs | None = None) -> str:
1321
1367
  if args is None:
@@ -1394,11 +1440,13 @@ class AbstractCircuit(abc.ABC):
1394
1440
  self._to_qasm_output(header, precision, qubit_order).save(file_path)
1395
1441
 
1396
1442
  def _json_dict_(self):
1397
- return protocols.obj_to_dict_helper(self, ['moments'])
1443
+ attribute_names = ['moments', 'tags'] if self.tags else ['moments']
1444
+ ret = protocols.obj_to_dict_helper(self, attribute_names)
1445
+ return ret
1398
1446
 
1399
1447
  @classmethod
1400
- def _from_json_dict_(cls, moments, **kwargs):
1401
- return cls(moments, strategy=InsertStrategy.EARLIEST)
1448
+ def _from_json_dict_(cls, moments, tags=(), **kwargs):
1449
+ return cls(moments, tags=tags, strategy=InsertStrategy.EARLIEST)
1402
1450
 
1403
1451
  def zip(
1404
1452
  *circuits: cirq.AbstractCircuit, align: cirq.Alignment | str = Alignment.LEFT
@@ -1462,7 +1510,7 @@ class AbstractCircuit(abc.ABC):
1462
1510
  if isinstance(align, str):
1463
1511
  align = Alignment[align.upper()]
1464
1512
 
1465
- result = cirq.Circuit()
1513
+ result = cirq.Circuit(tags=circuits[0].tags if circuits else ())
1466
1514
  for k in range(n):
1467
1515
  try:
1468
1516
  if align == Alignment.LEFT:
@@ -1531,7 +1579,7 @@ class AbstractCircuit(abc.ABC):
1531
1579
  for k in range(1, len(circuits)):
1532
1580
  offset, n_acc = _concat_ragged_helper(offset, n_acc, buffer, circuits[k].moments, align)
1533
1581
 
1534
- return cirq.Circuit(buffer[offset : offset + n_acc])
1582
+ return cirq.Circuit(buffer[offset : offset + n_acc], tags=circuits[0].tags)
1535
1583
 
1536
1584
  def get_independent_qubit_sets(self) -> list[set[cirq.Qid]]:
1537
1585
  """Divide circuit's qubits into independent qubit sets.
@@ -1610,7 +1658,10 @@ class AbstractCircuit(abc.ABC):
1610
1658
  # the qubits from one factor belong to a specific independent qubit set.
1611
1659
  # This makes it possible to create independent circuits based on these
1612
1660
  # moments.
1613
- return (self._from_moments(m[qubits] for m in self.moments) for qubits in qubit_factors)
1661
+ return (
1662
+ self._from_moments([m[qubits] for m in self.moments], tags=self.tags)
1663
+ for qubits in qubit_factors
1664
+ )
1614
1665
 
1615
1666
  def _control_keys_(self) -> frozenset[cirq.MeasurementKey]:
1616
1667
  controls = frozenset(k for op in self.all_operations() for k in protocols.control_keys(op))
@@ -1753,7 +1804,10 @@ class Circuit(AbstractCircuit):
1753
1804
  """
1754
1805
 
1755
1806
  def __init__(
1756
- self, *contents: cirq.OP_TREE, strategy: cirq.InsertStrategy = InsertStrategy.EARLIEST
1807
+ self,
1808
+ *contents: cirq.OP_TREE,
1809
+ strategy: cirq.InsertStrategy = InsertStrategy.EARLIEST,
1810
+ tags: Sequence[Hashable] = (),
1757
1811
  ) -> None:
1758
1812
  """Initializes a circuit.
1759
1813
 
@@ -1767,9 +1821,14 @@ class Circuit(AbstractCircuit):
1767
1821
  from `contents`, this determines how the operations are packed
1768
1822
  together. This option does not affect later insertions into the
1769
1823
  circuit.
1824
+ tags: A sequence of any type of object that is useful to attach metadata
1825
+ to this circuit as long as the type is hashable. If you wish the
1826
+ resulting circuit to be eventually serialized into JSON, you should
1827
+ also restrict the tags to be JSON serializable.
1770
1828
  """
1771
1829
  self._placement_cache: _PlacementCache | None = _PlacementCache()
1772
1830
  self._moments: list[cirq.Moment] = []
1831
+ self._tags = tuple(tags)
1773
1832
 
1774
1833
  # Implementation note: the following cached properties are set lazily and then
1775
1834
  # invalidated and reset to None in `self._mutated()`, which is called any time
@@ -1803,10 +1862,11 @@ class Circuit(AbstractCircuit):
1803
1862
  self._placement_cache = None
1804
1863
 
1805
1864
  @classmethod
1806
- def _from_moments(cls, moments: Iterable[cirq.Moment]) -> Circuit:
1865
+ def _from_moments(cls, moments: Iterable[cirq.Moment], tags: Sequence[Hashable]) -> Circuit:
1807
1866
  new_circuit = Circuit()
1808
1867
  new_circuit._moments[:] = moments
1809
1868
  new_circuit._placement_cache = None
1869
+ new_circuit._tags = tuple(tags)
1810
1870
  return new_circuit
1811
1871
 
1812
1872
  def _load_contents_with_earliest_strategy(self, contents: cirq.OP_TREE):
@@ -1865,7 +1925,7 @@ class Circuit(AbstractCircuit):
1865
1925
  from cirq.circuits.frozen_circuit import FrozenCircuit
1866
1926
 
1867
1927
  if self._frozen is None:
1868
- self._frozen = FrozenCircuit._from_moments(self._moments)
1928
+ self._frozen = FrozenCircuit._from_moments(self._moments, tags=self.tags)
1869
1929
  return self._frozen
1870
1930
 
1871
1931
  def unfreeze(self, copy: bool = True) -> cirq.Circuit:
@@ -1894,8 +1954,9 @@ class Circuit(AbstractCircuit):
1894
1954
  def copy(self) -> Circuit:
1895
1955
  """Return a copy of this circuit."""
1896
1956
  copied_circuit = Circuit()
1897
- copied_circuit._moments = self._moments[:]
1957
+ copied_circuit._moments[:] = self._moments
1898
1958
  copied_circuit._placement_cache = None
1959
+ copied_circuit._tags = self.tags
1899
1960
  return copied_circuit
1900
1961
 
1901
1962
  @overload
@@ -1955,7 +2016,7 @@ class Circuit(AbstractCircuit):
1955
2016
  def __mul__(self, repetitions: _INT_TYPE):
1956
2017
  if not isinstance(repetitions, (int, np.integer)):
1957
2018
  return NotImplemented
1958
- return Circuit(self._moments * int(repetitions))
2019
+ return Circuit(self._moments * int(repetitions), tags=self.tags)
1959
2020
 
1960
2021
  def __rmul__(self, repetitions: _INT_TYPE):
1961
2022
  if not isinstance(repetitions, (int, np.integer)):
@@ -1981,7 +2042,7 @@ class Circuit(AbstractCircuit):
1981
2042
  return NotImplemented
1982
2043
  inv_moments.append(inv_moment)
1983
2044
 
1984
- return cirq.Circuit(inv_moments)
2045
+ return cirq.Circuit(inv_moments, tags=self.tags)
1985
2046
 
1986
2047
  __hash__ = None # type: ignore
1987
2048
 
@@ -2466,6 +2527,18 @@ class Circuit(AbstractCircuit):
2466
2527
  def moments(self) -> Sequence[cirq.Moment]:
2467
2528
  return self._moments
2468
2529
 
2530
+ @property
2531
+ def tags(self) -> tuple[Hashable, ...]:
2532
+ return self._tags
2533
+
2534
+ def with_tags(self, *new_tags: Hashable) -> cirq.Circuit:
2535
+ """Creates a new tagged `Circuit` with `self.tags` and `new_tags` combined."""
2536
+ if not new_tags:
2537
+ return self
2538
+ new_circuit = Circuit(tags=self.tags + new_tags)
2539
+ new_circuit._moments[:] = self._moments
2540
+ return new_circuit
2541
+
2469
2542
  def with_noise(self, noise: cirq.NOISE_MODEL_LIKE) -> cirq.Circuit:
2470
2543
  """Make a noisy version of the circuit.
2471
2544
 
@@ -2480,7 +2553,7 @@ class Circuit(AbstractCircuit):
2480
2553
  """
2481
2554
  noise_model = devices.NoiseModel.from_noise_model_like(noise)
2482
2555
  qubits = sorted(self.all_qubits())
2483
- c_noisy = Circuit()
2556
+ c_noisy = Circuit(tags=self.tags)
2484
2557
  for op_tree in noise_model.noisy_moments(self, qubits):
2485
2558
  # Keep moments aligned
2486
2559
  c_noisy += Circuit(op_tree)
@@ -4919,3 +4919,52 @@ def test_append_speed() -> None:
4919
4919
  duration = time.perf_counter() - t
4920
4920
  assert len(c) == moments
4921
4921
  assert duration < 5
4922
+
4923
+
4924
+ def test_tagged_circuits() -> None:
4925
+ q = cirq.LineQubit(0)
4926
+ ops = [cirq.X(q), cirq.H(q)]
4927
+ tags = (sympy.Symbol("a"), "b")
4928
+ circuit = cirq.Circuit(ops)
4929
+ tagged_circuit = cirq.Circuit(ops, tags=tags)
4930
+ # Test equality
4931
+ assert tagged_circuit.tags == tags
4932
+ assert circuit != tagged_circuit
4933
+ assert not cirq.approx_eq(circuit, tagged_circuit)
4934
+ # Test _repr_ and _json_ round trips.
4935
+ cirq.testing.assert_equivalent_repr(tagged_circuit)
4936
+ cirq.testing.assert_json_roundtrip_works(tagged_circuit)
4937
+ # Test utility methods and constructors
4938
+ assert tagged_circuit.with_tags() is tagged_circuit
4939
+ assert circuit.with_tags(*tags) == tagged_circuit
4940
+ assert tagged_circuit.with_tags("c") == cirq.Circuit(ops, tags=[*tags, "c"])
4941
+ assert tagged_circuit.untagged == circuit
4942
+ assert circuit.untagged is circuit
4943
+ assert tagged_circuit.freeze().tags == tags
4944
+ assert tagged_circuit.unfreeze(copy=True).tags == tags
4945
+ assert tagged_circuit.unfreeze(copy=False).tags == tags
4946
+ # Test parameterized protocols
4947
+ assert cirq.is_parameterized(circuit) is False
4948
+ assert cirq.is_parameterized(tagged_circuit) is True
4949
+ assert cirq.parameter_names(tagged_circuit) == {"a"}
4950
+ assert cirq.resolve_parameters(tagged_circuit, {"a": 1}).tags == (1, "b")
4951
+ # Tags are not propagated to diagrams yet.
4952
+ assert str(circuit) == str(tagged_circuit)
4953
+ # Test tags are preserved through operations
4954
+ assert (tagged_circuit + circuit).tags == tags
4955
+ assert (circuit + tagged_circuit).tags == () # We only preserve the tags for the first one
4956
+ assert (2 * tagged_circuit).tags == tags
4957
+ assert (tagged_circuit * 2).tags == tags
4958
+ assert (tagged_circuit**-1).tags == tags
4959
+ assert tagged_circuit.with_noise(cirq.X).tags == tags
4960
+ for c in tagged_circuit.factorize():
4961
+ assert tagged_circuit.tags == tags
4962
+
4963
+ q2 = cirq.LineQubit(1)
4964
+ circuit2 = cirq.Circuit(cirq.X(q2), cirq.H(q2))
4965
+ assert tagged_circuit.zip(circuit2).tags == tags
4966
+ assert circuit2.zip(tagged_circuit).tags == () # We only preserve the tags for the first one
4967
+ assert tagged_circuit.concat_ragged(circuit2).tags == tags
4968
+ assert (
4969
+ circuit2.concat_ragged(tagged_circuit).tags == ()
4970
+ ) # We only preserve the tags for the first one
@@ -65,9 +65,12 @@ class FrozenCircuit(AbstractCircuit, protocols.SerializableByKey):
65
65
  self._tags = tuple(tags)
66
66
 
67
67
  @classmethod
68
- def _from_moments(cls, moments: Iterable[cirq.Moment]) -> FrozenCircuit:
68
+ def _from_moments(
69
+ cls, moments: Iterable[cirq.Moment], tags: Sequence[Hashable]
70
+ ) -> FrozenCircuit:
69
71
  new_circuit = FrozenCircuit()
70
72
  new_circuit._moments = tuple(moments)
73
+ new_circuit._tags = tuple(tags)
71
74
  return new_circuit
72
75
 
73
76
  @property
@@ -78,7 +81,7 @@ class FrozenCircuit(AbstractCircuit, protocols.SerializableByKey):
78
81
  return self
79
82
 
80
83
  def unfreeze(self, copy: bool = True) -> cirq.Circuit:
81
- return Circuit._from_moments(self._moments)
84
+ return Circuit._from_moments(self._moments, tags=self.tags)
82
85
 
83
86
  @property
84
87
  def tags(self) -> tuple[Hashable, ...]:
@@ -87,8 +90,7 @@ class FrozenCircuit(AbstractCircuit, protocols.SerializableByKey):
87
90
 
88
91
  @cached_property
89
92
  def untagged(self) -> cirq.FrozenCircuit:
90
- """Returns the underlying FrozenCircuit without any tags."""
91
- return self._from_moments(self._moments) if self.tags else self
93
+ return super().untagged
92
94
 
93
95
  def with_tags(self, *new_tags: Hashable) -> cirq.FrozenCircuit:
94
96
  """Creates a new tagged `FrozenCircuit` with `self.tags` and `new_tags` combined."""
@@ -103,13 +105,6 @@ class FrozenCircuit(AbstractCircuit, protocols.SerializableByKey):
103
105
  # Explicitly cached for performance
104
106
  return hash((self.moments, self.tags))
105
107
 
106
- def __eq__(self, other):
107
- super_eq = super().__eq__(other)
108
- if super_eq is not True:
109
- return super_eq
110
- other_tags = other.tags if isinstance(other, FrozenCircuit) else ()
111
- return self.tags == other_tags
112
-
113
108
  def __getstate__(self):
114
109
  # Don't save hash when pickling; see #3777.
115
110
  state = self.__dict__
@@ -174,23 +169,11 @@ class FrozenCircuit(AbstractCircuit, protocols.SerializableByKey):
174
169
 
175
170
  @_compat.cached_method
176
171
  def _is_parameterized_(self) -> bool:
177
- return super()._is_parameterized_() or any(
178
- protocols.is_parameterized(tag) for tag in self.tags
179
- )
172
+ return super()._is_parameterized_()
180
173
 
181
174
  @_compat.cached_method
182
175
  def _parameter_names_(self) -> AbstractSet[str]:
183
- tag_params = {name for tag in self.tags for name in protocols.parameter_names(tag)}
184
- return super()._parameter_names_() | tag_params
185
-
186
- def _resolve_parameters_(
187
- self, resolver: cirq.ParamResolver, recursive: bool
188
- ) -> cirq.FrozenCircuit:
189
- resolved_circuit = super()._resolve_parameters_(resolver, recursive)
190
- resolved_tags = [
191
- protocols.resolve_parameters(tag, resolver, recursive) for tag in self.tags
192
- ]
193
- return resolved_circuit.with_tags(*resolved_tags)
176
+ return super()._parameter_names_()
194
177
 
195
178
  def _measurement_key_names_(self) -> frozenset[str]:
196
179
  return self.all_measurement_key_names()
@@ -217,16 +200,6 @@ class FrozenCircuit(AbstractCircuit, protocols.SerializableByKey):
217
200
  except:
218
201
  return NotImplemented
219
202
 
220
- def _repr_args(self) -> str:
221
- moments_repr = super()._repr_args()
222
- tag_repr = ','.join(_compat.proper_repr(t) for t in self._tags)
223
- return f'{moments_repr}, tags=[{tag_repr}]' if self.tags else moments_repr
224
-
225
- def _json_dict_(self):
226
- attribute_names = ['moments', 'tags'] if self.tags else ['moments']
227
- ret = protocols.obj_to_dict_helper(self, attribute_names)
228
- return ret
229
-
230
203
  @classmethod
231
204
  def _from_json_dict_(cls, moments, *, tags=(), **kwargs):
232
205
  return cls(moments, strategy=InsertStrategy.EARLIEST, tags=tags)
@@ -96,15 +96,15 @@ def test_immutable() -> None:
96
96
  def test_tagged_circuits() -> None:
97
97
  q = cirq.LineQubit(0)
98
98
  ops = [cirq.X(q), cirq.H(q)]
99
- tags = [sympy.Symbol("a"), "b"]
99
+ tags = (sympy.Symbol("a"), "b")
100
100
  circuit = cirq.Circuit(ops)
101
101
  frozen_circuit = cirq.FrozenCircuit(ops)
102
102
  tagged_circuit = cirq.FrozenCircuit(ops, tags=tags)
103
103
  # Test equality
104
- assert tagged_circuit.tags == tuple(tags)
104
+ assert tagged_circuit.tags == tags
105
105
  assert circuit == frozen_circuit != tagged_circuit
106
+ assert not cirq.approx_eq(frozen_circuit, tagged_circuit)
106
107
  assert cirq.approx_eq(circuit, frozen_circuit)
107
- assert cirq.approx_eq(frozen_circuit, tagged_circuit)
108
108
  # Test hash
109
109
  assert hash(frozen_circuit) != hash(tagged_circuit)
110
110
  # Test _repr_ and _json_ round trips.
@@ -116,9 +116,29 @@ def test_tagged_circuits() -> None:
116
116
  assert tagged_circuit.with_tags("c") == cirq.FrozenCircuit(ops, tags=[*tags, "c"])
117
117
  assert tagged_circuit.untagged == frozen_circuit
118
118
  assert frozen_circuit.untagged is frozen_circuit
119
+ assert tagged_circuit.unfreeze(copy=True).tags == tags
120
+ assert tagged_circuit.unfreeze(copy=False).tags == tags
119
121
  # Test parameterized protocols
120
122
  assert cirq.is_parameterized(frozen_circuit) is False
121
123
  assert cirq.is_parameterized(tagged_circuit) is True
122
124
  assert cirq.parameter_names(tagged_circuit) == {"a"}
125
+ assert cirq.resolve_parameters(tagged_circuit, {"a": 1}).tags == (1, "b")
123
126
  # Tags are not propagated to diagrams yet.
124
127
  assert str(frozen_circuit) == str(tagged_circuit)
128
+ # Test tags are preserved through operations
129
+ assert (tagged_circuit + circuit).tags == tags
130
+ assert (circuit + tagged_circuit).tags == () # We only preserve the tags for the first one
131
+ assert (2 * tagged_circuit).tags == tags
132
+ assert (tagged_circuit * 2).tags == tags
133
+ assert (tagged_circuit**-1).tags == tags
134
+ for c in tagged_circuit.factorize():
135
+ assert tagged_circuit.tags == tags
136
+
137
+ q2 = cirq.LineQubit(1)
138
+ circuit2 = cirq.Circuit(cirq.X(q2), cirq.H(q2))
139
+ assert tagged_circuit.zip(circuit2).tags == tags
140
+ assert circuit2.zip(tagged_circuit).tags == () # We only preserve the tags for the first one
141
+ assert tagged_circuit.concat_ragged(circuit2).tags == tags
142
+ assert (
143
+ circuit2.concat_ragged(tagged_circuit).tags == ()
144
+ ) # We only preserve the tags for the first one
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cirq-core
3
- Version: 1.6.0.dev20250709210336
3
+ Version: 1.6.0.dev20250710201614
4
4
  Summary: A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
5
5
  Home-page: http://github.com/quantumlib/cirq
6
6
  Author: The Cirq Developers
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=emXpdD5ZvwLRlFAoQB8YatmZyU3b4e9jg6FppMTUhkU,33900
4
4
  cirq/_doc.py,sha256=BrnoABo1hk5RgB3Cgww4zLHUfiyFny0F1V-tOMCbdaU,2909
5
5
  cirq/_import.py,sha256=ixBu4EyGl46Ram2cP3p5eZVEFDW5L2DS-VyTjz4N9iw,8429
6
6
  cirq/_import_test.py,sha256=oF4izzOVZLc7NZ0aZHFcGv-r01eiFFt_JORx_x7_D4s,1089
7
- cirq/_version.py,sha256=S65gLkswFYFbacmVIIcqSpj4S2_5h7O1oBhn2M9Y8Yg,1278
8
- cirq/_version_test.py,sha256=WpxLgLhkE14_vDAssF-m_kPh73Uq4GUNr_kDfOxz5tE,155
7
+ cirq/_version.py,sha256=m4hvsoxwoTZW7nkIKNqyHusG5pkALz-3Xdvl2rSL-1A,1278
8
+ cirq/_version_test.py,sha256=M_J_DJkVg3XLVn4B7SC8WbPAqgWw3TF6OpKiEmr774U,155
9
9
  cirq/conftest.py,sha256=wSDKNdIQRDfLnXvOCWD3erheOw8JHRhdfQ53EyTUIXg,1239
10
10
  cirq/json_resolver_cache.py,sha256=hYyG53VJeV61X0oukK5ndZYega8lkL2FyaL1m0j6h5M,13556
11
11
  cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
@@ -16,12 +16,12 @@ cirq/circuits/_box_drawing_character_data.py,sha256=hExbMJHm9LGORhlhNiUvPiHquv4p
16
16
  cirq/circuits/_box_drawing_character_data_test.py,sha256=GyiNQDtiu_drzEe_y8DOXCFRYDKr2k8KetXN5RVDp18,1668
17
17
  cirq/circuits/_bucket_priority_queue.py,sha256=U564r2mou4aZsOlpVYiZCgirqS6mVznG3ESyawBt4gE,6749
18
18
  cirq/circuits/_bucket_priority_queue_test.py,sha256=MOby-UKYZQMe2n4KhqkfDCPrz-T_3eBbWDEa0_nadJQ,5627
19
- cirq/circuits/circuit.py,sha256=qrqB_dxLSOBOL39NP9yEibApEPtZc8AiHx71GJK6aWM,119540
19
+ cirq/circuits/circuit.py,sha256=pdRVuMGfCAjoBn5bh01IBR6EoiB8oOtChX_HvI6vBqk,122669
20
20
  cirq/circuits/circuit_operation.py,sha256=Bkmf3rY0QYwS8UC1c1rJpvfbSrQcYVkvSX45Hv059uQ,36302
21
21
  cirq/circuits/circuit_operation_test.py,sha256=JqRjSqt--Weceog4CAl4iZW8CNy0qJ_2svUyeAR6D-s,49411
22
- cirq/circuits/circuit_test.py,sha256=onltKv_LnYK9qqqUpIYgitVtoGyBIDA1LqgLAPT0JdI,164304
23
- cirq/circuits/frozen_circuit.py,sha256=CwlREZbCCrHJ6zTLLEbER_M7P4IsBEADhM5dbDBTvvM,9152
24
- cirq/circuits/frozen_circuit_test.py,sha256=b-SQdV7KFLt3KCo50eBkGs8q29B2H5PmMevVGk8UCN4,4294
22
+ cirq/circuits/circuit_test.py,sha256=Q1ywfvVp6ThmtkAKTk-5bq0cVKctncL0d1qoWxTL_r4,166579
23
+ cirq/circuits/frozen_circuit.py,sha256=UXwABQqBSnSKqSWmRluQPD09xZU0orSW3b3IHvDqxUw,7903
24
+ cirq/circuits/frozen_circuit_test.py,sha256=1Uk3g9St_nJFmu3IJ5hAcXWJjLfWFUXTCKQU1b0JJrE,5321
25
25
  cirq/circuits/insert_strategy.py,sha256=3995vK4U6O9RV4BXMoFl9Tf3ekxIiqxv71IuX80JtYo,3237
26
26
  cirq/circuits/insert_strategy_test.py,sha256=pBFgsylgRG1CS1h4JyzZQFP-xvh6fSbgpiZgxXfbpr4,1237
27
27
  cirq/circuits/moment.py,sha256=MK8H9YUQ44ofLdM1E_zOrJ4Sh5Ayh4ezDtwZkx1O-1E,28363
@@ -1220,8 +1220,8 @@ cirq/work/sampler.py,sha256=rxbMWvrhu3gfNSBjZKozw28lLKVvBAS_1EGyPdYe8Xg,19041
1220
1220
  cirq/work/sampler_test.py,sha256=SsMrRvLDYELyOAWLKISjkdEfrBwLYWRsT6D8WrsLM3Q,13533
1221
1221
  cirq/work/zeros_sampler.py,sha256=Fs2JWwq0n9zv7_G5Rm-9vPeHUag7uctcMOHg0JTkZpc,2371
1222
1222
  cirq/work/zeros_sampler_test.py,sha256=lQLgQDGBLtfImryys2HzQ2jOSGxHgc7-koVBUhv8qYk,3345
1223
- cirq_core-1.6.0.dev20250709210336.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1224
- cirq_core-1.6.0.dev20250709210336.dist-info/METADATA,sha256=DJxd5cHncmEgwK28oKZzXMm8LBc7zToCFmREDR7TlUg,4857
1225
- cirq_core-1.6.0.dev20250709210336.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1226
- cirq_core-1.6.0.dev20250709210336.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1227
- cirq_core-1.6.0.dev20250709210336.dist-info/RECORD,,
1223
+ cirq_core-1.6.0.dev20250710201614.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
1224
+ cirq_core-1.6.0.dev20250710201614.dist-info/METADATA,sha256=dH62aYT8Qf5VpA7jOWI5gAcL9yT-duDKmBy5905yxUg,4857
1225
+ cirq_core-1.6.0.dev20250710201614.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
1226
+ cirq_core-1.6.0.dev20250710201614.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
1227
+ cirq_core-1.6.0.dev20250710201614.dist-info/RECORD,,