U
    df                  
   @   s   d dl mZ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mZmZ d dlmZ ddlmZ dd	lm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  ddl!m"Z" ddl!m#Z# ej$j%ddddZ&ej$j%ddddZ'd8e
e(eee)ef  eee)ef  e
dddZ*G dd de+Z,G dd de+Z-G dd deZ.d9ej$j%ee(eee)ef  eee)ef  eee)ef  e(edd d!Z/d:ej$j%ee(eee)ef  eee)ef  e
d"d#d$Z0d;ej$j%eee)ef  eee)ef  e
d%d&d'Z1d<ej$j%eeee)ef  eee)ef  eee)ef  ed(d)d*Z2d=ej$j%eeee)ef  eee)ef  ed+d,d-Z3d>e
e(eee)ef  e(e(ee)ef ee)ef ej$j%d/d0d1Z4d?e
e(eee)ef  e(ee)ef ee)ef ej$j%d2d3d4Z5d@e
e(eee)ef  ej$j%d5d6d7Z6dS )A    )DictAnyListCallableTupleOptionalSetN)GraphModule)Tracer)TargetNodeArgument)_FusedModule   )fuse)prepare)convert) get_tensorrt_backend_config_dict)ObservedGraphModule))check_is_valid_convert_custom_config_dict&check_is_valid_fuse_custom_config_dict)check_is_valid_prepare_custom_config_dictcheck_is_valid_qconfig_dict)graph_pretty_str)get_custom_module_class_keys)modelreturnc                 C   s*   t | ts&tdtt|  d d d S )Nz,input model must be a GraphModule, Got type:z Please make zsure to follow the tutorials.)
isinstancer	   
ValueErrorstrtype)r    r!   E/tmp/pip-unpacked-wheel-ua33x9lu/torch/ao/quantization/quantize_fx.py_check_is_graph_module   s    

r#   c                 C   sb   g }|   D ],\}}t|tjjjr0|| qt| q|D ]}| j|= tjj	 | j|< q>dS )z1 Swap FloatFunctional with FXFloatFunctional
    N)
Znamed_childrenr   torchnn	quantizedZFloatFunctionalappend_swap_ff_with_fxffZ_modulesZFXFloatFunctional)r   Zmodules_to_swapnamemoduler!   r!   r"   r(   "   s    
r(   )graph_moduleis_qatfuse_custom_config_dictbackend_config_dictr   c                 C   s   t |  t| |||S )z Internal helper function to fuse modules in preparation for quantization

    Args:
        graph_module: GraphModule object from symbolic tracing (torch.fx.symbolic_trace)
    )r#   r   )r+   r,   r-   r.   r!   r!   r"   _fuse_fx1   s       r/   c                       s(   e Zd ZdZeed fddZ  ZS )Scopea/   Scope object that records the module path and the module type
    of a module. Scope is used to track the information of the module
    that contains a Node in a Graph of GraphModule. For example::

        class Sub(torch.nn.Module):
            def forward(self, x):
                # This will be a call_method Node in GraphModule,
                # scope for this would be (module_path="sub", module_type=Sub)
                return x.transpose(1, 2)

        class M(torch.nn.Module):
            def __init__(self):
                self.sub = Sub()

            def forward(self, x):
                # This will be a call_method Node as well,
                # scope for this would be (module_path="", None)
                x = x.transpose(1, 2)
                x = self.sub(x)
                return x

    )module_pathmodule_typec                    s   t    || _|| _d S N)super__init__r1   r2   )selfr1   r2   	__class__r!   r"   r5   Y   s    
zScope.__init__)__name__
__module____qualname____doc__r   r   r5   __classcell__r!   r!   r7   r"   r0   A   s   r0   c                       s>   e Zd ZdZeejjed fddZ	dd Z
dd Z  ZS )	ScopeContextManagerz A context manager to track the Scope of Node during symbolic tracing.
    When entering a forward function of a Module, we'll update the scope information of
    the current module, and when we exit, we'll restore the previous scope information.
    )scopecurrent_modulecurrent_module_pathc                    s8   t    |j| _|j| _|| _|| j_t|| j_d S r3   )r4   r5   r2   prev_module_typer1   prev_module_pathr?   r    )r6   r?   r@   rA   r7   r!   r"   r5   e   s    
zScopeContextManager.__init__c                 C   s   d S r3   r!   )r6   r!   r!   r"   	__enter__o   s    zScopeContextManager.__enter__c                 G   s   | j | j_| j| j_d S r3   )rC   r?   r1   rB   r2   )r6   argsr!   r!   r"   __exit__r   s    

zScopeContextManager.__exit__)r9   r:   r;   r<   r0   r$   r%   Moduler   r5   rD   rF   r=   r!   r!   r7   r"   r>   _   s     
r>   c                	       s   e Zd Zee ee d fddZejj	ee
dddZejj	edef eedf eeef ed fd	d
Zdeeeedf eeef ee ee ed fddZ  ZS )QuantizationTracer)skipped_module_namesskipped_module_classesc                    s2   t    || _|| _tdd | _i | _d| _d S )N T)r4   r5   rI   rJ   r0   r?   node_name_to_scopeZrecord_stack_traces)r6   rI   rJ   r7   r!   r"   r5   y   s    
zQuantizationTracer.__init__)mmodule_qualified_namer   c                 C   s>   |j drt|tjj p<|| jkp<t|| jkp<t|t	S )Nztorch.nn)
r:   
startswithr   r$   r%   Z
SequentialrI   r    rJ   r   )r6   rM   rN   r!   r!   r"   is_leaf_module   s    z!QuantizationTracer.is_leaf_module.)rM   forwardrE   kwargsr   c              
      sB   |  |}t| j||  t ||||W  5 Q R  S Q R X d S r3   )Zpath_of_moduler>   r?   r4   call_module)r6   rM   rQ   rE   rR   rN   r7   r!   r"   rS      s    
zQuantizationTracer.call_moduleN)kindtargetrE   rR   r)   	type_exprr   c                    s2   t  ||||||}| jj| jjf| j|j< |S r3   )r4   create_noder?   r1   r2   rL   r)   )r6   rT   rU   rE   rR   r)   rV   noder7   r!   r"   rW      s
    	zQuantizationTracer.create_node)NN)r9   r:   r;   r   r   r   r5   r$   r%   rG   boolrP   r   r   r   rS   r   r   r   r   rW   r=   r!   r!   r7   r"   rH   x   s*    


  

rH   F)r   qconfig_dictr,   prepare_custom_config_dictequalization_qconfig_dictr.   is_standalone_moduler   c              
   C   s.  |dkri }|dkri }t | t| t | |dg }|dg }t|  |s|dg }	|dd |	D 7 }|dg }
|dd |
D 7 }t|d	}||7 }|d
g }t||}t| || }|D ]}t||t	| | qt
||||}t||||j||||d}|D ]}t||t	| | q|S )a=   Internal helper function for prepare_fx
    Args:
      `model`, `qconfig_dict`, `prepare_custom_config_dict`, `equalization_qonfig_dict`:
      see docs for :func:`~torch.ao.quantization.prepare_fx`
      `is_standalone_module`: a boolean flag indicates whether we are
      quantizing a standalone module or not, a standalone module
      is a submodule of the parent module that is not inlined in the
forward graph of the parent module,
      the way we quantize standalone module is described in:
      :func:`~torch.ao.quantization._prepare_standalone_module_fx`
    NZnon_traceable_module_nameZnon_traceable_module_classZstandalone_module_namec                 S   s   g | ]}|d  qS r   r!   .0configr!   r!   r"   
<listcomp>   s     z_prepare_fx.<locals>.<listcomp>Zstandalone_module_classc                 S   s   g | ]}|d  qS r^   r!   r_   r!   r!   r"   rb      s    Z%float_to_observed_custom_module_classpreserved_attributes)r[   r\   r.   r]   )r   r   getr(   r   rH   r	   tracesetattrgetattrr/   r   rL   )r   rZ   r,   r[   r\   r.   r]   rI   rJ   Zstandalone_module_name_configsZstandalone_module_class_configsZfloat_custom_module_classesrc   Ztracerr+   	attr_namepreparedr!   r!   r"   _prepare_fx   st         
rj   )r   rZ   r,   r[   r.   r   c                 C   s   t | ||||ddS )a   [Internal use only] Prepare a standalone module, so that it can be used when quantizing the
    parent module.
    standalone_module means it a submodule that is not inlined in parent module,
    and will be quantized separately as one unit.

    How the standalone module is observed is specified by `input_quantized_idxs` and
    `output_quantized_idxs` in the prepare_custom_config for the standalone module

    Returns:

        * model(GraphModule): prepared standalone module. It has these attributes:

            * `_standalone_module_input_quantized_idxs(List[Int])`: a list of
              indexes for the graph input that is expected to be quantized,
              same as input_quantized_idxs configuration provided
              for the standalone module
            * `_standalone_module_output_quantized_idxs(List[Int])`: a list of
              indexs for the graph output that is quantized
              same as input_quantized_idxs configuration provided
              for the standalone module

    T)r.   r]   )rj   )r   rZ   r,   r[   r.   r!   r!   r"   _prepare_standalone_module_fx  s    rk   )r   r-   r.   r   c                 C   sd   t jd t| t j| }t }|r:t|dg }|D ]}t||t	| | q>t
|d||S )aZ   Fuse modules like conv+bn, conv+bn+relu etc, model must be in eval mode.
    Fusion rules are defined in torch.quantization.fx.fusion_pattern.py

    Args:

        * `model`: a torch.nn.Module model
        * `fuse_custom_config_dict`: Dictionary for custom configurations for fuse_fx, e.g.::

            fuse_custom_config_dict = {
              # Attributes that are not used in forward function will
              # be removed when constructing GraphModule, this is a list of attributes
              # to preserve as an attribute of the GraphModule even when they are
              # not used in the code, these attributes will also persist through deepcopy
              "preserved_attributes": ["preserved_attr"],
            }

    Example::

        from torch.ao.quantization import fuse_fx
        m = Model().eval()
        m = fuse_fx(m)

    z$quantization_api.quantize_fx.fuse_fxrc   F)r$   _C_log_api_usage_oncer   fxZsymbolic_tracesetrd   rf   rg   r/   )r   r-   r.   r+   rc   rh   r!   r!   r"   fuse_fx,  s    
rp   )r   rZ   r[   r\   r.   r   c                 C   s   t jd t| |d|||S )a]   Prepare a model for post training static quantization

    Args:
      * `model`: torch.nn.Module model, must be in eval mode

      * `qconfig_dict`: qconfig_dict is a dictionary with the following configurations::

          qconfig_dict = {
            # optional, global config
            "": qconfig?,

            # optional, used for module and function types
            # could also be split into module_types and function_types if we prefer
            "object_type": [
              (torch.nn.Conv2d, qconfig?),
              (torch.nn.functional.add, qconfig?),
              ...,
             ],

            # optional, used for module names
            "module_name": [
              ("foo.bar", qconfig?)
              ...,
            ],

            # optional, matched in order, first match takes precedence
            "module_name_regex": [
              ("foo.*bar.*conv[0-9]+", qconfig?)
              ...,
            ],

            # optional, used for matching object type invocations in a submodule by
            # order
            # TODO(future PR): potentially support multiple indices ('0,1') and/or
            #   ranges ('0:3').
            "module_name_object_type_order": [
              # fully_qualified_name, object_type, index, qconfig
              ("foo.bar", torch.nn.functional.linear, 0, qconfig?),
            ],

            # priority (in increasing order):
            #   global, object_type, module_name_regex, module_name,
            #   module_name_object_type_order
            # qconfig == None means fusion and quantization should be skipped for anything
            # matching the rule
          }

      * `prepare_custom_config_dict`: customization configuration dictionary for quantization tool::

          prepare_custom_config_dict = {
            # optional: specify the path for standalone modules
            # These modules are symbolically traced and quantized as one unit
            "standalone_module_name": [
               # module_name, qconfig_dict, prepare_custom_config_dict
               ("submodule.standalone",
                None,  # qconfig_dict for the prepare function called in the submodule,
                       # None means use qconfig from parent qconfig_dict
                {"input_quantized_idxs": [], "output_quantized_idxs": []}),  # prepare_custom_config_dict
                {}  # backend_config_dict, TODO: point to README doc when it's ready
            ],

            "standalone_module_class": [
                # module_class, qconfig_dict, prepare_custom_config_dict
                (StandaloneModule,
                 None,  # qconfig_dict for the prepare function called in the submodule,
                        # None means use qconfig from parent qconfig_dict
                {"input_quantized_idxs": [0], "output_quantized_idxs": [0]},  # prepare_custom_config_dict
                {})  # backend_config_dict, TODO: point to README doc when it's ready
            ],

            # 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
            # (only needed for static quantization)
            "float_to_observed_custom_module_class": {
               "static": {
                   CustomModule: ObservedCustomModule
               }
            },

            # the qualified names for the submodule that are not symbolically traceable
            "non_traceable_module_name": [
               "non_traceable_module"
            ],

            # the module classes that are not symbolically traceable
            # we'll also put dynamic/weight_only custom module here
            "non_traceable_module_class": [
               NonTraceableModule
            ],

            # By default, inputs and outputs of the graph are assumed to be in
            # fp32. Providing `input_quantized_idxs` will set the inputs with the
            # corresponding indices to be quantized. Providing
            # `output_quantized_idxs` will set the outputs with the corresponding
            # indices to be quantized.
            "input_quantized_idxs": [0],
            "output_quantized_idxs": [0],

            # Attributes that are not used in forward function will
            # be removed when constructing GraphModule, this is a list of attributes
            # to preserve as an attribute of the GraphModule even when they are
            # not used in the code, these attributes will also persist through deepcopy
            "preserved_attributes": ["preserved_attr"],
          }

      * `equalization_qconfig_dict`: equalization_qconfig_dict is a dictionary
        with a similar structure as qconfig_dict except it will contain
        configurations specific to equalization techniques such as input-weight
        equalization.

      * `backend_config_dict`: a dictionary that specifies how operators are quantized
         in a backend, this includes how the operaetors are observed,
         supported fusion patterns, how quantize/dequantize ops are
         inserted, supported dtypes etc. The structure of the dictionary is still WIP
         and will change in the future, please don't use right now.


    Return:
      A GraphModule with observer (configured by qconfig_dict), ready for calibration

    Example::

        import torch
        from torch.ao.quantization import get_default_qconfig
        from torch.ao.quantization import prepare_fx

        float_model.eval()
        qconfig = get_default_qconfig('fbgemm')
        def calibrate(model, data_loader):
            model.eval()
            with torch.no_grad():
                for image, target in data_loader:
                    model(image)

        qconfig_dict = {"": qconfig}
        prepared_model = prepare_fx(float_model, qconfig_dict)
        # Run calibration
        calibrate(prepared_model, sample_inference_data)

    z'quantization_api.quantize_fx.prepare_fxFr$   rl   rm   rj   )r   rZ   r[   r\   r.   r!   r!   r"   
prepare_fxT  s     rr   )r   rZ   r[   r.   r   c                 C   s   t jd t| |d||dS )a   Prepare a model for quantization aware training

    Args:
      * `model`: torch.nn.Module model, must be in train mode
      * `qconfig_dict`: see :func:`~torch.ao.quantization.prepare_fx`
      * `prepare_custom_config_dict`: see :func:`~torch.ao.quantization.prepare_fx`
      * `backend_config_dict`: see :func:`~torch.ao.quantization.prepare_fx`

    Return:
      A GraphModule with fake quant modules (configured by qconfig_dict), ready for
      quantization aware training

    Example::

        import torch
        from torch.ao.quantization import get_default_qat_qconfig
        from torch.ao.quantization import prepare_fx

        qconfig = get_default_qat_qconfig('fbgemm')
        def train_loop(model, train_data):
            model.train()
            for image, target in data_loader:
                ...

        float_model.train()
        qconfig_dict = {"": qconfig}
        prepared_model = prepare_fx(float_model, qconfig_dict)
        # Run calibration
        train_loop(prepared_model, train_loop)

    z+quantization_api.quantize_fx.prepare_qat_fxT)r.   rq   )r   rZ   r[   r.   r!   r!   r"   prepare_qat_fx  s    %rs   T)r+   is_referenceconvert_custom_config_dictr]   _remove_qconfigrZ   r.   r   c           
   	   C   s^   |dkri }t |  t| t| ||||||d}|dg }|D ]}	t||	t| |	 qB|S )ze `is_standalone_module`: see docs in :func:`~torch.ao.quantization.prepare_standalone_module_fx`
    N)Z_remove_qconfig_flagZconvert_qconfig_dictr.   rc   )r#   r   r   rd   rf   rg   )
r+   rt   ru   r]   rv   rZ   r.   r&   rc   rh   r!   r!   r"   _convert_fx"  s"    
rw   )r+   rt   ru   rv   rZ   r.   r   c                 C   s    t jd t| |||||dS )a   Convert a calibrated or trained model to a quantized model

    Args:
        * `graph_module`: A prepared and calibrated/trained model (GraphModule)
        * `is_reference`: flag for whether to produce a reference quantized model,
          which will be a common interface between pytorch quantization with
          other backends like accelerators
        * `convert_custom_config_dict`: dictionary for custom configurations for convert function::

            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": {
                 "static": {
                     ObservedCustomModule: QuantizedCustomModule
                 },
                 "dynamic": {
                     ObservedCustomModule: QuantizedCustomModule
                 },
                 "weight_only": {
                     ObservedCustomModule: QuantizedCustomModule
                 }
              },

              # Attributes that are not used in forward function will
              # be removed when constructing GraphModule, this is a list of attributes
              # to preserve as an attribute of the GraphModule even when they are
              # not used in the code
              "preserved_attributes": ["preserved_attr"],
            }

        * `_remove_qconfig`: Option to remove the qconfig attributes in the model after convert.

        * `qconfig_dict`: qconfig_dict with either same keys as what is passed to
          the qconfig_dict in `prepare_fx` API, with same values or `None`, or
          additional keys with values set to `None`

          For each entry whose value is set to None, we skip quantizing that entry in the model::

            qconfig_dict = {
              # used for object_type, skip quantizing torch.nn.functional.add
              "object_type": [
                (torch.nn.functional.add, None),
                (torch.nn.functional.linear, qconfig_from_prepare)
                ...,
              ],

              # sed for module names, skip quantizing "foo.bar"
              "module_name": [
                ("foo.bar", None)
                ...,
              ],
            }

         * `backend_config_dict`: A configuration for the backend which describes how
            operators should be quantized in the backend, this includes quantization
            mode support (static/dynamic/weight_only), dtype support (quint8/qint8 etc.),
            observer placement for each operators and fused operators. Detailed
            documentation can be found in torch/ao/quantization/backend_config/README.md

    Return:
        A quantized model (GraphModule)

    Example::

        # prepared_model: the model after prepare_fx/prepare_qat_fx and calibration/training
        quantized_model = convert_fx(prepared_model)

    z'quantization_api.quantize_fx.convert_fx)rv   rZ   r.   )r$   rl   rm   rw   )r+   rt   ru   rv   rZ   r.   r!   r!   r"   
convert_fxC  s    Nrx   )r+   rt   ru   r   c                 C   s   t | ||ddS )a|   [Internal use only] Convert a model produced by :func:`~torch.ao.quantization.prepare_standalone_module_fx`
    and convert it to a quantized model

    Returns a quantized standalone module, whether input/output is quantized is
    specified by prepare_custom_config_dict, with
    input_quantized_idxs, output_quantized_idxs, please
    see docs for prepare_fx for details
    T)r]   )rw   )r+   rt   ru   r!   r!   r"   _convert_standalone_module_fx  s    ry   )NN)NNNF)NN)NN)NNN)NN)NFTNN)FNTNN)FN)7typingr   r   r   r   r   r   r   r$   Ztorch.fxr	   Ztorch.fx._symbolic_tracer
   Ztorch.fx.noder   r   r   Ztorch.nn.intrinsicr   rn   r   r   Z
fx.convertr   Zbackend_configr   Zfx.graph_moduler   Zfx.qconfig_utilsr   r   r   r   Zfx.utilsr   r   r%   rG   r#   r(   rY   r   r/   objectr0   r>   rH   rj   rk   rp   rr   rs   rw   rx   ry   r!   r!   r!   r"   <module>   s   $  =    X  (   +    #  2     

#     

[  