6969
7070
7171_TGate = TypeVar ('_TGate' , bound = 'cirq.Gate' )
72+ _MOMENT_OR_OP = Union ['cirq.Moment' , 'cirq.Operation' ]
7273
7374CIRCUIT_TYPE = TypeVar ('CIRCUIT_TYPE' , bound = 'AbstractCircuit' )
7475document (
@@ -2095,49 +2096,6 @@ def earliest_available_moment(
20952096 last_available = k
20962097 return last_available
20972098
2098- def _pick_or_create_inserted_op_moment_index (
2099- self , splitter_index : int , op : 'cirq.Operation' , strategy : 'cirq.InsertStrategy'
2100- ) -> int :
2101- """Determines and prepares where an insertion will occur.
2102-
2103- Args:
2104- splitter_index: The index to insert at.
2105- op: The operation that will be inserted.
2106- strategy: The insertion strategy.
2107-
2108- Returns:
2109- The index of the (possibly new) moment where the insertion should
2110- occur.
2111-
2112- Raises:
2113- ValueError: Unrecognized append strategy.
2114- """
2115-
2116- if strategy is InsertStrategy .NEW or strategy is InsertStrategy .NEW_THEN_INLINE :
2117- self ._moments .insert (splitter_index , Moment ())
2118- self ._mutated ()
2119- return splitter_index
2120-
2121- if strategy is InsertStrategy .INLINE :
2122- if 0 <= splitter_index - 1 < len (self ._moments ) and self ._can_add_op_at (
2123- splitter_index - 1 , op
2124- ):
2125- return splitter_index - 1
2126-
2127- return self ._pick_or_create_inserted_op_moment_index (
2128- splitter_index , op , InsertStrategy .NEW
2129- )
2130-
2131- if strategy is InsertStrategy .EARLIEST :
2132- if self ._can_add_op_at (splitter_index , op ):
2133- return self .earliest_available_moment (op , end_moment_index = splitter_index )
2134-
2135- return self ._pick_or_create_inserted_op_moment_index (
2136- splitter_index , op , InsertStrategy .INLINE
2137- )
2138-
2139- raise ValueError (f'Unrecognized append strategy: { strategy } ' )
2140-
21412099 def _can_add_op_at (self , moment_index : int , operation : 'cirq.Operation' ) -> bool :
21422100 if not 0 <= moment_index < len (self ._moments ):
21432101 return True
@@ -2147,7 +2105,7 @@ def _can_add_op_at(self, moment_index: int, operation: 'cirq.Operation') -> bool
21472105 def insert (
21482106 self ,
21492107 index : int ,
2150- moment_or_operation_tree : Union [ 'cirq.Operation' , 'cirq. OP_TREE'] ,
2108+ moment_or_operation_tree : 'cirq.OP_TREE' ,
21512109 strategy : 'cirq.InsertStrategy' = InsertStrategy .EARLIEST ,
21522110 ) -> int :
21532111 """Inserts operations into the circuit.
@@ -2170,24 +2128,57 @@ def insert(
21702128 """
21712129 # limit index to 0..len(self._moments), also deal with indices smaller 0
21722130 k = max (min (index if index >= 0 else len (self ._moments ) + index , len (self ._moments )), 0 )
2173- if strategy != InsertStrategy .EARLIEST or index != len (self ._moments ):
2131+ if strategy != InsertStrategy .EARLIEST or k != len (self ._moments ):
21742132 self ._placement_cache = None
2175- for moment_or_op in list (ops .flatten_to_ops_or_moments (moment_or_operation_tree )):
2176- if self ._placement_cache :
2177- p = self ._placement_cache .append (moment_or_op )
2178- elif isinstance (moment_or_op , Moment ):
2179- p = k
2180- else :
2181- p = self ._pick_or_create_inserted_op_moment_index (k , moment_or_op , strategy )
2182- if isinstance (moment_or_op , Moment ):
2183- self ._moments .insert (p , moment_or_op )
2184- elif p == len (self ._moments ):
2185- self ._moments .append (Moment (moment_or_op ))
2186- else :
2187- self ._moments [p ] = self ._moments [p ].with_operation (moment_or_op )
2188- k = max (k , p + 1 )
2189- if strategy is InsertStrategy .NEW_THEN_INLINE :
2190- strategy = InsertStrategy .INLINE
2133+ mops = list (ops .flatten_to_ops_or_moments (moment_or_operation_tree ))
2134+ if self ._placement_cache :
2135+ batches = [mops ] # Any grouping would work here; this just happens to be the fastest.
2136+ elif strategy is InsertStrategy .NEW :
2137+ batches = [[mop ] for mop in mops ] # Each op goes into its own moment.
2138+ else :
2139+ batches = list (_group_into_moment_compatible (mops ))
2140+ for batch in batches :
2141+ # Insert a moment if inline/earliest and _any_ op in the batch requires it.
2142+ if (
2143+ not self ._placement_cache
2144+ and not isinstance (batch [0 ], Moment )
2145+ and strategy in (InsertStrategy .INLINE , InsertStrategy .EARLIEST )
2146+ and not all (
2147+ (strategy is InsertStrategy .EARLIEST and self ._can_add_op_at (k , op ))
2148+ or (k > 0 and self ._can_add_op_at (k - 1 , op ))
2149+ for op in cast (List ['cirq.Operation' ], batch )
2150+ )
2151+ ):
2152+ self ._moments .insert (k , Moment ())
2153+ if strategy is InsertStrategy .INLINE :
2154+ k += 1
2155+ max_p = 0
2156+ for moment_or_op in batch :
2157+ # Determine Placement
2158+ if self ._placement_cache :
2159+ p = self ._placement_cache .append (moment_or_op )
2160+ elif isinstance (moment_or_op , Moment ):
2161+ p = k
2162+ elif strategy in (InsertStrategy .NEW , InsertStrategy .NEW_THEN_INLINE ):
2163+ self ._moments .insert (k , Moment ())
2164+ p = k
2165+ elif strategy is InsertStrategy .INLINE :
2166+ p = k - 1
2167+ else : # InsertStrategy.EARLIEST:
2168+ p = self .earliest_available_moment (moment_or_op , end_moment_index = k )
2169+ # Place
2170+ if isinstance (moment_or_op , Moment ):
2171+ self ._moments .insert (p , moment_or_op )
2172+ elif p == len (self ._moments ):
2173+ self ._moments .append (Moment (moment_or_op ))
2174+ else :
2175+ self ._moments [p ] = self ._moments [p ].with_operation (moment_or_op )
2176+ # Iterate
2177+ max_p = max (p , max_p )
2178+ if strategy is InsertStrategy .NEW_THEN_INLINE :
2179+ strategy = InsertStrategy .INLINE
2180+ k += 1
2181+ k = max (k , max_p + 1 )
21912182 self ._mutated (preserve_placement_cache = True )
21922183 return k
21932184
@@ -2450,7 +2441,7 @@ def batch_insert(self, insertions: Iterable[Tuple[int, 'cirq.OP_TREE']]) -> None
24502441
24512442 def append (
24522443 self ,
2453- moment_or_operation_tree : Union [ 'cirq.Moment' , 'cirq. OP_TREE'] ,
2444+ moment_or_operation_tree : 'cirq.OP_TREE' ,
24542445 strategy : 'cirq.InsertStrategy' = InsertStrategy .EARLIEST ,
24552446 ) -> None :
24562447 """Appends operations onto the end of the circuit.
@@ -2841,8 +2832,40 @@ def _group_until_different(items: Iterable[_TIn], key: Callable[[_TIn], _TKey],
28412832 return ((k , [val (i ) for i in v ]) for (k , v ) in itertools .groupby (items , key ))
28422833
28432834
2835+ def _group_into_moment_compatible (inputs : Sequence [_MOMENT_OR_OP ]) -> Iterator [List [_MOMENT_OR_OP ]]:
2836+ """Groups sequential ops into those that can coexist in a single moment.
2837+
2838+ This function will go through the input sequence in order, emitting lists of sequential
2839+ operations that can go into a single moment. It does not try to rearrange the elements or try
2840+ to move them to open slots in earlier moments; it simply processes them in order and outputs
2841+ them. i.e. the output, if flattened, will equal the input.
2842+
2843+ Actual Moments in the input will always be emitted by themselves as a single-element list.
2844+
2845+ Examples:
2846+ [X(a), X(b), X(a)] -> [[X(a), X(b)], [X(a)]]
2847+ [X(a), X(a), X(b)] -> [[X(a)], [X(a), X(b)]]
2848+ [X(a), Moment(X(b)), X(c)] -> [[X(a)], [Moment(X(b))], [X(c)]]
2849+ """
2850+ batch : List [_MOMENT_OR_OP ] = []
2851+ batch_qubits : Set ['cirq.Qid' ] = set ()
2852+ for mop in inputs :
2853+ is_moment = isinstance (mop , cirq .Moment )
2854+ if (is_moment and batch ) or not batch_qubits .isdisjoint (mop .qubits ):
2855+ yield batch
2856+ batch = []
2857+ batch_qubits .clear ()
2858+ if is_moment :
2859+ yield [mop ]
2860+ continue
2861+ batch .append (mop )
2862+ batch_qubits .update (mop .qubits )
2863+ if batch :
2864+ yield batch
2865+
2866+
28442867def get_earliest_accommodating_moment_index (
2845- moment_or_operation : Union [ 'cirq.Moment' , 'cirq.Operation' ] ,
2868+ moment_or_operation : _MOMENT_OR_OP ,
28462869 qubit_indices : Dict ['cirq.Qid' , int ],
28472870 mkey_indices : Dict ['cirq.MeasurementKey' , int ],
28482871 ckey_indices : Dict ['cirq.MeasurementKey' , int ],
@@ -2938,7 +2961,7 @@ def __init__(self) -> None:
29382961 # For keeping track of length of the circuit thus far.
29392962 self ._length = 0
29402963
2941- def append (self , moment_or_operation : Union [ 'cirq.Moment' , 'cirq.Operation' ] ) -> int :
2964+ def append (self , moment_or_operation : _MOMENT_OR_OP ) -> int :
29422965 """Find placement for moment/operation and update cache.
29432966
29442967 Determines the placement index of the provided operation, assuming
0 commit comments