o
    XiQ,                     @   sz   d dl Z d dlZd dlmZ d dlZd dlZd dlZd dlm	Z
 d dlmZ dd ZG dd dZdd	ee fd
dZdS )    N)Iterator)__file__)onnx_exportc                 C   sV   dd }t t||  r"| tj}|tj}tj|| dS tj| | dS )zCompares two arrays knowing they contain strings.
    Raises an exception if the test fails.

    Args:
        expected: expected array
        value: value
    c                 S   s   zW dS  t y   Y dS w )NTF)
ValueError)x r   S/home/ubuntu/.local/lib/python3.10/site-packages/onnxscript/backend/onnx_backend.pyis_float   s
   z,assert_almost_equal_string.<locals>.is_floatN)allmapravelastypenpfloat32testingassert_almost_equal)expectedvaluer	   expected_floatvalue_floatr   r   r   assert_almost_equal_string   s   	r   c                   @   s|   e Zd ZdZedd Zedd Zedd Zdd	 Zd
d Z	e
dd Zdd ZdddZdd ZdddZdd ZdS )OnnxBackendTesta,  Definition of a backend test. It starts with a folder,
    in this folder, one onnx file must be there, then a subfolder
    for each test to run with this model.

    Args:
        folder: test folder
        onnx_path: onnx file
        onnx_model: loaded onnx file
        tests: list of test
    c                 C   sT   g }| D ]}t j|d }|dd }|t||f q|  dd |D S )Nr   _c                 S   s   g | ]}|d  qS )   r   ).0r   r   r   r   
<listcomp><   s    z)OnnxBackendTest._sort.<locals>.<listcomp>)ospathsplitextsplitappendintsort)	filenamestempfnameir   r   r   _sort4   s   zOnnxBackendTest._sortc                 C   s  t j| std| dt| d}| }W d    n1 s"w   Y  ztjt	|}W |S  t
y } zKt }z|| tj|}W n+ t
yw   zt|}W n t
yt   td| d| d|d d d|w Y nw W Y d }~|S W Y d }~|S d }~ww )NzFile not found: .rbzUnable to read z, error is z, content is d   )r   r   existsFileNotFoundErroropenreadonnxnumpy_helperto_arrayload_tensor_from_string	ExceptionSequenceProtoParseFromStringto_listload_model_from_stringRuntimeError)fullr&   
serializedloadedeseqr   r   r   _read_proto_from_file>   sB   



	
z%OnnxBackendTest._read_proto_from_filec                 C   s   g }|D ]:}t j| |}t|}t|tjtj	t
fr|}nt|tjr,tj|}ntdt|d|d|| q|S )NzUnexpected type z for r*   )r   r   joinr   r@   
isinstancer   ndarrayr1   
ModelProtolistTensorProtor2   r3   r:   typer!   )foldernamesresr'   r;   
new_tensortr   r   r   _loadU   s   
zOnnxBackendTest._loadc                 C   s   | j j d| jdS )Usual())	__class____name__rH   selfr   r   r   __repr__f   s   zOnnxBackendTest.__repr__c           
      C   s  t j|std|dt |}dd |D }t|dkr+td|d|d|| _t j||d	 | _	t
| j	| _g | _|D ]A}t j||}t j|rd
d t |D }tdd |D }tdd |D }tt||t||d}	| j|	 qDd S )NzUnable to find folder r*   c                 S   $   g | ]}t j|d  dv r|qS r   >   .onnxr   r   r   r   cr   r   r   r   n      $ z,OnnxBackendTest.__init__.<locals>.<listcomp>r   z$There is more than one onnx file in z ().r   c                 S   rV   )r   >   .pbrY   rZ   r   r   r   r   {   r\   c                 s       | ]
}| d r|V  qdS )input_N
startswithrZ   r   r   r   	<genexpr>|       z+OnnxBackendTest.__init__.<locals>.<genexpr>c                 s   r_   )output_Nra   rZ   r   r   r   rc   }   rd   )inputsoutputs)r   r   r-   r.   listdirlenr   rH   rA   	onnx_pathr1   load
onnx_modeltestsisdirr   r)   dictrM   r!   )
rT   rH   contentonxsubr;   pbrf   rg   rL   r   r   r   __init__j   s2   


zOnnxBackendTest.__init__c                 C   s   t j| jd S )zReturns the test name.r   )r   r   r    rH   rS   r   r   r   r'      s   zOnnxBackendTest.namec                 C   s
   t | jS )zReturns the number of tests.)ri   rm   rS   r   r   r   __len__      
zOnnxBackendTest.__len__Nc                 C   s  t |tjrt |tjr{|du r%|jtjkrd}n|jtjkr"d}nd}n|}|jtjkrRzt|| W dS  tyQ } ztd| d| d| j	 d|d}~ww ztj
j|||d	 W dS  tyz } ztd| d| d| j	 d|d}~ww t|d
r|j|jkrtd| d| d| j	 d|j d| d||jstd| d| d| j	 d|j d| ddS dS tdt|d)a  Compares the expected output and the output produced
        by the runtime. Raises an exception if not equal.

        Args:
            index: test index
            i: output index
            e: expected output
            o: output
            decimal: precision
        N         Output 	 of test  in folder z failed.decimalis_compatiblez failed (e.dtype=z, o=r]   z failed (e.shape=z$Comparison not implemented for type r*   )rB   r   rC   dtyper   float64object_r   AssertionErrorrH   r   r   hasattrr   shapeNotImplementedErrorrG   )rT   indexr(   r>   or~   deciexr   r   r   _compare_results   sl   
z OnnxBackendTest._compare_resultsc                 C   s
   d| j v S )z#Returns whether the test is random.	bernoulli)rH   rS   r   r   r   	is_random   rv   zOnnxBackendTest.is_randomc                 C   sP  |du rt t| D ]}| j||||d q
dS || j}||g| j| d R  }| j| d }t|t|krOtd| d| j dt| dt| d		tt||D ]O\}\}	}
| 	 r|	j
|
j
kr}td
| d| d| j d|	j
 d|
j
 d|	j|
jkrtd
| d| d| j d|	j d|
j dqV| j|||	|
|d qVdS )a  Executes a tests or all tests if index is None.
        The function crashes if the tests fails.

        Args:
            load_fct: loading function, takes a loaded onnx graph, and
                returns an object
            run_fct: running function, takes the result of previous
                function, the inputs, and returns the outputs
            index: index of the test to run or all.
            decimal: requested precision to compare results
        N)r   r~   rf   rg   z"Unexpected number of output (test z	, folder z), got z, expected r*   rz   r{   r|   z failed (type mismatch z != r]   z failed (shape mismatch r}   )rangeri   runrl   rm   r   rH   	enumeratezipr   r   r   r   )rT   load_fctrun_fctr   r~   r(   objgotr   r>   r   r   r   r   r      sP   
zOnnxBackendTest.runc                 C   s2  g }t | j}|d}dd |D }|td| |d | jD ]Z}|d |d D ]}|t	|dd q2|d	 |d
 |d D ]}|t	|dd qO|d	 |d |d |d |d |d |d q'd|}dd| j
 dt	|dg}|S )z^Returns a python code equivalent to the ONNX test.

        Returns:
            code
        
c                 S   s,   g | ]}|  d s|  ds|qS )printz# )striprb   )r   liner   r   r   r      s    z-OnnxBackendTest.to_python.<locals>.<listcomp>z oinf = OnnxInference(onnx_model)zxs = [rf   ,z        ]zys = [rg   z4feeds = {n: x for n, x in zip(oinf.input_names, xs)}zgot = oinf.run(feeds)z*goty = [got[k] for k in oinf.output_names]zfor y, gy in zip(ys, goty):z     self.assertEqualArray(y, gy) zdef z(self):z    )r   export2onnxrl   r    r!   textwrapdedentrA   rm   indentr'   )rT   rowscodelinestestinpoutfinalr   r   r   	to_python   s4   












 zOnnxBackendTest.to_pythonN)NN)rR   
__module____qualname____doc__staticmethodr)   r@   rM   rU   rt   propertyr'   ru   r   r   r   r   r   r   r   r   r   (   s"    
	



3
)r   returnc           	      c   s    t jt}t j|d| }t j|s%td|ddt | t |}|D ])}|dur7||s7q,t j||}t |}dd |D }t|dkrUt	|V  q,dS )	a  Collects test from a sub folder of `onnx/backend/test`.
    Works as an enumerator to start processing them
    without waiting or storing too much of them.

    Args:
        series: which subfolder to load, possible values: (`'node'`,
            ...)
        fct_filter: function `lambda testname: boolean` to load or skip
            the test, None for all

    Yields:
        list of @see cl OnnxBackendTest
    dataz"Unable to find series of tests in z, subfolders:
r   Nc                 S   rV   rW   rY   rZ   r   r   r   r   -  r\   z(enumerate_onnx_tests.<locals>.<listcomp>r   )
r   r   dirnamebackend_folderrA   r-   r.   rh   ri   r   )	series
fct_filterrootrr   rm   rL   rH   rp   rq   r   r   r   enumerate_onnx_tests  s(   



r   r   )r   r   typingr   numpyr   r1   onnx.numpy_helperonnx.backend.testr   r   onnxscript.backendr   r   r   r   r   r   r   r   <module>   s    k