
    z	iC                        S r SSKJr  SSKJrJr  SSKJrJrJ	r	J
r
  SSKJr  SSKJr  SSKJr  SSKJr  SS	KJr  S
SKJr   " S S5      r " S S5      rSS jr " S S5      rg)zVarious ways to divide a DAG into blocks of nodes, to split blocks of nodes
into smaller sub-blocks, and to consolidate blocks.    )annotations)IterableCallable)QuantumCircuitCircuitInstructionClassicalRegisterBit)condition_resources)
DAGCircuit)DAGDependency)	DAGOpNode)
DAGDepNode   )DAGCircuitErrorc                  n    \ rS rSrSrSS jrS rSS jrS rS r	S r
      SS	 jr     SS jrSrg
)BlockCollector   a  This class implements various strategies of dividing a DAG (direct acyclic graph)
into blocks of nodes that satisfy certain criteria. It works both with the
:class:`~qiskit.dagcircuit.DAGCircuit` and
:class:`~qiskit.dagcircuit.DAGDependency` representations of a DAG, where
DagDependency takes into account commutativity between nodes.

Collecting nodes from DAGDependency generally leads to more optimal results, but is
slower, as it requires to construct a DAGDependency beforehand. Thus, DAGCircuit should
be used with lower transpiler settings, and DAGDependency should be used with higher
transpiler settings.

In general, there are multiple ways to collect maximal blocks. The approaches used
here are of the form 'starting from the input nodes of a DAG, greedily collect
the largest block of nodes that match certain criteria'. For additional details,
see https://github.com/Qiskit/qiskit-terra/issues/5775.
c                    Xl         SU l        SU l        SU l        [	        U[
        5      (       a  SU l        g[	        U[        5      (       a  SU l        g[        S5      e)
Args:
    dag (Union[DAGCircuit, DAGDependency]): The input DAG.

Raises:
    DAGCircuitError: the input object is not a DAG.
NFTz
not a DAG.)	dag_pending_nodes
_in_degree_collect_from_back
isinstancer   is_dag_dependencyr   r   selfr   s     Z/home/james-whalen/.local/lib/python3.13/site-packages/qiskit/dagcircuit/collect_blocks.py__init__BlockCollector.__init__/   sW     CGDH"'c:&&%*D"]++%)D" ",//    c                    / U l         0 U l        U R                  5        HN  n[        U R	                  U5      5      nX R                  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_nodeslen_direct_predsappend)r   nodedegs      r   _setup_in_degrees BlockCollector._setup_in_degreesF   s`     !NN$Dd((./C$'OOD!ax##**40	 %r!   c                    U R                   (       d  U R                  R                  5       $ U R                  R                  5       $ )zReturns DAG nodes.)r   r   op_nodes	get_nodesr   s    r   r#   BlockCollector._op_nodesX   s1    %%88$$&&88%%''r!   c                   U R                   (       d  U R                  (       aC  U R                  R                  U5       Vs/ s H  n[	        U[
        5      (       d  M  UPM     sn$ U R                  R                  U5       Vs/ s H  n[	        U[
        5      (       d  M  UPM     sn$ U R                  (       aO  U R                  R                  UR                  5       Vs/ s H  nU R                  R                  U5      PM      sn$ U R                  R                  UR                  5       Vs/ s H  nU R                  R                  U5      PM      sn$ s  snf s  snf 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   r   
successorsr   r   predecessorsdirect_successorsnode_idget_nodedirect_predecessors)r   r'   predpred_ids       r   r%   BlockCollector._direct_preds_   s   
 %%&&)-)<)<T)Bb)BjQUW`Fa)Bbb)-)>)>t)Dd)D
SWYbHc)Ddd&& $(88#=#=dll#K#K HH%%g.#K  $(88#?#?#M#M HH%%g.#M  cd
$    EEE E!%E"0%E'c                   U R                   (       d  U R                  (       aC  U R                  R                  U5       Vs/ s H  n[	        U[
        5      (       d  M  UPM     sn$ U R                  R                  U5       Vs/ s H  n[	        U[
        5      (       d  M  UPM     sn$ U R                  (       aO  U R                  R                  UR                  5       Vs/ s H  nU R                  R                  U5      PM      sn$ U R                  R                  UR                  5       Vs/ s H  nU R                  R                  U5      PM      sn$ s  snf s  snf 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   r   r2   r   r   r1   r6   r4   r5   r3   )r   r'   succsucc_ids       r   _direct_succsBlockCollector._direct_succsu   s   
 %%&&)-)>)>t)Dd)D
SWYbHc)Ddd)-)<)<T)Bb)BjQUW`Fa)Bbb&& $(88#?#?#M#M HH%%g.#M  $(88#=#=dll#K#K HH%%g.#K  eb
r:   c                2    [        U R                  5      S:  $ )z5Returns whether there are uncollected (pending) nodesr   )r$   r   r.   s    r   _have_uncollected_nodes&BlockCollector._have_uncollected_nodes   s    4&&'!++r!   c                F   / n[        5       nU R                  n/ U l        U(       a  UR                  5       nUb:  UR                  5       nUR	                  UR
                  5        [        U5      U:*  nO[        5       nSnU" U5      (       ap  U(       ai  UR                  U5        UnU R                  U5       H@  n	U R                  U	==   S-  ss'   U R                  U	   S:X  d  M/  UR                  U	5        MB     OU R                  R                  U5        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.
Tr   r   )
setr   popcopyupdateqargsr$   r&   r>   r   )
r   	filter_fnmax_block_widthcurrent_blockcurrent_block_qargsunprocessed_pending_nodesr'   	new_qargswidth_within_budgetsucs
             r   collect_matching_block%BlockCollector.collect_matching_block   s
    !e$($7$7!  (,002D*/446	  ,&))n&G#E	&*##6$$T*&/#  --d3COOC(A-(s+q0188= 4
 ##**40- ('0 r!   Nc                  ^ U4S jnXPl         U R                  5         / nU R                  5       (       aO  U R                  USS9  U R                  TUS9n	U	(       a  UR	                  U	5        U R                  5       (       a  MO  U(       a'  / n
U H  nU
R                  [        U5      5        M     U
nU(       a5  / n
U H+  nU
R                  [        5       R                  U5      5        M-     U
nU R                   (       a  USSS2    Vs/ s H
  oSSS2   PM     nnU Vs/ s H  n[        U5      U:  d  M  UPM     nnU$ s  snf s  snf )ao  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.

After the blocks are collected, they can be optionally refined. The option
``split_blocks`` allows to split collected blocks into sub-blocks over disjoint
qubit subsets. The option ``split_layers`` allows to split collected blocks
into layers of non-overlapping instructions. The option ``min_block_size``
specifies the minimum number of gates in the block for the block to be collected.
The option ``max_block_width`` specificies the maximum number of qubits over
which a block can be defined.

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                   > T" U 5      (       + $ )z"Returns the opposite of filter_fn. )r'   rI   s    r   not_filter_fnABlockCollector.collect_all_matching_blocks.<locals>.not_filter_fn   s     &&r!   N)rJ   )
r   r)   rA   rQ   r&   extendsplit_block_into_layersBlockSplitterrunr$   )r   rI   split_blocksmin_block_sizesplit_layerscollect_from_backrJ   rV   matching_blocksmatching_block
tmp_blocksblocks    `          r   collect_all_matching_blocks*BlockCollector.collect_all_matching_blocks   sB   <	'
 #4  ?A**,,''t'L!88Tc8dN&&~6	 **,, J(!!"9%"@A )(O J(!!-/"5"5e"<= )(O ""8G"8MN8MuTrT{8MON />^oUU~A]5o^ O _s   	E E7E)r   r   r   r   r   )r   zDAGCircuit | DAGDependency)returnz Iterable[DAGOpNode | DAGDepNode])rI   r   rJ   z
int | Nonerg   list[DAGOpNode | DAGDepNode])T   FFN)__name__
__module____qualname____firstlineno____doc__r   r)   r#   r%   r>   rA   rQ   re   __static_attributes__rU   r!   r   r   r      sZ    "0.1$(,,,-!-4>-	%-d Fr!   r   c                  0    \ rS rSrSrS rS rS rS rSr	g)	r[   i  zSplits a block of nodes into sub-blocks over disjoint qubits.
The implementation is based on the Disjoint Set Union data structure.c                     0 U l         0 U l        g N)leadergroupr.   s    r   r   BlockSplitter.__init__  s    
r!   c                    XR                   ;  a  XR                   U'   / U R                  U'   U$ U R                   U   U:X  a  U$ U R                  U R                   U   5      U R                   U'   U R                   U   $ )zFind in DSU.)rs   rt   find_leader)r   indexs     r   rw   BlockSplitter.find_leader  sr    #!&KK "DJJuL;;u&L!--dkk%.@AE{{5!!r!   c                h   U R                  U5      nU R                  U5      nX4:X  a  g[        U R                  U   5      [        U R                  U   5      :  a  XCpCX0R                  U'   U R                  U   R	                  U R                  U   5        U R                  U   R                  5         g)zUnion in DSU.N)rw   r$   rt   rs   rY   clear)r   index1index2leader1leader2s        r   union_leadersBlockSplitter.union_leaders  s    ""6*""6*tzz'"#c$**W*=&>>&W&G

7""4::g#67

7!!#r!   c                t   U Hg  nUR                   nU(       d  M  US   nUSS  H  nU R                  XE5        M     U R                  U R                  U5         R	                  U5        Mi     / nU R
                  R                  5        H*  u  pWXW:X  d  M  UR	                  U R                  U   5        M,     U$ )z;Splits block of nodes into sub-blocks over disjoint qubits.r   r   N)rH   r   rt   rw   r&   rs   items)r   rd   r'   indicesfirstrx   blocksitems           r   r\   BlockSplitter.run'  s    DjjGAJE ""50 %JJt''./66t<  ;;,,.KE}djj/0 / r!   )rt   rs   N)
rj   rk   rl   rm   rn   r   rw   r   r\   ro   rU   r!   r   r[   r[     s    M	"$r!   r[   c                  ^ 0 m/ nU  H  n[        UR                  5      nUR                  UR                  5        [	        UR
                  SS5      nUb$  UR                  [        U5      R                  5        [        U4S jU 5       5      n[        U5      U::  a"  UR                  / 5        [        U5      U::  a  M"  U H  nUS-   TU'   M     X   R                  U5        M     U$ )zwSplits a block of nodes into sub-blocks of non-overlapping instructions
(or, in other words, into depth-1 sub-blocks).

_conditionNc              3  H   >#    U  H  nTR                  US 5      v   M     g7f)r   N)get).0bit
bit_depthss     r   	<genexpr>*split_block_into_layers.<locals>.<genexpr>I  s     C(3
sA..(s   "r   )rD   rH   rG   cargsgetattropr
   clbitsmaxr$   r&   )rd   layersr'   cur_bitscond	cur_depthr   r   s          @r   rZ   rZ   :  s     "$J13Ftzz?

#twwd3OO/5<<=C(CC	&kY&MM" &kY& C'!mJsO   &   Mr!   c                  $    \ rS rSrSrS rS rSrg)BlockCollapseriT  zThis class implements various strategies of consolidating blocks of nodes
in a DAG (direct acyclic graph). It works both with
the :class:`~qiskit.dagcircuit.DAGCircuit`
and :class:`~qiskit.dagcircuit.DAGDependency` DAG representations.
c                    Xl         g)r   Nr   r   s     r   r   BlockCollapser.__init__[  s	     r!   c           	       ^ [        U R                  R                  5       VVs0 s H  u  p4XC_M	     snnmTR                  [        U R                  R                  5       VVs0 s H  u  p4XC_M	     snn5        U GH  n[        5       n[        5       n[        5       nU H  n	UR                  U	R                  5        UR                  U	R                  5        [        U	R                  SS5      n
U
c  MU  UR                  [        U
5      R                  5        [        U
S   [        5      (       d  M  UR                  U
S   5        M     [        UU4S jS9n[        UU4S jS9n[        X5      nU H  nUR!                  U5        M     [        U5       VVs0 s H	  u  nnUU_M     nnnUR                  [        U5       VVs0 s H	  u  nnUU_M     snn5        U H=  n	UR#                  [%        U	R                  U	R                  U	R                  5      5        M?     U" U5      nU R                  R'                  UUUSS9  GM     U R                  $ s  snnf s  snnf s  snnf s  snnf )	zFor each block, constructs a quantum circuit containing instructions in the block,
then uses collapse_fn to collapse this circuit into a single operation.
r   Nr   c                   > TU    $ rr   rU   xglobal_index_maps    r   <lambda>6BlockCollapser.collapse_to_operation.<locals>.<lambda>      =Ma=Pr!   )keyc                   > TU    $ rr   rU   r   s    r   r   r     r   r!   F)cycle_check)	enumerater   qubitsrG   r   rD   rH   r   r   r   r
   r   r   addsortedr   add_registerr&   r   replace_block_with_op)r   r   collapse_fnidxwirerd   
cur_qubits
cur_clbits
cur_clregsr'   r   sorted_qubitssorted_clbitsqcregixqbwire_pos_mapr   r   s                      @r   collapse_to_operation$BlockCollapser.collapse_to_operationf  s    8A7QR7Q)#DI7QRIdhhoo<V W<Vys<V WXE JJ J!!$**-!!$**-twwd;#%%&9$&?&F&FG!$q'+<=="tAw/  #:3PQM":3PQM=B "$ " 2;=1IJ1Ivr2BF1ILJi6N O6NFBR6N OP		,TWWdjj$**MN  RB HH**5"lPU*VS T xx[ S W@ K Os   I#I
I!I'r   N)rj   rk   rl   rm   rn   r   r   ro   rU   r!   r   r   r   T  s    	1r!   r   N)rd   rh   )rn   
__future__r   collections.abcr   r   qiskit.circuitr   r   r   r	   qiskit.circuit.controlflowr
   qiskit.dagcircuit.dagcircuitr   qiskit.dagcircuit.dagdependencyr   qiskit.dagcircuit.dagnoder   qiskit.dagcircuit.dagdepnoder   
exceptionsr   r   r[   rZ   r   rU   r!   r   <module>r      sQ   7 " . U U : 3 9 / 3 'g gT0 0f4C Cr!   