U
    T?h0                     @   s   d dl mZ d dlmZmZ d dlZd dlmZmZ d dlm	Z	m
Z
mZmZ d dlmZ d dlmZ eeZG dd	 d	ZG d
d dZdS )    )	getLogger)OptionalTupleN)array_equalndarray)	NodeProtoTensorProtohelpernumpy_helper)onnx_pb)	OnnxModelc                   @   s  e Zd ZedddZeeeef dddZd-edd	d
Z	d.ee
ee ee dddZedddZedddZedd Zed/edddZed0edddZeejdddZed1eedd d!Ze
d"d#d$Zd%d& Zd'd( Zd)d* Zd+d, ZdS )2FusionUtilsmodelc                 C   s
   || _ d S Nr   )selfr    r   W/var/www/html/venv/lib/python3.8/site-packages/onnxruntime/transformers/fusion_utils.py__init__   s    zFusionUtils.__init__)
input_namereturnc                 C   sn   | j |}|d k	rL|jjjtjkrL| |\}}t	d| d d|fS t	d| d|d k	  d|fS )NzCasted graph input z	 to int32TzDid not cast graph input z to int32: found F)
r   Zfind_graph_inputtypeZtensor_typeZ	elem_typer   INT32cast_input_to_int32loggerdebug)r   r   Zgraph_inputZcast_output	cast_noder   r   r   cast_graph_input_to_int32   s    z%FusionUtils.cast_graph_input_to_int32int32)r   c                 C   sf   |d | }|dkr t tj}n0|dkr4t tj}n|dkrHt tj}ntd| |||}||fS )N_r   Zfloat32Zfloat16z"Invalid target_type: {target_type})intr   r   FLOATZFLOAT16
ValueErroradd_cast_node)r   r   Ztarget_typeoutput_nameto_typer   r   r   r   
cast_input   s    zFusionUtils.cast_inputN)r   r%   r$   
graph_namec           	      C   s   |d kr|d|  }|g}|d kr.| j  }||krX|| }|rX|jdkrX|jd g}tjd||gd}|jtd|g | j j	||d |S )NZ	_cast_to_Castr   )inputsoutputsto)r'   )
r   output_name_to_nodeop_typeinputr	   Z	make_node	attributeextendZmake_attributeadd_node)	r   r   r%   r$   r,   r'   r)   parent_noder   r   r   r   r#   /   s    
zFusionUtils.add_cast_nodec                 C   s   |  |dS )Nr   )r&   )r   r   r   r   r   r   J   s    zFusionUtils.cast_input_to_int32c                 C   s   | j  }|| }|D ]h}|jdkrd}|jD ]&}|jdkr.|jttjkr.d} qVq.|r|j	d }| j 
| | j || qd S )Nr(   Fr+   Tr   )r   input_name_to_nodesr-   r/   nameir    r   r   outputremove_nodereplace_input_of_all_nodes)r   r   r3   nodesnodeZis_int32Zattr$   r   r   r   remove_cast_int32M   s    



zFusionUtils.remove_cast_int32c                 C   sz   d}| j | |krJ| || j |  krJ|| j |  |  t|| j |  }|| j |< ||krl|| |  n
| g||< |S )Nr   )r.   removelenappend)r:   r5   new_input_namer3   old_input_referencer   r   r   update_node_input\   s     

zFusionUtils.update_node_inputr   c           
      C   s<   |j | }|j | }t||||}|dko6| | }	|	S )a  
        Before:
              (input)-->parent-->node-->(output)
        After:
              (input)-->parent-->
                |
                +----->node-->(output)

        This function returns a flag whether the parent node can be removed.
        r   )r.   r   rA   Zfind_graph_output)
r   r:   r2   r3   Znode_input_indexZparent_input_indexZold_input_namer?   r@   Zparent_can_be_removedr   r   r   skip_parentl   s
    

zFusionUtils.skip_parent)attribute_namec                 C   sV   |}| j D ]}|j|kr
t|}q
t|trJt|ttfoHt||ddS ||kS dS )a  Verify that a node has expected value for an attribute.

        Args:
            node (NodeProto): a node to check
            attribute_name (str): name of attribute
            expected_value (Any): expected value of the attribute
            default_value (Any, optional): default value if the attribute does not exist. Defaults to None.

        Returns:
            bool: whether the check is passed or not
        FZ	equal_nanN)r/   r4   r	   Zget_attribute_value
isinstancelistr   r   )r:   rC   expected_valuedefault_valuevalueattrr   r   r   check_node_attribute   s    


z FusionUtils.check_node_attribute)tensorc                 C   s   t | tjstdt|  t| jdks:| jtjjkrBtd| j	r~t
t
j| j	dd| j}t
|ddg}| | _	ntd| S )	zTranspose a 2-D INT8 TensorProto
        Args:
            tensor (TensorProto): tensor to be transposed
        Returns:
            tensor (TensorProto): transposed tensor
        z3Expected input type is an ONNX TensorProto but got    z'Only INT8 2-D tensors can be transposedZint8)dtype   r   zonly raw buffer supported)rE   
onnx_protor   r"   r   r=   dims	data_typeZINT8raw_datanumpyZreshapeZ
frombufferZ	transposetobytes)rL   Z
int32_dataZint32_transposed_datar   r   r   transpose_2d_int8_tensor   s    z$FusionUtils.transpose_2d_int8_tensorT)r:   r   c                 C   s   | j dkrtd| j   || jd }|dkr8dS |jdkpX|jdkoX|jd dk}|rf|sfdS t| jdkrxdS || jd }|j|jkrdS |dkrdS t	|dkS )	a  Verify if a provided QuantizeLinear (Q) / DequantizeLinear (DQ) node is a good candidate for fusion.
           It is a good candidate for fusion if:
           (1) The Q/DQ node is for per-tensor quantization if allow_per_tensor_quantization_only is `True`
           (2) The Q/DQ node should have constant scale
           (3) The Q/DQ node should have a zero point of 0
        Args:
            node (NodeProto): a Q/DQ node to check
        Returns:
            bool: whether the check is passed or not
        >   DequantizeLinearQuantizeLinearz+Provided node is not a Q/DQ node. Op Type: rO   NFr   rM   T)
r-   r   r   get_constant_valuer.   ndimshaper=   rT   all)r:   r   Z"allow_per_tensor_quantization_onlyscaleZscale_has_single_elementZ
zero_pointr   r   r   check_qdq_node_for_fusion   s     
"z%FusionUtils.check_qdq_node_for_fusion)input_indexc                 C   sV   t |j|kst| j|j| }t|trJt|ttfoHt||ddS ||kS dS )a7  Verify that a node has expected input value

        Args:
            node (NodeProto): a node to check
            input_index (int): index of its input to be verified
            expected_value (Any): expected value of the input

        Returns:
            bool: whether the check is passed or not
        FrD   N)	r=   r.   AssertionErrorr   rY   rE   rF   r   r   )r   r:   r_   rG   rI   r   r   r   check_node_input_value   s
    
z"FusionUtils.check_node_input_valuec                 C   s   g }| j  }| j  D ]@}|jdkr|jd |kr| j |jd |jd  || q|r| j | t	
dt| d dS )z>Remove Identity nodes, except those right before graph output.ZIdentityr   zRemoved z Identity nodesN)r   get_graphs_output_namesr9   r-   r6   r8   r.   r>   Zremove_nodesr   infor=   )r   nodes_to_removegraph_output_namesr:   r   r   r   remove_identity_nodes   s    

z!FusionUtils.remove_identity_nodesc                 C   s   | j   d S r   )r   remove_cascaded_cast_nodesr   r   r   r   rg      s    z&FusionUtils.remove_cascaded_cast_nodesc                 C   s   | j   d S r   )r   remove_useless_cast_nodesrh   r   r   r   ri     s    z%FusionUtils.remove_useless_cast_nodesc                 C   sB  | j jdd}|dkrdS g }| j  D ]`}|jdkr(||jd }||jd }|r(|r(||kr(td|j	 d|  |
| q(|r>t| j  }t| j  }|D ]}tt|j|@ rtt|j|@ st| j  |jd  dkr| j |jd |jd  nqn| j |jd |jd  | j | qdS )	ziRemove reshape node that is not needed based on symbolic shape inference: input and output has same shapeT)updateNZReshaper   zRemove reshape node z* since its input shape is same as output: rO   )r   Zinfer_runtime_shaper9   r-   Zget_edge_shaper.   r6   r   rc   r4   r>   setZget_graphs_input_namesrb   boolr=   r3   Zreplace_output_of_all_nodesr8   r7   )r   Zshape_inferrd   r:   Zinput_shapeZoutput_shapeZgraph_input_namesre   r   r   r   remove_useless_reshape_nodes  s4    
z(FusionUtils.remove_useless_reshape_nodes)r   )NNN)r   r   )N)T)__name__
__module____qualname__r   r   strr   rl   r   r&   r    r   r#   r   r;   staticmethodrA   rB   rK   rP   r   rV   r   r^   ra   rf   rg   ri   rm   r   r   r   r   r      s:   
   
*r   c                   @   s$   e Zd ZedeeedddZdS )NumpyHelperF)rL   
fill_zerosr   c                 C   s0   |r&ddl m} t| j|j| j dS t| S )Nr   )mapping)r[   rN   )onnxru   r   rQ   ZTENSOR_TYPE_TO_NP_TYPErR   r
   to_array)rL   rt   ru   r   r   r   rw   '  s    
zNumpyHelper.to_arrayN)F)rn   ro   rp   rr   r   rl   r   rw   r   r   r   r   rs   &  s   rs   )loggingr   typingr   r   rT   r   r   rv   r   r   r	   r
   r   rP   Z
onnx_modelr   rn   r   r   rs   r   r   r   r   <module>   s     