U
    dWk                     @   s  d dl Z d dlZd dlZd dlZd dlmZ d dlm  mZ d dl	m
Z
 d dlmZmZmZmZmZmZmZ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  d dl!m"Z" d	d
 Z#d4ddZ$d5ddZ%dd Z&dd Z'd6ddZ(d7ddZ)dd Z*dd Z+d8ddZ,dd  Z-d!d" Z.d9d#d$Z/dej0ddfd%d&Z1d:d'd(Z2d;d)d*Z3d<d,d-Z4d=d.d/Z5d0d1 Z6d>d2d3Z7dS )?    N)_FusedModule))get_default_dynamic_quant_module_mappings(get_default_static_quant_module_mappings2get_default_static_quant_reference_module_mappingsget_default_qat_module_mappings$get_default_qconfig_propagation_listno_observer_set_has_special_act_post_process_get_special_act_post_process   )get_qparam_dict)has_no_children_ignoring_parametrizations)DeQuantStubQuantWrapper)add_module_to_qconfig_obs_ctrdefault_dynamic_qconfigfloat16_dynamic_qconfig!float_qparams_weight_only_qconfig&float_qparams_weight_only_qconfig_4bitactivation_is_memoryless)type_before_parametrizationsc                 C   s    t | tjjjpt | tjjjS N)
isinstancetorchaoquantizationZObserverBaseZFakeQuantizeBasemodule r   B/tmp/pip-unpacked-wheel-ua33x9lu/torch/ao/quantization/quantize.pyis_activation_post_process   s    r     c           
      C   s   | t| |}| ||}t| d|}tjjj||  t|| }|| _| 	 D ]V\}}|rj|d | n|}	|dks|| dg ksRt
|| dg ksRt||||	 qRdS )a  This is a helper function for `propagate_qconfig_`

    Args:
        module: input module
        qconfig_dict: dictionary that maps from name of submodule to quantization
                     configuration
        qconfig_parent: quantization config of parent module, we will fallback to
                       this config when there is no specified config for current
                       module
        prefix: corresponding prefix of the current module, used as key in
                qconfig_dict
        prepare_custom_config_dict: dictionary for custom handling of modules
                                    see docs for :func:`~torch.ao.quantization.prepare_fx`

    Return:
        None, module is modified inplace with qconfig attached
    qconfig.NZnon_traceable_module_nameZnon_traceable_module_class)getr   getattrr   r   r   r"   Zassert_valid_qconfigr   named_childrentype_propagate_qconfig_helper)
r   qconfig_dictZqconfig_parentprefixprepare_custom_config_dictZmodule_qconfigZqconfig_with_device_checknamechildmodule_prefixr   r   r   r(   $   s&    
   r(   c                 C   s*   |dkri }|dkri }t | ||d dS )a  Propagate qconfig through the module hierarchy and assign `qconfig`
    attribute on each leaf module

    Args:
        module: input module
        qconfig_dict: dictionary that maps from name or type of submodule to
            quantization configuration, qconfig applies to all submodules of a
            given module unless qconfig for the submodules are specified (when
            the submodule already has qconfig attribute)
        prepare_custom_config_dict: dictionary for custom handling of modules
            see docs for :func:`~torch.ao.quantization.prepare_fx`

    Return:
        None, module is modified inplace with qconfig attached
    N)r+   )r(   )r   r)   r+   r   r   r   propagate_qconfig_L   s
    r/   c                 C   s
   |  |S )z3Forward hook that calls observer on the output
    activation_post_process)selfinputoutputr   r   r   _observer_forward_hookb   s    r5   c                 C   s   |  |d S )z7Forward pre hook that calls observer on the output
    r   r0   )r2   r3   r   r   r   _observer_forward_pre_hookg   s    r6   Fc                 C   sT   t | dstd|r4| t}| jj|jdd n| t}| j	j|jdd d S )Nr1   zGExpect activation_post_process attribute already attached to the moduleF)last)
hasattrAssertionErrorregister_forward_pre_hookr6   _forward_pre_hooksmove_to_endidregister_forward_hookr5   _forward_hooks)r   pre_hookhandler   r   r   %register_activation_post_process_hookl   s    

rB   c                    s  |dkrt  }|dkri } dkr`t| }t|dksDtd|t|dkr\tt|nd ddddd d fd	d
	}|  D ]\}}t|t	j
fkrqqt|tjtjfkrֈ|rԈ|j |_qt|tr|r|| qt|rt|}	|||	 q|dk	r<t||kr<|r|| q|rt||kr|t| |}
t| ||
 |t| t kr||
 qt||| | qt| rt| tj	jst| |kr||  dS )as  Add observer for the leaf child of the module.

    This function insert observer module to all leaf child module that
    has a valid qconfig attribute.

    Args:
        module: input module with qconfig attributes for all the leaf modules that we want to quantize
        qconfig_propagation_list: a list of quantizable modules that will have observers added to them
            if they are leaf nodes
        device: parent device, if any
        non_leaf_module_list: list of non-leaf modules we want to add observer

    Return:
        None, module is modified inplace with added observer modules and forward_hooks
    Nr   zSadd_observer_ only works with cpu or single-device CUDA modules, but got devices {}r   c                 S   s,   |d kr|   n| }|d k	r(|| |S r   )
activationto)r"   devicespecial_act_post_processrC   r   r   r   get_activation_post_process   s    
z2add_observer_.<locals>.get_activation_post_processc                 S   s   t | do| jd k	S )Nr"   r8   r"   )mr   r   r   needs_observation   s    z(add_observer_.<locals>.needs_observationc                    s>   | r:t | ts:| d| j | t| t| jd dS )zn Adds an activation post process module and register
        a pre or post hook that calls the module
        r1   r@   N)r   r   Z
add_moduler"   rB   r   )rI   rF   rE   rG   rJ   r   r   insert_activation_post_process   s      z5add_observer_.<locals>.insert_activation_post_process)N)N)r   get_unique_devices_lenr9   formatnextiterr&   r   nnZDropoutnnqZFloatFunctionalZQFunctionalr"   r1   r   r   r	   r
   
from_floatsetattrr   add_observer_r   r   Z
Sequential)r   qconfig_propagation_listZnon_leaf_module_listrE   custom_module_class_mappingdevicesrM   r,   r-   rF   Zobserved_childr   rL   r   rW   w   sP    







rW   c                 C   s$   dd |   D dd |  D B S )Nc                 S   s   h | ]
}|j qS r   rE   .0pr   r   r   	<setcomp>   s     z&get_unique_devices_.<locals>.<setcomp>c                 S   s   h | ]
}|j qS r   r[   r\   r   r   r   r_      s     )
parametersbuffersr   r   r   r   rN      s    rN   c                 C   sD   t | r t| dr | jr t| S |  D ]\}}t|| j|< q(| S )a{  Wrap the leaf child module in QuantWrapper if it has a valid qconfig
    Note that this function will modify the children of module inplace and it
    can return a new module which wraps the input module as well.

    Args:
        module: input module with qconfig attributes for all the leaf modules
        that we want to quantize

    Return:
        Either the inplace modified module with submodules wrapped in
        `QuantWrapper` based on qconfig or a new `QuantWrapper` module which
        wraps the input module, the latter case only happens when the input
        module is a leaf module and we want to quantize it.
    r"   )r   r8   r"   r   r&   add_quant_dequant_modules)r   r,   r-   r   r   r   rb      s
    rb   c                 C   s   t jd |dkri }|di }|s2t| } |}|dkrDt }t| dd tdd | 	 D spt
d t| |||d | S )	a  Prepares a copy of the model for quantization calibration or quantization-aware training.

    Quantization configuration should be assigned preemptively
    to individual submodules in `.qconfig` attribute.

    The model will be attached with observer or fake quant modules, and qconfig
    will be propagated.

    Args:
        `model`: input model to be modified in-place
        `inplace`: carry out model transformations in-place, the original module is mutated
        `allow_list`: list of quantizable modules
        `observer_non_leaf_module_list`: list of non-leaf modules we want to add observer
        `prepare_custom_config_dict`: customization configuration dictionary for prepare function

    .. code-block:: python

       # Example of prepare_custom_config_dict:
       prepare_custom_config_dict = {
           # user will manually define the corresponding observed
           # module class which has a from_float class method that converts
           # float custom module to observed custom module
           "float_to_observed_custom_module_class": {
               CustomModule: ObservedCustomModule
           }
        }

    z!quantization_api.quantize.prepareNZ%float_to_observed_custom_module_classr)   c                 s   s   | ]}t |d o|jV  qdS )r"   NrH   )r]   rI   r   r   r   	<genexpr>  s     zprepare.<locals>.<genexpr>zNone of the submodule got qconfig applied. Make sure you passed correct configuration through `qconfig_dict` or by assigning the `.qconfig` attribute directly on submodules)rY   )r   _C_log_api_usage_oncer$   copydeepcopyr   r/   anymoduleswarningswarnrW   )modelinplaceZ
allow_listobserver_non_leaf_module_listr+   rY   rX   r   r   r   prepare   s&    

  rq   c                    sD   t  drt jrt d d fdd	}|dd |dd d S )Nr1   Fc                    s^   | r
 j n j}| rtnt}t }| D ]\}}||kr*|| q*|D ]}|| qJd S r   )r;   r?   r6   r5   setitemsaddpop)r@   Zhook_mapZobserver_hookZhandle_ids_to_removeZ	handle_idhook_fnr   r   r   remove_hooks'  s    z5_remove_activation_post_process.<locals>.remove_hooksTrK   )F)r8   r    r1   delattr)r   rw   r   r   r   _remove_activation_post_process  s    



ry   c                 C   s0   |   D ]}t| qt| dr$| `t|  dS )zClean up the qconfig left in the module so that new qconfig can be
    propagated.

    Args:
        module: module to be cleaned up
    r"   N)children_remove_qconfigr8   r"   ry   )r   r-   r   r   r   r{   5  s
    

r{   c                 C   s\   t jd |dkrt }|s(t| } |   t| dd || f|  t| |dd | S )a  Quantize the input float model with post training static quantization.

    First it will prepare the model for calibration, then it calls
    `run_fn` which will run the calibration step, after that we will
    convert the model to a quantized model.

    Args:
        model: input float model
        run_fn: a calibration function for calibrating the prepared model
        run_args: positional arguments for `run_fn`
        inplace: carry out model transformations in-place, the original module is mutated
        mapping: correspondence between original module types and quantized counterparts

    Return:
        Quantized model.
    z"quantization_api.quantize.quantizeNTro   )	r   rf   rg   r   rh   ri   evalrq   convert)rn   run_fnrun_argsmappingro   r   r   r   quantizeD  s    
r   c                 C   sr  t jd |dkr|t jkrHtjttjttjttj	ttj
ttjti}nt|t jkr|tjttjttjttj	ttj
ttjti}n@|t jkrtjttjti}n$|t jkrtjti}ntd|npt|tr.|t jkrt}n@|t jkrt}n0|t jkrt}n |t jkrt}ntdt|tt|t|}|dkr>t }|sNt | } | !  t"| | t#| |dd | S )av  Converts a float model to dynamic (i.e. weights-only) quantized model.

    Replaces specified modules with dynamic weight-only quantized versions and output the quantized model.

    For simplest usage provide `dtype` argument that can be float16 or qint8. Weight-only quantization
    by default is performed for layers with large weights size - i.e. Linear and RNN variants.

    Fine grained control is possible with `qconfig` and `mapping` that act similarly to `quantize()`.
    If `qconfig` is provided, the `dtype` argument is ignored.

    Args:
        model: input model
        qconfig_spec: Either:

            - A dictionary that maps from name or type of submodule to quantization
              configuration, qconfig applies to all submodules of a given
              module unless qconfig for the submodules are specified (when the
              submodule already has qconfig attribute). Entries in the dictionary
              need to be QConfig instances.

            - A set of types and/or submodule names to apply dynamic quantization to,
              in which case the `dtype` argument is used to specify the bit-width

        inplace: carry out model transformations in-place, the original module is mutated
        mapping: maps type of a submodule to a type of corresponding dynamically quantized version
            with which the submodule needs to be replaced

    z*quantization_api.quantize.quantize_dynamicNzTDon't know how to quantize with default settings for {}. Provide full qconfig pleasez.Unknown dtype specified for quantize_dynamic: Tr|   )$r   rf   rg   qint8rS   ZLinearr   ZLSTMZGRUZLSTMCellZRNNCellZGRUCellZfloat16r   Zquint8ZEmbeddingBagr   Z	EmbeddingZquint4x2r   
ValueErrorrP   r   rr   RuntimeErrorstrdictzip	itertoolsrepeatr   rh   ri   r}   r/   r~   )rn   Zqconfig_specZdtyper   ro   Zdefault_qconfigr   r   r   quantize_dynamic`  s|    
      
      
  
 





r   c                 C   sl   t jd | jstd|dkr(t }|s6t| } t| dd t	| |ddd t
| t| dd | S )	a  
    Prepares a copy of the model for quantization calibration or
    quantization-aware training and converts it to quantized version.

    Quantization configuration should be assigned preemptively
    to individual submodules in `.qconfig` attribute.

    Args:
        model: input model to be modified in-place
        mapping: dictionary that maps float modules to quantized modules to be
                 replaced.
        inplace: carry out model transformations in-place, the original module
                 is mutated
    z%quantization_api.quantize.prepare_qatz1prepare_qat only works on models in training modeNrd   TF)r   ro   remove_qconfig)rp   ro   )r   rf   rg   Ztrainingr9   r   rh   ri   r/   r~   rq   rr   values)rn   r   ro   r   r   r   prepare_qat  s    
r   c                 C   sL   t jd |st| } |   t| dd || f|  t| dd | S )ag  Do quantization aware training and output a quantized model

    Args:
        model: input model
        run_fn: a function for evaluating the prepared model, can be a
                function that simply runs the prepared model or a training
                loop
        run_args: positional arguments for `run_fn`

    Return:
        Quantized model.
    z&quantization_api.quantize.quantize_qatTr|   )r   rf   rg   rh   ri   Ztrainr   r~   )rn   r   r   ro   r   r   r   quantize_qat  s    
r   Tc                 C   s<   t jd |st| } t| |d||d |r8t|  | S )ag  Converts submodules in input module to a different module according to `mapping`
    by calling `from_float` method on the target module class. And remove qconfig at the
    end if remove_qconfig is set to True.

    Args:
        `module`: prepared and calibrated module
        `mapping`: a dictionary that maps from source module type to target
                   module type, can be overwritten to allow swapping user defined
                   Modules
        `inplace`: carry out model transformations in-place, the original module
                   is mutated
        `convert_custom_config_dict`: custom configuration dictionary for convert function

    .. code-block:: python

       # Example of convert_custom_config_dict:
       convert_custom_config_dict = {
           # user will manually define the corresponding quantized
           # module class which has a from_observed class method that converts
           # observed custom module to quantized custom module
           "observed_to_quantized_custom_module_class": {
               ObservedCustomModule: QuantizedCustomModule
           }
       }

    z!quantization_api.quantize.convertT)ro   is_referenceconvert_custom_config_dict)r   rf   rg   rh   ri   _convertr{   )r   r   ro   r   r   r   r   r   r   r~     s    
   r~   c                 C   s   |dkr|rt  nt }|dkr$i }|di }|s>t| } i }|  D ]>\}}t|tsxt||krxt	||d|| t
|||||< qJ| D ]\}	}
|
| j|	< q| S )a  Converts submodules in input module to a different module according to `mapping`
    by calling `from_float` method on the target module class

    Args:
        module: input module
        mapping: a dictionary that maps from source module type to target
                 module type, can be overwritten to allow swapping user defined
                 Modules
        inplace: carry out model transformations in-place, the original module
                 is mutated
        is_reference: a flag to enable quantized reference module

    NZ)observed_to_quantized_custom_module_classT)r   r   r$   rh   ri   r&   r   r   r   r   swap_modulers   rc   )r   r   ro   r   r   rY   Zreassignr,   modkeyvaluer   r   r   r     s,    



 r   c                 C   sJ  | }t | drF| jdk	rFd}t| |krD|t|  | }d}nnt| |kr|t|  }t |dr|jr| jdk	szt| j }|| j t|}|| |}n
|| }d}|rF| j	
 D ]}|| q| j
 D ]}	|	tk	r||	 qt| }
t|
dkstd|
t|
dkr2tt|
nd}|rF|| |S )	a	  Swaps the module if it has a quantized counterpart and it has an
    `observer` attached.

    Args:
        mod: input module
        mapping: a dictionary that maps from nn module to nnq module

    Return:
        The corresponding quantized module of `mod`
    r"   NFT_IS_REFERENCEr   zQswap_module only works with cpu or single-device CUDA modules, but got devices {}r   )r8   r"   r   Zfrom_observedr   r9   Zweightr   rU   r;   r   r:   r?   r5   r>   rN   rO   rP   rQ   rR   rD   )r   r   rY   new_modZswappedZqmodZweight_post_processZweight_qparamsZpre_hook_fnrv   rZ   rE   r   r   r   r   6  s@    



r   c                 C   sZ   dd }t | dr$| j|||d < |  D ](\}}|rD||| n|}t||| q,dS )a,  Traverse the modules and save all observers into dict.
    This is mainly used for quantization accuracy debug
    Args:
        mod: the top module we want to save all observers
        prefix: the prefix for the current module
        target_dict: the dictionary used to save all the observers
    c                 S   s   | dkr| S | d S )Nr!   r#   r   )r*   r   r   r   
get_prefixp  s    z%get_observer_dict.<locals>.get_prefixr1   N)r8   r1   r&   get_observer_dict)r   target_dictr*   r   r,   r-   r.   r   r   r   r   h  s    
r   )Nr!   N)NN)F)NNNN)FNNN)NF)NF)F)NFTFN)NFFN)r!   )8rh   r   rl   r   Ztorch.nnrS   Ztorch.nn.quantizedZ	quantizedrT   Ztorch.nn.intrinsicr   Z+torch.ao.quantization.quantization_mappingsr   r   r   r   r   r   r	   r
   utilsr   r   Ztorch.ao.quantization.stubsr   r   Ztorch.ao.quantization.qconfigr   r   r   r   r   r   Ztorch.nn.utils.parametrizer   r    r(   r/   r5   r6   rB   rW   rN   rb   rq   ry   r{   r   r   r   r   r   r~   r   r   r   r   r   r   r   <module>   sb   (
      
(


V   
8
 
U

        
(      
(2