U
    dfD                     @   s  d dl mZmZmZmZmZmZ d dlZd dlm	Z	 d dl
mZ d dlmZ d dlmZmZmZmZmZmZ d dlmZ d dlmZ e	d	d
eeeeedddZe	d	d
G dd deZe	d	d
d.eeeej  ddddZe	d	d
eedddZe	d	d
eeedddZe	d	d
ej edddZ!e	d	d
ee" edddZ#e	d	d
ejeeeeef dd d!Z$e	d	d
ejeeed"d#d$Z%e	d	d
eeeeed%d&d'Z&d(d) Z'e	d	d
d/eeed+d,d-Z(dS )0    )AnyDictList
NamedTupleOptionalTupleN)compatibility)Graph)GraphModule)_get_qualified_nameArgumentmap_aggregatemap_argNodeTarget)lift_lowering_attrs_to_nodes)	ShapePropF)Zis_backward_compatible)	fx_moduleold_op
old_targetnew_op
new_targetc           	         s   t  }i  | jjD ]}|j|kr|j|krt|j fdd}t|j fdd}t|t	s`t
t|tsnt
||||||j |< q|| fdd |< q|| _dS )zModifies all nodes in fx_module.graph.nodes which match the specified op code and target,
    and updates them to match the new op code and targetc                    s    |  S N nZval_mapr   F/tmp/pip-unpacked-wheel-ua33x9lu/torch/fx/passes/graph_manipulation.py<lambda>!       z+replace_target_nodes_with.<locals>.<lambda>c                    s    |  S r   r   r   r   r   r   r   "   r   c                    s    |  S r   r   r   r   r   r   r   )   r   N)r	   graphnodesoptargetr   argskwargs
isinstancetupleAssertionErrordictZcreate_nodenameZ	node_copy)	r   r   r   r   r   Z	new_graphnoder$   r%   r   r   r   replace_target_nodes_with   s"    
    
r,   c                   @   s   e Zd ZU eed< eed< dS )
size_bytesoutput_size
total_sizeN)__name__
__module____qualname__int__annotations__r   r   r   r   r-   -   s   
r-   )r   r$   returnc                 C   sF   |dk	rt | j|  d}| jjD ]}|jdkr4 qBt| ||_q"dS )zGiven a fx graph module, update each node with its total size (weights + bias + output)
    and its output_size(output). For a non-module node, the total size is the output size.
    return total sizeNg        output)r   	propagater    r!   r"   get_size_of_noder-   )r   r$   Ztotal_size_of_graphr+   r   r   r   get_size_of_all_nodes3   s    
r9   )r+   r5   c                 C   s$   | j d}|s td|  d|S )Ntensor_metazNode zQ has no tensor metadata associated with it! Check that shape propagation has run.)metagetRuntimeError)r+   r:   r   r   r   get_tensor_metaF   s    
r>   )r   r+   r5   c                 C   s   d}|j dkrFt|  }||j }| }|D ]\}}|| 7 }q0t|}|j }	||	7 }|jr|t	j
g |jd }
nt	jg |jd }
|
| }|
|	 }t||S )zGiven a node with node.dtype and node.shape, return its total size and its output size.
    total_size = weights + bias + output_size
    r   call_module)dtype)r"   r)   named_modulesr#   Znamed_parametersZnumelr>   shapeis_quantizedtorchZ_empty_affine_quantizedr@   Zelement_sizetensorr-   )r   r+   Ztotal_num_of_elemsZsubmodule_dict	submodule
parametersr*   pr:   Zoutput_elemZsize_per_elem_bytesr/   r.   r   r   r   r8   S   s&    


 r8   )rB   r5   c                 C   s   t t| S r   strlist)rB   r   r   r   serialize_shapes   s    rL   )strider5   c                 C   s   t t| S r   rI   )rM   r   r   r   serialize_stridex   s    rN   )rE   weights
pcq_prefixr5   c                 C   s  i }i }| j s||fS t|  |d< |  tjtjhkrR|  |d< |  |d< |  tjtj	tj
hkr|   || d< | d|d< |t|| d || d |   || d< | d|d< |t|| d || d |  |d< ||fS )	aG  
    Args:
        tensor: The tensor from which we try to extract quantization information.
        weights: A dict that contains mapping from name to a tensor value.
        pcq_prefix: A string that we would use later on as prefix for per channel quantization information. This
            usually would be the key that we use to store info of `tensor`.

    Returns:
        scheme: Dict that stores the quantization information of `tensor`.
        per_channel_dict: Dict that stores the information of per_channel_scales and
            per_channel_zero_points of `tensor`. This Will be empty if `tensor` is not
            per channel quantized.

    `tensor` is per tensor quantized:
        scheme: {
            "qscheme": str(tensor.qscheme()),
            "q_scale": tensor.q_scale(),
            "q_zero_point": tensor.q_zero_point(),
        }

    `tensor` is per channel quantized:
        scheme: {
            "qscheme": str(tensor.qscheme()),
            "q_per_channel_scales": {pcq_prefix}_per_channel_scales,
            "q_per_channel_zero_points": {pcq_prefix}_per_channel_zero_points,
            "q_per_channel_axis": tensor.q_per_channel_axis()
        }
        per_channel_dict: {
            {pcq_prefix}_per_channel_scales: {
                "dtype": dtype,
                "shape": shape,
                "is_quantized": is_quantized,
                "stride": stride,
            }
            {pcq_prefix}_per_channel_zero_points: {
                "dtype": dtype,
                "shape": shape,
                "is_quantized": is_quantized,
                "stride": stride,
            }
        }
        weights would be updated with {
            {pcq_prefix}_per_channel_scales: tensor.q_per_channel_scales().float()
            {pcq_prefix}_per_channel_zero_points: tensor.q_per_channel_zero_points().int()
        }
    qschemeq_scaleq_zero_pointZ_per_channel_scalesq_per_channel_scalesZ_per_channel_zero_pointsq_per_channel_zero_pointsq_per_channel_axis)rC   rJ   rQ   rD   per_tensor_affineper_tensor_symmetricrR   rS   Zper_channel_affineZ per_channel_affine_float_qparamsZper_channel_symmetricrT   floatupdateserialize_weightrU   r3   rV   )rE   rO   rP   schemeper_channel_dictr   r   r   serialize_tensor_quantization}   sN    2

r^   )rE   rO   r*   r5   c                 C   s   |i i}t | j|| d< t| j|| d< t | j|| d< | j|| d< t|  || d< | jrt| ||\}}|| 	| |	| |S )Nr@   rB   requires_gradrC   rM   )
rJ   r@   rL   rB   r_   rC   rN   rM   r^   rZ   )rE   rO   r*   Zweight_dictZquantization_infor]   r   r   r   r[      s      
r[   )r+   weights_metadatarO   name_prefixr5   c              	   C   sd   i }| j  D ]P\}}t|tjrR|t||| d|  ||| d| < qt|||< q|S )N.)Zattrs_for_loweringitemsr&   rD   TensorrZ   r[   rJ   )r+   r`   rO   ra   rG   Zp_nameZp_valuer   r   r   serialize_leaf_module   s    re   c                 C   s   t |jdkrdS t|jd }|jdkr.dS t|j}|dr^||jd kr^d| | d< n$|dr||jd krd	| | d< dS )
z
    For quantized embedding tables we need to update the shape/type, so we check if the
    users of this get_attr node is a quantized EB and this is the weight for the EB, and
    update the dtype accordingly.
    r   Ncall_functionz*acc_ops.embedding_bag_byte_rowwise_offsetsweightzacc.uint8fusedr@   z*acc_ops.embedding_bag_4bit_rowwise_offsetszacc.uint4fused)lenusersrK   r"   r   r#   endswithr%   )rg   r*   r+   userZuser_targetr   r   r   _update_weight_fused_dtypes  s     

rl    )r   rO   r5   c                    sX  i }i |d< i |d< g |d< t |  }|r6| dnd}dd t|  | jjD ]}i }|jdkrvt||j ts|jd	kr|	| |jdkrt||j trt
t| |j||j}||d |j< nt||d |||j |d
< |jdkrt|j|d< nt|j|d< |jdkr
|jdrP|jtdd }	|	|_|	|d< n
||j }	|jd\}
}}|
r|| |
n| }|dk	std|
 dt||d}|dk	st| d|
 t|tjrp|	|krpt|||	}t||	| |d 	| |||	< nf|jdkrp|jdd}|dksJ|dksJ|dksJtd|dkr^d|d< n|dkrpd|d< |j|d< |j|d< ttddd}ttddd ttttf d fd d!}|jd	krt|j ||d"< t|d" d# t!t"fr|d" d# |d"< nt#|j  |d"< t#|j$ |d$< t#t"|j%& ||d%< |d  |g7  < qR|S )&an  Recursively Serializes a graph module (fx_module) to a dictionary which is later exported to JSON.
    It also adds all weights the provided weights dictionary by qualified_name.
    Dictionary Schema:
    MODULE
    {
        modules: {module_name: MODULE],
        nodes: [NODE],
        weights {qualified_name: WEIGHT},
    }
    NODE
    {
        shape: [],
        stride: [],
        dtype: dtype,
        is_quantized: bool,
        target: target,
        op_code: op_code,
        name: name,
        args: [],
        kwargs: {}
    }
    WEIGHT
    {
        dtype: dtype,
        is_quantized: bool,
        shape: [],
        QUANTIZATION,
    }
    QUANTIZATION
    {
        qscheme: qscheme,
        q_scale: float,
        q_zero_point: float,
        q_per_channel_scales, [],
        q_per_channel_zero_points: [],
        q_per_channel_axis, int
    }
    modulesrO   r!   rb   rm   c                 S   s   t | }t|jt|jt|jt|j|jd}|jr|t|j	d |d< |j	d t
jt
jhkr||j	d |d< |j	d |d< | jd}|d k	r| | @ }t|dkstd	| || |S )
N)rB   r@   r_   rM   rC   rQ   ZscalerR   Z
zero_pointrS   lowering_infor   z2Overlap found between lowering_info and node_rep: )r>   rL   rB   rJ   r@   r_   rN   rM   rC   ZqparamsrD   rW   rX   r;   r<   keysrh   r(   rZ   )r+   r:   node_repro   Zoverlapping_keysr   r   r   get_node_infoM  s0    

z'serialize_module.<locals>.get_node_infor?   r6   rG   rf   r#   Zget_attrzparent.Nzsubmod z
 not foundz not an attr of placeholderph_typeZinput_phZ	output_phz?When present, placeholder type must be 'input_ph' or 'ouput_ph'Zop_coder*   )	user_noder5   c                 S   s   dt | dS NT)Zis_noder*   )rJ   )ru   r   r   r   get_user_info  s    z'serialize_module.<locals>.get_user_info)argr5   c                 S   sB   t | tjjrdt| dS t | tjtjtjfr:t| S | S d S rv   )r&   rD   Zfxr   rJ   r@   Zmemory_formatrQ   )rx   r   r   r   get_arg_info  s
    z&serialize_module.<locals>.get_arg_infoc                    s    | }| |  |S r   )rZ   )rx   rq   ry   rr   r   r   get_output_arg_info  s    z-serialize_module.<locals>.get_output_arg_infor$   r   r%   ri   )'r)   rA   r   r    r!   r"   r&   r#   r
   rZ   serialize_modulegetattrre   r   rJ   
startswithrh   r*   
rpartitionZget_submoduler(   rD   rd   r[   rl   r;   r<   r   r   r   r   r   r$   r'   rK   r   r%   ri   rp   )r   rO   ra   Zserialized_dictZ
submodulesprefixr+   rq   Zserialized_modulequalnameZsubmod_path_Ztarget_nameZsubmodr#   rg   rt   rw   r{   r   rz   r   r|     s    (!

  







r|   )N)rm   ))typingr   r   r   r   r   r   rD   Ztorch.fx._compatibilityr   Ztorch.fx.graphr	   Ztorch.fx.graph_moduler
   Ztorch.fx.noder   r   r   r   r   r   Ztorch.fx.passes.param_fetchr   Ztorch.fx.passes.shape_propr   rJ   r,   r-   rd   r9   r>   r8   SizerL   r3   rN   r^   r[   re   rl   r|   r   r   r   r   <module>   sb         
c   