U
    Kc,                  !   @   s4  U d dl mZ d dlm  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Zdd Zdd	 Zd
d Zdd Zdd Zejejfeejejejfeejejfeejejejfeejejfeejejejfeejejfeejejejfeejejejfeejej ejfeej ejfeej!ejejfeej"ejejfeej#ej$ejfeej%ejfeej&ejfeiZ'e	eeej(ef f e)d< dddZ*dd Z+dd Z,dd Z-ejejfe,eejejejffe-eejejfe,eejejejffe-eejejfe,eejejejffe-eejejfe+ejejejfe+ejejejfe+ejejej fe,eejej fe+ej!ejejfe+ej"ejejfe+ej#ejej$fe,eejej%fe,eejej&fe,eiZ.e	eeej(ef f e)d< dd Z/d ee
e	eeej(ef f  dddZ0dS )!    N)UnionCallableTupleDictOptionalType)Pattern)get_combined_dict)MatchAllNodec                 C   s   |j |j kstdtjtjtjtjtjtj	i}| r|j
|jksHtd|jsVtd|jsdtd|t|d}|dk	r|||S td||fntj||S dS )a  Given the conv and bn modules, fuses them and returns the fused module

    Args:
        is_qat: a flag for whether we are using quantization aware training fusion
        or post training quantization fusion
        conv: Module instance of type conv2d/conv3d
        bn: Spatial BN instance that needs to be fused with the conv

    Examples::

        >>> m1 = nn.Conv2d(10, 20, 3)
        >>> b1 = nn.BatchNorm2d(20)
        >>> # xdoctest: +SKIP
        >>> m2 = fuse_conv_bn(m1, b1)
    :Conv and BN both must be in the same mode (train or eval).z?Output channel of Conv2d must match num_features of BatchNorm2dz7Only support fusing BatchNorm2d with affine set to TruezGOnly support fusing BatchNorm2d with tracking_running_stats set to TrueNCannot fuse train modules: {})trainingAssertionErrornnConv1dnniZConvBn1dConv2dZConvBn2dConv3dZConvBn3dnum_featuresout_channelsaffinetrack_running_statsgettypeNotImplementedErrorformatutilsfuse_conv_bn_eval)is_qatconvbnZfused_module_class_mapZfused_module_class r!   O/tmp/pip-unpacked-wheel-gikjz4vx/torch/ao/quantization/fuser_method_mappings.pyfuse_conv_bn   s&       
r#   c                 C   s  |j |j   kr|j ks$n tdd}| rtjtjtjtjtjtj	i}|j
|jks\td|jsjtd|jsxtd|t|d}|dk	r||||S td|||fndtjtjtjtjtjtji}|t|d}|dk	rtjj||}|||S td|||fdS )a  Given the conv and bn modules, fuses them and returns the fused module

    Args:
        is_qat: a flag for whether we are using quantization aware training fusion
        or post training quantization fusion
        conv: Module instance of type conv2d/conv3d
        bn: Spatial BN instance that needs to be fused with the conv

    Examples::

        >>> m1 = nn.Conv2d(10, 20, 3)
        >>> b1 = nn.BatchNorm2d(20)
        >>> r1 = nn.ReLU(inplace=False)
        >>> # xdoctest: +SKIP
        >>> m2 = fuse_conv_bn_relu(m1, b1, r1)
    r   Nz;Output channel of Conv must match num_features of BatchNormz5Only support fusing BatchNorm with affine set to TruezEOnly support fusing BatchNorm with tracking_running_stats set to Truer   zCannot fuse eval modules: {})r   r   r   r   r   ZConvBnReLU1dr   ZConvBnReLU2dr   ZConvBnReLU3dr   r   r   r   r   r   r   r   
ConvReLU1d
ConvReLU2d
ConvReLU3dr   fusionr   )r   r   r    ZreluZfused_moduleZmap_to_fused_module_trainZmap_to_fused_module_evalZ
fused_convr!   r!   r"   fuse_conv_bn_relu0   s>          

r(   c                 C   sh   |j |j kstd| rT|j|jks,td|js:td|jsHtdt||S tj	j
||S dS )a  Given the linear and bn modules, fuses them and returns the fused module

    Args:
        is_qat: a flag for whether we are using quantization aware training fusion
        or post training quantization fusion
        linear: Module instance of type Linear
        bn: BatchNorm1d instance that needs to be fused with the linear layer

    Examples::

        >>> m1 = nn.Linear(20, 10)
        >>> b1 = nn.BatchNorm1d(10)
        >>> # xdoctest: +SKIP
        >>> m2 = fuse_linear_bn(m1, b1)
    z<Linear and BN both must be in the same mode (train or eval).z@Output features of Linear must match num_features of BatchNorm1dz7Only support fusing BatchNorm1d with affine set to TruezGOnly support fusing BatchNorm1d with tracking_running_stats set to TrueN)r   r   r   Zout_featuresr   r   r   Z
LinearBn1dr   r   r'   Zfuse_linear_bn_eval)r   Zlinearr    r!   r!   r"   fuse_linear_bn_   s    r)   c                 C   s:   |j |j kstd| r"tdntjjj||ddS dS )a  Given ConvTranspose and bn modules, fuses them and returns the fused module

    Args:
        convt: Module instance of type ConvTransposeNd
        bn: BatchNormNd instance that needs to be fused with the linear layer.
            batch norm N should match the ConvTranspose N

    Examples::

        >>> m1 = nn.ConvTranspose2d(10, 20, 3)
        >>> b1 = nn.BatchNorm2d(20)
        >>> # xdoctest: +SKIP
        >>> m2 = fuse_convtranspose_bn(m1, b1)
    zCConvTranspose and BN both must be in the same mode (train or eval).z8Fusing ConvTranspose+BatchNorm not yet supported in QAT.T)Z	transposeN)r   r   	Exceptionr   r   r'   r   )r   Zconvtr    r!   r!   r"   fuse_convtranspose_bn|   s    
r+   c                    s    fdd}|S )z Given a sequential class for two modules, return a function that takes
    is_qat, and then two modules as argument, that ignores the is_qat flag
    and always returns the sequential that combines the two input modules
    c                    s
    ||S Nr!   r   m1m2
sequentialr!   r"   fuser_method   s    z)sequential_wrapper2.<locals>.fuser_methodr!   r1   r2   r!   r0   r"   sequential_wrapper2   s    r4   DEFAULT_OP_LIST_TO_FUSER_METHODc                 C   s<   |dkri }t t|}|| d}|dk	s8td| |S )zi Get fuser method for the given list of module types,
    return None if fuser method does not exist
    N"did not find fuser method for: {} )r	   r5   r   r   r   )Zop_listZadditional_fuser_method_mappingZall_mappingsr2   r!   r!   r"   get_fuser_method   s    r7   c                    s    fdd}|S )a   Given a sequential class for two modules, return a function that takes
    is_qat, and then two modules as argument, that ignores the is_qat flag
    and always returns the sequential that combines the two input modules, with
    the order of two inputs reversed
    c                    s
    ||S r,   r!   r-   r0   r!   r"   r2      s    z1reverse_sequential_wrapper2.<locals>.fuser_methodr!   r3   r!   r0   r"   reverse_sequential_wrapper2   s    r8   c                    s    fdd}|S )Nc                    s    | ||S r,   r!   )r   xyfr!   r"   reversed   s    zreverse2.<locals>.reversedr!   r<   r=   r!   r;   r"   reverse2   s    r?   c                    s    fdd}|S )Nc                    s   |\}} | |||S r,   r!   )r   r9   wr:   zr;   r!   r"   r=      s    zreverse3.<locals>.reversedr!   r>   r!   r;   r"   reverse3   s    rB   DEFAULT_PATTERN_TO_FUSER_METHODc                 C   sJ   g }t | ttfr>g }| D ]}|t| qttj| }n| tg}|S )a  
    Returns a list of valid patterns generated from the op_pattern,
    since MatchAllNode can match all types of nodes,
    e.g. pattern (torch.nn.Conv2d, torch.add) should also be able to match keys like
    (MatchAllNode, torch.add) and (torch.nn.Conv2d, MatchAllNode)

    Example Input:
    (torch.add, (torch.nn.ReLU, torch.nn.Conv2d))

    Example Output:
    [(torch.add, (torch.nn.ReLU, torch.nn.Conv2d)),
     (torch.add, (torch.nn.ReLU, MatchAllNode)),
     (torch.add, (MatchAllNode, torch.nn.Conv2d)),
     (torch.add, (MatchAllNode, MatchAllNode)),
     (MatchAllNode, (torch.nn.ReLU, torch.nn.Conv2d)),
     (MatchAllNode, (torch.nn.ReLU, MatchAllNode)),
     (MatchAllNode, (MatchAllNode, torch.nn.Conv2d)),
     (MatchAllNode, (MatchAllNode, MatchAllNode)),
    ]
    )
isinstancetuplelistappendget_valid_patterns	itertoolsproductr
   )
op_patternresultZ	sub_combsZsub_patternr!   r!   r"   rH      s    rH   )rK   fuser_method_mappingc                 C   sT   |dkrt }t| }d}|D ]} || d}|dk	r q:q|dk	sPtd| |S )z This will be made defult after we deparate the get_fuser_method
    Would like to implement this first and have a separate PR for deprecation
    Nr6   )rC   rH   r   r   r   )rK   rM   Zop_patternsr2   r!   r!   r"   get_fuser_method_new  s    rN   )N)N)1Ztorch.nnr   Ztorch.nn.intrinsicZ	intrinsicr   typingr   r   r   r   r   r   Ztorch.ao.quantization.utilsr   r	   r
   rI   r#   r(   r)   r+   r4   r   ZBatchNorm1dZReLUr   ZBatchNorm2dr   ZBatchNorm3dr$   r%   r&   ZLinearZ
LinearReLUZBNReLU2dZBNReLU3dZConvTranspose1dZConvTranspose2dZConvTranspose3dr5   Z
Sequential__annotations__r7   r8   r?   rB   rC   rH   rN   r!   r!   r!   r"   <module>   s     %/

  
  
  
 
 
 
 
 
 
 
 
 
 


  
  
  
 
 
 
 
 
 
 
 
 
 ! 