@@ -176,24 +176,26 @@ def __init__(
176
176
initial_layout : dict ,
177
177
circuit : Optional [Circuit ] = None ,
178
178
blocks : Optional [CircuitBlocks ] = None ,
179
- temp : Optional [bool ] = False , # 2# for temporary circuit
179
+ temp : Optional [bool ] = False , # 2# for temporary circuit
180
180
):
181
181
self .initial_layout = dict (sorted (initial_layout .items ()))
182
182
183
- #1# bidirectional mapping
184
- #1# self._p2l: physical qubit number i -> logical qubit number _p2l[i]
185
- #1# self._l2p: logical qubit number i -> physical qubit number _l2p[i]
186
- self ._l2p , self ._p2l = [0 ] * len (self .initial_layout ), [0 ] * len (self .initial_layout )
183
+ # 1# bidirectional mapping
184
+ # 1# self._p2l: physical qubit number i -> logical qubit number _p2l[i]
185
+ # 1# self._l2p: logical qubit number i -> physical qubit number _l2p[i]
186
+ self ._l2p , self ._p2l = [0 ] * len (self .initial_layout ), [0 ] * len (
187
+ self .initial_layout
188
+ )
187
189
for mapping in self .initial_layout .items ():
188
190
physical_qubit , logical_qubit = int (mapping [0 ][1 :]), mapping [1 ]
189
191
self ._l2p [logical_qubit ] = physical_qubit
190
192
self ._p2l [physical_qubit ] = logical_qubit
191
193
192
194
self ._temporary = temp
193
- if self ._temporary : # 2# if temporary circuit, no need to store the blocks
195
+ if self ._temporary : # 2# if temporary circuit, no need to store the blocks
194
196
return
195
197
196
- self ._nqubits = circuit .nqubits # 1# number of qubits
198
+ self ._nqubits = circuit .nqubits # 1# number of qubits
197
199
if circuit is None :
198
200
raise_error (ValueError , "Circuit must be provided." )
199
201
@@ -205,7 +207,7 @@ def __init__(
205
207
self ._routed_blocks = CircuitBlocks (Circuit (circuit .nqubits ))
206
208
self ._swaps = 0
207
209
208
- #1# previous: set_circuit_logical
210
+ # 1# previous: set_circuit_logical
209
211
def set_p2l (self , p2l_map : list ):
210
212
"""Sets the current physical to logical qubit mapping.
211
213
@@ -214,8 +216,8 @@ def set_p2l(self, p2l_map: list):
214
216
Args:
215
217
p2l_map (list): physical to logical mapping.
216
218
"""
217
- #1# update bidirectional mapping
218
- #4# use shallow copy
219
+ # 1# update bidirectional mapping
220
+ # 4# use shallow copy
219
221
self ._p2l = p2l_map .copy ()
220
222
self ._l2p = [0 ] * len (self ._p2l )
221
223
for i , l in enumerate (self ._p2l ):
@@ -234,9 +236,7 @@ def execute_block(self, block: Block):
234
236
Args:
235
237
block (:class:`qibo.transpiler.blocks.Block`): block to be removed.
236
238
"""
237
- self ._routed_blocks .add_block (
238
- block .on_qubits (self .get_physical_qubits (block ))
239
- )
239
+ self ._routed_blocks .add_block (block .on_qubits (self .get_physical_qubits (block )))
240
240
self .circuit_blocks .remove_block (block )
241
241
242
242
def routed_circuit (self , circuit_kwargs : Optional [dict ] = None ):
@@ -253,11 +253,8 @@ def routed_circuit(self, circuit_kwargs: Optional[dict] = None):
253
253
def final_layout (self ):
254
254
"""Returns the final physical-logical qubits mapping."""
255
255
256
- #1# return {"q0": lq_num0, "q1": lq_num1, ...}
257
- unsorted_dict = {
258
- "q" + str (i ): self ._p2l [i ]
259
- for i in range (self ._nqubits )
260
- }
256
+ # 1# return {"q0": lq_num0, "q1": lq_num1, ...}
257
+ unsorted_dict = {"q" + str (i ): self ._p2l [i ] for i in range (self ._nqubits )}
261
258
262
259
return dict (sorted (unsorted_dict .items ()))
263
260
@@ -273,14 +270,14 @@ def update(self, swap_l: tuple):
273
270
274
271
swap_p = self .logical_pair_to_physical (swap_l )
275
272
276
- #2# add the real SWAP gate, not a temporary circuit
273
+ # 2# add the real SWAP gate, not a temporary circuit
277
274
if not self ._temporary :
278
275
self ._routed_blocks .add_block (
279
276
Block (qubits = swap_p , gates = [gates .SWAP (* swap_p )])
280
277
)
281
278
self ._swaps += 1
282
279
283
- #1# update the bidirectional mapping
280
+ # 1# update the bidirectional mapping
284
281
p1 , p2 = swap_p
285
282
l1 , l2 = swap_l
286
283
self ._p2l [p1 ], self ._p2l [p2 ] = l2 , l1
@@ -294,7 +291,7 @@ def undo(self):
294
291
self ._routed_blocks .remove_block (last_swap_block )
295
292
self ._swaps -= 1
296
293
297
- #1# update the bidirectional mapping
294
+ # 1# update the bidirectional mapping
298
295
p1 , p2 = swap_p
299
296
l1 , l2 = swap_l
300
297
self ._p2l [p1 ], self ._p2l [p2 ] = l2 , l1
@@ -314,7 +311,7 @@ def get_physical_qubits(self, block: Union[int, Block]):
314
311
315
312
return tuple (self ._l2p [q ] for q in block .qubits )
316
313
317
- #1# logical_to_physical -> logical_pair_to_physical
314
+ # 1# logical_to_physical -> logical_pair_to_physical
318
315
def logical_pair_to_physical (self , logical_qubits : tuple ):
319
316
"""Returns the physical qubits associated to the logical qubit pair.
320
317
@@ -324,10 +321,11 @@ def logical_pair_to_physical(self, logical_qubits: tuple):
324
321
Returns:
325
322
tuple: physical qubit numbers associated to the logical qubit pair.
326
323
"""
327
- #1# return physical qubit numbers corresponding to the logical qubit pair
324
+ # 1# return physical qubit numbers corresponding to the logical qubit pair
328
325
return self ._l2p [logical_qubits [0 ]], self ._l2p [logical_qubits [1 ]]
329
326
330
- #1# circuit_to_logical(), circuit_to_physical() removed
327
+ # 1# circuit_to_logical(), circuit_to_physical() removed
328
+
331
329
332
330
class ShortestPaths (Router ):
333
331
"""A class to perform initial qubit mapping and connectivity matching.
@@ -382,9 +380,12 @@ def __call__(self, circuit: Circuit, initial_layout: dict):
382
380
routed_circuit = routed_circuit
383
381
)
384
382
385
- #1# final layout is reverted to the original labeling
383
+ # 1# final layout is reverted to the original labeling
386
384
final_layout = self .circuit .final_layout ()
387
- final_layout_restored = {"q" + str (self .node_mapping_inv [int (k [1 :])]): v for k , v in final_layout .items ()}
385
+ final_layout_restored = {
386
+ "q" + str (self .node_mapping_inv [int (k [1 :])]): v
387
+ for k , v in final_layout .items ()
388
+ }
388
389
return routed_circuit , final_layout_restored
389
390
390
391
def _find_new_mapping (self ):
@@ -434,15 +435,14 @@ def _add_swaps(candidate: tuple, circuitmap: CircuitMap):
434
435
"""
435
436
path = candidate [0 ]
436
437
meeting_point = candidate [1 ]
437
- forward = path [0 : meeting_point + 1 ] # 1# physical qubits
438
+ forward = path [0 : meeting_point + 1 ] # 1# physical qubits
438
439
backward = list (reversed (path [meeting_point + 1 :]))
439
- #1# apply logical swaps
440
+ # 1# apply logical swaps
440
441
for f in forward [1 :]:
441
442
circuitmap .update ((circuitmap ._p2l [f ], circuitmap ._p2l [forward [0 ]]))
442
443
for b in backward [1 :]:
443
444
circuitmap .update ((circuitmap ._p2l [b ], circuitmap ._p2l [backward [0 ]]))
444
445
445
-
446
446
def _compute_cost (self , candidate : tuple ):
447
447
"""Greedy algorithm that decides which path to take and how qubits should be walked.
448
448
@@ -454,14 +454,14 @@ def _compute_cost(self, candidate: tuple):
454
454
Returns:
455
455
(list, int): best path to move qubits and qubit meeting point in the path.
456
456
"""
457
- #2# CircuitMap might be used
457
+ # 2# CircuitMap might be used
458
458
temporary_circuit = CircuitMap (
459
459
initial_layout = self .circuit .initial_layout ,
460
460
circuit = Circuit (len (self .circuit .initial_layout )),
461
461
blocks = deepcopy (self .circuit .circuit_blocks ),
462
462
)
463
463
464
- #1# use set_p2l
464
+ # 1# use set_p2l
465
465
temporary_circuit .set_p2l (self .circuit ._p2l )
466
466
self ._add_swaps (candidate , temporary_circuit )
467
467
temporary_dag = deepcopy (self ._dag )
@@ -476,7 +476,7 @@ def _compute_cost(self, candidate: tuple):
476
476
all_executed = True
477
477
for block in temporary_front_layer :
478
478
if (
479
- #3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
479
+ # 3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
480
480
temporary_circuit .get_physical_qubits (block )
481
481
in self .connectivity .edges
482
482
or not temporary_circuit .circuit_blocks .search_by_index (
@@ -504,7 +504,7 @@ def _check_execution(self):
504
504
executable_blocks = []
505
505
for block in self ._front_layer :
506
506
if (
507
- #3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
507
+ # 3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
508
508
self .circuit .get_physical_qubits (block ) in self .connectivity .edges
509
509
or not self .circuit .circuit_blocks .search_by_index (block ).entangled
510
510
):
@@ -554,7 +554,7 @@ def _preprocessing(self, circuit: Circuit, initial_layout: dict):
554
554
initial_layout (dict): initial physical-to-logical qubit mapping.
555
555
"""
556
556
557
- #1# To simplify routing, some data is relabeled before routing begins.
557
+ # 1# To simplify routing, some data is relabeled before routing begins.
558
558
node_mapping , new_initial_layout = {}, {}
559
559
for i , node in enumerate (self .connectivity .nodes ):
560
560
node_mapping [node ] = i
@@ -596,8 +596,9 @@ def _append_final_measurements(self, routed_circuit: Circuit):
596
596
for measurement in self ._final_measurements :
597
597
original_qubits = measurement .qubits
598
598
routed_qubits = list (
599
- #1# use l2p to get physical qubit numbers
600
- self .circuit ._l2p [qubit ] for qubit in original_qubits
599
+ # 1# use l2p to get physical qubit numbers
600
+ self .circuit ._l2p [qubit ]
601
+ for qubit in original_qubits
601
602
)
602
603
routed_circuit .add (
603
604
measurement .on_qubits (dict (zip (original_qubits , routed_qubits )))
@@ -643,7 +644,7 @@ def __init__(
643
644
seed : Optional [int ] = None ,
644
645
):
645
646
self .connectivity = connectivity
646
- #1# map to revert the final layout to the original labeling
647
+ # 1# map to revert the final layout to the original labeling
647
648
self .node_mapping_inv = None
648
649
self .lookahead = lookahead
649
650
self .decay = decay_lookahead
@@ -698,9 +699,12 @@ def __call__(self, circuit: Circuit, initial_layout: dict):
698
699
routed_circuit = routed_circuit
699
700
)
700
701
701
- #1# final layout is reverted to the original labeling
702
+ # 1# final layout is reverted to the original labeling
702
703
final_layout = self .circuit .final_layout ()
703
- final_layout_restored = {"q" + str (self .node_mapping_inv [int (k [1 :])]): v for k , v in final_layout .items ()}
704
+ final_layout_restored = {
705
+ "q" + str (self .node_mapping_inv [int (k [1 :])]): v
706
+ for k , v in final_layout .items ()
707
+ }
704
708
return routed_circuit , final_layout_restored
705
709
706
710
@property
@@ -724,8 +728,8 @@ def _preprocessing(self, circuit: Circuit, initial_layout: dict):
724
728
initial_layout (dict): initial physical-to-logical qubit mapping.
725
729
"""
726
730
727
- #1# To simplify routing, some data is relabeled before routing begins.
728
- #1# physical qubit is reassigned to a range from 0 to len(self.connectivity.nodes) - 1.
731
+ # 1# To simplify routing, some data is relabeled before routing begins.
732
+ # 1# physical qubit is reassigned to a range from 0 to len(self.connectivity.nodes) - 1.
729
733
node_mapping , new_initial_layout = {}, {}
730
734
for i , node in enumerate (self .connectivity .nodes ):
731
735
node_mapping [node ] = i
@@ -769,8 +773,9 @@ def _append_final_measurements(self, routed_circuit: Circuit):
769
773
for measurement in self ._final_measurements :
770
774
original_qubits = measurement .qubits
771
775
routed_qubits = list (
772
- #1# use l2p to get physical qubit numbers
773
- self .circuit ._l2p [qubit ] for qubit in original_qubits
776
+ # 1# use l2p to get physical qubit numbers
777
+ self .circuit ._l2p [qubit ]
778
+ for qubit in original_qubits
774
779
)
775
780
routed_circuit .add (
776
781
measurement .on_qubits (dict (zip (original_qubits , routed_qubits )))
@@ -799,8 +804,8 @@ def _get_dag_layer(self, n_layer, qubits=False):
799
804
Otherwise, return the block numbers.
800
805
"""
801
806
802
- #3# depend on the 'qubits' flag, return the block number or target qubits
803
- #3# return target qubits -> to avoid using get_physical_qubits(block_num)
807
+ # 3# depend on the 'qubits' flag, return the block number or target qubits
808
+ # 3# return target qubits -> to avoid using get_physical_qubits(block_num)
804
809
if qubits :
805
810
layer_qubits = []
806
811
nodes = self ._dag .nodes (data = True )
@@ -816,7 +821,7 @@ def _find_new_mapping(self):
816
821
"""Find the new best mapping by adding one swap."""
817
822
candidates_evaluation = {}
818
823
819
- #4# use shallow copy
824
+ # 4# use shallow copy
820
825
self ._memory_map .append (self .circuit ._p2l .copy ())
821
826
for candidate in self ._swap_candidates ():
822
827
candidates_evaluation [candidate ] = self ._compute_cost (candidate )
@@ -835,31 +840,31 @@ def _find_new_mapping(self):
835
840
def _compute_cost (self , candidate : int ):
836
841
"""Compute the cost associated to a possible SWAP candidate."""
837
842
838
- #2# use CircuitMap for temporary circuit to save time
839
- #2# no gates, no block decomposition, no Circuit object
840
- #2# just logical-physical mapping
843
+ # 2# use CircuitMap for temporary circuit to save time
844
+ # 2# no gates, no block decomposition, no Circuit object
845
+ # 2# just logical-physical mapping
841
846
temporary_circuit = CircuitMap (
842
847
initial_layout = self .circuit .initial_layout ,
843
848
temp = True ,
844
849
)
845
850
846
- #1# use set_p2l
851
+ # 1# use set_p2l
847
852
temporary_circuit .set_p2l (self .circuit ._p2l )
848
853
temporary_circuit .update (candidate )
849
854
850
- #1# use p2l to check if the mapping is already in the memory
855
+ # 1# use p2l to check if the mapping is already in the memory
851
856
if temporary_circuit ._p2l in self ._memory_map :
852
857
return float ("inf" )
853
858
854
859
tot_distance = 0.0
855
860
weight = 1.0
856
861
for layer in range (self .lookahead + 1 ):
857
- #3# return gates' target qubit pairs in the layer
858
- #3# to avoid using get_physical_qubits(block_num)
862
+ # 3# return gates' target qubit pairs in the layer
863
+ # 3# to avoid using get_physical_qubits(block_num)
859
864
layer_gates = self ._get_dag_layer (layer , qubits = True )
860
865
avg_layer_distance = 0.0
861
866
for lq_pair in layer_gates :
862
- #3# logical qubit pairs to node numbers (physical qubit pairs) in the connectivity graph
867
+ # 3# logical qubit pairs to node numbers (physical qubit pairs) in the connectivity graph
863
868
qubits = temporary_circuit .logical_pair_to_physical (lq_pair )
864
869
avg_layer_distance += (
865
870
max (self ._delta_register [i ] for i in qubits )
@@ -881,7 +886,7 @@ def _swap_candidates(self):
881
886
(list): list of candidates.
882
887
"""
883
888
candidates = []
884
- #3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
889
+ # 3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
885
890
for block in self ._front_layer :
886
891
for qubit in self .circuit .get_physical_qubits (block ):
887
892
for connected in self .connectivity .neighbors (qubit ):
@@ -907,7 +912,7 @@ def _check_execution(self):
907
912
executable_blocks = []
908
913
for block in self ._front_layer :
909
914
if (
910
- #3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
915
+ # 3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
911
916
self .circuit .get_physical_qubits (block ) in self .connectivity .edges
912
917
or not self .circuit .circuit_blocks .search_by_index (block ).entangled
913
918
):
@@ -950,8 +955,8 @@ def _shortest_path_routing(self):
950
955
shortest_path_qubits = None
951
956
952
957
for block in self ._front_layer :
953
- #3# return node numbers (physical qubits) in the connectivity graph
954
- #3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
958
+ # 3# return node numbers (physical qubits) in the connectivity graph
959
+ # 3# might be changed to use _get_dag_layer(qubits=True) to avoid using get_physical_qubits(block_num)
955
960
q1 , q2 = self .circuit .get_physical_qubits (block )
956
961
distance = self ._dist_matrix [q1 , q2 ]
957
962
@@ -964,11 +969,12 @@ def _shortest_path_routing(self):
964
969
)
965
970
966
971
# move q1
967
- #1# qubit moving algorithm is changed
972
+ # 1# qubit moving algorithm is changed
968
973
q1 = self .circuit ._p2l [shortest_path [0 ]]
969
974
for q2 in shortest_path [1 :- 1 ]:
970
975
self .circuit .update ((q1 , self .circuit ._p2l [q2 ]))
971
976
977
+
972
978
def _create_dag (gates_qubits_pairs : list ):
973
979
"""Helper method for :meth:`qibo.transpiler.router.Sabre`.
974
980
@@ -984,7 +990,7 @@ def _create_dag(gates_qubits_pairs: list):
984
990
dag = nx .DiGraph ()
985
991
dag .add_nodes_from (range (len (gates_qubits_pairs )))
986
992
987
- #3# additionally store target qubits of the gates
993
+ # 3# additionally store target qubits of the gates
988
994
for i in range (len (gates_qubits_pairs )):
989
995
dag .nodes [i ]["qubits" ] = gates_qubits_pairs [i ]
990
996
@@ -1003,6 +1009,7 @@ def _create_dag(gates_qubits_pairs: list):
1003
1009
1004
1010
return _remove_redundant_connections (dag )
1005
1011
1012
+
1006
1013
def _remove_redundant_connections (dag : nx .DiGraph ):
1007
1014
"""Helper method for :func:`qibo.transpiler.router._create_dag`.
1008
1015
@@ -1015,9 +1022,9 @@ def _remove_redundant_connections(dag: nx.DiGraph):
1015
1022
(:class:`networkx.DiGraph`): reduced dag.
1016
1023
"""
1017
1024
new_dag = nx .DiGraph ()
1018
- #3# add nodes with attributes
1025
+ # 3# add nodes with attributes
1019
1026
new_dag .add_nodes_from (dag .nodes (data = True ))
1020
1027
transitive_reduction = nx .transitive_reduction (dag )
1021
1028
new_dag .add_edges_from (transitive_reduction .edges )
1022
1029
1023
- return new_dag
1030
+ return new_dag
0 commit comments