U
    Kc:J                     @   s  d Z 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	m
Z
mZmZ ddlmZ ddlmZ ddlmZ ddlmZ e	e
ee
e
f ee
ee
e
f f ef Zd	e_G d
d dZejjejjejjejjejjejjejjejjejj ejj!ejj"ejj#ejj$ejj%ejj&hZ'ejj(j)ejj(j*ejj(j+ejj(j,ejj(j-ejj(j.ejj(j/ejj(j0ejj(j1ejj(j2ejj(j3ejj(j4ejj(j5ejj(j6ejj(j7ejj(j8ejj(j9ejj(j:ejj(j;ej<ej=ej;ej>ej?ej@ejAejBejChZDejEddddddddddddddddddddd d!d"d#hZFd$d% ZGd&d' ZHd(d) ZId*d+ ZJeeKed,d-d.ZLd/d0 ZMd1d2 ZNd3d4 ZOd5d6 ZPd7d8 ZQd9d: ZRd;d< ZSd=d> ZTd?d@ ZUdAdB ZVeWdCdDdEZXdFdG ZYdHdI ZZej[ej[eWdJdKdLZ\e]e]eWej^eWee]e]f dMdNdOZ_dPdQ Z`dRdS ZaejjbejjbeeK dTdUdVZce
eeKef eeKef dWdXdYZde
dZd[d\d]Zee
eeKef dZd^d_d`Zfe
e]d[dadbZgejjbeedcf eeKeedcf f dddedfZhdgdd%d'd)d+d.d0d2d4d6d8d:d<d>d@dBdEdGdIdLdOdSdfgZidS )hz?
Utils shared by different modes of quantization (eager/graph)
    N)	QuantType)TupleAnyUnionCallableDictOptional)is_parametrized)OrderedDict)	signature)getfullargspecztorch.ao.quantization.utilsc                   @   s   e Zd ZdZdS )MatchAllNodezo A node pattern that matches all nodes, used in defining
    fusion patterns in FX Graph Mode Quantization
    N)__name__
__module____qualname____doc__ r   r   ?/tmp/pip-unpacked-wheel-gikjz4vx/torch/ao/quantization/utils.pyr      s   r   reluZrelu_
contiguousdetachZdetach_hardsigmoidZhardsigmoid_Zpermuterepeatrepeat_interleaveZreshapeZresize_shapesigmoidZsigmoid_sizesqueezeZsqueeze_tanhZtanh_	transpose	unsqueezeZ
unsqueeze_viewc                 C   sR   | j dko| jtk}| j dko&| jtk}| j dkoFt|t| j tk}|||fS )Ncall_functionZcall_methodZcall_module)optarget	func_listmethod_listtypestrmodule_type_list)nodemodulesZis_call_functionZis_call_methodZis_call_moduler   r   r   
check_nodeg   s     r,   c                 C   s   |   }|| |S N)copyupdate)Zdefault_dictZadditional_dictdr   r   r   get_combined_dictn   s    
r1   c                 C   s   | t jkp| t jkS r-   )torchper_tensor_affineZper_tensor_symmetricqschemer   r   r   is_per_tensors   s    
r6   c                 C   s   | t jt jt jfkS r-   )r2   per_channel_affineZ per_channel_affine_float_qparamsper_channel_symmetricr4   r   r   r   is_per_channelw   s    r9   )objfqnreturnc                 C   s   t t|d| S )zO
    Given an obj and a fqn such as "foo.bar.baz", returns gm.foo.bar.baz.
    .)	functoolsreducegetattrsplit)r:   r;   r   r   r   getattr_from_fqn|   s    rB   c                 C   s   t | dr| jnd }| j}||d}|s,|S t|r<tj}n2t|r`|tjkrTtj}| j	|d< nt
d| ||d< |  \}}||d< ||d< |S )Nr5   )r5   dtypeZaxiszUnrecognized qscheme: scale
zero_point)hasattrr5   rC   r6   r2   r3   r9   r8   r7   Zch_axisRuntimeErrorZcalculate_qparams)Zobserver_or_fake_quantr5   rC   ZqparamsrD   rE   r   r   r   get_qparam_dict   s"    

rH   c                 C   s@   t |}||i }t| |ks4tdt| ||t|  S )a   Get the observed/quantized custom module class that we need
    to swap `custom_module` to
    Input:
        custom_module: input, can be an instance of either a float or observed custom module
        custom_module_class_mapping: the float to observed or observed to quantized custom module class mapping
        qconfig: qconfig configured for the custom module

    Output:
        corresponding observed/quantized custom module class for input custom module instance
    zFdid not find corresponding observed module class for {} in mapping: {})get_quant_typegetr'   AssertionErrorformat)Zcustom_moduleZcustom_module_class_mappingqconfigZ
quant_typeZclass_mappingr   r   r   get_swapped_custom_module_class   s     rN   c                 C   s   | d k	st |  }|jS r-   )rK   
activationrC   )rM   rO   r   r   r   activation_dtype   s    rP   c                 C   s   | d k	st |  }|jS r-   )rK   weightrC   )rM   rQ   r   r   r   weight_dtype   s    rR   c                 C   s"   t | tjtjtjfko t|  S )z Given a qconfig, decide if the activation needs to be
    quantized or not, this includes quantizing to quint8, qint8 and float16
    )rP   r2   quint8qint8float16#activation_is_dynamically_quantizedrM   r   r   r   "activation_is_statically_quantized   s    rX   c                 C   s"   t | \}}}|tjtjtjfkS )z Given a qconfig, decide if the activation needs to be
    dynamically quantized or not, this includes dynamically quantizing to
    quint8, qint8 and float16
    )get_qconfig_dtypesr2   rS   rT   rU   )rM   rP   _activation_compute_dtyper   r   r   rV      s    rV   c                 C   s   t | tjtjfkS )z Given a qconfig, decide if the activation needs to be
    quantized to int8 or not, this includes quantizing to quint8, qint8
    )rP   r2   rS   rT   rW   r   r   r   activation_is_int8_quantized   s    r\   c                 C   s   t | tjkS )zY Given a qconfig, decide if the activation needs to be
    quantized to int32 or not
    )rP   r2   qint32rW   r   r   r   activation_is_int32_quantized   s    r^   c                 C   s   t | tjtjtjtjfkS )zL Given a qconfig, decide if the weight needs to be
    quantized or not
    )rR   r2   rS   rT   rU   quint4x2rW   r   r   r   weight_is_quantized   s    r`   c                 C   s   t | tjtjfkS )zW Given a qconfig, decide if the weight needs to be statically
    quantized or not
    )rR   r2   rS   rT   rW   r   r   r   weight_is_statically_quantized   s    ra   )r<   c                 C   s,   t | \}}}|tjko*|tjko*|tjkS )zU Given a qconfig, returns True if this op is using int8 dynamic
    quantization
    )rY   r2   rS   rT   )rM   rP   rR   r[   r   r   r    op_is_int8_dynamically_quantized   s    
rb   c                 C   s>   | dk	st |  }|  }t|dr,|jnd}|j|j|fS )zk returns the qconfig tuple for qconfig:
    (activation_dtype, weight_dtype, activation_compute_dtype)
    Ncompute_dtype)rK   rO   rQ   rF   rc   rC   )rM   rO   rQ   rc   r   r   r   rY      s
    rY   c                 C   s   | d k	st |  }|  }tjtjtjg}|j|krft|drP|j	|krPt
jS |j|kr`t
jS t
jS |jtjkrt|dr|j	|krt
jS |jtjkrt
jS td|j|jd S )Nrc   zKUnrecognized dtype combination in get_quant_type: activation({}),weight({}))rK   rO   rQ   r2   rS   rT   r_   rC   rF   rc   r   ZDYNAMICZSTATICZWEIGHT_ONLYrU   	ExceptionrL   )rM   rO   rQ   Zstatic_dtypesr   r   r   rI      s&    

 rI   )min_valmax_valr<   c                 C   s   |   dks|  dkr&td dS |  dks>| dkr~| tdkrd|tdkrdtd dS | |kstd| |nt| |kstd| |dS )z Checks if the given minimum and maximum values are valid, meaning that
    they exist and the min value is less than the max value.
    r   zMmust run observer before calling calculate_qparams. Returning default values.Finfz-infz!min {} should be less than max {}T)	ZnumelwarningswarnZdimfloatrK   rL   r2   all)re   rf   r   r   r   check_min_max_valid  s*     
rl   )	quant_min	quant_maxhas_customized_qrangerC   reduce_ranger<   c           
      C   s&  |r|t jkrd\}}nd\}}| | }}|dk	rD|dk	rD|| }}|| d }	|t jkrzd|	  k rndksn tdn(|t jkrd|	  k rdksn td	|r| d
 |d
  } }nd|t jkr|rd\} }nd\} }nB|t jkr |rd\} }nd\} }n|t jkrd\} }nd\} }| |fS )ztCalculates actual qmin and qmax based on the quantization range,
    observer datatype and if range is reduced.
    )r   )r      N   r      zRquantization range should be positive and not exceed the maximum bit range (=256).l        zYquantization range should be positive and not exceed the maximum bit range (=4294967296).   )i?   )i   )r   rw   )i   rq   )r      )r2   r]   rT   rK   rS   )
rm   rn   ro   rC   rp   Zinitial_quant_minZinitial_quant_maxZcustom_quant_minZcustom_quant_maxZ
qrange_lenr   r   r   calculate_qmin_qmax1  sP    



 

 






ry   c                 C   s8   |  dd}t|dkr$d|d fS |d |d fS dS )z,
    Turn 'foo.bar' into ['foo', 'bar']
    r=   rs    r   N)rsplitlen)r$   rr   r   r   _parent_namef  s    r~   c                 C   s:   t | jdkrdS t| r2t | jdko0d| jkS dS dS )z
    Checks if module._modules is empty or
    if module is a parametrization, checks that module._modules only has
    the 'parametrizations' module
    r   Trs   ZparametrizationsFN)r|   Z_modulesr	   )moduler   r   r   )has_no_children_ignoring_parametrizationsp  s
    r   )root	submoduler<   c                 C   s&   |   D ]\}}||kr|  S qdS )aS   Get the path (fully qualified name) of a submodule

    Example::

    >> class M(torch.nn.Module):
           def __init__(self):
               self.linear = torch.nn.Linear(5, 5)
           def forward(self, x):
               return self.linear(x)

    >> m = M()
    >> l = m.linear
    >> _get_path_of_module(m, l)
    "linear"
    N)Znamed_modules)r   r   npr   r   r   _get_path_of_module}  s    
r   )flocr<   c                    s    fdd|  D S )z Get local keyword arguments

    Example::

    >> def f(self, a, b=9):
           pass
    >> loc = {"a": 6, "c": 7}
    >> _get_signature_locals(f, loc)
    {"a": 6}
    c                    s$   i | ]\}}|t  jkr||qS r   )r   
parameters).0kvr   r   r   
<dictcomp>  s       z)_get_signature_locals.<locals>.<dictcomp>)items)r   r   r   r   r   _get_signature_locals  s    r   zOrderedDict[str, Any])r   r<   c                 C   sf   i }t | j D ]J\}}|j|jk	r2|j||< q|j|jkrHd||< q|j|jkri ||< qt|S )z Get all default keyword arguments from function signature

    Example::

    >> def f(self, a, b=9):
           pass
    >> _get_default_kwargs(f)
    {"b": 9}
    r   )	r   r   r   defaultemptykindVAR_POSITIONALVAR_KEYWORDr
   )r   kwargsnameparamr   r   r   _get_default_kwargs  s    


r   )funcr   r<   c                 C   s@   t | }t| |}| }| D ]\}}||kr"|||< q"|S )a)   Given a function and local function arguments, normalize the keyword
    arguments by filling in default arguments from function signature

    Example::

    >> def f(self, key1=3, key2=3):
           pass
    >> loc = {"key2": 6}
    >> _normalize_kwargs(f, loc)
    {"key1": 3, "key2": 6}
    )r   r   r.   r   )r   r   Zdefault_kwargsZlocal_kwargsnormalized_kwargsattrvalr   r   r   _normalize_kwargs  s    

r   c                 C   s   t t| jS )z Get number of positional args for a function

    Example::

    >> def f(self, key1=3, key2=3):
           pass
    >> _get_num_pos_args(f)
    3
    )r|   r   argsr   r   r   r   _get_num_pos_args  s    
r   .)modelexample_inputsr<   c                    sJ   | i   fdd}t jjj|t jj_z| |  W 5 t jj_X  S )a   Given a model and its example inputs, return a dictionary from
    fully qualified name of submodules to example_inputs for that submodule,
    e.g. {"linear1": (tensor1,), "linear2": (tensor2,), "sub": (tensor3,),
          "sub.linear1": (tensor4,), ...}

    Used to make quantizing submodules easier now that FX Graph Mode Quantization requries
    example inputs.

    Also works for keyword arguments with default values, we would flatten keyword
    arguments as positional arguments and fill in the missing keyword args with default
    values, e.g. if we have a forward function:
    def forward(self, x, key1=3, key2=3):
        ...

    and we call it with self.submodule(x, key2=6)
    we'll get example_inputs: (x, 3, 6)

    user can also override `key1` with positional arguments as well:
    for self.submodule(x, 5, key2=6)
    we'll get: (x, 5, 6)

    variable positional arguments and variable positional keyword arguments in forward
    function are not supported currently, so please make sure no submodules is using
    them.
    c           	         s   t | }t| j|}t| jd }|t| }|rP|rP|jdd |d8 }q2||  t	|}t
| }|d k	r| |< | f||S )Nrs   F)last)listr.   r   Zforwardr   r|   popitemextendvaluestupler   )	selfr   r   Zsubmodule_example_inputsr   num_argsZ
num_to_popZsubmodule_example_inputs_tupler;   Zfqn_to_example_inputsZorig_module_callr   r   r   _patched_module_call  s    

z7get_fqn_to_example_inputs.<locals>._patched_module_call)r2   nnModule__call__)r   r   r   r   r   r   get_fqn_to_example_inputs  s    

r   Pattern)jr   rh   r>   r2   Z torch.ao.quantization.quant_typer   typingr   r   r   r   r   r   Ztorch.nn.utils.parametrizer	   collectionsr
   inspectr   r   r   r   r   r   ZReLUZReLU6ZAdaptiveAvgPool1dZAdaptiveAvgPool2dZAdaptiveAvgPool3dZ	AvgPool1dZ	AvgPool2dZ	AvgPool3dZ	MaxPool1dZ	MaxPool2dZ	MaxPool3dZIdentityZHardsigmoidZSigmoidZTanhr)   Z
functionalZadaptive_avg_pool1dZadaptive_avg_pool2dZadaptive_avg_pool3dZeluZ	hardswishZinstance_normZ
layer_normZ
leaky_reluZsiluZmishZdropoutZ
max_pool1dZ
max_pool2dZ
max_pool3dr   ZhardtanhZ	hardtanh_r   r   r   r   r   stacksumr   r    catr%   Zmeanr&   r,   r1   r6   r9   r(   rB   rH   rN   rP   rR   rX   rV   r\   r^   r`   ra   boolrb   rY   rI   ZTensorrl   intrC   ry   r~   r   r   r   r   r   r   r   r   __all__r   r   r   r   <module>   s   (		

 
5
"
;