@@ -814,6 +814,9 @@ def has_measurements(self):
814
814
"""
815
815
return protocols .is_measurement (self )
816
816
817
+ def _is_measurement_ (self ) -> bool :
818
+ return any (protocols .is_measurement (op ) for op in self .all_operations ())
819
+
817
820
def are_all_measurements_terminal (self ) -> bool :
818
821
"""Whether all measurement gates are at the end of the circuit.
819
822
@@ -1375,8 +1378,7 @@ def save_qasm(
1375
1378
self ._to_qasm_output (header , precision , qubit_order ).save (file_path )
1376
1379
1377
1380
def _json_dict_ (self ):
1378
- ret = protocols .obj_to_dict_helper (self , ['moments' ])
1379
- return ret
1381
+ return protocols .obj_to_dict_helper (self , ['moments' ])
1380
1382
1381
1383
@classmethod
1382
1384
def _from_json_dict_ (cls , moments , ** kwargs ):
@@ -1750,10 +1752,17 @@ def __init__(
1750
1752
together. This option does not affect later insertions into the
1751
1753
circuit.
1752
1754
"""
1753
- # Implementation note: we set self._frozen = None any time self._moments
1754
- # is mutated, to "invalidate" the frozen instance.
1755
1755
self ._moments : List ['cirq.Moment' ] = []
1756
+
1757
+ # Implementation note: the following cached properties are set lazily and then
1758
+ # invalidated and reset to None in `self._mutated()`, which is called any time
1759
+ # `self._moments` is changed.
1760
+ self ._all_qubits : Optional [FrozenSet ['cirq.Qid' ]] = None
1756
1761
self ._frozen : Optional ['cirq.FrozenCircuit' ] = None
1762
+ self ._is_measurement : Optional [bool ] = None
1763
+ self ._is_parameterized : Optional [bool ] = None
1764
+ self ._parameter_names : Optional [AbstractSet [str ]] = None
1765
+
1757
1766
flattened_contents = tuple (ops .flatten_to_ops_or_moments (contents ))
1758
1767
if all (isinstance (c , Moment ) for c in flattened_contents ):
1759
1768
self ._moments [:] = cast (Iterable [Moment ], flattened_contents )
@@ -1764,6 +1773,12 @@ def __init__(
1764
1773
else :
1765
1774
self .append (flattened_contents , strategy = strategy )
1766
1775
1776
+ def _mutated (self ) -> None :
1777
+ """Clear cached properties in response to this circuit being mutated."""
1778
+ self ._all_qubits = None
1779
+ self ._frozen = None
1780
+ self ._is_parameterized = None
1781
+
1767
1782
@classmethod
1768
1783
def _from_moments (cls , moments : Iterable ['cirq.Moment' ]) -> 'Circuit' :
1769
1784
new_circuit = Circuit ()
@@ -1820,10 +1835,9 @@ def _load_contents_with_earliest_strategy(self, contents: 'cirq.OP_TREE'):
1820
1835
for i in range (length ):
1821
1836
if i in moments_by_index :
1822
1837
self ._moments .append (moments_by_index [i ].with_operations (op_lists_by_index [i ]))
1823
- self ._frozen = None
1824
1838
else :
1825
1839
self ._moments .append (Moment (op_lists_by_index [i ]))
1826
- self ._frozen = None
1840
+ self ._mutated ()
1827
1841
1828
1842
def __copy__ (self ) -> 'cirq.Circuit' :
1829
1843
return self .copy ()
@@ -1843,6 +1857,26 @@ def freeze(self) -> 'cirq.FrozenCircuit':
1843
1857
def unfreeze (self , copy : bool = True ) -> 'cirq.Circuit' :
1844
1858
return self .copy () if copy else self
1845
1859
1860
+ def all_qubits (self ) -> FrozenSet ['cirq.Qid' ]:
1861
+ if self ._all_qubits is None :
1862
+ self ._all_qubits = super ().all_qubits ()
1863
+ return self ._all_qubits
1864
+
1865
+ def _is_measurement_ (self ) -> bool :
1866
+ if self ._is_measurement is None :
1867
+ self ._is_measurement = super ()._is_measurement_ ()
1868
+ return self ._is_measurement
1869
+
1870
+ def _is_parameterized_ (self ) -> bool :
1871
+ if self ._is_parameterized is None :
1872
+ self ._is_parameterized = super ()._is_parameterized_ ()
1873
+ return self ._is_parameterized
1874
+
1875
+ def _parameter_names_ (self ) -> AbstractSet [str ]:
1876
+ if self ._parameter_names is None :
1877
+ self ._parameter_names = super ()._parameter_names_ ()
1878
+ return self ._parameter_names
1879
+
1846
1880
def copy (self ) -> 'Circuit' :
1847
1881
"""Return a copy of this circuit."""
1848
1882
copied_circuit = Circuit ()
@@ -1868,13 +1902,13 @@ def __setitem__(self, key, value):
1868
1902
raise TypeError ('Can only assign Moments into Circuits.' )
1869
1903
1870
1904
self ._moments [key ] = value
1871
- self ._frozen = None
1905
+ self ._mutated ()
1872
1906
1873
1907
# pylint: enable=function-redefined
1874
1908
1875
1909
def __delitem__ (self , key : Union [int , slice ]):
1876
1910
del self ._moments [key ]
1877
- self ._frozen = None
1911
+ self ._mutated ()
1878
1912
1879
1913
def __iadd__ (self , other ):
1880
1914
self .append (other )
@@ -1903,7 +1937,7 @@ def __imul__(self, repetitions: _INT_TYPE):
1903
1937
if not isinstance (repetitions , (int , np .integer )):
1904
1938
return NotImplemented
1905
1939
self ._moments *= int (repetitions )
1906
- self ._frozen = None
1940
+ self ._mutated ()
1907
1941
return self
1908
1942
1909
1943
def __mul__ (self , repetitions : _INT_TYPE ):
@@ -2047,7 +2081,7 @@ def _pick_or_create_inserted_op_moment_index(
2047
2081
2048
2082
if strategy is InsertStrategy .NEW or strategy is InsertStrategy .NEW_THEN_INLINE :
2049
2083
self ._moments .insert (splitter_index , Moment ())
2050
- self ._frozen = None
2084
+ self ._mutated ()
2051
2085
return splitter_index
2052
2086
2053
2087
if strategy is InsertStrategy .INLINE :
@@ -2105,19 +2139,17 @@ def insert(
2105
2139
for moment_or_op in list (ops .flatten_to_ops_or_moments (moment_or_operation_tree )):
2106
2140
if isinstance (moment_or_op , Moment ):
2107
2141
self ._moments .insert (k , moment_or_op )
2108
- self ._frozen = None
2109
2142
k += 1
2110
2143
else :
2111
2144
op = moment_or_op
2112
2145
p = self ._pick_or_create_inserted_op_moment_index (k , op , strategy )
2113
2146
while p >= len (self ._moments ):
2114
2147
self ._moments .append (Moment ())
2115
- self ._frozen = None
2116
2148
self ._moments [p ] = self ._moments [p ].with_operation (op )
2117
- self ._frozen = None
2118
2149
k = max (k , p + 1 )
2119
2150
if strategy is InsertStrategy .NEW_THEN_INLINE :
2120
2151
strategy = InsertStrategy .INLINE
2152
+ self ._mutated ()
2121
2153
return k
2122
2154
2123
2155
def insert_into_range (self , operations : 'cirq.OP_TREE' , start : int , end : int ) -> int :
@@ -2153,8 +2185,8 @@ def insert_into_range(self, operations: 'cirq.OP_TREE', start: int, end: int) ->
2153
2185
break
2154
2186
2155
2187
self ._moments [i ] = self ._moments [i ].with_operation (op )
2156
- self ._frozen = None
2157
2188
op_index += 1
2189
+ self ._mutated ()
2158
2190
2159
2191
if op_index >= len (flat_ops ):
2160
2192
return end
@@ -2200,7 +2232,7 @@ def _push_frontier(
2200
2232
if n_new_moments > 0 :
2201
2233
insert_index = min (late_frontier .values ())
2202
2234
self ._moments [insert_index :insert_index ] = [Moment ()] * n_new_moments
2203
- self ._frozen = None
2235
+ self ._mutated ()
2204
2236
for q in update_qubits :
2205
2237
if early_frontier .get (q , 0 ) > insert_index :
2206
2238
early_frontier [q ] += n_new_moments
@@ -2227,13 +2259,12 @@ def _insert_operations(
2227
2259
if len (operations ) != len (insertion_indices ):
2228
2260
raise ValueError ('operations and insertion_indices must have the same length.' )
2229
2261
self ._moments += [Moment () for _ in range (1 + max (insertion_indices ) - len (self ))]
2230
- self ._frozen = None
2262
+ self ._mutated ()
2231
2263
moment_to_ops : Dict [int , List ['cirq.Operation' ]] = defaultdict (list )
2232
2264
for op_index , moment_index in enumerate (insertion_indices ):
2233
2265
moment_to_ops [moment_index ].append (operations [op_index ])
2234
2266
for moment_index , new_ops in moment_to_ops .items ():
2235
2267
self ._moments [moment_index ] = self ._moments [moment_index ].with_operations (* new_ops )
2236
- self ._frozen = None
2237
2268
2238
2269
def insert_at_frontier (
2239
2270
self ,
@@ -2295,7 +2326,7 @@ def batch_remove(self, removals: Iterable[Tuple[int, 'cirq.Operation']]) -> None
2295
2326
old_op for old_op in copy ._moments [i ].operations if op != old_op
2296
2327
)
2297
2328
self ._moments = copy ._moments
2298
- self ._frozen = None
2329
+ self ._mutated ()
2299
2330
2300
2331
def batch_replace (
2301
2332
self , replacements : Iterable [Tuple [int , 'cirq.Operation' , 'cirq.Operation' ]]
@@ -2320,7 +2351,7 @@ def batch_replace(
2320
2351
old_op if old_op != op else new_op for old_op in copy ._moments [i ].operations
2321
2352
)
2322
2353
self ._moments = copy ._moments
2323
- self ._frozen = None
2354
+ self ._mutated ()
2324
2355
2325
2356
def batch_insert_into (self , insert_intos : Iterable [Tuple [int , 'cirq.OP_TREE' ]]) -> None :
2326
2357
"""Inserts operations into empty spaces in existing moments.
@@ -2341,7 +2372,7 @@ def batch_insert_into(self, insert_intos: Iterable[Tuple[int, 'cirq.OP_TREE']])
2341
2372
for i , insertions in insert_intos :
2342
2373
copy ._moments [i ] = copy ._moments [i ].with_operations (insertions )
2343
2374
self ._moments = copy ._moments
2344
- self ._frozen = None
2375
+ self ._mutated ()
2345
2376
2346
2377
def batch_insert (self , insertions : Iterable [Tuple [int , 'cirq.OP_TREE' ]]) -> None :
2347
2378
"""Applies a batched insert operation to the circuit.
@@ -2376,7 +2407,7 @@ def batch_insert(self, insertions: Iterable[Tuple[int, 'cirq.OP_TREE']]) -> None
2376
2407
if next_index > insert_index :
2377
2408
shift += next_index - insert_index
2378
2409
self ._moments = copy ._moments
2379
- self ._frozen = None
2410
+ self ._mutated ()
2380
2411
2381
2412
def append (
2382
2413
self ,
@@ -2407,7 +2438,7 @@ def clear_operations_touching(
2407
2438
for k in moment_indices :
2408
2439
if 0 <= k < len (self ._moments ):
2409
2440
self ._moments [k ] = self ._moments [k ].without_operations_touching (qubits )
2410
- self ._frozen = None
2441
+ self ._mutated ()
2411
2442
2412
2443
@property
2413
2444
def moments (self ) -> Sequence ['cirq.Moment' ]:
0 commit comments