
    gc                         d dl mZ d dlmZmZmZmZmZ d dlm	Z	 d dl
mZ d dlmZmZmZ d dlmZ  ee      Z G d de	      Z G d	 d
e      Zy)    )	getLogger)DictListOptionalTupleUnion)Fusion)FusionUtils)	NodeProtoTensorProtohelper)	OnnxModelc                       e Zd ZdZd dedef fdZdedede	eef   f   fdZ
d	ed
eeee   f   dedefdZd Zd Zd Zd Zd Zdede	eedef   f   fdZ	 	 	 d!ded	ededededef   dee   fdZd Zd Z	 d"dZd Zd Zd Z xZS )#FusionEmbedLayerNoMaskz
    Fuse embedding layer into one node (EmbedLayerNormalization).
    It supports the following model types: BERT, DistilBert, ALBert.
    modeldescriptionc                     t         |   |dddg|       t        |      | _        d | _        d| _        d | _        d | _        y )NEmbedLayerNormalizationLayerNormalizationSkipLayerNormalizationF)super__init__r
   utilsshape_infershape_infer_done	attention
embed_node)selfr   r   	__class__s      _/var/www/openai/venv/lib/python3.12/site-packages/onnxruntime/transformers/fusion_embedlayer.pyr   zFusionEmbedLayerNoMask.__init__   sP    %!#;<		
 !'
 %     addreturnNc                     | j                   j                  |dgdg      }|y | j                   j                  |dgdg      }|y |d   |d   fS )NGatherr      )r   match_parent_path)r   r"   gather_0_pathgather_1_paths       r    match_two_gatherz'FusionEmbedLayerNoMask.match_two_gather&   sa    

44S8*qcJ 

44S8*qcJ Qq!111r!   	layernorminput_name_to_nodesis_distil_bertc                 4   | j                   j                  |d|d      | _        | j                  y|j                  d   |vry||j                  d      }t	        |D cg c]  }|j
                   c}      }|g dk(  ri|D ]d  }|j
                  dk(  s| j                   j                  |g dg d	      }|7|d
   j                  d   |j                  d   k(  sZ|d   | _         y t        |      dk(  r|d   j
                  dk(  r|d   j                  d   |v r||d   j                  d      }	t        |	      dk(  r}|	d   j
                  dk(  rk|	d   j                  d   |v rW||	d   j                  d      }
|
D ]  }|j
                  dk(  s|| _         y t	        |
D cg c]  }|j
                   c}      }|r,|g dk7  r$|g dk7  r|g dk7  rt        j                  d       yy|g dk7  r|g dk7  rt        j                  d       yyc c}w c c}w )a  Check that LayerNormalization has a child of Attention node or subgraph like Attention.

        Args:
            layernorm (NodeProto): LayerNormalization node
            input_name_to_nodes (Dict[str, List[NodeProto]]): map from input name to nodes
            is_distil_bert (bool): whether it is DistilBert or not

        Returns:
            bool: whether there is Attention node or subgraph like Attention
        	AttentionF)	recursiveTr   )MatMulr1   r1   r   r   )Addr1   MultiHeadAttentionr1   )NNr   r      r&   r1   r2   )r1   r1   r1   Shaper   )r2   r1   r1   r1   r6   r6   )r2   r1   r1   r1   r6   z<No Attention like subgraph in children of LayerNormalization)r2   r1   r1   r1   )r   find_first_child_by_typer   outputsortedop_typer'   inputcross_attentionlenloggerdebug)r   r+   r,   r-   childrenchildchildren_typesnodepath1grandchildrennodess              r    check_attention_subgraphz/FusionEmbedLayerNoMask.check_attention_subgraph1   sP     <<{$75 = 
 >>%A&99&y'7'7':;H EH5H EF UU <<#;; JJ88I*E
 (U2Y__Q-?9CSCSTUCV-V/4Qx,# ! x=A(1+"5"5"AhqkFXFXYZF[_rFr/0B0B10EFMM"a'!!$,,5!!$++A.2EE+M!,<,C,CA,FG!D||{2)-# " "(E(JE5E(J!K  "cc"&]]"&TT[\    " 
 ! %  [\q !F: )Ks   H#Hc                 &   | j                   j                  |ddgddg      }|$| j                   j                  |g dg d      }|y|d   |d   }}|j                  d   |k7  ry| j                   j                  |g d	g d
fg dg dfg|      \  }}}|y|d   }	| j                  j                  |	dd      r| j                  j                  |	dd      sy|d   }
| j                  j                  |
dd      sy|d   }|j                  d   |k7  ryy)az    Match position embedding path from input_ids to Gather for DistilBert.

        Pattern is like the following:
                 (input_ids)
                      |
                     Shape
                       |                          |    Gather (indices=1)
                       |       |
                       |      Cast (optional)
                       |       |
                       |      Range (start=0, end=*, delta=1)
                       |       |
                       |    Unsqueeze
                       |    /
                      Expand
                        |
                      Gather
        Expandr6   r&   )rI   WhereReshaper6   )r&   r&   r5   r   Fr   r4   )	UnsqueezeRangeCastr%   r6   )r   r   r&   r   r   )rL   rM   r%   r6   )r   r   r&   r   r5   T)r   r'   r;   match_parent_pathsr   check_node_input_value)r   position_embedding_gather	input_idsoutput_name_to_noderD   expandshape_path2
range_nodegather_node
shape_nodes               r    #match_position_embedding_distilbertz:FusionEmbedLayerNoMask.match_position_embedding_distilbert   s9   * 

,,-FSZH[^_ab]cd=JJ00)7E
 }a%);;q>Y&jj33BOT:LI  
5! =1X
JJ--j!Q?DJJDeDefprsuvDwBi

11+q!D2Y
A)+r!   c                      y)aY  Match position embedding path from input_ids to Gather for Roberta.

        Roberta Embedding Layer Pattern (* is optional since it might be removed by ORT, ? is the padding word id):
          (input_ids) --> Equal(B=?) -- Not -- Cast(to=6) -- CumSum(axis=1) -- Mul -- Cast(to=7) -- Add(B=1) -- Cast(to=7)* --> Gather
                                                |                              ^
                                                V                              |
                                                +------------------------------+

        Roberta new pattern from transformers v4.9:
           (input_ids) --> Equal(B=?) -- Not -- Cast(to=6) -- CumSum(axis=1) -- Add(B=0) -- Mul -- Cast(to=7) -- Add(B=1) --> Gather
                                                |                                           ^
                                                V                                           |
                                                +-------------------------------------------+

        start_node = position_embedding_gather
        start_index = 1

        # match optional Cast node.
        parent = self.model.get_parent(start_node, start_index, output_name_to_node)
        if parent is None:
            return
        if parent.op_type == "Cast":
            if OnnxModel.get_node_attribute(parent, "to") != 7:
                return
            start_node = parent
            start_index = 0

        i, path, return_indices = self.model.match_parent_paths(
            start_node,
            [ (['Add', 'Cast', 'Mul', 'CumSum', 'Cast', 'Not', 'Equal'], [start_index, 0, 0, 0, 0, 0, 0]),
              (['Add', 'Cast', 'Mul', 'Add', 'CumSum', 'Cast', 'Not', 'Equal'], [start_index, 0, 0, 0, 0, 0, 0, 0])],
            output_name_to_node)

        if path is not None:
            # constant input of Add shall be 1.
            i, value = self.model.get_constant_input(path[0])
            if value != 1:
                return False

            _, self.padding_word_id = self.model.get_constant_input(path[-1])

            return input_ids == path[-1].input[0]
        F r   rR   rS   rT   s       r     match_position_embedding_robertaz7FusionEmbedLayerNoMask.match_position_embedding_roberta   s    Z r!   c                 N   | j                   j                  |ddgddg|      }|y|\  }}| j                   j                  |j                  d         }|t	        |j
                        dk(  r|j
                  d   dk(  rr| j                  j                  |ddg      rT| j                  j                  |ddg      r6t	        |j                        dk(  s| j                  j                  |ddg      sy| j                   j                         }|d	k  rt        j                  |d
dg      s y| j                  j                  |ddg      sy| j                   j                  |d|      }	|	y|	j                  dk(  r<| j                  j                  |	dd      sy| j                   j                  |	d|      }
n|	}
|
|
j                  dk7  ry| j                  j                  |
dd      sy| j                   j                  |
d|      }||j                  dk7  ry||j                  d   k(  S )a	    Match position embedding path from input_ids to Gather for BERT.

        BERT Embedding Layer Pattern:
                                    (input_ids)
                                   /                                          /          Shape
                                /              |
                              /              Gather (indices=1)
                             /                  |
                            /                  Add (optional, B=0)
                           /                    |
                        Gather (segment_ids) Unsqueeze (axes=0)
                           \        |           |
                            \     Gather      Slice (data[1,512], starts=0, ends=*, axes=1, steps=1)
                              \    /            |
                                Add          Gather
                                   \       /
                                      Add
                                       |
                                LayerNormalization
        SlicerL   r&   r5   Fr            axesr2   r%   r6   )r   r'   get_constant_valuer;   r=   rV   r   rQ   get_opset_versionr
   check_node_attribute
get_parentr:   )r   rR   rS   rT   pathslice	unsqueezeslice_weightopset_versionrC   gatherrV   s               r    match_position_embedding_bertz4FusionEmbedLayerNoMask.match_position_embedding_bert   s   , zz++%k"F	
 <yzz44U[[^D$L&&'1,""1%*

11%QC@

11%QC@U[[!Q&$***K*KESTWXVY*Z

446233IvsK::44YA3Gzz$$Y3FG<<<5 ::44T1a@ZZ**44GHFF>V^^x7

11&!Q?

%%fa1DE=EMMW4EKKN**r!   c                 T    | j                  |||      ry| j                  |||      ryy)NTF)rq   r\   r_   s       r    match_position_embeddingz/FusionEmbedLayerNoMask.match_position_embedding:  s5    --.GTgh 334MyZmnr!   c                    |j                   d   }|r|j                   d   nd}|j                   d   }| j                  s(| j                  j                  d      | _        d| _        | j                  | j                  j                  |      }| j                  j                  |      }|r|sJ t        |      dk(  rt        |      dk(  r|d   |d   k(  st        j                  d| d|        y|rQ| j                  j                  ||      s5t        j                  d	| d
| j                  j                  |              y| j                  j                  |j                   d         }	|	t        |	j                        dk7  rt        j                  d       y| j                  j                  |j                   d         }
|
7t        |
j                        dk7  s|	j                  d   |
j                  d   k7  rt        j                  d       y|rw| j                  j                  |j                   d         }|7t        |j                        dk7  s|	j                  d   |j                  d   k7  rt        j                  d       y|	j                  d   |
j                  d   k  rUt        j                  d|j                   d    d|	j                  d    d|j                   d    d|
j                  d           |r|	j                  d   j                  d   k  rUt        j                  d|j                   d    d|	j                  d    d|j                   d    d|j                  d           |
j                  d   |j                  d   k  rUt        j                  d|j                   d    d|
j                  d    d|j                   d    d|j                  d           y)zXSanity check of embedding weights, and match hidden_size of weights and shape of inputs.r&   NT)updater5   z^Cannot fuse EmbedLayerNormalization: input_ids and position_ids not matched in 2nd dimension: z vs FzYCannot fuse EmbedLayerNormalization: input_ids and segment_ids does not have same shape: z != r   zICannot fuse EmbedLayerNormalization: word embedding table is not expectedzMCannot fuse EmbedLayerNormalization: position embedding table is not expectedzLCannot fuse EmbedLayerNormalization: segment embedding table is not expectedzword_embedding_table (z) size z <= position_embedding_table (z <= segment_embedding_table (zposition_embedding_table ()r;   r   r   infer_runtime_shaper   get_edge_shaper=   r>   infocompare_shaperg   rV   warning)r   word_embedding_gathersegment_embedding_gatherrR   rS   segment_idsposition_idsinput_ids_shapeposition_ids_shapeword_embedding_tableposition_embedding_tablesegment_embedding_tables               r    check_embeddingz&FusionEmbedLayerNoMask.check_embeddingH  s   )//2	;S.44Q7Y]066q9$$#zz==T=JD$(D!'"..==iHO!%!1!1!@!@!N"'999O$)*+q0#A&*<Q*??t  vE  uF  FJ  K]  J^  _ 4#3#3#A#A)[#Yop  pA  AE  FJ  FV  FV  Fe  Fe  fq  Fr  Es  t #zz<<=R=X=XYZ=[\'3/C/I/I+Ja+OKKcd#'::#@#@AZA`A`abAc#d $,+112a7$**1-1I1O1OPQ1RRKKgh&*jj&C&CD\DbDbcdDe&f#'/.445:(..q15L5R5RST5UUjk  %%a(,D,J,J1,MMNN()>)D)DQ)G(HPdPjPjklPmOn  oM  Ng  Nm  Nm  no  Np  Mq  qx  yQ  yW  yW  XY  yZ  x[  \ #))!,0G0M0Ma0PP,-B-H-H-K,LGThTnTnopTqSr  sP  Qi  Qo  Qo  pq  Qr  Ps  sz  {R  {X  {X  YZ  {[  z\  ] (--a04K4Q4QRS4TT01J1P1PQR1S0TT[\t\z\z{|\}[~  \  ]u  ]{  ]{  |}  ]~  \  F  G^  Gd  Gd  ef  Gg  Fh  i r!   
input_namec                 6   d}| j                   j                  |      }|Y|j                  j                  j                  t
        j                  k7  r"| j                  j                  |      \  }}||fS |}||fS | j                  j                  |      \  }}||fS )a  Cast a graph input or node input to int32.

        Args:
            input_name (str): name of graph input or node input

        Returns:
            A tuple of casted input name and the cast node.
            int32_output (str): If input is int32, it is the input name, Otherwise it is output name of Cast node.
            input_cast_node (Union[None, NodeProto]): Cast node. It could be None if input is int32.
        N)	r   find_graph_inputtypetensor_type	elem_typer   INT32r   cast_input_to_int32)r   r   input_cast_nodegraph_inputint32_outputs        r    cast_to_int32z$FusionEmbedLayerNoMask.cast_to_int32  s     jj11*="++559J9JJ04

0N0Nz0Z-o _,,	  * _,, -1JJ,J,J:,V)L/_,,r!   rS   r{   rR   r|   r~   c	                    g }	| j                  |      \  }}
| j                  j                  d      }|j                  dk(  r|j                  d   }|j                  d   }n|j                  d   }|j                  d   }d}|R| j                  |j                  d         \  }}
|||j                  d   |j                  d   |j                  d   ||g}n#|d|j                  d   |j                  d   d||g}|6|j                  d       | j                  |      \  }}
|j                  |       |d	z   |d
z   g}|r||n|dz   }|j                  |       t        j                  d|||      }d|_        |j                  D ].  }|j                  dk(  s|j                  j                  |g       0 t        |j                        dk(  r0|j                  j                  t        j                  dd      g       |	j                  |       |	D ]%  }| j                  | j                  |j                  <   ' | j                   j                  |	       || _        |S )ag  Create an EmbedLayerNormalization node. Note that segment embedding is optional.

        Args:
            input_ids (str): input_ids for word embeddings
            layernorm (NodeProto): LayerNormalization or SkipLayerNormalization node.
            word_embedding_gather (NodeProto): the Gather node for word embedding
            position_embedding_gather (NodeProto): the Gather node for position embedding
            segment_embedding_gather (Union[None, NodeProto]): the Gather node for segment embedding, or None.

        Returns:
            NodeProto: the EmbedLayerNormalization node created.
        r   r   r&   r5   rc   Nr    _output_dummy_mask_index_embedding_sum)outputsnamezcom.microsoftepsilong-q=)r   r   create_node_namer:   r;   appendr   	make_nodedomain	attributer   extendr=   make_attributethis_graph_namenode_name_to_graph_namenodes_to_addr   )r   rS   r+   r{   rR   r|   r~   embedding_sum_outputembedding_sum_namer   rW   	node_namegammabetaembed_node_inputsr}   embed_node_outputsr   r   attrC   s                        r    create_fused_nodez(FusionEmbedLayerNoMask.create_fused_node  st   . )))4	1JJ//0IJ	 44OOA&E??1%DOOA&E??1%D #/!//0H0N0Nq0QRNK %++A.)//2(..q1! %++A.)//2! #$$R("00>OL!$$\2')3YAT5TU);)G%YYiMiD%%d+%%%&	

 ,
 &&Cxx9$$$++SE2 ' z##$)  '')>)>y')R(ST 	J' D6:6J6JD((3 !  .$r!   c                 ~    | j                   j                  |j                  d   |j                  d          d| _        y )Nr   T)r   replace_input_of_all_nodesr8   prune_graph)r   r+   r   s      r    finish_fusionz$FusionEmbedLayerNoMask.finish_fusion  s5    

--i.>.>q.A:CTCTUVCWXr!   c                     |j                   dk(  xr5 t        |j                        dkD  xr t        |j                  d         dkD  S )Nr   rc   r   )r:   r=   r8   )r   rC   s     r    "is_skip_layer_norm_with_sum_outputz9FusionEmbedLayerNoMask.is_skip_layer_norm_with_sum_output  sD     88nc$++>NQR>RnWZ[_[f[fgh[iWjmnWnnr!   c           
         | j                  |      }|y|\  }}|j                  d   }	|j                  d   }
| j                  ||d      sy| j                  |d |      sy|j                  dk(  rL| j                  |      }d}|}|r|j                  d   nd }|d uxr | j                  j                  |      d u}n|}|j                  dk(  rdnd}t        |j                        |kD  r|j                  |   nd }|d uxr | j                  j                  |      d u}|xr ||v xr t        ||         dkD  }|d uxr |j                  dk7  xs |xs |}| j                  |	|||||
||r|nd       }|r:d	|j                  |<   |s)| j                  j                  ||j                  d
          | j                  ||       y)NFr&   r-   r   rc   r2   r   )r   r   _no_use__to_be_removed_r5   T)r*   r;   rG   r   r:   r   r8   r   find_graph_outputr=   r   r   r   )r   r+   add_before_layernormr,   rT   optional_segment_gather
two_gatherr{   rR   rS   r~   need_embedding_sum_outputsum_output_indexnode_with_sum_output
sum_outputis_sum_graph_outputis_sum_used_by_multiple_nodesr   s                     r    	fuse_gpt2z FusionEmbedLayerNoMask.fuse_gpt2  s/   ( **+?@
;E88)//2	066q9,,Y8K\a,b##$94AZ[  88(,(O(OPY(Z% #, 0I))!,tJ#-T#9"u

@\@\]g@hpt@t#7 $8$@$@E$Iqq +2236FF %++,<= 
 $.T#9"u

@\@\]g@hpt@to
.A AosK^_iKjGknoGo * *44)? )$,,5m9LmPm &
 ++!%#!:-@zd , 	

 %<U ''(89&

55j*BSBSTUBVW9j1r!   c                    | j                  |      }|y|\  }}|j                  d   }| j                  ||d      sy| j                  |||      sy| j	                  |d|      sy| j                  ||||d      }	| j                  ||	       y)a  Fuse embedding layer for DistilBert
        Args:
            layernorm (NodeProto): node of LayerNormalization or SkipLayerNormalization
            add_before_layernorm (NodeProto): the Add node before LayerNormalization, or the SkipLayerNormalization itself
            input_name_to_nodes (Dict[str, List[NodeProto]]): map from input name to nodes
            output_name_to_node (Dict[str, List[NodeProto]]): map from output name to nodes
        NFr&   Tr   )r*   r;   rG   rs   r   r   r   )
r   r+   r   r,   rT   r   r{   rR   rS   r   s
             r    fuse_distilbertz&FusionEmbedLayerNoMask.fuse_distilbertd  s    & **+?@
;E88)//2	,,Y8K\`,a,,-F	Sfg##$94AZ[++y"79RTX

 	9j1r!   c                    | j                   j                  |dgdg      }|y| j                  |d         }|y|\  }}|j                  d   }	| j	                  ||d      sy| j                   j                  |dgdg      }
|
y|
d   }| j                  ||	|      s| j                  ||	|      sy|}|}|}| j                  |||      sy| j                  |	||||      }| j                  ||       y)a  Fuse embedding layer for Bert
        Args:
            layernorm (NodeProto): node of LayerNormalization or SkipLayerNormalization
            add_before_layernorm (NodeProto): the Add node before LayerNormalization, or the SkipLayerNormalization itself
            input_name_to_nodes (Dict[str, List[NodeProto]]): map from input name to nodes
            output_name_to_node (Dict[str, List[NodeProto]]): map from output name to nodes
        r2   r   Fr&   r   r%   T)	r   r'   r*   r;   rG   rs   r   r   r   )r   r+   r   r,   rT   add_2_gatherr   r{   r|   rS   position_embedding_pathrR   tempr   s                 r    	fuse_bertz FusionEmbedLayerNoMask.fuse_bert  s=    zz334H5'TUSVW**<?;
:D77)//2	,,Y8K\a,b"&**">">?SV^U_bcad"e"*$;A$>!,,-F	Sfg001I9Vij+D'@$(,%##$9;SUno++!%$

 	9j1r!   c                     | j                   j                  |dgdg      }|j                  dk(  r|y |d   }d }n| j                   j                  |dgdg      }| j                   j                  |dgdg      }|||y |d   }|d   }n5|/|-| j                   j                  |dgdg      }|y |d   }|d   }n|}d }| j                  |||||      ry | j	                  ||||      ry | j                  ||||      ry y )Nr2   r   r   r%   r&   )r   r'   r:   r   r   r   )	r   rC   r,   rT   first_add_pathr   r   r(   r)   s	            r    fusezFusionEmbedLayerNoMask.fuse  sQ   55dUGaSI<<//%#1!#4 &*# JJ88zA3OM JJ88zA3OM$)B!)'5a'8$*7*:'*}/D!%!=!=dUGaS!Q!)'5a'8$*7*:''+$*.'>>&(;=PRi
 &:<OQde>>$ 46IK^_ `r!   )zno mask)NFN)N)__name__
__module____qualname____doc__r   strr   r   r   r   r*   r   r   boolrG   r\   r`   rq   rs   r   r   r   r   r   r   r   r   r   r   __classcell__r   s   @r    r   r      sO   
i c 	2I 	2%eIyDX>Y8Y2Z 	2RR "#tI"67R 	R
 
Rh<|-^F+PHT- -c5y;Q6Q0R -< '+"`` `  )	`
 $-` #(i"8` sm`D 
o rvOb'R0d"r!   r   c                   6     e Zd Zddef fdZd Z fdZ xZS )FusionEmbedLayerNormalizationr   c                 4    t         |   |d       || _        y )Nz	with mask)r   r   use_mask_index)r   r   r   r   s      r    r   z&FusionEmbedLayerNormalization.__init__  s    ,,r!   c                    | j                   }t        |j                        dk(  r<|j                  j                  |       t        j                  d|j                         nxt        |j                        dkD  r?|j                  d   s0||j                  d<   t        j                  d|j                         n!t        j                  d|j                         y |D ]z  }t        j                  d|j                         |j                  dk(  r|j                  d   |j                  d<   O|j                  d	k(  s_|j                  d   |j                  d
<   | y )N   zappend mask to %szreplace mask in %szskip mask in %szupdate mask_index in %sr/   r&   rc   r3   rd   )	r   r=   r;   r   r>   r?   r   r:   r8   )r   
mask_int32attention_nodesr   attention_nodes        r    replace_maskz*FusionEmbedLayerNormalization.replace_mask  s    __
z A%##J/LL,joo>!!"Q&z/?/?/B",JQLL-z?LL*JOO<-NLL2N4G4GH%%4*4*;*;A*>$$Q'''+??*4*;*;A*>$$Q' .r!   c                 H   d | _         d | _        d | _        t        |   |||       | j                  y | j
                  s't        j                  d       | j                  d       y | j                   3| j                  't        j                  d       | j                  d       y | j                   r| j                   j                  d   }n| j                  j                  d   }||   }| j                  j                  |      rB|D cg c]  }|j                  dv s| }}| j                  ||       | j                  d       y ||vr(t        j                  d|       | j                  d       y ||   }|j                  d	v r|D cg c]  }|j                  dv s| }}j                  d
k(  rA|j                  d   }t        |      t        |      k(  r| j                  j!                  |       | j                  ||       | j                  d       y y c c}w c c}w )NzG--use_mask_index is not set: EmbedLayerNormalization will not have maskz EmbedLayerNormalization(no mask)zLEmbedLayerNormalization will not have mask since attention node is not foundrc   rd   )r/   r3   z"EmbedLayerNormalization(with mask)zHEmbedLayerNormalization will not have mask since %s is not a node output)	ReduceSumrN   r   r   )r   r<   r   r   r   r   r>   r?   increase_counterr;   r   r   r:   r   r=   nodes_to_remover   )r   rC   r,   rT   r   children_nodesr   r   s          r    r   z"FusionEmbedLayerNormalization.fuse  s   #T.0CD??"""LLbc!!"DE>>!d&:&:&BLLgh!!"DE>>--a0J--33A6J,Z8::&&z20>v$,,RuButOvj/:!!"FG00LLceop!!"DE":.<<000>v$,,RuButOv||{*!ZZ]
~&#o*>>((//5j/:!!"FG 1 w ws   
HHHH)F)r   r   r   r   r   r   r   r   r   s   @r    r   r     s     -i -?*-H -Hr!   r   N)loggingr   typingr   r   r   r   r   fusion_baser	   fusion_utilsr
   onnxr   r   r   
onnx_modelr   r   r>   r   r   r^   r!   r    <module>r      sI     5 5  $ / /  	8	PV PfGH$: GHr!   