o
    i<2                     @   s|   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 )    )	getLoggerN)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d1defd	d
Z				d2dede
dedB dedB fddZdefddZdefddZedd Zed3defddZdededB fddZed4defddZed ejfd!d"Zed5dedefd$d%Zd&e
fd'd(Zd)d* Zd+d, Zd-d. Zd/d0 ZdS )6FusionUtilsmodelc                 C   s
   || _ d S N)r   )selfr    r   _/home/kim/smarthome/.venv/lib/python3.10/site-packages/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   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int32c                 C   sf   |d | }|dkrt tj}n|dkrt tj}n|dkr$t 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_inputNr"   r!   
graph_namec           	      C   s   |d u r|d|  }|g}|d u r| j  }||v r,|| }|r,|jdkr,|jd g}tjd||gd}|jtd|g | j j	||d |S )NZ	_cast_to_Castr   )inputsZ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   I   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   r/   nodesnodeZis_int32Zattr!   r   r   r   remove_cast_int32L   s    



zFusionUtils.remove_cast_int32c                 C   s|   d}| j | |v r%| || j |  v r%|| j |  |  t|| j |  }|| j |< ||v r7|| |  |S | g||< |S )Nr   )r*   removelenappend)r6   r1   new_input_namer/   old_input_referencer   r   r   update_node_input[   s    

zFusionUtils.update_node_inputr   c           
      C   s<   |j | }|j | }t||||}|dko| | }	|	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_parentk   s
   

zFusionUtils.skip_parentr6   c                 C   sV   |j dv sJ t|jdkr| j|jd S d }|jD ]}|jdkr(t|}q|S )N)ZSqueezeZ	Unsqueeze   axes)	r)   r9   r*   r   get_constant_valuer+   r0   r   get_attribute_value)r   r6   r@   attrr   r   r   get_squeeze_or_unsqueeze_axes   s   


z)FusionUtils.get_squeeze_or_unsqueeze_axesattribute_namec                 C   sR   |}| j D ]}|j|krt|}qt|tr%t|ttf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Z	equal_nan)r+   r0   r   rB   
isinstancelistr   r   )r6   rE   expected_valuedefault_valuevaluerC   r   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
        z3Expected input type is an ONNX TensorProto but got    z'Only INT8 2-D tensors can be transposedZint8)dtyper?   r   zonly raw buffer supported)rG   
onnx_protor   r   r   r9   dims	data_typeZINT8raw_datanumpyZreshapeZ
frombufferZ	transposetobytes)rM   Z
int32_dataZint32_transposed_datar   r   r   transpose_2d_int8_tensor   s   
z$FusionUtils.transpose_2d_int8_tensorTc                 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
        >   ZDequantizeLinearZQuantizeLinearz+Provided node is not a Q/DQ node. Op Type: r?   NFr   rN   T)
r)   r   r   rA   r*   ndimshaper9   rT   all)r6   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   sR   t |j|ks	J | j|j| }t|tr%t|ttf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
        FrF   )r9   r*   r   rA   rG   rH   r   r   )r   r6   r\   rI   rK   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 |vr,| j |jd |jd  || q|rB| j | t	
dt| d dS dS )z>Remove Identity nodes, except those right before graph output.ZIdentityr   zRemoved z Identity nodesN)r   get_graphs_output_namesr5   r)   r2   r4   r*   r:   Zremove_nodesr   infor9   )r   nodes_to_removegraph_output_namesr6   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   rd   
     z&FusionUtils.remove_cascaded_cast_nodesc                 C   rc   r   )r   remove_useless_cast_nodesre   r   r   r   rg     rf   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 ]F}tt|j|@ rtt|j|@ st| j  |jd  dkr| 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)updateNZReshaper   zRemove reshape node z* since its input shape is same as output: r?   )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_namesra   r   r   r   remove_useless_reshape_nodes  s8   

z(FusionUtils.remove_useless_reshape_nodes)r   )NNN)r   r   r   )T)__name__
__module____qualname__r
   r   strtuplerj   r   r#   r   r    r   r7   staticmethodr=   r>   r   r   rD   rL   rP   r   rV   r[   r]   rb   rd   rg   rk   r   r   r   r   r      sD    


*r   c                   @   s(   e Zd ZeddededefddZdS )	NumpyHelperFrM   
fill_zerosr   c                 C   s$   |rt | jt| jdS t| S )N)rX   rO   )r   rQ   r   Ztensor_dtype_to_np_dtyperR   r   to_array)rM   rs   r   r   r   rt   3  s   

zNumpyHelper.to_arrayN)F)rl   rm   rn   rq   r   rj   r   rt   r   r   r   r   rr   2  s    rr   )loggingr   rT   r   r   Zonnxr   r   r   r   r	   rP   Z
onnx_modelr
   rl   r   r   rr   r   r   r   r   <module>   s     $