o
    ;ήcOH                     @   sF  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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fddZ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			d5d"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			d5d(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, Zd-ed.edededededed#ee d$ee d%ee d/efd0d1Zd2d3 Z e!d4kr!e   dS dS )6    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   N/tmp/pip-target-vg8gfxp4/lib/python/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$   
input_maskrandom_mask_lengthc                 C   s   | j jjtjtjtjfv sJ |rBtt	|d d |}t
j||ft
jd}t
j||ft
jd}||d|jd d|jd f< n
t
j||ft
jd}| j jjtjkr[t
|}|S | j jjtjkrht
|}|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
        random_mask_length (bool): whether mask according to random padding length

    Returns:
        np.ndarray: the input tensor created
          r"   Nr      )r   r   r   r   r   r   r   r   r   intr   r#   r   onesshaper   r   )r%   r   r   r&   actual_seq_lenr   tempr   r   r   fake_input_mask_dataM   s"   
"

r/   	directoryinputsc              	   C   s   t j| s$zt |  W n ty   td|   Y nw td|   ntd|   d}| D ]2\}}t||}t	t j
| d|d}||  W d   n1 sYw   Y  |d7 }q0d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wbNr)   )ospathexistsmkdirOSErrorprintitemsr   
from_arrayopenjoinformatwriteSerializeToString)r0   r1   indexnamer   tensorfiler   r   r   output_test_datau   s    
rD   
test_casesverboserandom_seedc
                 C   s   |dusJ t j| t| g }
t|D ]7}t|| ||}|j|i}|r0t|| |||j< |r<t|| ||	||j< |rIt|
dkrIt	d| |

| q|
S )a7  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
        random_mask_length (bool): whether mask random number of words at the end

    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   seedranger    rA   r$   r/   lenr8   append)r   r   rE   r   rF   rG   r
   r!   r%   r&   
all_inputs	test_caseinput_1r1   r   r   r   fake_test_data   s   


rO   rH   c	                 C   s6   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
        random_mask_length (bool): whether mask random number of words at the end

    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.)rO   rJ   r8   )r   r   rE   rH   rF   r
   r!   r%   r&   r   rL   r   r   r   generate_test_data   s    rP   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   )rJ   inputfind_graph_input
get_parentop_type)
onnx_model
embed_nodeinput_indexrR   graph_inputparent_noder   r   r   get_graph_input_from_embed_node   s   

r[   rV   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_initializersrS   
ValueErrorrJ   r=   get_nodes_by_op_typer[   rA   lower)rV   r\   r]   r^   graph_inputsr
   r!   r%   expected_inputsembed_nodesrW   rR   input_name_lowerr   r   r   find_bert_inputs   sh   








rl   	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   r;   ParseFromStringreadr	   rl   )rm   r\   r]   r^   modelrC   rV   r   r   r   get_bert_inputsY  s   rr   c                  C   s  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! |  }|S )"Nz--modelTzbert onnx model path.)requiredr   helpz--output_dirFz4output test data path. Default is current directory.)rs   r   defaultrt   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)rs   actionrt   )rF   z--only_input_tensorsz-only save input tensors and no output tensors)only_input_tensors)argparseArgumentParseradd_argumentstrr*   set_defaults
parse_args)parserargsr   r   r   parse_argumentsu  s|   r   rq   
output_dirry   c                 C   s4  t | |||	\}}}t||||||||dd	}t|D ]\}}tj|dt| }t|| q|
r4dS ddl}|	| }dd |
 D }t|D ]M\}}tj|dt| }|||}t|D ]3\}}tt|| |}ttj|d|d	}||  W d   n1 sw   Y  qcqJdS )
aw  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
    F)r&   test_data_set_Nr   c                 S   s   g | ]}|j qS r   )rA   ).0outputr   r   r   
<listcomp>  s    z-create_and_save_test_data.<locals>.<listcomp>zoutput_{}.pbr2   )rr   rP   	enumerater3   r4   r<   r}   rD   onnxruntimeInferenceSessionget_outputsrunr   r:   r   asarrayr;   r=   r>   r?   )rq   r   r   r   rE   rH   rF   r\   r]   r^   ry   r
   r!   r%   rL   ir1   r0   r   sessionoutput_namesresultoutput_nametensor_resultrC   r   r   r   create_and_save_test_data  s>   
r   c                  C   s   t  } | j}|d u rt| j}tj|jd| j	| j
}|d ur-t|}|jddd ntd t| j|| j	| j
| j| j| j| j| j| j| j td| d S )Nzbatch_{}_seq_{}T)parentsexist_okz7Directory existed. test data files will be overwritten.z Test data is saved to directory:)r   r   r   rq   r3   r4   r<   parentr=   r   r   r6   r8   r   samplesrH   rF   r\   r]   r^   ry   )r   r   pr4   r   r   r   main  s.   
r   __main__)NNN)"rz   r3   r   pathlibr   typingr   r   r   numpyr   onnxr   r   r   rV   r	   r*   ndarrayr    r$   boolr/   r}   rD   rO   rP   r[   rl   rr   r   r   r   __name__r   r   r   r   <module>   s  

(	

4	
.
^
K	

?
!
