@@ -78,6 +78,7 @@ def run(self, circuit: Circuit, data: dict[str, Any] = {}) -> None:
7878 active_bins : list [Bin | None ] = [
7979 None for _ in range (circuit .num_qudits )
8080 ]
81+
8182 # Tracks the first cycle in circuit not included in partitioned_circuit
8283 dividing_line : dict [int , int ] = {
8384 i : 0 if circuit ._front [i ] is None else circuit ._front [i ].cycle # type: ignore # noqa
@@ -134,7 +135,7 @@ def process_pending_bins() -> None:
134135 if all (q in loc for q in qudits ):
135136 prev_op = partitioned_circuit .pop (p )
136137 pg = cast (CircuitGate , prev_op .gate )
137- prev_circ = pg ._circuit
138+ prev_circ = pg ._circuit . copy ()
138139 local_loc = [loc .index (q ) for q in qudits ]
139140 subc .insert_circuit (0 , prev_circ , local_loc )
140141
@@ -146,7 +147,7 @@ def process_pending_bins() -> None:
146147 if all (q in qudits for q in loc ):
147148 prev_op = partitioned_circuit .pop (p )
148149 pg = cast (CircuitGate , prev_op .gate )
149- prev_circ = pg ._circuit
150+ prev_circ = pg ._circuit . copy ()
150151 lloc = [qudits .index (q ) for q in loc ]
151152 prev_circ .append_circuit (subc , lloc )
152153 subc .become (prev_circ )
@@ -166,6 +167,7 @@ def process_pending_bins() -> None:
166167 for qudit in bin .qudits :
167168 dividing_line [qudit ] = bin .ends [qudit ] + 1 # type: ignore # noqa
168169 need_to_reprocess = True
170+ break
169171
170172 for bin in to_remove :
171173 pending_bins .remove (bin )
@@ -175,16 +177,16 @@ def process_pending_bins() -> None:
175177 point = CircuitPoint (cycle , op .location [0 ])
176178 location = op .location
177179
178- # Get all currently active bins that share atleast one qudit
180+ # Get all currently active bins that share at least one qudit
179181 overlapping_bins : list [Bin ] = list ({
180182 active_bins [q ] for q in location # type: ignore
181183 if active_bins [q ] is not None
182184 })
183185
184186 # Get all the currently active bins that can have op added to them
185187 admissible_bins = [
186- b for b in overlapping_bins
187- if b .can_accommodate (location , self .block_size )
188+ bin for bin in overlapping_bins
189+ if bin .can_accommodate (location , self .block_size )
188190 ]
189191
190192 # Close location on inadmissible overlapping bins
@@ -193,57 +195,50 @@ def process_pending_bins() -> None:
193195 if close_bin_qudits (bin , location , cycle ):
194196 num_closed += 1
195197
196- # If we cannot add this op to any bin, make a new one
198+ # Select bin or make new one
197199 if len (admissible_bins ) == 0 :
200+ # If we cannot add this op to any bin, make a new one
198201 assert all (active_bins [q ] is None for q in location )
199- new_bin = Bin (point , location )
200- for q in location :
201- active_bins [q ] = new_bin
202-
203- # Block qudits to prevent circular dependencies
204- for bin in overlapping_bins :
205- bin .blocked_qudits .update (new_bin .qudits )
206-
207- for active_bin in active_bins :
208- if active_bin is None or active_bin == bin :
209- continue
210-
211- indirect = active_bin .blocked_qudits
212- indirect = indirect .intersection (bin .qudits )
213- if len (indirect ) != 0 :
214- active_bin .blocked_qudits .update (new_bin .qudits )
202+ selected_bin = Bin ()
215203
216204 else :
217- # Add to first admissible bin
218- selected_bin = admissible_bins [0 ]
219-
220- # Deactivate the rest
221- for bin in admissible_bins [1 :]:
222- if close_bin_qudits (bin , location , cycle ):
223- num_closed += 1
224-
225- # Add op to selected_bin
226- selected_bin .add_op (point , location )
227- for q in location :
228- if active_bins [q ] is None :
229- active_bins [q ] = selected_bin
230- else :
231- assert active_bins [q ] == selected_bin
232-
233- # Block qudits to prevent circular dependencies
234- for bin in overlapping_bins :
235- if bin == selected_bin :
236- continue
237- bin .blocked_qudits .update (selected_bin .qudits )
238-
239- for active_bin in active_bins :
240- if active_bin is None or active_bin == bin :
241- continue
242-
243- indirect = active_bin .blocked_qudits
244- indirect = indirect .intersection (bin .qudits )
245- if len (indirect ) != 0 :
246- active_bin .blocked_qudits .update (new_bin .qudits )
205+ # Otherwise select an admissible bin
206+ found = False
207+ for bin in admissible_bins :
208+ if all (q in bin .qudits for q in location ):
209+ selected_bin = bin
210+ found = True
211+ break
212+
213+ if not found :
214+ selected_bin = admissible_bins [0 ]
215+
216+ # Close the overlapping qudits on the other admissible bins
217+ for bin in admissible_bins :
218+ if bin != selected_bin :
219+ if close_bin_qudits (bin , location , cycle ):
220+ num_closed += 1
221+
222+ # Add op to selected_bin
223+ selected_bin .add_op (point , location )
224+ for q in location :
225+ if active_bins [q ] is None :
226+ active_bins [q ] = selected_bin
227+ else :
228+ assert active_bins [q ] == selected_bin
229+
230+ # Block qudits to prevent circular dependencies
231+ for active_bin in active_bins :
232+ if active_bin is None :
233+ continue
234+ if active_bin == selected_bin :
235+ continue
236+
237+ indirect = active_bin .blocked_qudits
238+ indirect = indirect .union (active_bin .qudits )
239+ indirect = indirect .intersection (selected_bin .qudits )
240+ if len (indirect ) != 0 :
241+ active_bin .blocked_qudits .update (selected_bin .qudits )
247242
248243 # If a new bin was finalized, reprocess pending bins
249244 if num_closed >= 5 :
@@ -258,6 +253,13 @@ def process_pending_bins() -> None:
258253 # Process remaining bins
259254 process_pending_bins ()
260255
256+ if len (pending_bins ) != 0 :
257+ raise RuntimeError (
258+ 'Unable to process all pending bins during partitioning.\n '
259+ 'This should never happen and is a major issue'
260+ ', please make a bug report containing the input circuit.' ,
261+ )
262+
261263 # Become partitioned circuit
262264 circuit .become (partitioned_circuit , False )
263265
@@ -268,30 +270,26 @@ class Bin:
268270 id : int = 0
269271 """Unique ID counter for Bin instances."""
270272
271- def __init__ (
272- self ,
273- point : CircuitPoint ,
274- location : CircuitLocation ,
275- ) -> None :
273+ def __init__ (self ) -> None :
276274 """Can start a new bin from an operation."""
277275
278276 # The qudits in the bin
279- self .qudits : list [int ] = list ( location )
277+ self .qudits : list [int ] = []
280278
281279 # The starting cycles for each qudit (inclusive)
282- self .starts : dict [int , int ] = {q : point . cycle for q in location }
280+ self .starts : dict [int , int ] = {}
283281
284282 # The ending cycles for each qudit (inclusive)
285- self .ends : dict [int , int | None ] = {q : None for q in location }
283+ self .ends : dict [int , int | None ] = {}
286284
287285 # The qudits that can still accept new gates
288- self .active_qudits : list [int ] = list ( location )
286+ self .active_qudits : list [int ] = []
289287
290288 # Qudits that cannot be added to the bin
291289 self .blocked_qudits : set [int ] = set ()
292290
293291 # Points for each operation in this bin
294- self .op_list : list [CircuitPoint ] = [point ]
292+ self .op_list : list [CircuitPoint ] = []
295293
296294 self .id = Bin .id
297295 Bin .id += 1
@@ -302,11 +300,10 @@ def __hash__(self) -> int:
302300 def __eq__ (self , other : object ) -> bool :
303301 return isinstance (other , Bin ) and self .id == other .id
304302
305- def add_op (
306- self ,
307- point : CircuitPoint ,
308- location : CircuitLocation ,
309- ) -> None :
303+ def __repr__ (self ) -> str :
304+ return 'Bin ' + str (self .id )
305+
306+ def add_op (self , point : CircuitPoint , location : CircuitLocation ) -> None :
310307 """Add an operation the bin."""
311308 for q in location :
312309 if q not in self .qudits :
@@ -322,7 +319,11 @@ def can_accommodate(self, loc: CircuitLocation, block_size: int) -> bool:
322319 An op can be added to the bin if all overlapping qudits are active in
323320 the bin and if the new bin won't be too large.
324321 """
325- if any (q in loc for q in self .blocked_qudits ):
322+ if any (
323+ q in self .blocked_qudits
324+ and q not in self .active_qudits
325+ for q in loc
326+ ):
326327 return False
327328
328329 overlapping_qudits_are_active = all (
@@ -334,3 +335,6 @@ def can_accommodate(self, loc: CircuitLocation, block_size: int) -> bool:
334335 too_big = len (set (self .qudits + list (loc ))) > size_limit
335336
336337 return overlapping_qudits_are_active and not too_big
338+
339+
340+ 1
0 commit comments