
    z	i?                         S r SSKJr  SSK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JrJr  SS	KJr  \R,                  R.                   " S
 S\5      5       rg)z*Pass for Hoare logic circuit optimization.    )TransformationPass)QuantumRegisterControlledGateGate)
DAGCircuit)UnitaryGate)matrix_equal)CircuitError)CZGateCU1GateMCU1Gate)	optionalsc                      ^  \ rS rSrSrSU 4S jjrS rS rS rS r	S r
S	 rS
 rSS jrS rS rSS jrS rS rS rSrU =r$ )HoareOptimizer   zThis is a transpiler pass using Hoare logic circuit optimization.
The inner workings of this are detailed in:
https://arxiv.org/abs/1810.00375
c                    > SSK Jn  [        TU ]  5         SU l        SU l        SU l        SU l        SU l        Xl	        g)z
Args:
    size (int): size of gate cache, in number of gates
Raises:
    MissingOptionalLibraryError: if unable to import z3 solver
   )_gate_extensionN)
 r   super__init__solver	variablesgatenum	gatecachevarnumsize)selfr   r   	__class__s      i/home/james-whalen/.local/lib/python3.13/site-packages/qiskit/transpiler/passes/optimization/hoare_opt.pyr   HoareOptimizer.__init__   s;     	&	    c                     SSK nS[        U5      -   S-   [        U R                  U   5      -   nUR                  U5      nU R                  U==   S-  ss'   U R                  U   R                  U5        U$ )a8  After each gate generate a new unique variable name for each of the
    qubits, using scheme: 'q[id]_[gatenum]', e.g. q1_0 -> q1_1 -> q1_2,
                                                  q2_0 -> q2_1
Args:
    qubit (Qubit): qubit to generate new variable for
Returns:
    BoolRef: z3 variable of qubit state
r   Nq_r   )z3strr   Boolr   append)r   qubitr&   varnamevars        r    _gen_variableHoareOptimizer._gen_variable1   sk     	E
"S(3t||E/B+CCgggUq u$$S)
r"   c                     SSK nUR                   Hz  nSU R                  U'   / U R                  U'   / U R                  U'   0 U R
                  U'   U R                  U5      nU R                  R                  UR                  U5      5        M|     g)z}create boolean variables for each qubit and apply qb == 0 condition
Args:
    dag (DAGCircuit): input DAG to get qubits from
r   N)
r&   qubitsr   r   r   r   r-   r   addNot)r   dagr&   qbtxs        r    _initializeHoareOptimizer._initializeB   su    
 	::C !DLL"$DNN3"$DNN3!DKK""3'AKKOOBFF1I& r"   c           	         SSK n/ nU H#  nUR                  U R                  U5      5        M%      U R                  R	                  UR                  X!R                  " XF-   6 5      5        [        U5       HD  u  pU R                  R	                  UR                  UR                  U5      Xh   U	:H  5      5        MF     g! [         a     N`f = f)a  create boolean variables for each qubit the gate is applied to
    and apply the relevant post conditions.
    a gate rotating out of the z-basis will not have any valid
    post-conditions, in which case the qubit state is unknown
Args:
    gate (Gate): gate to inspect
    ctrl_ones (BoolRef): z3 condition asserting all control qubits to 1
    trgtqb (list((QuantumRegister, int))): list of target qubits
    trgtvar (list(BoolRef)): z3 variables corresponding to latest state
                             of target qubits
r   N)
r&   r)   r-   r   r1   Implies_postconditionsAttributeError	enumerater2   )
r   gate	ctrl_onestrgtqbtrgtvarr&   new_varsr4   itvars
             r    _add_postconditions"HoareOptimizer._add_postconditionsQ   s     	COOD..s34 	KKOOBJJy2F2FI[2]^_ !)GAKKOOBJJrvvi'8(+:MNO *  		s   9B> >
C
Cc           	      H   SSK nSnU R                  R                  5          UR                  " U6 n[	        U[
        5      (       Ga  U(       Ga  [        U5      S:X  a  U R                  R                  UR                  UR                  X#S   5      5      5        U R                  R                  5       UR                  :H  nU R                  R                  5         U R                  R                  5         U R                  R                  UR                  X#S   5      5        U R                  R                  5       UR                  :H  nU=(       d    UnOaU R                  R                  UR                  X$R                  U5      5      5        U R                  R                  5       UR                  :H  n U R                  R                  5         U$ ! [         aE    U R                  R                  U5        U R                  R                  5       UR                  :H  n Njf = f)a=  use z3 sat solver to determine triviality of gate
Args:
    gate (Gate): gate to inspect
    ctrl_ones (BoolRef): z3 condition asserting all control qubits to 1
    trgtvar (list(BoolRef)): z3 variables corresponding to latest state
                             of target qubits
Returns:
    bool: if gate is trivial
r   NFr   )r&   r   push_trivial_if
isinstanceboollenr1   r2   Andcheckunsatpopr;   )	r   r=   r>   r@   r&   trivial	triv_condsol1sol2s	            r    
_test_gateHoareOptimizer._test_gatek   s{    		:(('2I
 )T**W!2KKOOBFF266)QZ+H$IJ;;,,."((:DKKOO%KK$$&KKOOBFF9aj$AB;;,,."((:D"ldGy&&2C DE++++-9%  	6KKOOI&kk'')RXX5G	6s   G AH! H!c                 j   Sn[        UR                  5      n[        5       nUR                  U5        [	        [        [        U5      UR                  5      5      n[        U[        5      (       a  U R                  U5      n[        U[        [        [        45      (       aT  U(       dM  US   S:  aD  US   S-
  US'   USUS    X'S   S-   S -   U-   nU R                  U5      nU(       d  US   S:  a  MD  U(       a0  U V	s/ s H  oU	   PM	     n
n	UR                  UR                  U
5        XFU4$ s  sn	f )aM  use z3 sat solver to determine if all control qubits are in 1 state,
     and if so replace the Controlled - U by U.
Args:
    gate (Gate): gate to inspect
    ctrlvar (list(BoolRef)): z3 variables corresponding to latest state
                             of control qubits
    trgtvar (list(BoolRef)): z3 variables corresponding to latest state
                             of target qubits
Returns:
    Tuple(bool, DAGCircuit, List)::
      * bool:if controlled gate can be replaced.
      * DAGCircuit: with U applied to the target qubits.
      * List: with indices of target qubits.
Fr   r   N)r   
num_qubitsr   add_qreglistrangerK   rI   r   _check_removalr   r   r   apply_operation_back	base_gate)r   r=   ctrlvarr@   removeqargr3   qb	ctrl_varsqir0   s              r    _remove_controlHoareOptimizer._remove_control   s    t/lT%Gdoo67dN++((1F dVWh788A1	1#Gbe,w!uqy{/CCgM	,,Y7 A
 )+,22hF,$$T^^V<B -s    D0c                 6   SS K nUR                  " U6 nU R                  R                  5         U R                  R	                  UR                  U5      5        U R                  R                  5       UR                  :H  nU R                  R                  5         U$ )Nr   )	r&   rL   r   rG   r1   r2   rM   rN   rO   )r   r^   r&   r>   r_   s        r    r[   HoareOptimizer._check_removal   sj    FFG$	y)*""$0r"   c                    SSK n[        UR                  5       5      nU GH  nUR                  nU R	                  U5      u  pgpUR
                  " U6 n
U R                  XWU	5      u  pnU(       ab  UR                  XL5      n[        [        UR                  5       5      5      nUR                  nU R	                  U5      u  pgpUR
                  " U6 n
U R                  XZU	5      nU(       a  UR                  U5        OU R                  S:  a  UR                   HC  nU R                  U   R!                  U5        U R"                  U   S-
  U R$                  U   U'   ME     UR                   H<  n['        U R                  U   5      U R                  :  d  M+  U R)                  X5        M>     U R+                  XZX5        GM     g)a  traverse DAG in topological order
    for each gate check: if any control is 0, or
                         if triviality conditions are satisfied
    if yes remove gate from dag
    apply postconditions of gate
Args:
    dag (DAGCircuit): input DAG to optimize in place
r   Nr   )r&   rY   topological_op_nodesop_seperate_ctrl_trgtrL   rd   substitute_node_with_dagnextitervaluesrT   remove_op_noder   qargsr   r)   r   r   rK   _multigate_optrD   )r   r3   r&   nodesnoder=   r%   r^   r?   r@   r>   remove_ctrlnew_dagmapped_nodesrP   r4   s                   r    _traverse_dagHoareOptimizer._traverse_dag   s~    	 S--/0D77D*.*B*B4*H'A(I&*&:&:4'&R#K!  #;;DJD!4!4!678ww.2.F.Ft.L+FFFG,	oodw?G""4(Q::CNN3'..t4-1\\#->-BDKK$T* &  ::C4>>#./499<++C5 & $$TfF= r"   c                 Z   SnU[        U R                  U   5      S-
  :  Gaq  SnU R                  U   U   nU R                  U   US-      nU R                  U5      S   nU R                  U5      S   n	US-  nX:w  a  M}   U H=  n
U R                  U
   R                  U5      nU R                  U
   US-      ULd  M;  SnM?     Xg/nU(       a  U R                  U5      (       a  U R                  U5      (       aw  US-  nU Hl  nUR                  U5        Ub$  U R                  U   R                  U5      U:  d  M;  UR                   H!  n
U R                  U
   R                  U5        M#     Mn     U[        U R                  U   5      S-
  :  a  GMp  gg! [        [
        4 a     GM  f = f)a  remove gates that have the same set of target qubits, follow each
    other immediately on these target qubits, and combine to the
    identity (consider sequences of length 2 for now)
Args:
    dag (DAGCircuit): the directed acyclic graph to run on.
    qubit (Qubit): qubit cache to inspect
    from_idx (int): only gates whose indexes in the cache are larger
                    than this value can be removed
r   r   T   FN)rK   r   rk   index
IndexError
ValueError_is_identity_seq_as_onerp   rq   r_   )r   r3   r*   from_idxrB   r)   node1node2trgtqb1trgtqb2r4   idxseqrt   s                 r    _remove_successive_identity*HoareOptimizer._remove_successive_identity   s    #dnnU+,q00FNN5)!,ENN5)!a%0E..u5a8G..u5a8GFA!"C..-33E:C~~c*3735@!& # .C$++C00T5E5Ec5J5JQD&&t,'4>>%+@+F+Ft+Lx+W#'::C NN3/66t< $.  + #dnnU+,q000 
+ s   9F >F F*)F*c                 `   [        U5      S:X  d   e US   R                  US   R                  R                  5       p2UR                  UR                  pTUR
                  UR
                  pv[        U[        5      (       a  UR                  nUR                  n[        U[        5      (       a  UR                  nUR                  nU[        L a  U[        L a  Xg:H  =(       a    U=(       a    U$ U[        L a  U[        L a  [        US   US   SS9$ X#:H  =(       a    XE:H  $ ! [         a     gf = f)zdetermine whether the sequence of gates combines to the identity
    (consider sequences of length 2 for now)
Args:
    sequence (list(DAGOpNode)): gate sequence to inspect
Returns:
    bool: if gate sequence combines to identity
r{   r   r   FT)ignore_phase)rK   rj   inverser
   params
definitionrI   r   r]   
base_classr   r   r	   )r   sequencegate1gate2par1par2def1def2s           r    r   HoareOptimizer._is_identity  s    8}!!!	#A;>>8A;>>+A+A+C5 \\5<<d%%u'7'7de^,,OOE  e^,,OOE  
 D=Ud]<1D1T1k!e{&:QatDD~.$,.)  		s   +D   
D-,D-c                    SSK JnJnJn  SSK n[	        U5      S:X  d   eU R                  US   5      S   nU R                  US   5      S   nU R                  R                  5         U R                  R                  U" U" U" U6 U" U" U6 5      5      U" U" U" U6 5      U" U6 5      5      5        U R                  R                  5       UR                  :H  nU R                  R                  5         U$ )aD  use z3 solver to determine if the gates in the sequence are either
    all executed or none of them are executed, based on control qubits
    (consider sequences of length 2 for now)
Args:
    sequence (list(DAGOpNode)): gate sequence to inspect
Returns:
    bool: if gate sequence is only executed completely or not at all
r   )OrrL   r2   Nr{   r   )r&   r   rL   r2   rK   rk   r   rG   r1   rM   rN   rO   )	r   r   r   rL   r2   r&   ctrlvar1ctrlvar2ress	            r    r   HoareOptimizer._seq_as_one9  s     	$#8}!!!++HQK8;++HQK8;s3>3sH~#67Sh=PRUW_R`9ab	
 kk!RXX-
r"   c           	      n   U R                   U   (       d  gU R                  XU5        [        U R                   U   5      U R                  :  a  Uc  gUc1  Sn[	        5       nUR                  U5        U R                   U   S   /nOU R                   U   USS2   nU Hn  nUR                   Vs/ s H  owU;  d  M
  UPM     nnUR                  U5        U H1  n	U R                   U	   R                  U5      n
U R                  XXS9  M3     Mp     U R                   U   US-   S U R                   U'   gs  snf )aG  
Args:
    dag (DAGCircuit): the directed acyclic graph to run on.
    qubit (Qubit): qubit whose gate cache is to be optimized
    max_idx (int): a value indicates a recursive call, optimize
                   and remove gates up to this point in the cache
    dnt_rec (list(int)): don't recurse on these qubit caches (again)
Nr   )max_idxdnt_recr   )
r   r   rK   r   setr1   rq   updater|   rr   )r   r3   r*   r   r   	gates_tbrrt   r5   new_qbr4   r   s              r    rr   HoareOptimizer._multigate_optR  s'    ~~e$ 	((W=t~~e$%		1go_GeGKK.q12I u-gkrk:ID!%@A/?aF@NN6"nnS)//5##Cc#K 	  !%u 5gkm Du As   .	D2;D2c                 <   UR                   n[        U[        5      (       a  UR                  nOSnUR                  SU nUR                  US n U Vs/ s H$  o`R
                  U   U R                  U   U      PM&     nnU Vs/ s H$  o`R
                  U   U R                  U   U      PM&     nnXGXX4$ s  snf s  snf ! [         aQ    U Vs/ s H  o`R
                  U   S   PM     Os  snf nnU Vs/ s H  o`R
                  U   S   PM     Os  snf nn Nhf = f)zaGet the target qubits and control qubits if available,
as well as their respective z3 variables.
r   Nr   )rj   rI   r   num_ctrl_qubitsrq   r   r   KeyError)	r   rt   r=   numctrlctrlqbr?   ra   r^   r@   s	            r    rk   "HoareOptimizer._seperate_ctrl_trgtx  s    wwdN++**GGHW%GH%	@KQR6R~~b)$++b/$*?@6GRKQR6R~~b)$++b/$*?@6GR 11 SR 	@8>?"~~b)"-?G?8>?"~~b)"-?G?G	@sH   C  +B6?C  +B;0C  6
C   DC*)D5DDDc                 n    SSK nUR                  5       U l        0 U l        0 U l        0 U l        0 U l        g)zEReset HoareOptimize internal state,
so it can be run multiple times.
r   N)r&   Solverr   r   r   r   r   )r   r&   s     r    _resetHoareOptimizer._reset  s0     	iikr"   c                     U R                  5         U R                  U5        U R                  U5        U R                  S:  a$  UR                   H  nU R                  X5        M     U$ )zm
Args:
    dag (DAGCircuit): the directed acyclic graph to run on.
Returns:
    DAGCircuit: Transformed DAG.
r   )r   r6   rx   r   r0   rr   )r   r3   r4   s      r    runHoareOptimizer.run  sU     	399q=zz##C- "
r"   )r   r   r   r   r   r   )
   )N)NN)__name__
__module____qualname____firstlineno____doc__r   r-   r6   rD   rT   rd   r[   rx   r   r   r   rr   rk   r   r   __static_attributes____classcell__)r   s   @r    r   r      sa    
$"'P4#J%N
,G\$=L /D2$EL2&
 r"   r   N)r   qiskit.transpiler.basepassesr   qiskit.circuitr   r   r   qiskit.dagcircuitr   qiskit.circuit.libraryr   (qiskit.quantum_info.operators.predicatesr	   qiskit.circuit.exceptionsr
   %qiskit.circuit.library.standard_gatesr   r   r   qiskit.utilsr   
_optionalsHAS_Z3require_in_instancer    r"   r    <module>r      sS    1 ; @ @ ( . A 2 K K 0 &&K' K 'Kr"   