o
    ;ήc                     @   s   d dl Z d dlZd dlZd dlmZ d dlmZ d dlmZm	Z	m
Z
mZ d dlmZ d dlmZmZmZmZmZmZmZmZ d dlmZ e eZG dd	 d	ZdS )
    N)deque)Path)DictListOptionalTuple)convert_float_to_float16)AttributeProto
GraphProto
ModelProto	NodeProtoTensorProtohelpernumpy_helper
save_model)SymbolicShapeInferenceHelperc                   @   s  e Zd Zdd Zdd Zdd Zi dfdd	Zd
d Zdd Zdd Z	dd Z
dd Zdd Zdd Zdd Zdd Zdd Zdd Zd d! Zdd#d$Zdd%d&Zdd'd(Zdd)d*Zed+d, Zd-d. Zed/d0 Zd1d2 Zd3d4 Zd5d6 Zdd7d8Zdd9d:Zdd;d<Z g fd=d>Z!d"d"g d"fd?d@Z"dAdB Z#	"	"ddCdDZ$ddFdGZ%ddHdIZ&dJdK Z'dLdM Z(ddOdPZ)dQdR Z*ddSdTZ+ddUdVZ,dWdX Z-dYe.fdZd[Z/ed\e0d]e.fd^d_Z1d`da Z2dbdc Z3ddddeZ4ddfdgZ5ddhdiZ6djdk Z7dldm Z8ddndoZ9ddpdqZ:edrds Z;dtdu Z<ddvdwZ=ddxdyZ>dzd{ Z?ed|d} Z@d~d ZAe		E		dddZBdddZCdd ZDdd ZEedeFdeFdeGfddZHdd ZIde.fddZJd"S )	OnnxModelc                 C   s   |  | d S N)
initializeselfmodel r   J/tmp/pip-target-vg8gfxp4/lib/python/onnxruntime/transformers/onnx_model.py__init__   s   zOnnxModel.__init__c                 C   s"   || _ i | _d | _d| _d | _d S NT)r   _node_name_suffixshape_infer_helperenable_shape_infer
all_graphsr   r   r   r   r      s
   
zOnnxModel.initializec                 C   s
   d| _ d S )NF)r   r   r   r   r   disable_shape_inference   s   
z!OnnxModel.disable_shape_inferenceFc                 C   sd   | j r0| jd u s
|rt| j| _z| j|r| jW S W d S    d| _ tdt d  Y d S d S )NFzfailed in shape inferencer   )r   r   r   r   inferprintsysexc_info)r   dynamic_axis_mappingupdater   r   r   infer_runtime_shape"   s   zOnnxModel.infer_runtime_shapec                 C   sD   i }|   D ]}|jD ]}||vr|g||< q|| | qq|S r   )nodesinputappend)r   input_name_to_nodesnode
input_namer   r   r   r,   0   s   
zOnnxModel.input_name_to_nodesc                 C   s*   i }|   D ]}|jD ]}|||< qq|S r   )r)   output)r   output_name_to_noder-   output_namer   r   r   r0   :   s   

zOnnxModel.output_name_to_nodec                 C   s,   g }|   D ]}|jD ]}|| qq|S r   )graphsr-   r+   )r   	all_nodesgraphr-   r   r   r   r)   A   s   
zOnnxModel.nodesc                 C   s   | j jS r   )r   r4   r    r   r   r   r4   H   s   zOnnxModel.graphc                 C   s   | j d ur| j S g | _ | jjg}|rY|d}| j | |jD ]6}|jD ]0}|jtj	j
kr<t|jts6J ||j |jtj	jkrU|jD ]}t|tsOJ || qFq%q |s| j S Nr   )r   r   r4   popr+   r-   	attributetyper	   AttributeTypeGRAPH
isinstancegr
   GRAPHSr2   )r   graph_queuer4   r-   attrr<   r   r   r   r2   K   s(   





zOnnxModel.graphsc                 C   .   g }|   D ]}|jD ]}||j qq|S r   )r2   r*   r+   name)r   input_namesr4   r*   r   r   r   get_graphs_input_names^      
z OnnxModel.get_graphs_input_namesc                 C   r@   r   )r2   r/   r+   rA   )r   output_namesr4   r/   r   r   r   get_graphs_output_namese   rD   z!OnnxModel.get_graphs_output_namesc                 C   s$   |   D ]}||jv r|  S qd S r   )r2   r-   r   r-   r4   r   r   r   get_graph_by_nodel   
   
zOnnxModel.get_graph_by_nodec                 C   s$   |   D ]}||jkr|  S qd S r   )r2   rA   )r   
graph_namer4   r   r   r   get_graph_by_namer   rI   zOnnxModel.get_graph_by_namec                 C   s>   t |jD ]\}}|jD ]}||v r|    S qqt|jS r   )	enumerater-   r*   len)r   r4   outputsidxr-   r*   r   r   r   get_topological_insert_idx   s   

z#OnnxModel.get_topological_insert_idc                 C   s(   |   D ]}||jv r|j| qd S r   )r2   r-   removerG   r   r   r   remove_node   s
   
zOnnxModel.remove_nodec                 C   s   |D ]}|  | qd S r   )rR   )r   nodes_to_remover-   r   r   r   remove_nodes   s   zOnnxModel.remove_nodesNc                 C   sV   |d u s|| j jjkr| j jj|g d S | |}| ||j}|j|| d S r   )	r   r4   rA   r-   extendrK   rP   r/   insert)r   r-   rJ   r4   
insert_idxr   r   r   add_node   s
   
zOnnxModel.add_nodec                 C   s@   |d u r| j jj| d S |D ]}||j }| || qd S r   )r   r4   r-   rU   rA   rX   )r   nodes_to_addnode_name_to_graph_namer-   rJ   r   r   r   	add_nodes   s   
zOnnxModel.add_nodesc                 C   H   |d u s|| j jjkr| j jj|g d S | |}|j|g d S r   )r   r4   rA   initializerrU   rK   )r   tensorrJ   r4   r   r   r   add_initializer      
zOnnxModel.add_initializerc                 C   r\   r   )r   r4   rA   r*   rU   rK   )r   r*   rJ   r4   r   r   r   	add_input   r`   zOnnxModel.add_inputc                 C   H   t |tr
t |tsJ tt| jD ]}| j| |kr!|| j|< qd S r   )r;   strrangerM   r*   )r-   old_input_namenew_input_namejr   r   r   replace_node_input      
zOnnxModel.replace_node_inputc                 C   "   | j jjD ]	}t||| qd S r   )r   r4   r-   r   rh   )r   re   rf   r-   r   r   r   replace_input_of_all_nodes      z$OnnxModel.replace_input_of_all_nodesc                 C   rb   r   )r;   rc   rd   rM   r/   )r-   old_output_namenew_output_namerg   r   r   r   replace_node_output   ri   zOnnxModel.replace_node_outputc                 C   rj   r   )r   r4   r-   r   ro   )r   rm   rn   r-   r   r   r   replace_output_of_all_nodes   rl   z%OnnxModel.replace_output_of_all_nodesc                 C   s4   |   D ]}|jD ]}|j|kr|    S q	qd S r   )r2   r]   rA   )r   rA   r4   r^   r   r   r   get_initializer   s   

zOnnxModel.get_initializerc                 C   s*   g }|   D ]}|j|kr|| q|S r   )r)   op_typer+   )r   rr   r)   r-   r   r   r   get_nodes_by_op_type   s   

zOnnxModel.get_nodes_by_op_typec                 C   sD   |d u r|   }g }|jD ]}||v r|| D ]}|| qq|S r   )r,   r/   r+   )r   r-   r,   childrenr/   r   r   r   get_children   s   
zOnnxModel.get_childrenc                 C   s:   |d u r|   }g }|jD ]}||v r|||  q|S r   )r0   r*   r+   )r   r-   r0   parentsr*   r   r   r   get_parents   s   
zOnnxModel.get_parentsc                 C   s@   |d u r|   }t|j|krd S |j| }||vrd S || S r   )r0   rM   r*   )r   r-   ir0   r*   r   r   r   
get_parent   s   
zOnnxModel.get_parentc                 C   s^   t |jD ]'\}}||v r,|| }|j|kr ||vr ||f  S td| d|j  qdS )a  
        Find parent node based on constraints on op_type.

        Args:
            node (str): current node name.
            parent_op_type (str): constraint of parent node op_type.
            output_name_to_node (dict): dictionary with output name as key, and node as value.
            exclude (list): list of nodes that are excluded (not allowed to match as parent).

        Returns:
            parent: The matched parent node. None if not found.
            index: The input index of matched parent node. None if not found.
        zTo find first z
, current NN)rL   r*   rr   loggerdebug)r   r-   parent_op_typer0   excluderx   r*   parentr   r   r   match_first_parent   s   zOnnxModel.match_first_parentc           	      C   s   |dusJ |du s|dksJ |du r|   }|du r1| ||||\}}|dur/|| |S |t|jkrHtd| dt|j  dS | |||}|dur^|j|kr^||vr^|S |durntd| d|j  dS )a"  
        Find parent node based on constraints on op_type and index.
        When input_index is None, we will find the first parent node based on constraints, and return_indice will be appended the corresponding input index.

        Args:
            node (str): current node name.
            parent_op_type (str): constraint of parent node op_type.
            input_index (int or None): only check the parent given input index of current node.
            output_name_to_node (dict): dictionary with output name as key, and node as value.
            exclude (list): list of nodes that are excluded (not allowed to match as parent).
            return_indice (list): a list to append the input index when input_index is None.

        Returns:
            parent: The matched parent node.
        Nr   zinput_index z >= node inputs zExpect z, Got )	r0   r   r+   rM   r*   r{   r|   ry   rr   )	r   r-   r}   input_indexr0   r~   return_indicer   indexr   r   r   match_parent  s$   
zOnnxModel.match_parentc                 C   s^   t |D ](\}}t|tst|tsJ g }| ||d |d ||}|r,|||f  S qdS )Nr      )NN)rL   r;   r   r   match_parent_path)r   r-   pathsr0   rx   pathr   matchedr   r   r   match_parent_paths4  s   zOnnxModel.match_parent_pathsc              	   C   s   t |t |ks
J |du r|  }|}g }t|D ]1\}}	| j||	|| |g |d}
|
du rDtjd| d||  d|	 dd  dS ||
 |
}q|S )a   
        Find a sequence of input edges based on constraints on parent op_type and index.
        When input_index is None, we will find the first parent node based on constraints, and return_indice will be appended the corresponding input index.

        Args:
            node (str): current node name.
            parent_op_types (str): constraint of parent node op_type of each input edge.
            parent_input_index (list): constraint of input index of each input edge. None means no constraint.
            output_name_to_node (dict): dictionary with output name as key, and node as value.
            return_indice (list): a list to append the input index when there is no constraint on input index of an edge.

        Returns:
            parents: a list of matched parent node.
        N)r~   r   zFailed to match index=z parent_input_index=z	 op_type=T)
stack_info)rM   r0   rL   r   r{   r|   r+   )r   r-   parent_op_typesparent_input_indexr0   r   current_nodematched_parentsrx   rr   matched_parentr   r   r   r   =  s.   
zOnnxModel.match_parent_pathTc           	      C   sj   |  ||}t|}t|dkr3| }|j|kr|S |r-|  ||}|D ]}|| q%t|dksd S r5   )ru   r   rM   r6   rr   
appendleft)	r   r-   
child_typer,   	recursivert   dqr   childr   r   r   find_first_child_by_typeo  s   

z"OnnxModel.find_first_child_by_typec           	      C   sz   |d u r|   }| ||}t|}t|dkr;| }|j|kr#|S |r5| ||}|D ]}|| q-t|dksd S r5   )r0   rw   r   rM   r6   rr   r   )	r   r-   parent_typer0   r   rv   r   r   r   r   r   r   find_first_parent_by_type~  s   

z#OnnxModel.find_first_parent_by_typec                 C   sh   |  dD ]}|jd |kr#|jD ]}|jdkr"t|j    S qq| |}|d ur2t|S d S )NConstantr   value)rs   r/   r7   rA   r   to_arraytrq   )r   r1   r-   attr]   r   r   r   get_constant_value  s   



zOnnxModel.get_constant_valuec                 C   s6   t |jD ]\}}| |}|d ur||f  S qdS )Nrz   )rL   r*   r   )r   r-   rx   r*   r   r   r   r   get_constant_input  s   
zOnnxModel.get_constant_inputư>c                 C   s8   |  |\}}|d ur|jdkrt|| |k r|S dS )Nr   r   )r   sizeabs)r   r-   expected_valuedeltarx   r   r   r   r   find_constant_input  s   "zOnnxModel.find_constant_inputc              	   C   sd   |  |}|d u rt| d| d dS t|j|kr0t| d| d| d|j  dS dS )N z is not initializer.Fz shall have z dimensions. Got shape T)r   r{   r|   rM   shape)r   r1   
dimensionsdescriptionr   r   r   r   $is_constant_with_specified_dimension  s   
"z.OnnxModel.is_constant_with_specified_dimensionc                 C   s   |  |||dkS r5   )r   )r   r-   r   r   r   r   r   has_constant_input  s   zOnnxModel.has_constant_inputc           
      C   s   |d u r|   }||jd  }g }t|}t|dkrK| }||v r$q||vrE|| |jD ]}||v rD|| }|D ]}	||	 q<q0t|dks|S r5   )r,   r/   r   rM   r6   r+   r   )
r   	root_node
stop_nodesr,   rt   unique_nodesr   r   r/   r   r   r   r   get_children_subgraph_nodes  s&   

z%OnnxModel.get_children_subgraph_nodesc                 C   sP   g }|j jD ]}|dr||j q|dr ||j q|d q|S )zConvert tensor shape to list	dim_value	dim_param?)r   dimHasFieldr+   r   r   )r   tensor_type
shape_listdr   r   r   tensor_shape_to_list  s   

zOnnxModel.tensor_shape_to_listinput_or_outputc                 C   s\   dd | j jjD }||v r|| jjS | |}|r |jjjS | |}|r,|jjjS dS )zMTry get data type given a name (could be initializer, graph input or output).c                 S   s   i | ]}|j |jqS r   )rA   r8   ).0objr   r   r   
<dictcomp>  s    z'OnnxModel.get_dtype.<locals>.<dictcomp>N)r   r4   
value_infor   	elem_typefind_graph_inputr8   find_graph_output)r   r   tensor_type_mapgraph_inputgraph_outputr   r   r   	get_dtype  s   



zOnnxModel.get_dtyper-   attribute_namec                 C   s,   | j D ]}|j|krt|}|  S qd S r   )r7   rA   r   get_attribute_value)r-   r   r?   r   r   r   r   get_node_attribute  s   


zOnnxModel.get_node_attributec                 C   s~   |   }d}|  D ]"}|jdkr,| j|d|d}|r,|jdkr,|jd |jd< |d7 }q
|dkr=td| |   dS dS )av  Remove Cast node that are followed by another Cast node like  --> Cast --> Cast -->
        Note that this shall be used carefully since it might introduce semantic change.
        For example, float -> int -> float could get different value than the original float value.
        So, it is recommended to used only in post-processing of mixed precision conversion.
        r   Cast)r0   r   zRemoved %d cascaded Cast nodesN)r0   r)   rr   ry   r*   r{   infoprune_graph)r   r0   removed_countr-   r   r   r   r   remove_cascaded_cast_nodes  s   
z$OnnxModel.remove_cascaded_cast_nodesc                    s   j dddu rtd dS  fdd}g }  D ] }|jdkr>||jd }||jd }|r>||kr>|| q|rt 	 }t 
 }|D ]3}tt|j|@ rqtt|j|@ sp |jd |jd  nqO |jd |jd   | qOtd	t| dS dS )
zKRemove cast nodes that are not needed: input and output has same data type.T)r'   Nz>Skip removing useless cast nodes since shape inference failed.c                    s:     | }|r	|S j|  jjdrj|  jjjS d S )Nr   )r   	known_vi_r8   r   r   r   )input_or_output_namedtyper   shape_inferr   r   get_data_type  s   
z:OnnxModel.remove_useless_cast_nodes.<locals>.get_data_typer   r   z4Removed %d Cast nodes with output type same as input)r(   r{   r   r)   rr   r*   r/   r+   setrC   rF   boolrp   rk   rR   rM   )r   r   rS   r-   input_dtypeoutput_dtypegraph_input_namesgraph_output_namesr   r   r   remove_useless_cast_nodes  s2   


z#OnnxModel.remove_useless_cast_nodesc                 C   s   t d | jd|d d S )NzbThe function convert_model_float32_to_float16 is deprecated. Use convert_float_to_float16 instead!T)use_symbolic_shape_inferkeep_io_types)r{   warningr   )r   cast_input_outputr   r   r    convert_model_float32_to_float163  s   z*OnnxModel.convert_model_float32_to_float16c                    s   d vrd d< | j }|rt|}|j|ddd}d|i}| fdddD  t|fi |}| | |   |   d	S )
aA  Convert a model to half (default) or mixed precision.
           To use mixed precision, user need specify which graph inputs, outputs, operator type or list of nodes shall keep in float32.
           By default, we use symbolic shape inference to get shape and type information. If not, ONNX shape inference will be used.
           Note that symbolic/ONNX shape inference might fail, and the conversion might not proceed without shape and type information.

        Args:
            use_symbolic_shape_infer (bool, optional): use symbolic shape inference instead of onnx shape inference. Defaults to True.
            keep_io_types (Union[bool, List[str]], optional): It could be boolean or a list of float32 input/output names.
                                                              If True, model inputs/outputs should be left as float32. Defaults to False.
            op_block_list (List[str], optional): List of operator types to leave as float32.
                                                 Defaults to None, which will use `float16.DEFAULT_OP_BLOCK_LIST` as default.
            node_block_list (List[str], optional): List of node names to leave as float32. Defaults to None.
            force_fp16_initializers(bool): force converting all float initializers to float16.
                                           Default to false, which will convert only the one needed to avoid precision loss.
            min_positive_val (float, optional): minimal positive value. Defaults to 1e-7.
            max_finite_val (float, optional): maximal finite value. Defaults to 1e4.
        r   TF)
auto_mergeguess_output_rankdisable_shape_inferc                    s   i | ]}| v r| | qS r   r   )r   keykwargsr   r   r   V  s    z6OnnxModel.convert_float_to_float16.<locals>.<dictcomp>)r   min_positive_valmax_finite_valop_block_listnode_block_listforce_fp16_initializersN)r   r   infer_shapesr'   r   r   r   r   )r   r   r   r   r   
parameters
fp16_modelr   r   r   r   9  s    

z"OnnxModel.convert_float_to_float16c              	   C   s   |r| dr	|n|d }n|d }d}|| jv r!| j| d }n.|  D ])}|jrN|j|rNzt|jt|d }t|d |}W q% tyM   Y q%w q%|| j|< |t	| S )ar  Create a unique node name that starts with a prefix (default is operator type).
           The name will not be duplicated with any name that generated or existed in current graphs.
        Args:
            op_type (str): operator type
            name_prefix (str, optional): prefix of node name. Defaults to None.

        Returns:
            str: node name
        _r   r   N)
endswithr   r)   rA   
startswithintrM   max
ValueErrorrc   )r   rr   name_prefixprefixsuffixr-   r   r   r   r   create_node_namek  s"   

zOnnxModel.create_node_namec                 C   &   | j jjD ]}|j|kr|  S qd S r   )r   r4   r*   rA   )r   r.   r*   r   r   r   r     
   
zOnnxModel.find_graph_inputc                 C   r   r   )r   r4   r/   rA   )r   r1   r/   r   r   r   r     r   zOnnxModel.find_graph_outputc           	      C   s   |d u r|   }g }| ||}t|}t|dkrC| }||v r#q||vr=|| |jD ]}||v r<|||  q/t|dks|S r5   )r0   rw   r   rM   r6   r+   r*   r   )	r   r-   r   r0   r   rv   r   r   r*   r   r   r   get_parent_subgraph_nodes  s"   

z#OnnxModel.get_parent_subgraph_nodesc                 C   sr   g }|j D ]}| |r||vr|| q|r7| |g }|D ]}|j D ]}| |r5||vr5|| q%q |S )z@
        Find graph inputs that linked to current node.
        )r*   r   r+   r   )r   r   r   graph_inputsr*   parent_nodesr-   r   r   r   get_graph_inputs  s   



zOnnxModel.get_graph_inputsc                 C   s,   d}|j D ]}|| kr|  S |d7 }qdS )Nr   r   r   )r*   )node_output
child_noder   r*   r   r   r   r     s   

zOnnxModel.input_indexc                 C   sr   |   }g }|  }|D ]}|jdkr|jd |vr|| q| | t|dkr7tdt|  d S d S )Nr   r   zRemoved unused constant nodes: )	r,   r)   rr   r/   r+   rT   rM   r{   r|   )r   r,   unused_nodesr)   r-   r   r   r   remove_unused_constant  s   

z OnnxModel.remove_unused_constantc                 C   s~  t |  dkrtd dS |du rdd | jjjD }|  }g }|D ]}||v rD|| }||v r4q%| |g }|	| |
| q%g }| jjjD ]}||vrW|	| qL| | g }	| jjjD ]}|j|vrp|		| qd|	D ]
}| jjj| qs|  }
g }| jjjD ]}|j|
vr|	| q|D ]
}| jjj| q|s|	s|rtdt |t |	t | |   dS )a@  
        Prune graph to keep only required outputs. It removes unnecessary inputs and nodes.
        Nodes are not linked (directly or indirectly) to any required output will be removed.

        Args:
            outputs (list): a list of graph outputs to retain. If it is None, all graph outputs will be kept.
        r   z)Skip prune_graph since graph has subgraphNc                 S      g | ]}|j qS r   rA   )r   r/   r   r   r   
<listcomp>      z)OnnxModel.prune_graph.<locals>.<listcomp>z<Graph pruned: {} inputs, {} outputs and {} nodes are removed)rM   r2   r{   r|   r   r4   r/   r0   r   r+   rU   r-   rT   rA   rQ   r,   r*   r   formatupdate_graph)r   rN   r0   r3   r/   	last_noder)   rS   r-   output_to_remover,   input_to_remover*   r   r   r   r     sX   








zOnnxModel.prune_graphc                 C   sx  | j j}g }|jD ]'}|jdv rtd|j   d S |jdkr0|jD ]}||vr/|| q$q	|r;td|  g }|jD ]}|j|vrL|| q@|D ]}|j	| qOdd |D }tdt
| d|  g }	g }
|jD ]}|j|vr| |js|	| qs|
|j qs|	D ]}|j	| qd	d |	D }tdt
|	 d
|  |rtd|
  |   d S )N)LoopScanIfz,Skip update_graph since graph has operator: r   zremaining input names: c                 S   r  r   r  r   r*   r   r   r   r  +  r  z*OnnxModel.update_graph.<locals>.<listcomp>zremove z unused inputs: c                 S   r  r   r  )r   r]   r   r   r   r  9  r  z unused initializers: zremaining initializers:)r   r4   r-   rr   r{   r|   r*   r+   rA   rQ   rM   r]   r   r  )r   verboser4   remaining_input_namesr-   r.   inputs_to_remover*   names_to_removeweights_to_removeweights_to_keepr]   r   r   r   r    sF   








zOnnxModel.update_graphc              	   C   sb   |D ],}|j D ]&}||v rq||v r-|| D ]}||vr,td| d|     dS qqqdS )Nz,it is not safe to remove nodes since output z is used by FT)r/   r{   r|   )r   rS   keep_outputsr,   r0   node_to_remover	  impacted_noder   r   r   is_safe_to_fuse_nodes@  s   

zOnnxModel.is_safe_to_fuse_nodesc                 C   s  dgt | j }i }g }t| jD ]6\}}tdd |jD ||< || dkr0|| j|  q|jD ]}||vr?|g||< q3|| | q3qdd | jD }dd | jD }|| }	|	  d }
|	D ]*}|
|krkqd|}
||v r|| D ]}|| d ||< || dkr|| j|  quqdd}t |}||k r|| jD ]'}||v r|| D ]}|| d ||< || dkr|| j|  |d }qq|d }||k s|t | jkrt	d| d	t | j d
| j|  | 
d | j| d S )Nr   c                 s   s    | ]}|rd V  qdS )r   Nr   )r   r   r   r   r   	<genexpr>V  s    z3OnnxModel.graph_topological_sort.<locals>.<genexpr>c                 S   r  r   r  )r   initr   r   r   r  b  r  z4OnnxModel.graph_topological_sort.<locals>.<listcomp>c                 S   r  r   r  r  r   r   r   r  c  r  r   zGraph is not a DAG: end=z, len(graph.node)=z, graph.node[end]=r-   )rM   r-   rL   sumr*   r+   r]   sortr/   RuntimeError
ClearFieldrU   )r4   
deps_countdeps_to_nodessorted_nodesnode_idxr-   r.   initializer_namesr   rB   prev_input_namestartendr/   r   r   r   graph_topological_sortO  s^   

 
z OnnxModel.graph_topological_sortc                 C   s   t | jj d S r   )r   r'  r   r4   r    r   r   r   topological_sort  s   zOnnxModel.topological_sort   c           	   	   C   s   t |jjddd |rht |j}|jddd |d }|r#t |jnd }tj|r8td|  t	| |rNtj|rMtd|  t	| nt
|r[td| dt| |d||||d d S t| | d S )	NT)rv   exist_okz.datazDelete the existed onnx file: z'Delete the existed external data file: zOutput directory (z!) for external data is not empty.)save_as_external_dataall_tensors_to_one_filelocationsize_thresholdconvert_attribute)r   r   mkdirrA   osr   existsr{   r   rQ   listdirr  r   )	r   output_pathr+  r,  r.  r/  
output_direxternal_data_pathr-  r   r   r   save  s4   	





zOnnxModel.savec                 C   s|   t d |   |dr+t|d}|tt W d    n1 s%w   Y  n	t	| j||| t d|  d S )Nz Sort graphs in topological orderz.jsonwzModel saved to )
r{   r   r(  r   openwriterc   r   r   r7  )r   r4  use_external_data_formatr,  outr   r   r   save_model_to_file  s   

zOnnxModel.save_model_to_filec                 C   s2   g }| j jjD ]}| |jdu r|| q|S )z[
        Returns real graph inputs (excluding initializers from older onnx model).
        N)r   r4   r*   rq   rA   r+   )r   r   r*   r   r   r   'get_graph_inputs_excluding_initializers  s   
z1OnnxModel.get_graph_inputs_excluding_initializersc                 C   s*   | j jD ]}|jdv r|j  S qtd)zGet opset version of onnx domain

        Raises:
            RuntimeError: ONNX model has no opset for default domain.

        Returns:
            int: opset version of onnx domain.
        ) zai.onnxz*ONNX model has no opset for default domain)r   opset_importdomainversionr  )r   opsetr   r   r   get_opset_version  s
   	

zOnnxModel.get_opset_versiontensor1tensor2returnc                 C   sP   | j |j ks| j|jkrdS | dr|dr| j|jkS t| t|kS )a"  Returns True when two tensors have same value.
           Note that name can be different.

        Args:
            tensor1 (TensorProto): initializer 1
            tensor2 (TensorProto): initializer 2

        Returns:
            bool: True when two intializers has same value.
        Fraw_data)	data_typedimsr   rH  r   r   )rE  rF  r   r   r   has_same_value  s
   zOnnxModel.has_same_valuec                 C   s  t |  dkrtd t | jjj}dg| }t|d D ]'}|| dkr(qt|d |D ]}t	| jjj| | jjj| rE|||< q/qd}t|D ] }|| dkrm|d7 }| 
| jjj| j| jjj||  j qM|dkr|   td| d dS dS )zRemove initializers with duplicated values, and only keep the first one.
        It could help reduce size of models (like ALBert) with shared weights.
        Note: this function does not process subgraph.
        r   z9remove_duplicated_initializer does not process subgraphs.r   r   zRemoved z# initializers with duplicated valueN)rM   r2   r{   r   r   r4   r]   rd   r   rK  rk   rA   r  r#   )r   initializer_countsamerx   rg   countr   r   r   remove_duplicated_initializer  s0   

  z'OnnxModel.remove_duplicated_initializerr   c                 C   s@  t |  dkrtd dd | jjjD dd | jjjD  }| jjjD ]}|j	|vr:||j	 |vr:||j	 |_	q&| jjj
D ]J}tt |jD ]}|j| |vre||j|  |vre||j|  |j|< qItt |jD ]}|j| |vr||j|  |vr||j|  |j|< qmq@| jjjD ]}|j	|vr||j	 |_	qdS )zAdd prefix to initializer or intermediate outputs in graph. Main graph inputs and outputs are excluded.
        It could help avoid conflicting in name of node_args when merging two graphs.
        Note: this function does not process subgraph.
        r   z/add_prefix_to_names does not process subgraphs.c                 S   r  r   r  )r   rx   r   r   r   r    r  z1OnnxModel.add_prefix_to_names.<locals>.<listcomp>c                 S   r  r   r  )r   or   r   r   r    r  N)rM   r2   r{   r   r   r4   r*   r/   r]   rA   r-   rd   r   )r   r   excludedr]   r-   rg   r   r   r   r   add_prefix_to_names  s2   
(

zOnnxModel.add_prefix_to_namesr   rz   r   )r   )T)F)FTr)  F)FT)K__name__
__module____qualname__r   r   r!   r(   r,   r0   r)   r4   r2   rC   rF   rH   rK   rP   rR   rT   rX   r[   r_   ra   staticmethodrh   rk   ro   rp   rq   rs   ru   rw   ry   r   r   r   r   r   r   r   r   r   r   r   r   r   rc   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r   r  r  r'  r(  r7  r=  r>  rD  r   r   rK  rO  rR  r   r   r   r   r      s    











1

2




&

2"




;-
7
*
r   )loggingr1  r$   collectionsr   pathlibr   typingr   r   r   r   float16r   onnxr	   r
   r   r   r   r   r   r   r   r   	getLoggerrS  r{   r   r   r   r   r   <module>   s   (
