
    z	iF                         S r SSKrSSKJrJrJrJrJr  SSKJ	r	J
r
  SSKJr  SSKJr  SSKJrJrJrJrJrJr  SSKJr  SS	KJr   " S
 S5      r " S S\5      rg)z<Search for star connectivity patterns and replace them with.    N)IterableUnionOptionalListTuple)floorlog10)Barrier)SwapGate)	DAGOpNode
DAGOutNode	DAGInNode
DAGDepNodeDAGDependency
DAGCircuit)TransformationPass)Layoutc                   4    \ rS rSrSrS	S jrS rS rS rSr	g)
	StarBlock    z<Defines blocks representing star-shaped pieces of a circuit.Nc                 @    X l         X0l        Uc  / U l        g UU l        g N)centernum2qnodes)selfr   r   r   s       j/home/james-whalen/.local/lib/python3.13/site-packages/qiskit/transpiler/passes/routing/star_prerouting.py__init__StarBlock.__init__#   s    
 =R
e
    c                     U R                   $ )z,Returns the list of nodes used in the block.)r   r   s    r   	get_nodesStarBlock.get_nodes(   s    zzr    c                    Sn[        UR                  5      S:X  a  U R                  R                  U5        SnU$ U R                  cN  [        UR                  5      U l        U R                  R                  U5        U =R                  S-  sl        SnU$ [        U R                  [
        5      (       a  UR                  S   U R                  ;   aH  UR                  S   U l        U R                  R                  U5        U =R                  S-  sl        SnU$ UR                  S   U R                  ;   aF  UR                  S   U l        U R                  R                  U5        U =R                  S-  sl        SnU$ U R                  UR                  ;   a2  U R                  R                  U5        U =R                  S-  sl        SnU$ )z
If node can be added to block while keeping the block star-shaped, and
return True. Otherwise, does not add node to block and returns False.
F   Tr   )lenqargsr   appendr   setr   
isinstance)r   nodeaddeds      r   append_nodeStarBlock.append_node,   si    tzz?aJJd#E. - [[ djj/DKJJd#JJ!OJE$ # S))zz!}+"jjm

!!$'

a
  A$++-"jjm

!!$'

a
  {{djj(

!!$'

a
r    c                     U R                   $ )z>
Returns the number of two-qubit quantum gates in this block.
)r   r"   s    r   sizeStarBlock.sizeO   s     zzr    )r   r   r   )NNr   )
__name__
__module____qualname____firstlineno____doc__r   r#   r.   r1   __static_attributes__ r    r   r   r       s    F4
!Fr    r   c                      ^  \ rS rSrSrU 4S jrS rS\\\	\
4      4S jrS rS rS	 rS
 r SS jrS rS\\\4   S\S\\\   \\\	   \\
   4   4   4S jrS rSrU =r$ )StarPreRoutingV   a^  Run star to linear pre-routing

This pass is a logical optimization pass that rewrites any
solely 2q gate star connectivity subcircuit as a linear connectivity
equivalent with swaps.

For example:

  .. plot::
     :alt: Circuit diagram output by the previous code.
     :include-source:

     from qiskit.circuit import QuantumCircuit
     from qiskit.transpiler.passes import StarPreRouting

     qc = QuantumCircuit(10)
     qc.h(0)
     qc.cx(0, range(1, 5))
     qc.h(9)
     qc.cx(9, range(8, 4, -1))
     qc.measure_all()
     StarPreRouting()(qc).draw("mpl")

This pass was inspired by a similar pass described in Section IV of:
C. Campbell et al., "Superstaq: Deep Optimization of Quantum Programs,"
2023 IEEE International Conference on Quantum Computing and Engineering (QCE),
Bellevue, WA, USA, 2023, pp. 1020-1032, doi: 10.1109/QCE57702.2023.00116.
c                 >   > SU l         SU l        [        TU ]  5         g)r;   N)_pending_nodes
_in_degreesuperr   )r   	__class__s    r   r   StarPreRouting.__init__t   s!     MQMQr    c                     / U l         0 U l        U R                  U5       HN  n[        U R	                  X5      5      nX0R                  U'   US:X  d  M3  U R                   R                  U5        MP     g)a  For an efficient implementation, for every node we keep the number of its
unprocessed immediate predecessors (called ``_in_degree``). This ``_in_degree``
is set up at the start and updated throughout the algorithm.
A node is leaf (or input) node iff its ``_in_degree`` is 0.
When a node is (marked as) collected, the ``_in_degree`` of each of its immediate
successor is updated by subtracting 1.
Additionally, ``_pending_nodes`` explicitly keeps the list of nodes whose
``_in_degree`` is 0.
r   N)r>   r?   	_op_nodesr'   _direct_predsr)   )r   dagr,   degs       r   _setup_in_degrees StarPreRouting._setup_in_degrees{   sb     !NN3'Dd((34C$'OOD!ax##**40	 (r    returnc                 l    [        U[        5      (       d  UR                  5       $ UR                  5       $ )zReturns DAG nodes.)r+   r   op_nodesr#   )r   rF   s     r   rD   StarPreRouting._op_nodes   s'    #}--<<>!==?"r    c                 &   [        U[        5      (       d9  UR                  U5       Vs/ s H  n[        U[        5      (       d  M  UPM     sn$ UR	                  UR
                  5       Vs/ s H  oAR                  U5      PM     sn$ s  snf s  snf )zReturns direct predecessors of a node. This function takes into account the
direction of collecting blocks, that is node's predecessors when collecting
backwards are the direct successors of a node in the DAG.
)r+   r   predecessorsr   direct_predecessorsnode_idget_node)r   rF   r,   predpred_ids        r   rE   StarPreRouting._direct_preds   sv    
 #}--%(%5%5d%;[%;Tz$PY?ZD%;[[9<9P9PQUQ]Q]9^_9^gLL)9^__ \_   B	B	,Bc                 &   [        U[        5      (       d9  UR                  U5       Vs/ s H  n[        U[        5      (       d  M  UPM     sn$ UR	                  UR
                  5       Vs/ s H  oAR                  U5      PM     sn$ s  snf s  snf )zReturns direct successors of a node. This function takes into account the
direction of collecting blocks, that is node's successors when collecting
backwards are the direct predecessors of a node in the DAG.
)r+   r   
successorsr   direct_successorsrQ   rR   )r   rF   r,   succsucc_ids        r   _direct_succsStarPreRouting._direct_succs   sp    
 #}--%(^^D%9Y%9TZi=XD%9YY9<9N9Nt||9\]9\gLL)9\]] Z]rV   c                 2    [        U R                  5      S:  $ )z5Returns whether there are uncollected (pending) nodesr   )r'   r>   r"   s    r   _have_uncollected_nodes&StarPreRouting._have_uncollected_nodes   s    4&&'!++r    c                    U R                   n/ U l         [        5       nU(       a  / nU H  nU" U5      =(       a    UR                  U5      nU(       aW  U R                  X5       H@  nU R                  U==   S-  ss'   U R                  U   S:X  d  M/  UR                  U5        MB     M  U R                   R                  U5        M     UnU(       a  M  U$ )aM  Iteratively collects the largest block of input nodes (that is, nodes with
``_in_degree`` equal to 0) that match a given filtering function.
Examples of this include collecting blocks of swap gates,
blocks of linear gates (CXs and SWAPs), blocks of Clifford gates, blocks of single-qubit gates,
blocks of two-qubit gates, etc.  Here 'iteratively' means that once a node is collected,
the ``_in_degree`` of each of its immediate successor is decreased by 1, allowing more nodes
to become input and to be eligible for collecting into the current block.
Returns the block of collected nodes.
r&   r   )r>   r   r.   r\   r?   r)   )	r   rF   	filter_fnunprocessed_pending_nodescurrent_blocknew_pending_nodesr,   r-   sucs	            r   collect_matching_block%StarPreRouting.collect_matching_block   s     %)$7$7! ! ( "1!$KM,E,Ed,K#11#<,1,??3/14-44S9  =
 ''..t4 2 ):% (' r    c                   ^	 S m	U	4S jnU R                  U5        / n/ nU R                  5       (       al  U R                  XS9  U R                  UT	S9nUR                  5       U:  a  UR	                  U5        UR	                  U5        U R                  5       (       a  Ml  U VVs/ s H  owR
                    H  oPM     M     nnnXE4$ s  snnf )a  Collects all blocks that match a given filtering function filter_fn.
This iteratively finds the largest block that does not match filter_fn,
then the largest block that matches filter_fn, and so on, until no more uncollected
nodes remain. Intuitively, finding larger blocks of non-matching nodes helps to
find larger blocks of matching nodes later on. The option ``min_block_size``
specifies the minimum number of gates in the block for the block to be collected.

By default, blocks are collected in the direction from the inputs towards the outputs
of the circuit. The option ``collect_from_back`` allows to change this direction,
that is collect blocks from the outputs towards the inputs of the circuit.

Returns the list of matching blocks only.
c                     [        U R                  5      S:*  =(       a^    [        U R                  5      S:H  =(       a?    [        U R                  SS5      SL =(       a    [        U R                  [        5      (       + $ )z8Specifies which nodes can be collected into star blocks.   r   	conditionN)r'   r(   cargsgetattropr+   r
   )r,   s    r   rb   =StarPreRouting.collect_all_matching_blocks.<locals>.filter_fn   s`     DJJ1$ 5

Oq(5DGG[$74?5 #477G44	r    c                    > T" U 5      (       + $ )z"Returns the opposite of filter_fn.r9   )r,   rb   s    r   not_filter_fnAStarPreRouting.collect_all_matching_blocks.<locals>.not_filter_fn   s     &&r    )rb   )rH   r_   rg   r1   r)   r   )
r   rF   min_block_sizerr   matching_blocksprocessing_ordermatching_blockpnrb   s
            @r   collect_all_matching_blocks*StarPreRouting.collect_all_matching_blocks   s    &		'
 	s# ,.**,,'''E!88	8RN""$6&&~6##N3 **,, (8I'7!AAA'7I00 Js   &C
c                    U R                  USS9u  p#U(       d  U$ [        S U 5       5      (       a  U$ U R                  XU5      u  pE[        UR                  5       VVs0 s H  u  pgXv_M	     nnn[        U5      U R                  S'   U R                  S   c  XR                  S'   [        [        U5       V	V
s0 s H  u  pUR                  U
   U	_M     sn
n	5      nU R                  S   =n(       aO  UR                  UR                  UR                  UR                  5      UR                  5      U R                  S'   U$ XR                  S'   U$ s  snnf s  sn
n	f )Nrk   rt   c              3   F   #    U  H  oR                  5       S :  v   M     g7f)   N)r1   ).0bs     r   	<genexpr>%StarPreRouting.run.<locals>.<genexpr>  s     1[vvx!|[s   !original_layoutoriginal_qubit_indicesvirtual_permutation_layout)	 determine_star_blocks_processingallstar_preroute	enumeratequbitsr   property_setcomposeinverse)r   rF   star_blocksrv   new_dagqubit_mappingindexqubitinput_qubit_mappingidxout
new_layoutcurrent_layouts                r   runStarPreRouting.run   s]   (,(M(Mcbc(M(d%J1[111 J "&!3!3CFV!W AJ#**@UV@Uu|@UV/56I/J+,56>:M67)MBZ[BZhcSZZ_c1BZ[\
!../KLL>L>H>P>P&&szz3::>

?D:;  ?I:; W
 \s   !E;E
rF   rt   c                 *    U R                  XS9u  p4X44$ )a  Returns star blocks in dag and the processing order of nodes within these star blocks
Args:
    dag (DAGCircuit or DAGDependency): a dag on which star blocks should be determined.
    min_block_size (int): minimum number of two-qubit gates in a star block.

Returns:
    List[StarBlock]: a list of star blocks in the given dag
    Union[List[DAGOpNode], List[DAGDepNode]]: a list of operations specifying processing order
r}   )rz   )r   rF   rt   blocksrv   s        r   r   /StarPreRouting.determine_star_blocks_processing   s+     $(#C#C $D $
  ''r    c                   ^^ 0 n[        U5       H   u  pVUR                  5        H  nXTU'   M	     M"     TR                  5       n[        5       n	[	        [        [        TR                  5      5      5      n
U4S jnSn[        U5       Vs/ s H2  n[        UR                  5      S:  d  M  UR                  S:w  d  M0  UPM4     nn[        U5      S:  a  US   nOSn[        [        [        U5      5      5      S-   n[        U5       VVs0 s H-  u  nnUS[        U5      R                  [        U5      5       3_M/     snnmUU4S jnTR!                  US	9 GH  nUR#                  US5      nUGb  UU	;   a  M"  U	R%                  U5        UU   nUR&                  nUR(                  n[        U5      S
:X  aM  U HE  nUR+                  UR,                  U" UR                  U
TR                  5      UR.                  SS9  MG     M  SnSnU GH  n[        UR                  5      S:X  d  UR                  U:X  aD  UR+                  UR,                  U" UR                  U
TR                  5      UR.                  SS9  Mq  U(       aU  UcR  UnUR+                  UR,                  U" UR                  U
TR                  5      UR.                  SS9  UR                  nM  UR+                  UR,                  U" UR                  U
TR                  5      UR.                  SS9  UULa  [1        UR,                  [2        5      (       d  UR+                  [5        5       U" UR                  U
TR                  5      UR.                  SS9  TR7                  UR                  S   5      R8                  nTR7                  UR                  S   5      R8                  nU
U   U
U   sU
U'   U
U'   UR                  nGM     SnGM  UR+                  UR,                  U" UR                  U
TR                  5      UR.                  SS9  GM     X4$ s  snf s  snnf )a  Returns star blocks in dag and the processing order of nodes within these star blocks
Args:
    dag (DAGCircuit or DAGDependency): a dag on which star prerouting should be performed.
    blocks (List[StarBlock]): a list of star blocks in the given dag.
    processing_order (Union[List[DAGOpNode], List[DAGDepNode]]): a list of operations specifying
    processing order

Returns:
    new_dag: a dag specifying the pre-routed circuit
    qubit_mapping: the final qubit mapping after pre-routing
c                 6   >^^ [        UUU4S jU  5       5      $ )Nc              3   f   >#    U  H&  nTTTR                  U5      R                        v   M(     g 7fr   find_bitr   )r   r   rF   r   r   s     r   r   GStarPreRouting.star_preroute.<locals>._apply_mapping.<locals>.<genexpr>G  s,     ]W\ecll5.A.G.G HIW\s   .1)tuple)r(   r   r   rF   s    ``r   _apply_mapping4StarPreRouting.star_preroute.<locals>._apply_mappingF  s    ]W\]]]r    Tr&   barrierr   Nac                   > TR                  U S 5      nUb  U$ [        U [        [        45      (       a  [	        U R
                  5      $ SR                  U4S j[        R                  " U R                  U R                  5       5       5      $ )N,c              3   ^   >#    U  H"  nTR                  U5      R                  S  v   M$     g7f)04dNr   )r   qrF   s     r   r   HStarPreRouting.star_preroute.<locals>.tie_breaker_key.<locals>.<genexpr>`  s+      8_13<<?((-.8_s   *-)getr+   r   r   strwirejoin	itertoolschainr(   rm   )r,   rv   rF   processing_order_index_maps     r   tie_breaker_key5StarPreRouting.star_preroute.<locals>.tie_breaker_keyZ  sv    9==dDI+''$J 788499~%88 8A

TXT^T^8_  r    )keyrk   F)check)r   r#   copy_empty_liker*   listranger'   r   reversedr(   namer   r	   r   zfillinttopological_op_nodesr   addr   r   apply_operation_backro   rm   r+   r
   r   r   r   )r   rF   r   rv   node_to_block_idiblockr,   r   processed_block_idsr   r   is_first_starro   last_2q_gate
int_digitsr   r   block_idsequencecenter_node
inner_nodeswap_sourceprevindex_0index_1r   s    `                        @r   r   StarPreRouting.star_preroute1  s-    !&)HA))*& * * %%'!eU3szz?34	^  /0
0RXX" )+I)= 0 	 

 |q '?LL5%5!6781<
  ))9:&
:t Ac%j&&s:7899:&
"
	 ,,,AD'++D$7H#22#''1 x( ;;#llx=A%&.
44&MM*:+;+;]CJJW&,,"'	 5  '/ ""*JJ,,-2
8H8HD8P44&MM*:+;+;]CJJW&,,"'	 5  !$)<&144&MM*:+;+;]CJJW&,,"'	 5   *// 00"&z'7'7

S"((#	 1  &5jX_>`>`44$J*:+;+;]CJJW&,,"'	 5  #&,,z/?/?/B"C"I"I"%,,z/?/?/B"C"I"I)'2)'2 Gg.g0F
 &++DW #+X !& ,,GG"4::}cjjIJJ	 - O BZ %%O
&
s   
P>+P>=P>
4Q)r?   r>   )rk   )r3   r4   r5   r6   r7   r   rH   r   r   r   r   rD   rE   r\   r_   rg   rz   r   r   r   r   r   r   r   r   r   r8   __classcell__)rA   s   @r   r;   r;   V   s    :1$#y*/D)E F #`^,!L /1b@(]23(EH(	tId9otJ7G&G HH	I("@& @&r    r;   )r7   r   typingr   r   r   r   r   mathr   r	   qiskit.circuitr
   qiskit.circuit.libraryr   qiskit.dagcircuitr   r   r   r   r   r   qiskit.transpiler.basepassesr   qiskit.transpiler.layoutr   r   r;   r9   r    r   <module>r      sI    C  9 9  " +  < +3 3l[&' [&r    