U
    h.                     @   s   d dl mZ d dl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)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	edddZ
edddZedd Zed+edddZed,edddZeejdddZed-eedddZedd d!Zd"d# Zd$d% Zd&d' Zd(d) ZdS ).FusionUtilsmodelc                 C   s
   || _ d S Nr   )selfr    r   I/tmp/pip-unpacked-wheel-socb9apf/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_inputcast_output	cast_noder   r   r   cast_graph_input_to_int32   s    z%FusionUtils.cast_graph_input_to_int32int32)r   c           	      C   s   |d | }|g}| j  }||krF|| }|rF|jdkrF|jd g}tjd||gd}|dkrlttj}n0|dkrttj	}n|dkrttj
}ntd|jtd	|g | j | ||fS )
N_Castr   )inputsoutputsr   Zfloat32Zfloat16z"Invalid target_type: {target_type}to)r   output_name_to_nodeop_typeinputr   Z	make_nodeintr   r   FLOATZFLOAT16
ValueError	attributeextendZmake_attributeadd_node)	r   r   Ztarget_typer   r!   r$   parent_noder   Zto_typer   r   r   
cast_input   s$    
zFusionUtils.cast_inputc                 C   s   |  |dS )Nr   )r.   )r   r   r   r   r   r   :   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   r/   nodesnodeZis_int32ZattZoutput_namer   r   r   remove_cast_int32=   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)r6   r1   new_input_namer/   old_input_referencer   r   r   update_node_inputL   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   r=   Zfind_graph_output)
r   r6   r-   r/   Znode_input_indexZparent_input_indexZold_input_namer;   r<   Zparent_can_be_removedr   r   r   skip_parent\   s
    

zFusionUtils.skip_parentN)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*   r0   r   Zget_attribute_value
isinstancelistr   r   )r6   r?   expected_valuedefault_valuevalueattrr   r   r   check_node_attributer   s    


z FusionUtils.check_node_attribute)tensorc                 C   s   t | tjstdt|  t| jdks8| jtjjkr@t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
        z5Expected input type is an ONNX TensorProto but got %s   z'Only INT8 2-D tensors can be transposedZint8)dtype   r   zonly raw buffer supported)rA   
onnx_protor   r)   r   r9   dims	data_typeZINT8raw_datanumpyZreshapeZ
frombufferZ	transposetobytes)rH   Z
int32_dataZint32_transposed_datar   r   r   transpose_2d_int8_tensor   s    z$FusionUtils.transpose_2d_int8_tensorT)r6   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: rK   NFr   rI   T)
r%   r   r   get_constant_valuer&   ndimshaper9   rP   all)r6   r   Z"allow_per_tensor_quantization_onlyZ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
        Fr@   N)	r9   r&   AssertionErrorr   rU   rA   rB   r   r   )r   r6   rZ   rC   rE   r   r   r   check_node_input_value   s
    
z"FusionUtils.check_node_input_valuec                 C   s   g }| j  D ]F}|jdkr|jd | j  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   r5   r%   r2   get_graphs_output_namesr4   r&   r:   Zremove_nodesr   infor9   )r   nodes_to_remover6   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   ra      s    z&FusionUtils.remove_cascaded_cast_nodesc                 C   s   | j   d S r   )r   remove_useless_cast_nodesrb   r   r   r   rc      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: rK   )r   Zinfer_runtime_shaper5   r%   Zget_edge_shaper&   r2   r   r^   r0   r:   setZget_graphs_input_namesr]   boolr9   r/   Zreplace_output_of_all_nodesr4   r3   )r   Zshape_inferr_   r6   Zinput_shapeZoutput_shapeZgraph_input_namesZgraph_output_namesr   r   r   remove_useless_reshape_nodes   s4    
z(FusionUtils.remove_useless_reshape_nodes)r   )r   r   )N)T)__name__
__module____qualname__r   r   strr   rf   r   r.   r   r7   staticmethodr=   r>   rG   rL   r   rR   r   rY   r'   r\   r`   ra   rc   rg   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)rH   
fill_zerosr   c                 C   s0   |r&ddl m} t| j|j| j dS t| S )Nr   )mapping)rW   rJ   )onnxro   r   rM   ZTENSOR_TYPE_TO_NP_TYPErN   r	   to_array)rH   rn   ro   r   r   r   rq     s    
zNumpyHelper.to_arrayN)F)rh   ri   rj   rl   r   rf   r   rq   r   r   r   r   rm     s   rm   )loggingr   typingr   rP   r   r   rp   r   r   r   r	   r
   rL   Z
onnx_modelr   rh   r   r   rm   r   r   r   r   <module>   s     