o
    gTY                     @   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 dededed	ed
e
jf
ddZdededed
e
jfddZdedefddZ	d;dedededededed
e
jfddZdedeee
jf fddZdededed	eded ededededededefd!d"Zdededed#edededededededefd$d%Zd&d' Z			d<d(ed)ee d*ee d+ee d
eee
j ee
j ee
j f f
d,d-Z			d<d.ed)ee d*ee d+ee d
eee
j ee
j ee
j f f
d/d0Zd1d2 Zd3ed4edededed#eded)ee d*ee d+ee d5edededefd6d7Z d8d9 Z!e"d:kr>e!  dS dS )=    N)Path)DictOptionalTuple)
ModelProtoTensorProtonumpy_helper)	OnnxModel	input_ids
batch_sizesequence_lengthdictionary_sizereturnc                 C   sr   | j jjtjtjtjfv sJ tjj	|||ftj
d}| j jjtjkr*t|}|S | j jjtjkr7t|}|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nprandomrandintint32float32int64)r
   r   r   r   data r   ^/var/www/visachat/venv/lib/python3.10/site-packages/onnxruntime/transformers/bert_test_data.pyfake_input_ids_data   s   


r    segment_idsc                 C   sn   | j jjtjtjtjfv sJ tj||ftj	d}| j jjtjkr(t
|}|S | j jjtjkr5t|}|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   zerosr   r   r   )r!   r   r   r   r   r   r   fake_segment_ids_data2   s   


r$   max_sequence_lengthaverage_sequence_lengthc                 C   sH   |dkr|| ks
J d| | krt d| |  | S t dd| d S )N      )r   r   )r%   r&   r   r   r   get_random_lengthM   s   r)   r(   
input_maskrandom_sequence_length	mask_typec                 C   sJ  | j jjtjtjtjfv sJ |dkr8tj|tj	d}|r,t
|D ]	}t||||< q!nt
|D ]}|||< q0n|dkr}tj||ftj	d}|rbt
|D ]}t||}t
|D ]}	d|||	f< qWqLntj||ftj	d}
|
|d|
jd d|
jd f< n|dksJ tj|d d tj	d}|rt
|D ]	}t||||< qt
|d D ]6}|dkr||| d  ||d   nd||| < |dkr||| d  ||d   nd|d| d | < qn(t
|D ]}|||< qt
|d D ]}|| ||| < || |d| d | < q| j jjtjkrt|}|S | j jjtjkr#t|}|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   onesr   ranger)   r#   shaper   r   )r*   r   r   r&   r+   r,   r   iactual_seq_lenjtempr   r   r   fake_input_mask_dataW   sZ   


"0:


r5   	directoryinputsc              	   C   s   t j| s(zt |  W n ty   td|  d Y nw td|  d ntd|  d t| D ]1\}\}}t	||}t
t j| d| dd	}||  W d
   n1 sbw   Y  q6d
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 z failedz#Successfully created the directory  zWarning: directory z$ existed. Files will be overwritten.input_.pbwbN)ospathexistsmkdirOSErrorprint	enumerateitemsr   
from_arrayopenjoinwriteSerializeToString)r6   r7   indexnamer   tensorfiler   r   r   output_test_data   s   rM   
test_casesverboserandom_seedc                 C   s   |dusJ t j| t| g }t|D ]9}t|| ||}|j|i}|r0t|| |||j< |r>t|| ||	|
|||j< |rKt|dkrKt	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   seedr/   r    rJ   r$   r5   lenrA   append)r   r   rN   r   rO   rP   r
   r!   r*   r&   r+   r,   
all_inputs
_test_caseinput_1r7   r   r   r   fake_test_data   s"   "



rW   rQ   c                 C   s:   d}t | ||||||||||	|
}t||krt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.)rW   rR   rA   )r   r   rN   rQ   rO   r
   r!   r*   r&   r+   r,   r   rT   r   r   r   generate_test_data   s$    rX   c                 C   s`   |t |jkr	d S |j| }| |}|d u r.| ||}|d ur.|jdkr.| |jd }|S )NCastr   )rR   inputfind_graph_input
get_parentop_type)
onnx_model
embed_nodeinput_indexrZ   graph_inputparent_noder   r   r   get_graph_input_from_embed_node%  s   

rc   r^   input_ids_namesegment_ids_nameinput_mask_namec                 C   s  |   }|dure| |}|du rtd| d}|r,| |}|du r,td| d}|r@| |}|du r@td| d|rEdnd |rKdnd }t||kr`td| dt| |||fS t|dkrttdt| | d	}	t|	dkr|	d }
t| |
d}t| |
d}t| |
d
}|du r|D ]}|j }d|v r|}q|du rtd|||fS d}d}d}|D ]}|j }d|v r|}qd|v sd|v r|}q|}q|r|r|r|||fS td)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 EmbedLayerNormalization   maskz#Failed to find attention mask inputtokensegmentz?Fail to assign 3 inputs. You might try rename the graph inputs.)'get_graph_inputs_excluding_initializersr[   
ValueErrorrR   get_nodes_by_op_typerc   rJ   lower)r^   rd   re   rf   graph_inputsr
   r!   r*   expected_inputsembed_nodesr_   rZ   input_name_lowerr   r   r   find_bert_inputs2  sb   








rt   	onnx_filec                 C   sT   t  }t| d}||  W d   n1 sw   Y  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
    rbN)r   rE   ParseFromStringreadr	   rt   )ru   rd   re   rf   modelrL   r^   r   r   r   get_bert_inputs  s   rz   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.)r{   r   defaultr|   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)r{   actionr|   )rO   z--only_input_tensorsz-only save input tensors and no output tensors)only_input_tensorsz-az--average_sequence_lengthz)average sequence length excluding padding)r}   r   r|   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   r   ry   
output_dirr   c                 C   sV  t | |||	\}}}t|||||||||||}t|D ]\}}tj|dt| }t|| q|
r5dS ddl}d|	 v rCddgndg}|j
| |d}dd | D }t|D ]N\}}tj|dt| }|||}t|D ]4\}}tt|| |}ttj|d	| d
d}||  W d   n1 sw   Y  qsqZd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
    test_data_set_Nr   CUDAExecutionProviderCPUExecutionProvider)	providersc                 S   s   g | ]}|j qS r   )rJ   ).0outputr   r   r   
<listcomp>O  s    z-create_and_save_test_data.<locals>.<listcomp>output_r:   r;   )rz   rX   rB   r<   r=   rF   r   rM   onnxruntimeget_available_providersInferenceSessionget_outputsrunr   rD   r   asarrayrE   rG   rH   )ry   r   r   r   rN   rQ   rO   rd   re   rf   r   r&   r+   r,   r
   r!   r*   rT   r1   r7   r6   r   r   sessionoutput_namesresultoutput_nametensor_resultrL   r   r   r   create_and_save_test_data  sJ   "r   c                  C   s   t  } | jdkr| j| _| j}|d u r(t| j}tj|j	d| j
 d| j }|d ur8t|}|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   batch__seq_T)parentsexist_okz7Directory existed. test data files will be overwritten.z Test data is saved to directory:)r   r&   r   r   r   ry   r<   r=   rF   parentr   r?   rA   r   samplesrQ   rO   rd   re   rf   r   r+   r,   )r   r   pr=   r   r   r   mainZ  s8   

 r   __main__)r(   )NNN)#r   r<   r   pathlibr   typingr   r   r   numpyr   onnxr   r   r   r^   r	   r   ndarrayr    r$   r)   boolr5   r   rM   rW   rX   rc   rt   rz   r   r   r   __name__r   r   r   r   <module>   s*  

I	

:	

4
^
d	

L
'
