
    zbjB^                     v    S SK Jr  S SK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  \" \5      r " S S\5      rg)	    )	getLoggerN)Fusion)FusionUtils)	NodeProtohelpernumpy_helper)	OnnxModelc                   &  ^  \ rS rSrSrS\4U 4S jjrS\S\S-  4S jr	S\S\
S-  4S	 jrS
\S\S\
4S jrS\S\
4S jrS
\S\
4S jrS\S\S\
4S jrS\S\S\4S jrS\S\4S jrS\S\4S jr S!S\S\S\S\S\
S\S-  S\4S jjrS rS rU =r$ )"FusionMultiHeadAttentionDiT   uQ  
Fuse MultiHeadAttention for Diffusion Transformer (DiT) models like F5-TTS.

Recognizes attention patterns where Q, K, V are pre-computed (e.g., after RoPE)
and K is pre-transposed, with optional Cast nodes for mixed-precision (FP16) inference
and a custom scalar scale factor before Softmax.

Supported patterns (anchored at Softmax):

    MatMul(Q, K^T) → [Cast(FP16→FP32)] → Mul(scale) → Softmax → [Cast(FP32→FP16)] → MatMul(attn, V)
        → Transpose(perm=0,2,1,3) → Reshape → output

Where:
    - Q is in BNSH format (post-RoPE or post-projection)
    - K is pre-transposed to BNHS format (via Transpose(perm=0,1,3,2) or natively)
    - V is in BNSH format
    - Scale is an arbitrary scalar constant (e.g., 100.0 for DiT, or 1/sqrt(d_k))
    - Cast nodes are optional (present in FP16 models for FP32 Softmax stability)
modelc                 &   > [         TU ]  USS/S9  g )NMultiHeadAttentionSoftmax)fused_op_typesearch_op_types)super__init__)selfr   	__class__s     ڣ/tmp/claude-0/-home-danvics-docker-quiz/c1e0577a-e42c-4a3d-b1ea-3edd61103a4e/scratchpad/stt/lib/python3.13/site-packages/onnxruntime/transformers/fusion_mha_dit.pyr   $FusionMultiHeadAttentionDiT.__init__%   s    .BU^T_`    mul_nodereturnNc                 j   [        S5       H  nU R                  R                  UR                  U   5      nUc  M0  [	        U[
        R                  5      (       a-  UR                  S:X  a  [        UR                  5       5      s  $ M|  [	        U[        [        45      (       d  M  [        U5      s  $    g)zExtract the scalar scale constant from a Mul node.

The scale can be in either input[0] or input[1].

Returns:
    float: the scale value, or None if not found.
   N   )ranger   get_constant_valueinput
isinstancenpndarraysizefloatitemint)r   r   ivalues       r   get_scale_from_mul.FusionMultiHeadAttentionDiT.get_scale_from_mul(   s     qAJJ11(..2CDE eRZZ00zzQ$UZZ\22 'U|44 <'  r   c                    SS/n[        S5       H  nU R                  R                  UR                  U   5      nUc  M0  [	        U[
        R                  5      (       a  UR                  S:H  X#'   Mb  [	        U[        [        45      (       d  M  SX#'   M     US   (       a  US   (       d  gUS   (       a  US   (       d  gg)zDetermine which input of Mul is the data (non-constant) input.

Returns:
    int: the input index (0 or 1) of the data input, or None if ambiguous.
Fr   Nr   Tr   )
r   r   r    r!   r"   r#   r$   r%   r(   r&   )r   r   is_scalar_constantr)   r*   s        r   get_data_input_of_mul1FusionMultiHeadAttentionDiT.get_data_input_of_mul:   s     $U^qAJJ11(..2CDE eRZZ00,1JJ!O&)U|44,0&)  a );A)>a );A)>r   tensor_nameoutput_name_to_nodec                 x   X;  a  gX!   nSnSnUb  XE:  a  UR                   S:X  a^  [        R                  " US5      nU/ SQ:X  a?  UR                  S   U;   a*  X#R                  S      nU R	                  U5      nUS:  a  U$  gUR                  S   U;   a  X#R                  S      nO gUS-  nUb  XE:  a  M  g)uW  Detect num_heads by walking upstream from a BNSH tensor looking for a Reshape node.

Typical upstream patterns:
    Reshape(shape=[B, S, N, H]) → Transpose(perm=0,2,1,3) → ... → tensor_BNSH
    Reshape(shape=Concat(..., N, H)) → Transpose(perm=0,2,1,3) → ... → tensor_BNSH

Returns:
    int: number of heads, or 0 if not detected.
r   
   	Transposepermr   r   r      r   )op_typer	   get_node_attributer!   _get_num_heads_from_reshape)	r   r1   r2   currentdepth	max_depthr6   parent	num_headss	            r   detect_num_heads,FusionMultiHeadAttentionDiT.detect_num_headsO   s     1 &2	!e&7+- 33GVD<'}}Q'+>>!4]]15E!F$($D$DV$L	$q=#,,  }}Q#66-mmA.>?  QJE% !e&7( r   nodec                    UR                   S:w  a  g[        UR                  5      S:  ah  U R                  R	                  UR                  S   5      nUb=  [        U[        R                  5      (       a  UR                  S:X  a  [        US   5      $ [        UR                  5      S:  Ga.  UR                  S   U R                  R                  S5       Vs1 s H  o3R                  S   iM     sn;   a  U R                  R                  S5       Vs/ s H&  o3R                  S   UR                  S   :X  d  M$  UPM(     nnU(       a  [        US   R                  5      S:X  av  U R                  R	                  US   R                  S   5      nUbH  [        U[        R                  5      (       a)  UR                  S:X  a  [        UR                  5       5      $ gs  snf s  snf )zExtract num_heads from a Reshape node's shape parameter.

Handles:
    - Static shape constant: [B, S, num_heads, head_dim]
    - Concat-based shape: Concat([B_dim], [S_dim], [num_heads], [head_dim])
Reshaper   r   r      Concat)r9   lenr!   r   r    r"   r#   r$   r%   r(   get_nodes_by_op_typeoutputr'   )r   rC   shape_valuenconcat_nodesr*   s         r   r;   7FusionMultiHeadAttentionDiT._get_num_heads_from_reshapew   sz    <<9$ tzz?a**77

1FK&:k2::+N+NS^ScScghSh;q>** tzz?aDJJqM4::KjKjksKt5uKtahhqkKt5u$u'+zz'F'Fx'Pq'P!T\T\]^T_cgcmcmnocpTpA'PLqLO$9$9 :a ?

55l1o6K6KA6NO$!%44q"5::<00 6vqs   G#G!:G!c                    U R                   R                   R                  R                   H  nUR                  U:X  d  M  UR                  R
                  R                  nU(       d  M>  [        UR                  5      S:X  d  MY  UR                  S   nUR                  S:  d  Mz  UR                  s  $    U R                   R                   R                  R                   H  nUR                  U:X  d  M  UR                  R
                  R                  nU(       d  M>  [        UR                  5      S:X  d  MY  UR                  S   nUR                  S:  d  Mz  UR                  s  $    g)zTry to detect num_heads from a BNSH tensor's shape in graph inputs or value_info.

For BNSH tensors, the N dimension (index 1) is num_heads.
rF   r   r   )r   graphr!   nametypetensor_typeshaperH   dim	dim_value
value_info)r   r1   inprT   dim_nvis         r   !detect_num_heads_from_input_shape=FusionMultiHeadAttentionDiT.detect_num_heads_from_input_shape   s     ::##))//Cxx;&,,225S^q0!IIaLE*$. 0 **""((33Bww+%++115S^q0!IIaLE*$. 4 r   reshape_outtranspose_outc                 >    U R                  UR                  S   5      $ )zTry to detect num_heads from the output Transpose's input shape.

The Transpose converts BNSH -> BSNH. The N dimension gives us num_heads.
r   )r[   r!   )r   r]   r^   s      r   detect_num_heads_from_output8FusionMultiHeadAttentionDiT.detect_num_heads_from_output   s     
 55m6I6I!6LMMr   
input_nameoutput_namec                    SnU R                   R                  U5      nUcO  [        R                  " [        R
                  " / SQSS9US9nU R                   R                  X@R                  5        [        R                  " SX/U/U R                   R                  S5      S9nU R                  R                  U5        U R                  U R                  UR                  '   U$ )zAdd a Reshape node to convert 4D BxSxNxH to 3D BxSxD.

Args:
    input_name: input name for the 4D tensor of shape BxSxNxH.
    output_name: output name for the 3D tensor of shape BxSxD.

Returns:
    str: the output name.
bsnh_to_bsd_reshape_dims)r   r   int64)dtype)rQ   rE   inputsoutputsrQ   )r   get_initializerr   
from_arrayr#   arrayadd_initializerthis_graph_namer   	make_nodecreate_node_namenodes_to_addappendnode_name_to_graph_namerQ   )r   rb   rc   new_dims_namenew_dimsreshape_nodes         r   reshape_to_3d)FusionMultiHeadAttentionDiT.reshape_to_3d   s     3::--m<#..rxx
'/RYfgHJJ&&x1E1EF''. M,,Y7	
 	  .:>:N:N$$\%6%67r   c           
          US-   n[         R                  " SU/U/U R                  R                  SSS9/ SQS9nU R                  R                  U5        U R                  U R                  UR                  '   U$ )z4Add a Transpose node to convert BNSH to BSNH format._BSNHr5   Transpose_BNSH_to_BSNHname_prefixr7   rQ   r6   	r   rq   r   rr   rs   rt   rp   ru   rQ   r   rb   rc   transpose_nodes       r   transpose_bnsh_to_bsnh2FusionMultiHeadAttentionDiT.transpose_bnsh_to_bsnh   |     7*))LM,,[F^,_
 	  0<@<P<P$$^%8%89r   c           
          US-   n[         R                  " SU/U/U R                  R                  SSS9/ SQS9nU R                  R                  U5        U R                  U R                  UR                  '   U$ )z4Add a Transpose node to convert BNHS to BNSH format._BNSHr5   Transpose_BNHS_to_BNSHr~   r   r   r8   r   r   r   r   s       r   transpose_bnhs_to_bnsh2FusionMultiHeadAttentionDiT.transpose_bnhs_to_bnsh   r   r   qkvrJ   r@   scalec                 \   US:  d   eXU/nU/n[         R                  " SUUU R                  R                  S5      S9n	SU	l        U	R
                  R                  [         R                  " SU5      /5        Ub1  U	R
                  R                  [         R                  " SU5      /5        U	$ )ac  Create a MultiHeadAttention node.

Args:
    q: name of query input (BSD format, 3D).
    k: name of key input (BNSH format, 4D).
    v: name of value input (BNSH format, 4D).
    output: output name of MHA.
    num_heads: number of attention heads.
    scale: optional custom scale factor for attention logits.

Returns:
    NodeProto: the node created.
r   r   ri   zcom.microsoftr@   r   )r   rq   r   rr   domain	attributeextendmake_attribute)
r   r   r   r   rJ   r@   r   
mha_inputsmha_outputsmha_nodes
             r   create_multihead_attention_node;FusionMultiHeadAttentionDiT.create_multihead_attention_node   s    , 1}}AY
h## ,,-AB	
 *!!6#8#8i#P"QR%%v'<'<We'L&MNr   c           	         UR                   S:X  d   eUnU R                  R                  UR                  S   5      (       a  g [        R
                  " US5      nUb  US;  a  g Uc  U R                  R                  5       S:  a  g S nU R                  R                  U/ SQ/ SQU5      nUb  Uu  phpO+U R                  R                  U/ SQ/ S	QU5      nUc  g Uu  pn
[        R                  " U	S
/ SQ5      (       d  g S nU R                  R                  U/ SQ/ SQ5      nUb  Uu  pnO)U R                  R                  USS/SS /5      nUc  g Uu  pU R                  U5      nUc  [        R                  S5        g U R                  U5      nUc  g U(       a  UR                  S   OUR                  S   nUR                  U   U:w  ab  U(       a/  U R                  R                  U/ SQSSU-
  S/5      nUc  g Uu  pnO,U R                  R                  USS/SSU-
  /5      nUc  g Uu  pUR                  S   nUR                  S   nUR                  S   nSnUb,  UU;   a&  UU   nUR                   S:X  a  UR                  S   nSnU R                  R!                  USSUS9nUb/  [        R                  " US
/ SQ5      (       a  UR                  S   nOU R#                  U5      nSnUnUU;   a&  UU   nUR                   S:X  a  UR                  S   nSnU R                  R%                  U5      nU R                  R%                  U5      nU R                  R%                  U5      nUb!  Ub  UU:w  a  [        R                  SUU5        g Ub!  Ub  UU:w  a  [        R                  SUU5        g Uc  Ub%  U(       d  Ub  Uc  [        R                  SUU5        g U(       a$  Ub!  Ub  UU:w  a  [        R                  SUU5        g U R'                  UU5      n U S::  a  U R'                  UU5      n U S::  a  U R)                  U5      n U S::  a  U R)                  U5      n U S::  a  U R+                  X5      n U S::  a  [        R                  S5        g U R-                  U5      n!U R/                  U!U!S-   5      n"UR                  S   UR                  S   UR                  S   /n#Ub  U#R1                  UR                  S   5        Ub  U#R1                  UR                  S   5        U# H5  n$U$U;   d  M  [3        UU$   5      S:  d  M  [        R                  SU$5          g    U R5                  U"UUU
R                  S   U US9n%U R6                  R1                  U%5        U R8                  U R:                  U%R<                  '   XU
/n&Ub  U&R1                  U5        U R                  R?                  U&U
R                  S   /X#5      (       d  [        R                  S 5        g U R@                  RC                  U&5        SU l"        g )!Nr   r   axis)rf   r8      )CastMatMulr5   rE   )r   r   r   r   r   )r   r5   rE   )r   r   r   r6   r7   )Mulr   r   )r   Nr   r   r   z9fuse_dit_attention: failed to extract scale from Mul noder   Fr   Tr5   )input_indexr2   r   zBfuse_dit_attention: Q/V element type mismatch (%s vs %s), skippingzBfuse_dit_attention: Q/K element type mismatch (%s vs %s), skippingzlfuse_dit_attention: cast nodes present, V not traced through Cast, types unverifiable (q=%s, v=%s), skippingzLfuse_dit_attention: K Cast source dtype mismatch with Q (%s vs %s), skippingz.fuse_dit_attention: failed to detect num_heads_BSDzDfuse_dit_attention: intermediate %s has multiple consumers, skipping)r   r   r   rJ   r@   r   zFfuse_dit_attention: downstream nodes have external consumers, skipping)#r9   r   find_graph_outputrJ   r	   r:   get_opset_versionmatch_child_pathr   check_node_attributematch_parent_pathr+   loggerdebugr/   r!   match_parentr   	get_dtyperA   r[   r`   r   ry   rt   rH   r   rs   rp   ru   rQ   is_safe_to_fuse_nodesnodes_to_remover   prune_graph)'r   rC   input_name_to_nodesr2   softmaxr   cast_after_softmaxchild_nodes	matmul_svr^   r]   cast_before_softmaxparent_nodes	mul_scale	matmul_qkr   data_input_idxexpected_data_sourceparent_nodes_altq_bnshk_transposed_inputv_bnshv_traced_through_cast
v_producerk_transpose_nodek_bnshk_traced_through_castk_bnsh_for_dtype
k_producerq_dtypek_dtypev_dtyper@   q_bsnhq_bsdintermediate_outputsr1   r   r   s'                                          r   fuse FusionMultiHeadAttentionDiT.fuse  s   ||y((( ::''q(9:: ++GV<G 3<DJJ88:R?
 " jj116,	
 "HSE=+ **552(#	K "4?1Ik //v|TT
 # zz33%

 #8D5II  ::77!D	L
 ##/ I
 ''	2=LLTU 33I>! AT299!<YbYiYijkYl??>*.BB"#'::#?#?-N*A.$ 
 $+<L9		#'::#?#?H%N*+$ 
 $+'7$	
 #&__Q/# !&)f8K.K,V4J!!V+#))!,(,%  ::22{GZ 3 
 'K,L,LM]_egs,t,t%++A.F 001CDF !&!22,-=>J!!V+#-#3#3A#6 (,% **&&v.**&&'78**&&v.7#67g;MLL]_fhop7#67g;MLL]_fhop  +/A/MWl'/@	  !W%8W=PU\`gUgLL^
 
 ))&2EF	>--f6IJI>>>vFI>>>vFI>99+UI>LLIJ
 ,,V4""66F?; !* 0 0 3Y5E5Ea5H'..YZJ[\* ''(;(B(B1(EF) ''(:(A(A!(DE/K11c:Mk:Z6[^_6_cepq 0 77%%a( 8 
 	  *6:6J6J$$X]]3 %[A)""#56
 zz//k00346I
 
 LLab##O4  r   )r   )N)__name__
__module____qualname____firstlineno____doc__r	   r   r   r&   r+   r(   r/   strdictrA   r;   r[   r`   ry   r   r   r   r   __static_attributes____classcell__)r   s   @r   r   r      sD   (ai a9  $i C$J *&C &d &s &P	 c 6S S 0N	 NR[ N`c N # # 2    * #(( ( 	(
 ( ( t|( 
(TE  E r   r   )loggingr   numpyr#   fusion_baser   fusion_utilsr   onnxr   r   r   
onnx_modelr	   r   r   r    r   r   <module>r      s4   
    $ 0 0  	8	E & E r   