U
    <ct                     @   s  d dl Z d dlZd dlmZ d dlm  m  mZ d dlm  m  m	Z
 d dlm  m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d	d
dddddddgZejejejdZedejjj dZ!G dd dejjj ej"Z#G dd de#ej$Z%G dd de%Z&G dd	 d	e
j$ej"Z'G dd
 d
e#ej(Z)G dd de)Z*G dd de
j(ej"Z+G dd de#ej,Z-G dd de-Z.G dd de
j,ej"Z/d d Z0d!d Z1dS )"    N)init)fuse_conv_bn_weights)_single_pair_triple)	Parameter)TypeVarConvBn1dConvBnReLU1d
ConvReLU1dConvBn2dConvBnReLU2d
ConvReLU2dConvBn3dConvBnReLU3d
ConvReLU3dupdate_bn_statsfreeze_bn_stats)         MOD)boundc                       s   e Zd ZdZeZd%ddZdd	 Zd
d Z fddZ	dd Z
dd Zdd Zdd Zdd Z fddZdd Zd&ddZ fdd Zed!d" Zd#d$ Z  ZS )'	_ConvBnNdr   h㈵>皙?FNc                 C   s   t jjj| |||||||||	d| |s2td|| _| jrB|nd| _t	| |||dd| _
| j | _|
rtt|| _n| dd  |   | jr|r|   q|   n|   d| _d S )NF'qconfig must be provided for QAT moduleTbias)nnmodulesconv_ConvNd__init__AssertionErrorqconfigtraining	freeze_bn_BN_CLASS_MAPbnweightweight_fake_quantr   torchemptyr   Zregister_parameterreset_bn_parametersr   r   0_enable_slow_path_for_better_numerical_stability)selfin_channelsout_channelskernel_sizestridepaddingdilationZ
transposedZoutput_paddinggroupsr   padding_modeepsmomentumr&   r$   dim r;   M/tmp/pip-unpacked-wheel-gikjz4vx/torch/nn/intrinsic/qat/modules/conv_fused.pyr"      s2          

z_ConvBnNd.__init__c                 C   s   | j   d S N)r(   reset_running_statsr/   r;   r;   r<   r>   H   s    z_ConvBnNd.reset_running_statsc                 C   sd   | j   t| j j t| j j | jd k	r`t| j\}}dt	| }t| j| | d S )Nr   )
r(   r>   r   Zuniform_r)   Zzeros_r   Z_calculate_fan_in_and_fan_outmathsqrt)r/   Zfan_in_r   r;   r;   r<   r-   K   s    

z_ConvBnNd.reset_bn_parametersc                    s   t t|   d S r=   )superr   reset_parametersr?   	__class__r;   r<   rD   U   s    z_ConvBnNd.reset_parametersc                 C   s   d| _ d| j_| S )NFTr&   r(   r%   r?   r;   r;   r<   r   X   s    z_ConvBnNd.update_bn_statsc                 C   s   d| _ d| j_| S )NTFrG   r?   r;   r;   r<   r   ]   s    z_ConvBnNd.freeze_bn_statsc                 C   s   | j r| |S | |S r=   )r.   _forward_slow_forward_approximater/   inputr;   r;   r<   _forwardb   s    
z_ConvBnNd._forwardc           
      C   s   | j jdk	stt| j j| j j }| j j| }dgt| jj }d|d< dgt| jj }d|d< | 	| j|
| }| jdk	rtj| j|jd}ntj| j|j|jd}| |||}||
| }	| jdk	r|	| j
| }	|  |	}|S )zApproximated method to fuse conv and bn. It requires only one forward pass.
        conv_orig = conv / scale_factor where scale_factor = bn.weight / running_std
        Nr   r   )dtypedevicerN   )r(   running_varr#   r+   rA   r8   r)   lenshaper*   reshaper   Z
zeros_likerN   zerosr1   rP   _conv_forward)
r/   rK   running_stdscale_factorweight_shape
bias_shapescaled_weight	zero_biasr    Z	conv_origr;   r;   r<   rI   g   s"    


z_ConvBnNd._forward_approximatec              	   C   s  | j jdk	st| j jdk	s ttj| j| jj|j	d}dgt
| jj }d|d< dgt
| jj }d|d< | j jr| || j|}t . | jdkr|n|| j| }|  | W 5 Q R X t| j j| j j }| j j| }| | j|| }	| ||	|}
| j jrdgttdt
| jj }||}t||| |}t|| j j }|| }|
||9 }
|}|}n"| j j| jdk	r| jnd }|}| j j| j j| |  }|
||7 }
| jdk	r|
| j| j |7 }
|
S )aH  
        A more accurate but slow method to compute conv bn fusion, following https://arxiv.org/pdf/1806.08342.pdf
        It requires two forward passes but handles the case bn.weight == 0

        Conv: Y = WX + B_c
        Conv without bias: Y0 = WX = Y - B_c, Y = Y0 + B_c

        Batch statistics:
          mean_Y = Y.mean()
                 = Y0.mean() + B_c
          var_Y = (Y - mean_Y)^2.mean()
                = (Y0 - Y0.mean())^2.mean()
        BN (r: bn.weight, beta: bn.bias):
          Z = r * (Y - mean_Y) / sqrt(var_Y + eps) + beta
            = r * (Y0 - Y0.mean()) / sqrt(var_Y + eps) + beta

        Fused Conv BN training (std_Y = sqrt(var_Y + eps)):
          Z = (r * W / std_Y) * X + r * (B_c - mean_Y) / std_Y + beta
            = (r * W / std_Y) * X - r * Y0.mean() / std_Y + beta

        Fused Conv BN inference (running_std = sqrt(running_var + eps)):
          Z = (r * W / running_std) * X - r * (running_mean - B_c) / running_std + beta

        QAT with fused conv bn:
          Z_train = fake_quant(r * W / running_std) * X * (running_std / std_Y) - r * Y0.mean() / std_Y + beta
                  = conv(X, fake_quant(r * W / running_std)) * (running_std / std_Y) - r * Y0.mean() / std_Y + beta
          Z_inference = conv(X, fake_quant(r * W / running_std)) - r * (running_mean - B_c) / running_std + beta
        NrO   r   rM   r   r   )r(   rQ   r#   running_meanr+   rU   r1   r)   rP   rN   rR   rS   r%   rV   Zno_gradr   rT   rA   r8   r*   listrangeZmeanZsquare)r/   rK   r\   rY   rZ   Zconv_outZconv_out_biasrW   rX   r[   Zconv_bnZavg_dimsZ
batch_meanZ	batch_varZ	batch_stdZunscale_factorZ
fused_meanZ	fused_stdZ
fused_biasr;   r;   r<   rH      sJ    


z_ConvBnNd._forward_slowc                    s   t t|  S r=   )rC   r   
extra_reprr?   rE   r;   r<   r`      s    z_ConvBnNd.extra_reprc                 C   s
   |  |S r=   )rL   rJ   r;   r;   r<   forward   s    z_ConvBnNd.forwardTc                 C   s(   || _ | js$|  D ]}|| q| S )z
        Batchnorm's training behavior is using the self.training flag. Prevent
        changing it if BN is frozen. This makes sure that calling `model.train()`
        on a model with a frozen BN will behave properly.
        )r%   r&   childrentrain)r/   modemoduler;   r;   r<   rc      s
    z_ConvBnNd.trainc              	      s   | dd }|d ks|dkrdddddd}	|	 D ]X\}
}|| |krl|||  |||
 < |||  q4||
 |krzq4|r4|||
  q4tt| ||||||| d S )	Nversionr   gammabetar]   rQ   num_batches_tracked)z	bn.weightzbn.biaszbn.running_meanzbn.running_varzbn.num_batches_tracked)getitemspopappendrC   r   _load_from_state_dict)r/   Z
state_dictprefixZlocal_metadatastrictZmissing_keysZunexpected_keysZ
error_msgsrf   Zv2_to_v1_namesZv2_nameZv1_namerE   r;   r<   rn     s2    	
      z_ConvBnNd._load_from_state_dictc                 C   s   t || jks(td| j d | jj t|ds:td|jsHtd|j}|d |d  }}| |j|j|j|j	|j
|j|j|jdk	|j|j|jd	|}|j|_|j|_|j|j_|j|j_|j|j_|j|j_|j|j_|S )
zCreate a qat module from a float module or qparams_dict

            Args: `mod` a float module, either produced by torch.ao.quantization utilities
            or directly from user
        zqat.z.from_float only works for r$   z,Input float module must have qconfig definedz,Input float module must have a valid qconfigr   r   NF)type_FLOAT_MODULEr#   __name__hasattrr$   r0   r1   r2   r3   r4   r5   r6   r   r7   r8   r9   r)   r(   r]   rQ   ri   )clsmodr$   r    r(   Z
qat_convbnr;   r;   r<   
from_float#  s6    	    




z_ConvBnNd.from_floatc                 C   s   t | }|| j| j| j| j| j| j| j| j	d k	| j
	}tj| j |_| j	d k	rjtj| j	 |_	|jrt|j|j	| jj| jj| jj| jj| jj	\|_|_	|jrg }|| | }|| |j| }|| j |S || j |S d S r=   )rq   _FLOAT_CONV_MODULEr0   r1   r2   r3   r4   r5   r6   r   r7   r+   r   r   r)   detach_FLOAT_BN_MODULEr   r(   r]   rQ   r8   _FLOAT_RELU_MODULErm   _FUSED_FLOAT_MODULErc   r%   )r/   ru   r    r   reluZ	conv_relur;   r;   r<   to_floatC  sF    





z_ConvBnNd.to_float)r   r   FNr   )T)rs   
__module____qualname___versionr   rr   r"   r>   r-   rD   r   r   rL   rI   rH   r`   ra   rc   rn   classmethodrw   r~   __classcell__r;   r;   rE   r<   r      s.         
*
[
"
r   c                
   @   s0   e Zd ZdZejZdZej	Z
ejZdd	d
ZdS )r	   a  
    A ConvBn1d module is a module fused from Conv1d and BatchNorm1d,
    attached with FakeQuantize modules for weight,
    used in quantization aware training.

    We combined the interface of :class:`torch.nn.Conv1d` and
    :class:`torch.nn.BatchNorm1d`.

    Similar to :class:`torch.nn.Conv1d`, with FakeQuantize modules initialized
    to default.

    Attributes:
        freeze_bn:
        weight_fake_quant: fake quant module for weight

    Nr   r   rU   r   r   Fc                 C   sT   t |}t |}t |}t |}tj| ||||||dt d|||	|
|||dd d S )NFr   r   r:   )r   r   r"   r/   r0   r1   r2   r3   r4   r5   r6   r   r7   r8   r9   r&   r$   r;   r;   r<   r"     s$              zConvBn1d.__init__)
r   r   r   r   NrU   r   r   FN)rs   r   r   __doc__r   BatchNorm1drz   r{   nnir	   rr   Conv1drx   r"   r;   r;   r;   r<   r	   k  s                 c                
       sX   e Zd ZdZejZejZ	ej
ZejZejZd fd	d
	Zdd Ze fddZ  ZS )r
   a  
    A ConvBnReLU1d module is a module fused from Conv1d, BatchNorm1d and ReLU,
    attached with FakeQuantize modules for weight,
    used in quantization aware training.

    We combined the interface of :class:`torch.nn.Conv1d` and
    :class:`torch.nn.BatchNorm1d` and :class:`torch.nn.ReLU`.

    Similar to `torch.nn.Conv1d`, with FakeQuantize modules initialized to
    default.

    Attributes:
        weight_fake_quant: fake quant module for weight

    r   r   NrU   r   r   Fc                    s(   t  |||||||||	|
||| d S r=   )rC   r"   r   rE   r;   r<   r"     s         zConvBnReLU1d.__init__c                 C   s   t t| |S r=   )Fr}   r	   rL   rJ   r;   r;   r<   ra     s    zConvBnReLU1d.forwardc                    s   t t| |S r=   )rC   r
   rw   ru   rv   rE   r;   r<   rw     s    zConvBnReLU1d.from_float)
r   r   r   r   NrU   r   r   FN)rs   r   r   r   r   r
   rr   r   r   rx   r   rz   ReLUr{   r   r|   r"   ra   r   rw   r   r;   r;   rE   r<   r
     s(                c                       sP   e Zd ZdZejZejZ	dZ
ejZd fdd	Zd	d
 Ze fddZ  ZS )r   aC  A ConvReLU1d module is a fused module of Conv1d and ReLU, attached with
    FakeQuantize modules for weight for
    quantization aware training.

    We combined the interface of :class:`~torch.nn.Conv1d` and
    :class:`~torch.nn.BatchNorm1d`.

    Attributes:
        weight_fake_quant: fake quant module for weight

    Nr   r   TrU   c                    sF   t t| j|||||||||	|
d
 |
s0td|
| _| j | _d S N)r3   r4   r5   r6   r   r7   r$   r   )rC   r   r"   r#   r$   r)   r*   r/   r0   r1   r2   r3   r4   r5   r6   r   r7   r$   rE   r;   r<   r"     s        zConvReLU1d.__init__c                 C   s   t | || | j| jS r=   r   r}   rV   r*   r)   r   rJ   r;   r;   r<   ra     s    zConvReLU1d.forwardc                    s   t t| |S r=   )rC   r   rw   r   rE   r;   r<   rw     s    zConvReLU1d.from_float)r   r   r   r   TrU   N)rs   r   r   r   r   r   rr   r   r   rx   rz   r   r{   r"   ra   r   rw   r   r;   r;   rE   r<   r     s              c                
   @   s0   e Zd ZdZejZejZ	ej
ZdZdd	d
ZdS )r   a  
    A ConvBn2d module is a module fused from Conv2d and BatchNorm2d,
    attached with FakeQuantize modules for weight,
    used in quantization aware training.

    We combined the interface of :class:`torch.nn.Conv2d` and
    :class:`torch.nn.BatchNorm2d`.

    Similar to :class:`torch.nn.Conv2d`, with FakeQuantize modules initialized
    to default.

    Attributes:
        freeze_bn:
        weight_fake_quant: fake quant module for weight

    Nr   r   rU   r   r   Fc                 C   sT   t |}t |}t |}t |}tj| ||||||dt d|||	|
|||dd d S )NFr   r   r   )r   r   r"   r   r;   r;   r<   r"     s$              zConvBn2d.__init__)
r   r   r   r   NrU   r   r   FN)rs   r   r   r   r   r   rr   r   Conv2drx   BatchNorm2drz   r{   r"   r;   r;   r;   r<   r     s                 c                
       sX   e Zd ZdZejZejZ	ej
ZejZejZd fd	d
	Zdd Ze fddZ  ZS )r   a  
    A ConvBnReLU2d module is a module fused from Conv2d, BatchNorm2d and ReLU,
    attached with FakeQuantize modules for weight,
    used in quantization aware training.

    We combined the interface of :class:`torch.nn.Conv2d` and
    :class:`torch.nn.BatchNorm2d` and :class:`torch.nn.ReLU`.

    Similar to `torch.nn.Conv2d`, with FakeQuantize modules initialized to
    default.

    Attributes:
        weight_fake_quant: fake quant module for weight

    r   r   NrU   r   r   Fc                    s,   t t| |||||||||	|
||| d S r=   )rC   r   r"   r   rE   r;   r<   r"   3  s         zConvBnReLU2d.__init__c                 C   s   t t| |S r=   )r   r}   r   rL   rJ   r;   r;   r<   ra   G  s    zConvBnReLU2d.forwardc                    s   t t| |S r=   )rC   r   rw   r   rE   r;   r<   rw   J  s    zConvBnReLU2d.from_float)
r   r   r   r   NrU   r   r   FN)rs   r   r   r   r   r   rr   r   r   rx   r   rz   r   r{   r   r|   r"   ra   r   rw   r   r;   r;   rE   r<   r     s(                c                       sP   e Zd ZdZejZejZ	dZ
ejZd fdd	Zd	d
 Ze fddZ  ZS )r   aC  A ConvReLU2d module is a fused module of Conv2d and ReLU, attached with
    FakeQuantize modules for weight for
    quantization aware training.

    We combined the interface of :class:`~torch.nn.Conv2d` and
    :class:`~torch.nn.BatchNorm2d`.

    Attributes:
        weight_fake_quant: fake quant module for weight

    Nr   r   TrU   c                    sF   t t| j|||||||||	|
d
 |
s0td|
| _| j | _d S r   )rC   r   r"   r#   r$   r)   r*   r   rE   r;   r<   r"   _  s        zConvReLU2d.__init__c                 C   s   t | || | j| jS r=   r   rJ   r;   r;   r<   ra   k  s    zConvReLU2d.forwardc                    s   t t| |S r=   )rC   r   rw   r   rE   r;   r<   rw   o  s    zConvReLU2d.from_float)r   r   r   r   TrU   N)rs   r   r   r   r   r   rr   r   r   rx   rz   r   r{   r"   ra   r   rw   r   r;   r;   rE   r<   r   N  s              c                
   @   s0   e Zd ZdZejZejZ	ej
ZdZdd	d
ZdS )r   a  
    A ConvBn3d module is a module fused from Conv3d and BatchNorm3d,
    attached with FakeQuantize modules for weight,
    used in quantization aware training.

    We combined the interface of :class:`torch.nn.Conv3d` and
    :class:`torch.nn.BatchNorm3d`.

    Similar to :class:`torch.nn.Conv3d`, with FakeQuantize modules initialized
    to default.

    Attributes:
        freeze_bn:
        weight_fake_quant: fake quant module for weight

    Nr   r   rU   r   r   Fc                 C   sT   t |}t |}t |}t |}tj| ||||||dt d|||	|
|||dd d S )NFr   r   r   )r   r   r"   r   r;   r;   r<   r"     s.    zConvBn3d.__init__)
r   r   r   r   NrU   r   r   FN)rs   r   r   r   r   r   rr   r   Conv3drx   BatchNorm3drz   r{   r"   r;   r;   r;   r<   r   s  s              c                
       sX   e Zd ZdZejZejZ	ej
ZejZejZd fd	d
	Zdd Ze fddZ  ZS )r   a  
    A ConvBnReLU3d module is a module fused from Conv3d, BatchNorm3d and ReLU,
    attached with FakeQuantize modules for weight,
    used in quantization aware training.

    We combined the interface of :class:`torch.nn.Conv3d` and
    :class:`torch.nn.BatchNorm3d` and :class:`torch.nn.ReLU`.

    Similar to `torch.nn.Conv3d`, with FakeQuantize modules initialized to
    default.

    Attributes:
        weight_fake_quant: fake quant module for weight

    r   r   NrU   r   r   Fc                    s,   t t| |||||||||	|
||| d S r=   )rC   r   r"   r   rE   r;   r<   r"     s    
zConvBnReLU3d.__init__c                 C   s   t t| |S r=   )r   r}   r   rL   rJ   r;   r;   r<   ra     s    zConvBnReLU3d.forwardc                    s   t t| |S r=   )rC   r   rw   r   rE   r;   r<   rw     s    zConvBnReLU3d.from_float)
r   r   r   r   NrU   r   r   FN)rs   r   r   r   r   r   rr   r   r   rx   r   rz   r   r{   r   r|   r"   ra   r   rw   r   r;   r;   rE   r<   r     s(             &c                       sP   e Zd ZdZejZejZ	dZ
ejZd fdd	Zd	d
 Ze fddZ  ZS )r   aC  A ConvReLU3d module is a fused module of Conv3d and ReLU, attached with
    FakeQuantize modules for weight for
    quantization aware training.

    We combined the interface of :class:`~torch.nn.Conv3d` and
    :class:`~torch.nn.BatchNorm3d`.

    Attributes:
        weight_fake_quant: fake quant module for weight

    Nr   r   TrU   c                    sF   t t| j|||||||||	|
d
 |
s0td|
| _| j | _d S r   )rC   r   r"   r#   r$   r)   r*   r   rE   r;   r<   r"     s    
zConvReLU3d.__init__c                 C   s   t | || | j| jS r=   r   rJ   r;   r;   r<   ra   )  s    zConvReLU3d.forwardc                    s   t t| |S r=   )rC   r   rw   r   rE   r;   r<   rw   .  s    zConvReLU3d.from_float)r   r   r   r   TrU   N)rs   r   r   r   r   r   rr   r   r   rx   rz   r   r{   r"   ra   r   rw   r   r;   r;   rE   r<   r     s           c                 C   s(   t | tttttttgkr$|   d S r=   )	rq   setr
   r   r   r	   r   r   r   rv   r;   r;   r<   r   2  s    c                 C   s(   t | tttttttgkr$|   d S r=   )	rq   r   r
   r   r   r	   r   r   r   r   r;   r;   r<   r   8  s    )2r@   r+   Ztorch.nnr   Ztorch.ao.nn.intrinsicZaoZ	intrinsicr   Ztorch.ao.nn.qatZqatZnnqatZtorch.nn.functionalZ
functionalr   r   Ztorch.nn.utilsr   Ztorch.nn.modules.utilsr   r   r   Ztorch.nn.parameterr   typingr   __all__r   r   r   r'   r   r    r!   r   Z_FusedModuler   r   r	   r
   r   r   r   r   r   r   r   r   r   r   r   r;   r;   r;   r<   <module>   sF        T,3%,3%DD7