U
    <ci                     @  s  d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlmZmZmZmZmZmZmZmZ ddlZddlZddlm  mZ ddlmZ ddlmZmZmZ ddlm Z  ddl!m"Z" dd	l#m$Z$ d
Z%ee$ej&ej'f Z(e"j)dd Z*ddddZ+e"j)ddddZ,e"j)dUddddZ-e"j)dd Z.e"j)e%fdddddZ/e"j)ddd d d!d!d!d"d#d$d%Z0e"j)d&d' Z1e"j)d(d) Z2e"j)d*d+ Z3e"j)d,d- Z4e"j)d"d.d/d0Z5G d1d2 d2Z6e"j)d3d4d5d6d7d8d9d:Z7e"j)d3d;d<d5d=d>d?d@Z8e"j)d3d;d<d5d=d>dAdBZ9e"j)dVd3d4dCd7dDdEdFZ:e"j)dddddej;j<j=dddGdGdGdddddde%dHdIdfd3dJdKd!dLdMdMdNdOd!d!d!d!dPdQd!d!d!d!dd d d"dRdSdTZ>dS )WzFunctions to verify exported ONNX model is functionally equivalent to original PyTorch model.

ONNX Runtime is required, and is used as the ONNX backend for export verification.
    )annotationsN)AnyCallableDictMappingOptionalSequenceTupleUnion)_C)
_constants_experimentalutils)GLOBALS)	_beartype)Number)ZCPUExecutionProviderc                 C  s6   g }| D ](}t |tr&|t| q|| q|S N)
isinstancetupleextend_flatten_tuplesappend)elem	flattenedt r   ;/tmp/pip-unpacked-wheel-gikjz4vx/torch/onnx/verification.pyr   !   s    
r   zUnion[list, np.ndarray]returnc                 C  s   t | tjr0| jr"|    S |   S nlt | ttfrLdd | D S t | t	t
tfrft| S t | trg }| D ]}|t|t| | g qx|S | S )Nc                 S  s   g | ]}t |qS r   	_to_numpy).0inpr   r   r   
<listcomp>4   s     z_to_numpy.<locals>.<listcomp>)r   torchTensorZrequires_graddetachcpunumpylistr   boolintfloatnparraydictr   r    )r   r   kr   r   r   r    -   s    

r    r)   c                 C  s0   | D ]&}t |ttfs ||nt|| q|S r   )r   r)   r   r   _inline_flatten_list)inputsZres_listir   r   r   r1   ?   s     r1   Tc                 C  s0   g }| D ]}| tj||d qdd |D S )Ncast_onnx_acceptedc                 S  s   g | ]}t |qS r   r   )r!   vr   r   r   r#   O   s     z$_unpack_to_numpy.<locals>.<listcomp>)r   r   Zunpack_quantized_tensor)valuesr5   Zvalue_unpackedvaluer   r   r   _unpack_to_numpyH   s    r9   c           	      C  s   i }|r*t |d tr*|d }|d d }tt|}i }| D ]\}}t|||< qBt|}|  }t|D ]D\}}|t|ks|| j	|krt
d| d| |||| j	< qp| d |}|S )Nz(got too many positional inputs. inputs: z. kw_inputs: )r   r/   r9   r   itemsr    
get_inputs	enumeratelenname
ValueErrorrun)	ort_sessionr2   Z	kw_inputs
ort_inputsZ
input_nameinputZort_session_inputsr3   ort_outsr   r   r   _run_ortR   s$    rF   zUnion[str, io.BytesIO]zSequence[str])modelort_providersc                 C  sj   zdd l }W n tk
r(   tdY nX |d kr6t}| }d|_|jt| trV| n|  ||d}|S )Nr   z0onnxruntime is required for export verification.   )Z	providers)	onnxruntimeImportError_ORT_PROVIDERSZSessionOptionsZlog_severity_levelZInferenceSessionr   strgetvalue)rG   rH   rJ   Zsession_optionsrB   r   r   r   _ort_sessionh   s    rO   z'Union[Sequence[_NumericType], Sequence]zEOptional[Union[_NumericType, Sequence[_NumericType], Sequence, Dict]]r,   r*   zOptional[float]rE   pt_outsrtolatolcheck_shapecheck_dtypeignore_noneacceptable_error_percentagec                 C  sP  |rt j|\}}nt|gg }t|dd}	t| g } t| t|	ksftdt|  dt|	 d|r|dksz|dk rtdt| |	D ]\}
}z0|st	
|
|\}
}t jj|
||||d	d
 W q tk
rH } zd|r6dt	t	j|
|||dt	|
j  }||kr6td| d| d| d W Y q W 5 d}~X Y qX qdS )a  
    Compare ONNX Runtime and PyTorch outputs.

    Args:
        ort_outs: outputs from ONNX Runtime.
        pt_outs: outputs from PyTorch.
        rtol: relative tolerance in comparison between ONNX and PyTorch outputs.
        atol: absolute tolerance in comparison between ONNX and PyTorch outputs.
        ignore_none: Whether to ignore None type in
            torch output, which is usually the case with tracing. Set this to False, if
            torch output should keep None type, which is usually the case with exporting
            ScriptModules.
        acceptable_error_percentage: acceptable percentage of element mismatches in comparison.
            It should be a float of value between 0.0 and 1.0.

    Raises:
        AssertionError: if outputs from ONNX model and PyTorch model are not
            equal up to specified precision.
        ValueError: if arguments provided are invalid.
    Fr4   z(Number of outputs differ ONNX runtime: (z) PyTorch: ()g      ?g        zAIf set, acceptable_error_percentage should be between 0.0 and 1.0T)rR   rS   rU   Z	equal_nan   )rR   rS   zSuppressed AssertionError:
z.
Error percentage z within acceptable range .N)r$   jit_flattenr1   r9   r>   AssertionErrorr@   zipr-   Zbroadcast_arraysZtestingZassert_closesumiscloseprodshapewarningswarn)rE   rQ   rR   rS   rT   rU   rV   rW   _Z
pt_outs_npZort_outZpt_outeZerror_percentager   r   r   _compare_ort_pytorch_outputs   sV    



rg   c                 C  s<   t | tjtfr| f} t| } |r0t|}ni }| |fS )a  Prepare input for PyTorch model execution.

    Any future changes/formatting to the input before dispatching to the PyTorch
    model should be made in this function.

    Args:
        args: positional arguments for PyTorch model forward method.
        kwargs: keyword arguments for PyTorch model forward method.

    Returns:
        args: positional arguments for PyTorch model forward method.
        kwargs: keyword arguments for PyTorch model forward method.
    )r   r$   r%   r/   copydeepcopy)argskwargsr   r   r   _prepare_input_for_pytorch   s    
rl   c                 C  sD   t | |\} }|s,t| d tr,| i f }n|r<| |f }n| }|S )a  Prepare input for ONNX model export.

    Any future changes/formatting to the input before dispatching to the
    :func:`torch.onnx.export` api should be made in this function.

    Args:
        args: positional arguments for PyTorch model forward method.
        kwargs: keyword arguments for PyTorch model forward method.

    Returns:
        onnx_inputs: positional arguments for ONNX model export, as `args` in
            :func:`torch.onnx.export`.
    r:   )rl   r   r/   )rj   rk   onnx_inputsr   r   r   _prepare_input_for_export   s    rn   c                   s^   t | | |r tj \ }n r< d i kr< dd  |dk	rV fdd|D S  S dS )a  Prepare input for ONNX model execution in ONNX Runtime.

    Any future changes/formatting to the input before dispatching to the ONNX Runtime
    InferenceSession run should be made in this function.

    Args:
        args: positional arguments for PyTorch model forward method.
        kwargs: keyword arguments for PyTorch model forward method.

    Returns:
        onnx_inputs: positional arguments for ONNX model execution in ONNX Runtime.
    r:   Nc                   s   g | ]} | qS r   r   )r!   r3   rm   r   r   r#     s     z*_prepare_input_for_ort.<locals>.<listcomp>)rn   r$   r[   r\   )rj   rk   remained_onnx_input_idxflattenre   r   ro   r   _prepare_input_for_ort  s    
rr   c                 C  s4   zt | W S  tk
r.   td |  Y S X dS )zHUsed for preserving original model in case forward mutates model states.zHFailed to clone model. Model state might be mutated during verification.N)rh   ri   	Exceptionrc   rd   )rG   r   r   r   _try_clone_model  s    rt   )rW   c                   sJ   t j 	f
dd}||| |rF|D ]}||i  q6dS )a  Compare outputs from ONNX model runs with outputs from PyTorch model runs.

    ONNX Runtime is used for model execution backend for ONNX model.

    Raises:
        AssertionError: if outputs from ONNX model and PyTorch model are not
            equal up to specified precision.
    c              
     sT   t | |\}}t}|||}t| |}t|}t||	 d d S )NrP   )rl   rt   rr   rF   rg   )
input_argsinput_kwargsZpt_argsZ	pt_kwargs
model_copyrQ   rC   rE   
rW   rS   rU   rT   rq   rV   rG   rB   rp   rR   r   r   $compare_ort_pytorch_model_with_input@  s(    
   
zH_compare_ort_pytorch_model.<locals>.compare_ort_pytorch_model_with_inputN)r   beartype)rG   rB   ru   rv   additional_test_inputsrp   rq   rV   rR   rS   rT   rU   rW   ry   Ztest_input_argsr   rx   r   _compare_ort_pytorch_model'  s     
r|   c                   @  s^   e Zd ZdZejdddddZejdd Zejddd	d
dZejddddZ	dS )
_GraphDiffz7A class to represent the difference between two graphs._C.Graphgraph_agraph_bc                 C  s   || _ || _dS )zConstruct a _GraphDiff object.

        Args:
            graph_a (_C.Graph): First graph to compare.
            graph_b (_C.Graph): Second graph to compare.
        Nr   )selfr   r   r   r   r   __init__a  s    z_GraphDiff.__init__c                 C  s   |   S )z!See function :func:`diff_report`.)diff_report)r   r   r   r   __str__l  s    z_GraphDiff.__str__rM   )linesr   c                 C  s   d dd | D S )N
c                 S  s   g | ]}d | qS )	r   )r!   liner   r   r   r#   s  s     z&_GraphDiff._indent.<locals>.<listcomp>)join
splitlines)r   r   r   r   r   _indentq  s    z_GraphDiff._indentr   c                 C  s4  | j }| j}t|}t|}||kr(dS t|d|d}d| d|g}t	|
 |
 D ]\}}t|t|krh|d tt|dt|d}	d| d|	g}
|r| nd}|r|
d| t|g |r| nd}|r|
d| t|g ||
  q*qhd	|S )
a)  Return a string representation of the graph difference.

        The report shows the first pair of nodes that diverges. It also shows the source
        location of the pair of nodes.

        Returns:
            graph_diff_report (str): A string representation of the graph difference.
         TzGraph diff:zFirst diverging operator:z
node diff:NzFormer source location:zLatter source location:r   )r   r   rM   difflibZndiffr   r   r   	itertoolszip_longestZnodesr   ZsourceRanger   )r   r   r   Zgraph_a_strZgraph_b_strZ
graph_diffgraph_diff_reportZnode_aZnode_bZ	node_diffZsource_printoutZstack_aZstack_br   r   r   r   u  s@    
 
 
z_GraphDiff.diff_reportN)
__name__
__module____qualname____doc__r   rz   r   r   r   r   r   r   r   r   r}   ^  s   

r}   z.Union[torch.nn.Module, torch.jit.ScriptModule]z3Sequence[Tuple[Tuple[Any, ...], Mapping[str, Any]]]z_experimental.ExportOptionszfCallable[[torch.nn.Module, Tuple[Any, ...], Mapping[str, Any], _experimental.ExportOptions], _C.Graph]rM   )rG   test_input_groupsexport_optionsmodel_to_graph_funcr   c           	      C  s`   t |dk rtdd}|D ]>\}}|| |||}|dkr@|}qt|| }|r|  S qdS )a  Check if graph produced by `model_to_graph_func` is the same across `test_input_groups`.

    Args:
        model: See :func:`check_export_model_diff`.
        test_input_groups: See :func:`check_export_model_diff`.
        export_options: See :func:`check_export_model_diff`.
        model_to_graph_func: A function to convert a PyTorch model to a JIT IR graph.

    Returns:
        graph_diff_report (str): A string representation of the graph difference.
       z3Need at least two groups of test inputs to compare.Nr   )r>   r@   r}   r   )	rG   r   r   r   Zref_jit_graphrj   rk   	jit_graphr   r   r   r   _check_graph_diff  s    
r   zTuple[Any, ...]zMapping[str, Any]r~   )rG   rj   rk   r   r   c           	   
   C  s`   |j }|j}t| ||< t||}t| |} t| |\}}}}|W  5 Q R  S Q R X dS )az  As part of the ONNX export steps, create a traced JIT graph from a PyTorch model.

    Args:
        model: See :func:`check_export_model_diff`.
        args: See :func:`check_export_model_diff`.
        kwargs: See :func:`check_export_model_diff`.
        export_options: See :func:`check_export_model_diff`.

    Returns:
        jit_graph (_C.Graph): A traced JIT graph.
    N)trainingverboser   exporter_contextrn   Z_pre_trace_quant_modelZ_create_jit_graph)	rG   rj   rk   r   r   r   export_inputsr   re   r   r   r   _traced_graph_from_model  s    
r   c                 C  s   |j }|j}|j}|j}|j}|j}	|j}
|j}|dkr>tj	}t
| | |sftjr^tjj}ntjj}|t_|t_t
| ||v t
|j||}|	dkri }	t
|	| |
| t||}t
| |}t
j| |||
|||||	d	\}}}|W  5 Q R  S Q R X dS )ay  As part of the ONNX export steps, export an ONNX JIT graph from a PyTorch model.

    Args:
        model: See :func:`check_export_model_diff`.
        args: See :func:`check_export_model_diff`.
        kwargs: See :func:`check_export_model_diff`.
        export_options: See :func:`check_export_model_diff`.

    Returns:
        onnx_graph (_C.Graph): An ONNX JIT graph.
    N)r   dynamic_axes)opset_versionoperator_export_typeexport_modules_as_functionsr   r   r   input_namesoutput_namesr   ZONNX_DEFAULT_OPSETr   Z_setup_trace_module_map_C_onnxZ_CAFFE2_ATEN_FALLBACKZOperatorExportTypesZONNX_ATEN_FALLBACKZONNXr   Zexport_onnx_opset_versionr   Z_decide_constant_foldingdo_constant_foldingZ_validate_dynamic_axesrn   Z_decide_input_formatZ_model_to_graph)rG   rj   rk   r   r   r   r   r   r   r   r   r   r   r   Z
onnx_graphre   r   r   r   _onnx_graph_from_model  sP    
  
r   z%Optional[_experimental.ExportOptions])rG   r   r   r   c                 C  s8   |dkrt  n|}t| ||t}|r*|S t| ||tS )a  Verify exported model discrepancy between different groups of inputs.

    A graph is exported for each group of inputs. The exported graphs are then compared
    to each other, and discrepancies of first pair of nodes are reported. This function
    first checks the jit graph. If no discrepancies were found, it then checks the onnx
    graph.

    Unless otherwise specified, the jit/ONNX graph is expected to be the same, regardless
    of the inputs used for exporting. A discrepancy implies the graph exported is
    not accurate when run on other groups of inputs, which will typically results in
    runtime errors or mismatching output.

    Args:
        model (torch.nn.Module or torch.jit.ScriptModule): The model to be exported.
        test_input_groups (Sequence[Tuple[Tuple[Any, ...], Mapping[str, Any]]]): A sequence
            of input groups to be used to export the model. Each input group is a pair of
            (args, kwargs).
        export_options (_experimental.ExportOptions, optional): An _experimental.ExportOptions
            object that controls the export behavior.

    Returns:
        str: A string containing the diff of the exported models.
    N)r   ZExportOptionsr   r   r   )rG   r   r   Zjit_diff_reportr   r   r   check_export_model_diff1  s           r   FgMbP?gHz>z$Union[torch.Tensor, Tuple[Any, ...]]zOptional[Mapping[str, Any]]zMOptional[Mapping[str, Union[Mapping[int, str], Mapping[str, Sequence[int]]]]]zOptional[Sequence[str]]ztorch.onnx.TrainingModezOptional[int]z8Optional[Sequence[Union[torch.Tensor, Tuple[Any, ...]]]]zOptional[Sequence[int]])rG   ru   rv   r   r   r   r   r   r   keep_initializers_as_inputsr   fixed_batch_sizeuse_external_datar{   rp   rq   rV   rT   rU   rH   rR   rS   rW   c                 K  s   |t jjjkr|   n|t jjjkr.|   t   t	 }t
 }|rj|t }tj|d}t||}t| }tj| |||||	||||||
d t||}t|||||||||||||d W 5 Q R X W 5 Q R X dS )a  Verify model export to ONNX with ONNX Runtime.

    Args:
        model (torch.nn.Module or torch.jit.ScriptModule): See :func:`torch.onnx.export`.
        input_args (tuple): See :func:`torch.onnx.export`.
        input_kwargs (dict): See :func:`torch.onnx.export`.
        do_constant_folding (bool, optional): See :func:`torch.onnx.export`.
        dynamic_axes (dict, optional): See :func:`torch.onnx.export`.
        input_names (list, optional): See :func:`torch.onnx.export`.
        output_names (list, optional): See :func:`torch.onnx.export`.
        training (torch.onnx.TrainingMode): See :func:`torch.onnx.export`.
        opset_version (int, optional): See :func:`torch.onnx.export`.
        keep_initializers_as_inputs (bool, optional): See :func:`torch.onnx.export`.
        verbose (bool, optional): See :func:`torch.onnx.export`.
        fixed_batch_size (bool, optional): Legacy argument, used only by rnn test cases.
        use_external_data (bool, optional): Explicitly specify whether to export the
            model with external data.
        additional_test_inputs (list, optional): List of tuples. Each tuple is a group of
            input arguments to test. Currently only *args are supported.
        remained_onnx_input_idx (list, optional): If provided, only the specified inputs
            will be passed to the ONNX model. Supply a list when there are unused inputs
            in the model. Since unused inputs will be removed in the exported ONNX
            model, supplying all inputs will cause an error on unexpected inputs.
            This parameter tells the verifier which inputs to pass into the ONNX model.
        flatten (bool, optional): Default True. If True, unpack nested list/tuple/dict
            inputs into a flattened list of Tensors for ONNX. Set this to False if nested
            structures are to be preserved for ONNX, which is usually the case with
            exporting ScriptModules.
        ignore_none (bool, optional): Whether to ignore None type in
            torch output, which is usually the case with tracing. Set this to False, if
            torch output should keep None type, which is usually the case with exporting
            ScriptModules. Default to True.
        check_shape (bool, optional): Whether to check the shapes between
            PyTorch and ONNX Runtime outputs are exactly the same. Set this to False to allow
            output shape broadcasting. Default to True.
        check_dtype (bool, optional): Whether to check the dtypes between
            PyTorch and ONNX Runtime outputs are consistent. Default to True.
        ort_providers (sequence, optional): ONNX Runtime providers to use.
        rtol (float, optional): relative tolerance in comparison between ONNX and PyTorch outputs.
        atol (float, optional): absolute tolerance in comparison between ONNX and PyTorch outputs.
        acceptable_error_percentage (float, optional): acceptable percentage of element mismatches in comparison.
            It should be a float of value between 0.0 and 1.0.

    Raises:
        AssertionError: if outputs from ONNX model and PyTorch model are not
            equal up to specified precision.
        ValueError: if arguments provided are invalid.
    z
model.onnx)	r   r   r   r   r   r   r   r   r   )rG   rB   ru   rv   r{   rp   rq   rV   rR   rS   rT   rU   rW   N)r$   onnxTrainingModeZTRAININGZtrainEVALevalZno_grad
contextlib	ExitStackioBytesIOenter_contexttempfileTemporaryDirectoryospathr   rn   rt   r   Z_exportrO   r|   )rG   ru   rv   r   r   r   r   r   r   r   r   r   r   r{   rp   rq   rV   rT   rU   rH   rR   rS   rW   re   stackZmodel_fZtmpdir_pathZinputs_for_exportrw   rB   r   r   r   verify]  sR    O


r   )T)N)?r   
__future__r   r   rh   r   r   r   r   r   rc   typingr   r   r   r   r   r   r	   r
   r(   r-   r$   Ztorch._C._onnxr   Z_onnxr   Z
torch.onnxr   r   r   Ztorch.onnx._globalsr   Ztorch.onnx._internalr   Ztorch.typesr   rL   r%   ZndarrayZ_NumericTyperz   r   r    r1   r9   rF   rO   rg   rl   rn   rr   rt   r|   r}   r   r   r   r   r   r   r   r   r   r   r   r   <module>   s   (
	
L



6I)C +