U
    Kc"                     @   sl   d dl mZ d dlmZmZmZmZ d dlZd dlm	  m
Z ddlmZ dgZdd ZG d	d deZdS )
    )reduce)CallableOptionalTupleUnionN   )BaseSparsifierWeightNormSparsifierc                 C   s    | |d  }| |d  }||fS )Nr    )idxshapeZrowscolsr
   r
   W/tmp/pip-unpacked-wheel-gikjz4vx/torch/ao/sparsity/sparsifier/weight_norm_sparsifier.py_flat_idx_to_2d   s    r   c                       sl   e Zd ZdZdeeeef ee eee	ef  d fddZ
ddd	Zdd
dZdddZdd Z  ZS )r	   a  Weight-Norm Sparsifier

    This sparsifier computes the norm of every sparse block and "zeroes-out" the
    ones with the lowest norm. The level of sparsity defines how many of the
    blocks is removed.

    This sparsifier is controlled by three variables:
    1. `sparsity_level` defines the number of *sparse blocks* that are zeroed-out
    2. `sparse_block_shape` defines the shape of the sparse blocks. Note that
        the sparse blocks originate at the zero-index of the tensor.
    3. `zeros_per_block` is the number of zeros that we are expecting in each
        sparse block. By default we assume that all elements within a block are
        zeroed-out. However, setting this variable sets the target number of
        zeros per block. The zeros within each block are chosen as the *smallest
        absolute values*.

    Args:

        sparsity_level: The target level of sparsity
        sparse_block_shape: The shape of a sparse block (see note below)
        zeros_per_block: Number of zeros in a sparse block
        norm: Norm to use. Could be either `int` or a callable.
            If `int`, only L1 and L2 are implemented.

    Note::
        The `sparse_block_shape` is tuple representing (block_ROWS, block_COLS),
        irrespective of what the rows / cols mean in the data tensor. That means,
        if you were to sparsify a weight tensor in the nn.Linear, which has a
        weight shape `(Cout, Cin)`, the `block_ROWS` would refer to the output
        channels, while the `block_COLS` would refer to the input channels.

    Note::
        All arguments to the WeightNormSparsifier constructor are "default"
        arguments and could be overriden by the configuration provided in the
        `prepare` step.
          ?r      N)sparsity_levelsparse_block_shapezeros_per_blocknormc                    s   |d krt dd |}|||d}|d kr.d}t|r>|| _n8|dkrRdd | _n$|dkrfdd | _ntd| d	t j|d
 d S )Nc                 S   s   | | S Nr
   xyr
   r
   r   <lambda>;       z/WeightNormSparsifier.__init__.<locals>.<lambda>)r   r   r      r   c                 S   s   |   S r   )absTr
   r
   r   r   F   r   c                 S   s   | |  S r   r
   r   r
   r
   r   r   H   r   zL-z is not yet implemented.)defaults)r   callablenorm_fnNotImplementedErrorsuper__init__)selfr   r   r   r   r!   	__class__r
   r   r&   5   s     zWeightNormSparsifier.__init__c                 C   sJ   |dkr"|dk	st tj||d}|j||dd tj||||d|_|S )zCCreates patches of size `block_shape` after scattering the indices.Ndevicer   )dimindexvalue)Zoutput_sizekernel_sizestride)AssertionErrortorchonesZscatter_Ffolddata)r'   output_shaper,   indicesblock_shapemaskinput_shaper+   r
   r
   r   _scatter_fold_block_maskM   s    z-WeightNormSparsifier._scatter_fold_block_maskc                 C   sh  |j dd \}}|\}}	|||  | }
|	||	  |	 }|dkrTtj|||jd}|dkrlt||_|S |dkrt||_|S tdd |}|dkrtj	|ddddf ||d	d
}|
 }t|}|d|d}tt|| }tdt|d |}tj||ddd\}}||j }| jd||
 || f|||d | |j d|d|f  |_|S )a  Creates a tensor-level mask.

        Tensor-level mask is described as a mask, where the granularity of sparsification of the
        smallest patch is the sparse_block_shape. That means, that for a given mask and a
        sparse_block_shape, the smallest "patch" of zeros/ones could be the sparse_block_shape.

        In this context, `sparsity_level` describes the fraction of sparse patches.
        Nr*         ?g        c                 S   s   | | S r   r
   r   r
   r
   r   r   o   r   z8WeightNormSparsifier._make_tensor_mask.<locals>.<lambda>r   T)r/   r0   Z	ceil_moder   r   Fkr,   largest)r,   r7   r8   r9   r:   )r   r2   r3   r+   
zeros_liker6   	ones_liker   r4   Z
avg_pool2dflattenlenrepeatintroundmaxmintopkreshaper<   squeeze
contiguous)r'   r6   r;   r   r   r:   hwblock_hblock_wdhdwvalues_per_blockZ
num_blocksZthreshold_idx_
sorted_idxmask_reshaper
   r
   r   _make_tensor_maskW   sH    	      &z&WeightNormSparsifier._make_tensor_maskc                 C   s8  |dkrt j|j|jd}|jdd \}}|\}}|||  | }	|||  | }
tdd |}||krzt ||_|S t j||	 ||
 |j|jd}|t j	 ||d|d|f< t
j|ddddf ||d}||j}t j||dd	d
\}}| jd||j||d | |jd|d|f  |_|S )a  Creates a block-level mask.

        Block-level mask is described as a mask, where the granularity of sparsification of the
        largest patch is the sparse_block_shape. That means that for a given mask and a
        sparse_block_shape, the sparsity is computed only within a patch of a size sparse_block_shape.

        In this context the `zeros_per_block` describes the number of zeroed-out elements within a patch.
        Nr*   r=   c                 S   s   | | S r   r
   r   r
   r
   r   r      r   z7WeightNormSparsifier._make_block_mask.<locals>.<lambda>)dtyper+   )r/   r0   r   Fr?   )r,   r8   r7   r9   r:   )r2   r3   r   r+   r   rB   r6   rZ   Zfill_nanr4   ZunfoldrL   rK   r<   rM   rN   )r'   r6   r   r   r:   rO   rP   rQ   rR   rS   rT   rU   Zpadded_dataZunfolded_datarX   rV   rW   r
   r
   r   _make_block_mask   s2    	    &z%WeightNormSparsifier._make_block_maskc                 K   s   t dd |}||krtd|dk r.tdt|j|d j}|dksP|dkr^t||_nl|dkr|||kr|t||_nN| 	t||}	| j
|	|	j||d}
||kr| j|	||d}t|
|}
|
|_d S )	Nc                 S   s   | | S r   r
   r   r
   r
   r   r      r   z2WeightNormSparsifier.update_mask.<locals>.<lambda>zYNumber of zeros per block cannot be more than the total number of elements in that block.r   z-Number of zeros per block should be positive.r>   )r6   r;   r   r   )r6   r   r   )r   
ValueErrorgetattrZparametrizationsr:   r2   rC   r6   rB   r#   rY   r   r\   
logical_or)r'   moduleZtensor_namer   r   r   kwargsrU   r:   ZwwZtensor_maskZ
block_maskr
   r
   r   update_mask   s2       z WeightNormSparsifier.update_mask)r   r   NN)NNN)N)N)__name__
__module____qualname____doc__floatr   rG   r   r   r   r&   r<   rY   r\   rb   __classcell__r
   r
   r(   r   r	      s$   %    
     


0
')	functoolsr   typingr   r   r   r   r2   Ztorch.nn.functionalnnZ
functionalr4   Zbase_sparsifierr   __all__r   r	   r
   r
   r
   r   <module>   s   