o
    ;ήc&                     @   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defddZdedeeef fddZdefdd	Z	defd
dZ
ed"defddZedejfddZed#dedefddZdefddZdd Zdd Zdd Zd d! ZdS )$FusionUtilsmodelc                 C   s
   || _ d S N)r   )selfr    r   L/tmp/pip-target-vg8gfxp4/lib/python/onnxruntime/transformers/fusion_utils.py__init__   s   
zFusionUtils.__init__
input_namereturnc                 C   sn   | j |}|d ur&|jjjtjkr&| |\}}t	d| d d|fS t	d| d|d u  d|fS )NzCasted graph input z	 to int32TzDid not cast graph input z to int32: found F)
r   find_graph_inputtypetensor_type	elem_typer   INT32cast_input_to_int32loggerdebug)r   r   graph_inputcast_output	cast_noder   r   r   cast_graph_input_to_int32   s   z%FusionUtils.cast_graph_input_to_int32c                 C   s   |d }|g}| j  }||v r!|| }|r!|jdkr!|jd g}tjd||gd}|jtdt	t
jg | j | ||fS )N_int32Castr   )inputsoutputsto)r   output_name_to_nodeop_typeinputr   	make_node	attributeextendmake_attributeintr   r   add_node)r   r   r   r#   r&   parent_noder   r   r   r   r      s   
zFusionUtils.cast_input_to_int32c                 C   s   | j  }|| }|D ]4}|jdkr?d}|jD ]}|jdkr*|jttjkr*d} nq|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   r0   nodesnodeis_int32attoutput_namer   r   r   remove_cast_int320   s    



zFusionUtils.remove_cast_int32Nattribute_namec                 C   sX   |}| j D ]}|j|krt|}qt|tr(t|ts!t|to't||ddS ||k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	equal_nan)r*   r1   r   get_attribute_value
isinstancelistr   r   )r7   r<   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r td| j	r?t
t
j| j	dd| j}t
|ddg}| | _	| S td)	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 transposedint8)dtype   r   zonly raw buffer supported)r@   
onnx_protor   
ValueErrorr   lendims	data_typeINT8raw_datanumpyreshape
frombuffer	transposetobytes)rG   
int32_dataint32_transposed_datar   r   r   transpose_2d_int8_tensorX   s   
z$FusionUtils.transpose_2d_int8_tensorTr7   c                 C   s   | j dvrtd| j   || jd }|du rdS |jdkp,|jdko,|jd dk}|r3|s3dS t| jdkr<dS || jd }|j|jkrLdS |du rRd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
        >   QuantizeLinearDequantizeLinearz+Provided node is not a Q/DQ node. Op Type: rK   NFr   rH   T)
r'   r   r   get_constant_valuer(   ndimshaperN   rS   all)r7   r   "allow_per_tensor_quantization_onlyscalescale_has_single_element
zero_pointr   r   r   check_qdq_node_for_fusionp   s    
"z%FusionUtils.check_qdq_node_for_fusioninput_indexc                 C   sX   t |j|ks	J | j|j| }t|tr(t|ts!t|to't||ddS ||k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=   )rN   r(   r   r]   r@   rA   r   r   )r   r7   rf   rB   rD   r   r   r   check_node_input_value   s   
z"FusionUtils.check_node_input_valuec                 C   s   g }| j  D ]#}|jdkr*|jd | j  vr*| j |jd |jd  || q|r@| j | t	
dt| d dS dS )z>Remove Identity nodes, except those right before graph output.Identityr   zRemoved z Identity nodesN)r   r6   r'   r3   get_graphs_output_namesr5   r(   appendremove_nodesr   inforN   )r   nodes_to_remover7   r   r   r   remove_identity_nodes   s   

z!FusionUtils.remove_identity_nodesc                 C      | j   d S r   )r   remove_cascaded_cast_nodesr   r   r   r   rp         z&FusionUtils.remove_cascaded_cast_nodesc                 C   ro   r   )r   remove_useless_cast_nodesrq   r   r   r   rs      rr   z%FusionUtils.remove_useless_cast_nodesc                 C   s$  | j jdd}|du rdS g }| j  D ]0}|jdkrD||jd }||jd }|rD|rD||krDtd|j	 d|  |
| q|rt| j  }t| j  }|D ]8}tt|j|@ rztt|j|@ sy| j |jd |jd  nqW| j |jd |jd  | j | qWdS dS )ziRemove reshape node that is not needed based on symbolic shape inference: input and output has same shapeT)updateNReshaper   zRemove reshape node z* since its input shape is same as output: )r   infer_runtime_shaper6   r'   get_edge_shaper(   r3   r   rl   r1   rj   setget_graphs_input_namesri   boolreplace_output_of_all_nodesr5   r4   )r   shape_inferrm   r7   input_shapeoutput_shapegraph_input_namesgraph_output_namesr   r   r   remove_useless_reshape_nodes   s4   

z(FusionUtils.remove_useless_reshape_nodesr   )T)__name__
__module____qualname__r   r   strr   rz   r    r   r;   staticmethodrF   rL   r   rZ   r   re   r-   rg   rn   rp   rs   r   r   r   r   r   r      s     
*r   c                   @   s(   e Zd ZeddededefddZdS )	NumpyHelperFrG   
fill_zerosr   c                 C   s0   |rddl m} t| j|j| j dS t| S )Nr   )mapping)r_   rJ   )onnxr   r   rO   TENSOR_TYPE_TO_NP_TYPErP   r	   to_array)rG   r   r   r   r   r   r      s   

zNumpyHelper.to_arrayN)F)r   r   r   r   r   rz   r   r   r   r   r   r   r      s    r   )loggingr   typingr   rS   r   r   r   r   r   r   r	   r
   rL   
onnx_modelr   r   r   r   r   r   r   r   r   <module>   s    S