U
    d                     @   s   d dl 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 dd Zd	d
 ZdddZedfddZdedfddZdedfddZdedfddZdS )    N)get_fuser_method)fuse_conv_bn)fuse_conv_bn_relutype_before_parametrizations)ListOptionalc                 C   s&   | d}| }|D ]}t||}q|S )N.)splitgetattr)modelsubmodule_keytokenscur_mods r   F/tmp/pip-unpacked-wheel-ua33x9lu/torch/ao/quantization/fuse_modules.py_get_module   s
    
r   c                 C   sB   | d}|d d }| }|D ]}t||}qt||d | d S )Nr	   )r
   r   setattr)r   r   moduler   Z
sub_tokensr   r   r   r   r   _set_module   s    
r   c                 C   s   t dd | D }t||}|dkr2td|dgt|  }||f|  }| d j D ]\}}|| | d j|= q\| d j D ]\}}	|	|	 | d j|= q||d< t
dt| D ] }
t }| d j|_|||
< q|S )a|  Returns a list of modules that fuses the operations specified
     in the input module list.

    Fuses only the following sequence of modules:
    conv, bn
    conv, bn, relu
    conv, relu
    linear, bn
    linear, relu
    For these sequences, the first element in the output module list performs
    the fused operation. The rest of the elements are set to nn.Identity()
    c                 s   s   | ]}t |V  qd S Nr   ).0mr   r   r   	<genexpr>-   s     z%fuse_known_modules.<locals>.<genexpr>NzCannot fuse modules: {}r   r      )tupler   NotImplementedErrorformatlenZ_forward_pre_hooksitemsZregister_forward_pre_hookZ_forward_hooksZregister_forward_hookrangennZIdentityZtraining)mod_listis_qatadditional_fuser_method_mappingtypesZfuser_methodnew_modZfusedZ	handle_idZpre_hook_fnZhook_fniidentityr   r   r   fuse_known_modules    s$    



r+   c           
      C   sh   |d kri }| di }g }|D ]}|t| | q ||||}t|D ]\}	}t| |||	  qJd S )Nr&   )getappendr   	enumerater   )
r   modules_to_fuser%   
fuser_funcfuse_custom_config_dictr&   r$   itemZnew_mod_listr)   r   r   r   _fuse_modules_helperE   s    r3   Fc                 C   sP   |st | } tdd |D r2t| |||| n|D ]}t| |||| q6| S )Nc                 s   s   | ]}t |tV  qd S r   )
isinstancestr)r   Zmodule_elementr   r   r   r   X   s     z _fuse_modules.<locals>.<genexpr>)copydeepcopyallr3   )r   r/   r%   inplacer0   r1   Zmodule_listr   r   r   _fuse_modulesT   s    
r:   c                 C   s   t | |d|tddS )aD  Fuses a list of modules into a single module

    Fuses only the following sequence of modules:
    conv, bn
    conv, bn, relu
    conv, relu
    linear, relu
    bn, relu
    All other sequences are left unchanged.
    For these sequences, replaces the first item in the list
    with the fused module, replacing the rest of the modules
    with identity.

    Args:
        model: Model containing the modules to be fused
        modules_to_fuse: list of list of module names to fuse. Can also be a list
                         of strings if there is only a single list of modules to fuse.
        inplace: bool specifying if fusion happens in place on the model, by default
                 a new model is returned
        fuser_func: Function that takes in a list of modules and outputs a list of fused modules
                    of the same length. For example,
                    fuser_func([convModule, BNModule]) returns the list [ConvBNModule, nn.Identity()]
                    Defaults to torch.ao.quantization.fuse_known_modules
        `fuse_custom_config_dict`: custom configuration for fusion

    .. code-block:: python

       # Example of fuse_custom_config_dict
       fuse_custom_config_dict = {
           # Additional fuser_method mapping
           "additional_fuser_method_mapping": {
               (torch.nn.Conv2d, torch.nn.BatchNorm2d): fuse_conv_bn
           },
       }

    Returns:
        model with fused modules. A new copy is created if inplace=True.

    Examples::

            >>> m = M().eval()
            >>> # m is a module containing the sub-modules below
            >>> modules_to_fuse = [ ['conv1', 'bn1', 'relu1'], ['submodule.conv', 'submodule.relu']]
            >>> fused_m = torch.ao.quantization.fuse_modules(m, modules_to_fuse)
            >>> output = fused_m(input)

            >>> m = M().eval()
            >>> # Alternately provide a single list of modules to fuse
            >>> modules_to_fuse = ['conv1', 'bn1', 'relu1']
            >>> fused_m = torch.ao.quantization.fuse_modules(m, modules_to_fuse)
            >>> output = fused_m(input)

    FNr%   r9   r0   r1   r:   r+   r   r/   r9   r0   r1   r   r   r   fuse_modulesa   s    6r>   c                 C   s   t | |d|tddS )z$ QAT version for `fuse_modules`
    TNr;   r<   r=   r   r   r   fuse_modules_qat   s    r?   )N)r6   Ztorch.nnr#   Z+torch.ao.quantization.fuser_method_mappingsr   r   r   Ztorch.nn.utils.parametrizer   typingr   r   r   r   r+   r3   r:   r>   r?   r   r   r   r   <module>   s   	
%>