U
    hgY                     @   s  d dl Z d dlZd dlZd dlmZ d dlmZmZmZ d dl	Z
d dlmZmZmZ d dlmZ eeeee
jdddZeeee
jd	d
dZeedddZd,eeeeeee
jdddZeeee
jf dddZeeeeeeeeeeeedddZeeeeeeeeeeedddZdd Zd-eee ee ee eee
j ee
j ee
j f ddd Zd.eee ee ee eee
j ee
j ee
j f d!d"d#Zd$d% Zeeeeeeeee ee ee eeeed&d'd(Z d)d* Z!e"d+kre!  dS )/    N)Path)DictOptionalTuple)
ModelProtoTensorProtonumpy_helper)	OnnxModel)	input_ids
batch_sizesequence_lengthdictionary_sizereturnc                 C   sp   | j jjtjtjtjfksttj	j
|||ftjd}| j jjtjkrRt|}n| j jjtjkrlt|}|S )a`  Create input tensor based on the graph input of input_ids

    Args:
        input_ids (TensorProto): graph input of the input_ids input tensor
        batch_size (int): batch size
        sequence_length (int): sequence length
        dictionary_size (int): vocabulary size of dictionary

    Returns:
        np.ndarray: the input tensor created
    )sizedtype)typetensor_type	elem_typer   FLOATINT32INT64AssertionErrornprandomrandintint32float32int64)r
   r   r   r   data r   K/tmp/pip-unpacked-wheel-socb9apf/onnxruntime/transformers/bert_test_data.pyfake_input_ids_data   s    

r!   )segment_idsr   r   r   c                 C   sl   | j jjtjtjtjfksttj	||ftj
d}| j jjtjkrNt|}n| j jjtjkrht|}|S )a,  Create input tensor based on the graph input of segment_ids

    Args:
        segment_ids (TensorProto): graph input of the token_type_ids input tensor
        batch_size (int): batch size
        sequence_length (int): sequence length

    Returns:
        np.ndarray: the input tensor created
    r   )r   r   r   r   r   r   r   r   r   zerosr   r   r   )r"   r   r   r   r   r   r    fake_segment_ids_data2   s    

r%   Zmax_sequence_lengthaverage_sequence_lengthc                 C   sL   |dkr|| kst d| | kr4td| |  | S tdd| d S d S )N      )r   r   r   r&   r   r   r    get_random_lengthM   s    r*   r)   )
input_maskr   r   r'   random_sequence_length	mask_typer   c                 C   s\  | j jjtjtjtjfkst|dkrrtj	|tj
d}|rXt|D ]}t||||< qBnt|D ]}|||< q`n|dkrtj||ftj
d}|rt|D ](}t||}t|D ]}	d|||	f< qqn4tj	||ftj
d}
|
|d|
jd d|
jd f< n |dksttj|d d tj
d}|rt|D ]}t||||< q2t|d D ]r}|dkr~||| d  ||d   nd||| < |dkr||| d  ||d   nd|d| d | < qTnTt|D ]}|||< qt|d D ].}|| ||| < || |d| d | < q| j jjtjkr<t|}n| j jjtjkrXt|}|S )a"  Create input tensor based on the graph input of segment_ids.

    Args:
        input_mask (TensorProto): graph input of the attention mask input tensor
        batch_size (int): batch size
        sequence_length (int): sequence length
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type (int): mask type - 1: mask index (sequence length excluding paddings). Shape is (batch_size).
                                     2: 2D attention mask. Shape is (batch_size, sequence_length).
                                     3: key len, cumulated lengths of query and key. Shape is (3 * batch_size + 2).

    Returns:
        np.ndarray: the input tensor created
    r(   r#   r)   Nr      )r   r   r   r   r   r   r   r   r   Zonesr   ranger*   r$   shaper   r   )r+   r   r   r'   r,   r-   r   iZactual_seq_lenjtempr   r   r    fake_input_mask_dataW   sN    

$2@
r4   )	directoryinputsc              
   C   s   t j| sJzt |  W n  tk
r:   td|   Y qVX td|   ntd|   d}| D ]R\}}t||}t	t j
| d| dd}||  W 5 Q R X |d7 }qbd	S )
zOutput input tensors of test data to a directory

    Args:
        directory (str): path of a directory
        inputs (Dict[str, np.ndarray]): map from input name to value
    z#Creation of the directory %s failedz&Successfully created the directory %s z9Warning: directory %s existed. Files will be overwritten.r   Zinput_.pbwbr(   N)ospathexistsmkdirOSErrorprintitemsr   
from_arrayopenjoinwriteSerializeToString)r5   r6   indexnamer   Ztensorfiler   r   r    output_test_data   s    rH   )r   r   
test_casesr   verboserandom_seedr
   r"   r+   r'   r,   r-   c                 C   s   |dk	st tj| t| g }t|D ]r}t|| ||}|j|i}|r`t|| |||j< |r|t|| ||	|
|||j< |rt	|dkrt
d| || q.|S )a  Create given number of input data for testing

    Args:
        batch_size (int): batch size
        sequence_length (int): sequence length
        test_cases (int): number of test cases
        dictionary_size (int): vocabulary size of dictionary for input_ids
        verbose (bool): print more information or not
        random_seed (int): random seed
        input_ids (TensorProto): graph input of input IDs
        segment_ids (TensorProto): graph input of token type IDs
        input_mask (TensorProto): graph input of attention mask
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type (int): mask type 1 is mask index; 2 is 2D mask; 3 is key len, cumulated lengths of query and key

    Returns:
        List[Dict[str,numpy.ndarray]]: list of test cases, where each test case is a dictionary
                                       with input name as key and a tensor as value
    Nr   zExample inputs)r   r   r   seedr/   r!   rF   r%   r4   lenr>   append)r   r   rI   r   rJ   rK   r
   r"   r+   r'   r,   r-   
all_inputsZ
_test_caseZinput_1r6   r   r   r    fake_test_data   s,    "

     

rP   )r   r   rI   rL   rJ   r
   r"   r+   r'   r,   r-   c                 C   s:   d}t | ||||||||||	|
}t||kr6td |S )a  Create given number of input data for testing

    Args:
        batch_size (int): batch size
        sequence_length (int): sequence length
        test_cases (int): number of test cases
        seed (int): random seed
        verbose (bool): print more information or not
        input_ids (TensorProto): graph input of input IDs
        segment_ids (TensorProto): graph input of token type IDs
        input_mask (TensorProto): graph input of attention mask
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type (int): mask type 1 is mask index; 2 is 2D mask; 3 is key len, cumulated lengths of query and key

    Returns:
        List[Dict[str,numpy.ndarray]]: list of test cases, where each test case is a dictionary
                                       with input name as key and a tensor as value
    i'  z$Failed to create test data for test.)rP   rM   r>   )r   r   rI   rL   rJ   r
   r"   r+   r'   r,   r-   r   rO   r   r   r    generate_test_data   s$     rQ   c                 C   s`   |t |jkrd S |j| }| |}|d kr\| ||}|d k	r\|jdkr\| |jd }|S )NZCastr   )rM   inputfind_graph_inputZ
get_parentZop_type)
onnx_model
embed_nodeZinput_indexrR   Zgraph_inputZparent_noder   r   r    get_graph_input_from_embed_node'  s    

rV   )rT   input_ids_namesegment_ids_nameinput_mask_namer   c                 C   s  |   }|dk	r| |}|dkr0td| d}|rX| |}|dkrXtd| d}|r| |}|dkrtd| d|rdnd |rdnd }t||krtd| dt| |||fS t|dkrtdt| | d	}	t|	dkrv|	d }
t| |
d}t| |
d}t| |
d
}|dkrZ|D ]}|j }d|kr:|}q:|dkrltd|||fS d}d}d}|D ]>}|j }d|kr|}nd|ksd|kr|}n|}q|r|r|r|||fS tddS )a  Find graph inputs for BERT model.
    First, we will deduce inputs from EmbedLayerNormalization node.
    If not found, we will guess the meaning of graph inputs based on naming.

    Args:
        onnx_model (OnnxModel): onnx model object
        input_ids_name (str, optional): Name of graph input for input IDs. Defaults to None.
        segment_ids_name (str, optional): Name of graph input for segment IDs. Defaults to None.
        input_mask_name (str, optional): Name of graph input for attention mask. Defaults to None.

    Raises:
        ValueError: Graph does not have input named of input_ids_name or segment_ids_name or input_mask_name
        ValueError: Expected graph input number does not match with specified input_ids_name, segment_ids_name
                    and input_mask_name

    Returns:
        Tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]]: input tensors of input_ids,
                                                                                 segment_ids and input_mask
    Nz Graph does not have input named r(   r   zExpect the graph to have z inputs. Got r.   z'Expect the graph to have 3 inputs. Got ZEmbedLayerNormalization   maskz#Failed to find attention mask inputtokensegmentz?Fail to assign 3 inputs. You might try rename the graph inputs.)Z'get_graph_inputs_excluding_initializersrS   
ValueErrorrM   Zget_nodes_by_op_typerV   rF   lower)rT   rW   rX   rY   Zgraph_inputsr
   r"   r+   Zexpected_inputsZembed_nodesrU   rR   Zinput_name_lowerr   r   r    find_bert_inputs4  sf    












r`   )	onnx_filerW   rX   rY   r   c              	   C   s@   t  }t| d}||  W 5 Q R X t|}t||||S )a  Find graph inputs for BERT model.
    First, we will deduce inputs from EmbedLayerNormalization node.
    If not found, we will guess the meaning of graph inputs based on naming.

    Args:
        onnx_file (str): onnx model path
        input_ids_name (str, optional): Name of graph input for input IDs. Defaults to None.
        segment_ids_name (str, optional): Name of graph input for segment IDs. Defaults to None.
        input_mask_name (str, optional): Name of graph input for attention mask. Defaults to None.

    Returns:
        Tuple[Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]]: input tensors of input_ids,
                                                                                 segment_ids and input_mask
    rb)r   rA   ZParseFromStringreadr	   r`   )ra   rW   rX   rY   modelrG   rT   r   r   r    get_bert_inputs  s
    re   c                  C   sJ  t  } | jddtdd | jddtd dd | jd	dtd
dd | jddtddd | jddtd dd | jddtd dd | jddtd dd | jddtd
dd | jddtddd | jddddd | jdd | jdddd d | jdd! | jd"d#d$td%d& | jd'd(ddd)d | jdd* | jd+dtd,d-d |  }|S ).Nz--modelTzbert onnx model path.)requiredr   helpz--output_dirFz4output test data path. Default is current directory.)rf   r   defaultrg   z--batch_sizer(   zbatch size of inputz--sequence_length   z maximum sequence length of inputz--input_ids_namezinput name for input idsz--segment_ids_namezinput name for segment idsz--input_mask_namezinput name for attention maskz	--samplesz$number of test cases to be generatedz--seedr.   zrandom seedz	--verbose
store_truezprint verbose information)rf   actionrg   )rJ   z--only_input_tensorsz-only save input tensors and no output tensors)only_input_tensorsz-az--average_sequence_lengthz)average sequence length excluding padding)rh   r   rg   z-rz--random_sequence_lengthz3use uniform random instead of fixed sequence length)r,   z--mask_typer)   z^mask type: (1: mask index, 2: raw 2D mask, 3: key lengths, cumulated lengths of query and key))argparseArgumentParseradd_argumentstrintset_defaults
parse_args)parserargsr   r   r    parse_arguments  s    rw   )rd   
output_dirr   r   rI   rL   rJ   rW   rX   rY   rl   r'   r,   r-   c                 C   sB  t | |||	\}}}t|||||||||||}t|D ](\}}tj|dt| }t|| q8|
rjdS ddl}d|	 krddgndg}|j
| |d}dd | D }t|D ]\}}tj|dt| }|||}t|D ]T\}}tt|| |}ttj|d	| d
d}||  W 5 Q R X qqdS )aI  Create test data for a model, and save test data to a directory.

    Args:
        model (str): path of ONNX bert model
        output_dir (str): output directory
        batch_size (int): batch size
        sequence_length (int): sequence length
        test_cases (int): number of test cases
        seed (int): random seed
        verbose (bool): whether print more information
        input_ids_name (str): graph input name of input_ids
        segment_ids_name (str): graph input name of segment_ids
        input_mask_name (str): graph input name of input_mask
        only_input_tensors (bool): only save input tensors,
        average_sequence_length (int): average sequence length excluding paddings
        random_sequence_length (bool): whether use uniform random number for sequence length
        mask_type(int): mask type
    Ztest_data_set_Nr   ZCUDAExecutionProviderZCPUExecutionProvider)	providersc                 S   s   g | ]
}|j qS r   )rF   ).0outputr   r   r    
<listcomp>Q  s     z-create_and_save_test_data.<locals>.<listcomp>Zoutput_r7   r8   )re   rQ   	enumerater9   r:   rB   rq   rH   onnxruntimeZget_available_providersZInferenceSessionget_outputsrunr   r@   r   ZasarrayrA   rC   rD   )rd   rx   r   r   rI   rL   rJ   rW   rX   rY   rl   r'   r,   r-   r
   r"   r+   rO   r1   r6   r5   r~   ry   sessionZoutput_namesresultZoutput_nameZtensor_resultrG   r   r   r    create_and_save_test_data  sB    "

r   c                  C   s   t  } | jdkr| j| _| j}|d krPt| j}tj|j	d| j
 d| j }|d k	rpt|}|jddd ntd t| j|| j
| j| j| j| j| j| j| j| j| j| j| j td| d S )Nr   Zbatch_Z_seq_T)parentsexist_okz7Directory existed. test data files will be overwritten.z Test data is saved to directory:)rw   r'   r   rx   r   rd   r9   r:   rB   parentr   r<   r>   r   ZsamplesrL   rJ   rW   rX   rY   rl   r,   r-   )rv   rx   pr:   r   r   r    main\  s8    

 r   __main__)r)   )NNN)NNN)#rn   r9   r   pathlibr   typingr   r   r   Znumpyr   Zonnxr   r   r   rT   r	   rr   Zndarrayr!   r%   r*   boolr4   rq   rH   rP   rQ   rV   r`   re   rw   r   r   __name__r   r   r   r    <module>	   s       I;4   ^   eL'
