@@ -1709,7 +1709,95 @@ def __init__(
1709
1709
"""
1710
1710
self ._moments : List ['cirq.Moment' ] = []
1711
1711
with _compat .block_overlapping_deprecation ('.*' ):
1712
- self .append (contents , strategy = strategy )
1712
+ if strategy == InsertStrategy .EARLIEST :
1713
+ self ._load_contents_with_earliest_strategy (contents )
1714
+ else :
1715
+ self .append (contents , strategy = strategy )
1716
+
1717
+ def _load_contents_with_earliest_strategy (self , contents : 'cirq.OP_TREE' ):
1718
+ """Optimized algorithm to load contents quickly.
1719
+
1720
+ The default algorithm appends operations one-at-a-time, letting them
1721
+ fall back until they encounter a moment they cannot commute with. This
1722
+ is slow because it requires re-checking for conflicts at each moment.
1723
+
1724
+ Here, we instead keep track of the greatest moment that contains each
1725
+ qubit, measurement key, and control key, and append the operation to
1726
+ the moment after the maximum of these. This avoids having to check each
1727
+ moment.
1728
+
1729
+ Args:
1730
+ contents: The initial list of moments and operations defining the
1731
+ circuit. You can also pass in operations, lists of operations,
1732
+ or generally anything meeting the `cirq.OP_TREE` contract.
1733
+ Non-moment entries will be inserted according to the EARLIEST
1734
+ insertion strategy.
1735
+ """
1736
+ # These are dicts from the qubit/key to the greatest moment index that has it. It is safe
1737
+ # to default to `-1`, as that is interpreted as meaning the zeroth index onward does not
1738
+ # have this value.
1739
+ qubit_indexes : Dict ['cirq.Qid' , int ] = defaultdict (lambda : - 1 )
1740
+ mkey_indexes : Dict ['cirq.MeasurementKey' , int ] = defaultdict (lambda : - 1 )
1741
+ ckey_indexes : Dict ['cirq.MeasurementKey' , int ] = defaultdict (lambda : - 1 )
1742
+
1743
+ # We also maintain the dict from moment index to moments/ops that go into it, for use when
1744
+ # building the actual moments at the end.
1745
+ op_lists_by_index : Dict [int , List ['cirq.Operation' ]] = defaultdict (list )
1746
+ moments_by_index : Dict [int , 'cirq.Moment' ] = {}
1747
+
1748
+ # For keeping track of length of the circuit thus far.
1749
+ length = 0
1750
+
1751
+ # "mop" means current moment-or-operation
1752
+ for mop in ops .flatten_to_ops_or_moments (contents ):
1753
+ mop_qubits = mop .qubits
1754
+ mop_mkeys = protocols .measurement_key_objs (mop )
1755
+ mop_ckeys = protocols .control_keys (mop )
1756
+
1757
+ # Both branches define `i`, the moment index at which to place the mop.
1758
+ if isinstance (mop , Moment ):
1759
+ # We always append moment to the end, to be consistent with `self.append`
1760
+ i = length
1761
+ moments_by_index [i ] = mop
1762
+ else :
1763
+ # Initially we define `i` as the greatest moment index that has a conflict. `-1` is
1764
+ # the initial conflict, and we search for larger ones. Once we get the largest one,
1765
+ # we increment i by 1 to set the placement index.
1766
+ i = - 1
1767
+
1768
+ # Look for the maximum conflict; i.e. a moment that has a qubit the same as one of
1769
+ # this op's qubits, that has a measurement or control key the same as one of this
1770
+ # op's measurement keys, or that has a measurement key the same as one of this op's
1771
+ # control keys. (Control keys alone can commute past each other). The `ifs` are
1772
+ # logically unnecessary but seem to make this slightly faster.
1773
+ if mop_qubits :
1774
+ i = max (i , * [qubit_indexes [q ] for q in mop_qubits ])
1775
+ if mop_mkeys :
1776
+ i = max (i , * [mkey_indexes [k ] for k in mop_mkeys ])
1777
+ i = max (i , * [ckey_indexes [k ] for k in mop_mkeys ])
1778
+ if mop_ckeys :
1779
+ i = max (i , * [mkey_indexes [k ] for k in mop_ckeys ])
1780
+ i += 1
1781
+ op_lists_by_index [i ].append (mop )
1782
+
1783
+ # Update our dicts with data from the latest mop placement. Note `i` will always be
1784
+ # greater than the existing value for all of these, by construction, so there is no
1785
+ # need to do a `max(i, existing)`.
1786
+ for q in mop_qubits :
1787
+ qubit_indexes [q ] = i
1788
+ for k in mop_mkeys :
1789
+ mkey_indexes [k ] = i
1790
+ for k in mop_ckeys :
1791
+ ckey_indexes [k ] = i
1792
+ length = max (length , i + 1 )
1793
+
1794
+ # Finally, once everything is placed, we can construct and append the actual moments for
1795
+ # each index.
1796
+ for i in range (length ):
1797
+ if i in moments_by_index :
1798
+ self ._moments .append (moments_by_index [i ].with_operations (op_lists_by_index [i ]))
1799
+ else :
1800
+ self ._moments .append (Moment (op_lists_by_index [i ]))
1713
1801
1714
1802
def __copy__ (self ) -> 'cirq.Circuit' :
1715
1803
return self .copy ()
@@ -1888,11 +1976,11 @@ def earliest_available_moment(
1888
1976
moment = self ._moments [k ]
1889
1977
if moment .operates_on (op_qubits ):
1890
1978
return last_available
1891
- moment_measurement_keys = protocols . measurement_key_objs ( moment )
1979
+ moment_measurement_keys = moment . _measurement_key_objs_ ( )
1892
1980
if (
1893
1981
not op_measurement_keys .isdisjoint (moment_measurement_keys )
1894
1982
or not op_control_keys .isdisjoint (moment_measurement_keys )
1895
- or not protocols . control_keys ( moment ).isdisjoint (op_measurement_keys )
1983
+ or not moment . _control_keys_ ( ).isdisjoint (op_measurement_keys )
1896
1984
):
1897
1985
return last_available
1898
1986
if self ._can_add_op_at (k , op ):
@@ -1973,14 +2061,9 @@ def insert(
1973
2061
Raises:
1974
2062
ValueError: Bad insertion strategy.
1975
2063
"""
1976
- moments_and_operations = list (
1977
- ops .flatten_to_ops_or_moments (
1978
- ops .transform_op_tree (moment_or_operation_tree , preserve_moments = True )
1979
- )
1980
- )
1981
2064
# limit index to 0..len(self._moments), also deal with indices smaller 0
1982
2065
k = max (min (index if index >= 0 else len (self ._moments ) + index , len (self ._moments )), 0 )
1983
- for moment_or_op in moments_and_operations :
2066
+ for moment_or_op in ops . flatten_to_ops_or_moments ( moment_or_operation_tree ) :
1984
2067
if isinstance (moment_or_op , Moment ):
1985
2068
self ._moments .insert (k , moment_or_op )
1986
2069
k += 1
0 commit comments