U
    d?^                     @   s
  U d dl Z d dlZd dlmZ d dlmZmZ d dlmZ d dl	m
Z
mZ d dlmZmZ d dlmZmZmZmZmZmZmZmZmZ d dlmZ d dlZd dlZejjjdgejjjdgejjj dgejjj!dgejjj"d	gejjj#d	gejjj$d
giZ%ejjj"ejjj#ejjj$hZ&ejjjd	gejjjd	gejjj d	gejjj!d	gejjj"d
gejjj#d
gejjj$dgiZ'dXe(dddZ)dd Z*eeee(eee(f ee(ef f  dddZ+dYeejj,eee(ejj,f eee(ee(e-f f e.e(ed	ddZ/ee dddZ0dd Z1eedddZ2ee.ed d!d"Z3e(ed#d$d%Z4eeee  d&d'd(Z5e
ee e
d)d*d+Z6ejj,ed,d-d.Z7ejj,ee(eed/d0d1Z8e(eeee(ejj,f eee(ee(e-f f eeef d2d3d4Z9eee(ejj,f eee.f e.d5d6d7Z:eee; d&d8d9Z<ee; eegee; f d:d;d<Z=ed=d>Z>e>d?d@ej.e=dge?e=d	gie>d?dAe;e<ie>d?dBe;e<ie>d?dCe;e<ie>d?dDe;e=dgie>d?dEe;e<ie>d?ej@e;e<ie>d?dFe;e=dgie>d?dGe;e=dgie>d?ejAe;e=dgie>d?dHe;e<iiZBee>eee-ejCf eegee; f f f eDdI< i ZEeee-ejCf eegee; f f eDdJ< eeee-ejCf eegee; f f d&dKdLZFee.d&dMdNZGee.d&dOdPZHdZeee(ej,f eeej,  eee dQdRdSZIeeedTf eedUdVdWZJdS )[    N)is_per_tensoris_per_channel)is_activation_post_process)GraphModulemap_arg)GraphNode)	CallableOptionalListDictAnySetTupleUnionType)
namedtuple            T)returnc                 C   s  t d}t d}dddddd}i }d	}|D ]}t|||< q0g }| jD ]}	t|	j}
|rl|
d
d}
t|	j}|r||kr|| }t|	j}|r|	|}|rd|
d d}|	|}|rd|
d d}|d
d}t|	j}|r|d
d}t|	j}t||
||||fD ] \}}t|| t|||< q||
||||g qLd}d}||j||7 }|D ]}||j||7 }qp|r|d7 }|S )zyReturns a printable representation of the ops in the graph of g.
    If shorten is True, tries to abbreviate fields.
    z<built-in function (.*)>z <built-in method (.*) of type.*>ZplchdrZgt_prmZcl_funZcl_modZcl_meth)placeholderget_attrcall_functioncall_modulecall_method)nameoptargetargskwargsactivation_post_processZobsz<bi_fun r   >z	<bi_meth  z9{:<{name}} {:<{op}} {:<{target}} {:<{args}} {:<{kwargs}}
z'*obs_{n} = activation_post_process_{n}
)recompilelennodesstrr   replacer   r   searchgroupr    r!   zipmaxappendformat)gshortenZbuilt_in_func_reZbuilt_in_meth_reZop_dictZmax_lensZ	col_namessresultsnr   r   r   Zbuilt_in_funcZbuilt_in_methr    r!   kvres_strZ
format_strresult r:   B/tmp/pip-unpacked-wheel-ua33x9lu/torch/ao/quantization/fx/utils.pygraph_pretty_str*   sX    








r<   c                 C   s>   t | jstd|  \}}t|}t|}| j}|||fS )Nz)Only per tensor quantization is supported)r   qschemeAssertionErrorcalculate_qparamsfloatintdtype)r"   scale
zero_pointrB   r:   r:   r;   get_per_tensor_qparamsl   s    rE   )r"   r   c           
      C   s  | j }d}t| dr| j}d}|tjtjfkrd}|  \}}t| jrht	| j
}||||d}tj}qt|}t	|}|||d}tj}nr|tjkrd}d}d|i}nV|tjkr|tjtjtjfkrd}tj}tjjjd	k}	||	d
}ntd|   dS |||fS )z Given an activation_post_process module,
    return node_type(e.g. call_function), quantize op(e.g. quantize_per_tensor) and a dictionary
    of extracted qparams from the module
    Ncompute_dtyper   )_scale__zero_point_Z_axis__dtype_)rG   rH   rI   r   torI   Zfbgemm)rI   Z_reduce_range_z?Unsupported activation_post_process in get_quantize_node_info: )rB   hasattrrF   torchZquint8qint8r?   r   r=   rA   ch_axisZquantize_per_channelr@   Zquantize_per_tensorfloat16Zfloat32Zquantize_per_tensor_dynamicbackends	quantizedZenginewarningswarn)
r"   rB   rF   quantize_op	node_typerC   rD   rN   qparamsZreduce_ranger:   r:   r;   get_quantize_node_infot   s8    




rW   _output)	in_node
obs_moduleobs_nodemodulesquantized_graphnode_name_to_scopeis_inputoutput_prefixr   c                 C   s  |r\t |j}|r|d nd}	d}
|D ]&}|jdkr&|jtjjjkr&|}
 qNq&|
rV|
}	d}n| }	|}|	r|	j|kr||	j \}}nd}|d }|}t	|}|dk	st
d| |\}}}| g}| D ]<\}}|dkrt|||| | |}|| q|| q|||t|i S )a
   Add quantization nodes (eg. quantize_per_tensor/per_channel) for given node to graph
    with the qparams calculated from activation_post_process (obs_module).
    The observer node (obs_node) is used to find the FQN of the user of act_post_process.
    e.g. Given input `node` in `node = self.conv(x)`, insert node:
    `quantized_node = torch.quantize_per_tensor(x, self._scale_0, self._zer_point_0, self._dtype_0)`
    where self._scale_0, self._zero_point_0 and self._dtype_0 are
    calculated from `obs_module`
    r   Nr   _inputr$   z7Expecting quantize node info not to be None, observer: )rG   rH   )listusersr   r   rL   nn
functionallinearr   rW   r>   itemscreate_getattr_from_valuer/   create_nodetuple)rY   rZ   r[   r\   r]   r^   r_   r`   rc   Zfirst_linear_use_or_first_useZlinear_noder5   prefixmodule_path_root_modulegraphZmaybe_quantize_node_inforU   rT   rV   inputskeyvalueZqparam_noder:   r:   r;   quantize_node   s<    


rs   c                 C   sD   t  }| |i }dD ]$}||i }t | }||O }qt|S )a_   Get all the unique custom module keys in the custom config dict
    e.g.
    Input:
    custom_config_dict = {
        "float_to_observed_custom_module_class": {
           "static": {
               CustomModule1: ObservedCustomModule
           },
           "dynamic": {
               CustomModule2: DynamicObservedCustomModule
           },
           "weight_only": {
               CustomModule3: WeightOnlyObservedCustomModule
           },
        },
    }

    Output:
    # extract all the keys in "static", "dynamic" and "weight_only" dict
    [CustomModule1, CustomModule2, CustomModule3]
    )ZstaticZdynamicZweight_only)setgetkeysrb   )Zcustom_config_dictZcustom_config_dict_keyZfloat_custom_module_classesZcustom_module_mappingZ
quant_modeZquant_mode_custom_module_configZ quant_mode_custom_module_classesr:   r:   r;   get_custom_module_class_keys   s    
rw   c                 C   s6   | t jkrt jjjS | t jkr(t jjjS td| d S )Nz&can't get linear prepack op for dtype:)rL   rO   opsrQ   Zlinear_prepack_fp16rM   Zlinear_prepack	Exception)rB   r:   r:   r;   get_linear_prepack_op_for_dtype   s
    



rz   )conv_opr   c                 C   sV   t jjjt jjjt jjjt jjjt jjj	t jjj
i}|| d }|sRtd| |S )NzDidn't find prepack op for {})rL   rd   re   conv1drx   rQ   Zconv1d_prepackconv2dZconv2d_prepackconv3dZconv3d_prepackru   r>   r0   )r{   Zprepack_opsZ
prepack_opr:   r:   r;   get_qconv_prepack_op   s       r   )r{   has_relur   c                 C   s   t jjjt jjjt jjjt jjjt jjj	t jjj
it jjjt jjjt jjjt jjjt jjj	t jjj	id}|| | }|std| ||S )N)TFz4Can't find corresponding quantized conv op for {} {})rL   rd   re   r|   rx   rQ   Zconv1d_relur}   Zconv2d_relur~   Zconv3d_reluru   r>   r0   )r{   r   Zqconv_opZqconvr:   r:   r;   get_qconv_op
  s$          r   )rk   r   c                    s&     dd tjjd fdd}|S )N.rm   )modulec                    s>   t d fdd}d}||}t| |r:|d7 }||}q|S )Nic                    s    t |  S N)r)   r   rk   r:   r;   get_attr_name%  s    zOget_new_attr_name_with_prefix.<locals>.get_new_attr_name.<locals>.get_attr_namer   r   )rA   rK   )r   r   r   	attr_namer   r:   r;   get_new_attr_name$  s    

z8get_new_attr_name_with_prefix.<locals>.get_new_attr_name)r*   rL   rd   Module)rk   r   r:   r   r;   get_new_attr_name_with_prefix!  s    	r   noder   c                 C   s   | g}| g}|r|  } t| jt| j  }|D ]H}t|tsDq4|jdkrT dS || |jdkrr|j	t
ks4|| q4q|S )a   Starting from a target node, trace back until we hit inpu or
    getattr node. This is used to extract the chain of operators
    starting from getattr to the target node, for example
    def forward(self, x):
      observed = self.observer(self.weight)
      return F.linear(x, observed)
    collect_producer_nodes(observed) will either return a list of nodes that
    produces the observed node or None if we can't extract a self contained
    graph without free variables(inputs of the forward function).
    r   Nr   )poprb   r    r!   values
isinstancer   r   r/   r   getattr)r   r(   ZfrontierZall_argsargr:   r:   r;   collect_producer_nodes/  s    


r   )rootproducer_nodesr   c                    sl   t |dkstd|  t }i   fdd}|D ]}||| |< q6|||d  t| |}|S )a-   Construct a graph module from extracted producer nodes
    from `collect_producer_nodes` function
    Args:
      root: the root module for the original graph
      producer_nodes: a list of nodes we use to construct the graph
    Return:
      A graph module constructed from the producer nodes
    r   z'list of producer nodes can not be emptyc                    s   t |  fddS )Nc                    s    |  S r   r:   r   envr:   r;   <lambda>[      zDgraph_module_from_producer_nodes.<locals>.load_arg.<locals>.<lambda>)r   )ar   r:   r;   load_argZ  s    z2graph_module_from_producer_nodes.<locals>.load_arg)r'   r>   reverser   Z	node_copyoutputr   )r   r   ro   r   Zproducer_nodeZgraph_moduler:   r   r;    graph_module_from_producer_nodesJ  s    

r   )r   r   c                 C   s^   dd |   D dd |  D B }t|dks>td|t|dkrVtt|nd}|S )z
    Returns the unique device for a module, or None if no device is found.
    Throws an error if multiple devices are detected.
    c                 S   s   h | ]
}|j qS r:   device.0pr:   r:   r;   	<setcomp>g  s     z/assert_and_get_unique_device.<locals>.<setcomp>c                 S   s   h | ]
}|j qS r:   r   r   r:   r:   r;   r   h  s     r   zMprepare only works with cpu or single-device CUDA modules, but got devices {}r   N)
parametersbuffersr'   r>   r0   nextiter)r   Zdevicesr   r:   r:   r;   assert_and_get_unique_deviceb  s    r   )r   ro   rk   rr   r   c                 C   s>   t |}|| }t| }| |tj||d |d|}|S )z
    Given a value of any type, creates a getattr node corresponding to the value and
    registers the value as a buffer to the module.
    r   r   )r   r   Zregister_bufferrL   Ztensorri   )r   ro   rk   rr   r   r   r   Z	attr_noder:   r:   r;   rh   p  s    rh   )	node_namerC   rD   r\   r]   r^   r   c                 C   s@   |d }||  \}}t |||d |}	t |||d |}
|	|
fS )z
    Create getattr nodes in the quantized graph for scale and zero point values.
    The nodes are registered with the root_module of the model.
    r$   rG   rH   )rh   )r   rC   rD   r\   r]   r^   rn   rl   rm   Z
scale_nodeZzero_point_noder:   r:   r;   create_qparam_nodes}  s
    r   )r   r\   cacher   c           	      C   s  |r| |kr||  S d}t | ts*d}n| jdkr<d}n| jdkrzt | jtsVtt|| j rvt| jd ||}nb| jdkrd}nP| jdkr| jt	j
krt| jd ||}n$| jdkrd}n| jtkr| jd d	krd}n| jd
kr| jdkrd}nd}| jD ]}t |trx|D ]P}t |tr$t|||}|pJ| }|r$| }|rf||| < |    S q$nZt |trnLt |trt|||}|p| }|r| }|r||| < |  S nd}| }q|r||| < |S )z
    If we know for sure that all of this node's args have no
    tensors (are primitives), return True.  If we either
    find a tensor or are not sure, return False. Note: this
    function is not exact.
    FTr   r   r   r   r   r   )ndimshaper   size)r   r   r   r   r)   r>   r   all_node_args_have_no_tensorsr    operatorgetitemr   rb   rA   )	r   r\   r   r9   Zfound_one_tensorr   Zlist_elZ!this_list_el_args_have_no_tensorsZthis_arg_args_have_no_tensorsr:   r:   r;   r     sj    








r   c                 C   s   t tdt| jS )z2
    Returns all node arg indices after first
    r   )rb   ranger'   r    r   r:   r:   r;   all_node_args_except_first  s    r   )arg_indicesr   c                    s   t tt d fdd}|S )zu
    Constructs a function that takes a node as arg and returns the arg_indices
    that are valid for node.args
    r   c                    s    fddD S )Nc                    s   g | ]}|t  jk r|qS r:   )r'   r    )r   r   r   r:   r;   
<listcomp>  s      z=return_arg_list.<locals>.arg_indices_func.<locals>.<listcomp>r:   r   r   r   r;   arg_indices_func  s    z)return_arg_list.<locals>.arg_indices_func)r   r   rA   )r   r   r:   r   r;   return_arg_list  s    r   NodeInfoz	op targetr   Zmasked_fillZpermuterepeatZreshaper   	transpose	unsqueezeZ
unsqueeze_viewNON_OBSERVABLE_ARG_DICTEMPTY_ARG_DICTc                 C   s   t | j| j}t|tS )z
    Returns a dict with of non float tensor types as keys and values which correspond to a
    function to retrieve the list (which takes the node as an argument)
    )r   r   r   r   ru   r   )r   infor:   r:   r;   (get_non_observable_arg_indexes_and_types  s    r   c                 C   s   | j dko| jdkS )zd
    Returns true if this node results in an integer, even if some of the args
    are Tensors.
    r   r   )r   r   r   r:   r:   r;   node_return_type_is_int   s    r   c                 C   s&   | j dko | jtko | jd dk}|S )z Returns True if this node is a node that takes a Tensor as input and output some
    meta information about the Tensor, e.g. shape, size etc.
    r   r   r   )r   r   r   r    )r   r9   r:   r:   r;   is_get_tensor_info_node(  s     r   )r   r\   target_module_typetarget_functional_typer   c                 C   sj   | j  D ]Z\}}|jdkr@|dk	r@t|t|j |r@|  S |jdkr
|dk	r
|j|kr
|  S q
dS )a%   Gets the next module that matches what is needed in
    is_target_module_type if it exists

    Args:
        node: The node whose users we want to look at
        target_module_type: Module type that we want to check
        target_functional_type: Functional type that we want to check
    r   Nr   )rc   rg   r   r   r)   r   )r   r\   r   r   userrm   r:   r:   r;   maybe_get_next_module0  s    
r   .)r]   create_node_argsold_noder   c                 C   s   | j | }|j|_|S )zU
    Creates `new_node` and copies the necessary metadata to it from `old_node`.
    )ri   Zstack_trace)r]   r   r   Znew_noder:   r:   r;   'create_node_from_old_node_preserve_metaI  s    
r   )T)rX   )NN)Kr%   rL   Ztorch.nnrd   Ztorch.ao.quantization.utilsr   r   Ztorch.ao.quantization.quantizer   Ztorch.fxr   r   Ztorch.fx.graphr   r   typingr	   r
   r   r   r   r   r   r   r   collectionsr   r   rR   re   r|   r}   r~   rf   Z
layer_normZ
group_normZinstance_normZWEIGHT_INDEX_DICTZNON_QUANTIZABLE_WEIGHT_OPSZBIAS_INDEX_DICTr)   r<   rE   rW   r   typeboolrs   rw   rz   r   r   r   r   r   r   rh   r   r   rA   r   r   r   r@   r   r   r   rB   __annotations__r   r   r   r   r   r   r:   r:   r:   r;   <module>   s$   ,       
       
B./  >
  
(K"	
       
   
  4%,0	  
