o
    NeAV                     @   s   d Z ddlmZ ddlZddlZddlZddlZddlm	Z	 ddl
mZ ddlZg dZG dd de	ZG d	d
 d
e	Zdd Zdd Zdd Zdd Zdd Zdd Zdd Ze ai add Zdd Zejddfdd Zejddfd!d"ZdS )#z#
Utilities of Auto SParsity (ASP).
    )print_functionN)Enum)permutations)
calculate_densitycheck_mask_1dget_mask_1dcheck_mask_2dget_mask_2d_greedyget_mask_2d_bestcreate_maskcheck_sparsityMaskAlgoCheckMethodc                   @   s   e Zd ZdZdZdZdZdS )r   z
    A collection of all mask generating algorithms.
    There currently are three algorithms, `MASK_1D`, `MASK_2D_GREEDY` and `MASK_2D_BEST`
    r   r	   r
   N)__name__
__module____qualname____doc__MASK_1DZMASK_2D_GREEDYZMASK_2D_BEST r   r   SD:\Projects\ConvertPro\env\Lib\site-packages\paddle/fluid/contrib/sparsity/utils.pyr   $   s
    r   c                   @   s$   e Zd ZdZdZdZedd ZdS )r   zz
    A collection of all sparsity checking approaches.
    There currently are two methods, `CHECK_1D` and `CHECK_2D`
    r   r   c                 C   s(   t | ts	J d| tjkrtjS tjS )a  
        Get sparsity checking method by mask generating algorithm.

        Args:
            mask_algo (MaskAlgo): The algorithm of mask generating.
        Returns:
            CheckMethod: The corresponded sparsity checking method.
        Examples:
            .. code-block:: python

            import numpy as np
            from paddle.static.sparsity import MaskAlgo
            from paddle.fluid.contrib.sparsity import CheckMethod

            CheckMethod.get_checking_method(MaskAlgo.MASK_1D)
            # CheckMethod.CHECK_1D

            CheckMethod.get_checking_method(MaskAlgo.MASK_2D_GREEDY)
            # CheckMethod.CHECK_2D

            CheckMethod.get_checking_method(MaskAlgo.MASK_2D_BEST)
            # CheckMethod.CHECK_2D
        z!mask_algo should be MaskAlgo type)
isinstancer   r   r   CHECK_1DCHECK_2D)Z	mask_algor   r   r   get_checking_method6   s   
zCheckMethod.get_checking_methodN)r   r   r   r   r   r   staticmethodr   r   r   r   r   r   .   s    r   c                 C   s"   |   }tt|d j|j S )a  
    Return the density of the input tensor.

    Args:
        x (nparray): The input tensor.
    Returns:
        float: The density of :attr:`x`.
    Examples:
        .. code-block:: python
          import paddle
          import numpy as np

          x = np.array([[0, 1, 3, 0],
                        [1, 1, 0, 1]])
          paddle.incubate.asp.calculate_density(x) # 0.625
    r   )flattenfloatnpnonzerosize)xZx_flattenedr   r   r   r   W   s   r   c                 C   s   t | jdksJ d| jd | }| jd | dkrDt| jd | jd ||  f}| |ddd| jd f< |j}|d||fS | d|| jfS )a  
    Reshape the input 2D matrix to shape (-1, m).
    If the second dimension of :attr:`mat` is not a multiples of :attr:`m`, 
    then this function would pad the remainder with 0 before reshaping.

    .. math::

        remainder = mat.shape[1] % m

    Args:
        mat (nparray): The input 2D matrix.
        m (int): The second dimension of reshaped matrix.
    Returns:
        tuple: A pair of the reshaped and padded matrix and the shape of padded matrix (non-reshaping).
       $The input mat should be a 2D matrix!   r   N)lenshaper   zerosreshape)matm	remainder
mat_paddedr&   r   r   r   _reshape_1dl   s   "r-   c                 C   sf   t | jdkrt| d| jd |\}}nt| |\}}|D ]}t|d j|| kr0 dS qdS )aX  
    Check if every row of the input matrix :attr:`mat` is in 1D `n:m` sparse pattern.
    This function would pad the second dimension of :attr:`mat` by zero 
    to be a multiples of :attr:`m` if necessary.

    1D `n:m` sparse pattern: At least :attr:`n` zeros in every :math:`1 \times m` block.

    Args:
        mat (nparray): The input matrix.
        n (int): n of `n:m` sparse pattern.
        m (int): m of `n:m` sparse pattern.
    Returns:
        bool: True if every row of :attr:`mat` is in 1D n:m sparse pattern, else False.
    Examples:
        .. code-block:: python

          import numpy as np
          import paddle.fluid.contrib.sparsity as sparsity

          x = np.array([[0, 1, 3, 0],
                        [1, 0, 0, 1]])
          sparsity.check_mask_1d(x, 2, 4) # True

          x = np.array([[0, 1, 5, 4],
                        [1, 0, 0, 1]])
          sparsity.check_mask_1d(x, 2, 4) # False

          # x would be padded to shape (2, 8)
          x = np.array([[0, 1, 0, 4, 6],
                        [1, 0, 0, 1, 7]])
          sparsity.check_mask_1d(x, 2, 4) # True
    r#   r   FT)r%   r&   r-   r(   r   r   r   )r)   nr*   mat_flatternr&   sub_matr   r   r   r      s   !r   c           
      C   s   t | |\}}t|}t| }t|jd D ]}|| }tt|}	d|||	d|  f< q||}|ddd| jd f |ddddf< |S )a  
    Generate 1D `n:m` sparse pattern mask of the input matrix :attr:`mat` 
    in row-directory. This function would pad the second dimension of :attr:`mat` 
    by zero to be a multiples of :attr:`m` before mask generation.

    1D `n:m` sparse pattern: At least :attr:`n` zeros in every :math:`1 \times m` block.

    Args:
        mat (nparray): The input matrix.
        n (int): n of `n:m` sparse pattern.
        m (int): m of `n:m` sparse pattern.
    Returns:
        nparray: The 1D `n:m` sparse mask of :attr:`mat`.
    Examples:
        .. code-block:: python

          import numpy as np
          import paddle.fluid.contrib.sparsity as sparsity

          mat = np.array([[0, 1, 5, 4],
                          [2, 7, 3, 6]])
          mask = sparsity.get_mask_1d(mat, 2, 4)
          # nparray([[0, 0, 1, 1],
          #          [0, 1, 0, 1]])
          sparsity.check_mask_1d(mask, 2, 4) # True
    r   Nr#   )	r-   r   	ones_likeranger&   argsortabsolutetolistr(   )
r)   r.   r*   r/   r&   mask_flatternmaskir0   Zmin_order_indicesr   r   r   r      s   


*r   c                 C   s8  t | jdksJ d| jd | }| jd | }|dkr"| jd n| jd ||  |dkr3| jd n| jd ||  f}t|}| |d| jd d| jd f< t|d|| }d}td|jd |D ].}|| }	td|jd |D ]}
|
| }t|||	|
|f d}|||< |d7 }qwqh||jfS )a4  
    Reshape the input 2D matrix to shape (-1, :math:`m \times m`).
    In each dimension of :attr:`mat`, if it is not a multiples of :attr:`m`, 
    then this function would pad the remainder with 0 before reshaping.

    .. math::

        remainder_0 = mat.shape[0] % m \\
        remainder_1 = mat.shape[1] % m

    Args:
        mat (nparray): The input 2D matrix.
        m (int): The square root of second dimension of reshaped matrix.
    Returns:
        tuple: A pair of the reshaped and padded matrix and the shape of padded matrix (non-reshaping).
    r!   r"   r   r#   Nr$   )r%   r&   r   r'   emptyr(   r2   squeeze)r)   r*   Zremainder_0Zremainder_1Z	new_shaper,   r/   curr_idx	row_startrow_end	col_startcol_endr0   r   r   r   _reshape_2d   s2   
 

r@   c              	   C   s~   t | |\}}|D ]3}tt|||dk}ttj|dd|| kdkr<ttj|dd|| kdkr< dS q	dS )a  
    Check if every :math:`m \times m` block of the input matrix :attr:`mat` is in 2D `n:m` sparse pattern.
    This function would pad each dimension of :attr:`mat` by zero to be a multiples of 
    :attr:`m` if necessary.

    2D `n:m` sparse pattern: At least :math:`n \times n` zeros in every :math:`m \times m` block 
    under the constraint of at least :attr:`n` zeros for each row and column.

    Args:
        mat (nparray): The input matrix.
        n (int): n of `n:m` sparse pattern.
        m (int): m of `n:m` sparse pattern.
    Returns:
        bool: True if  every :math:`m \times m` block of the input matrix :attr:`mat` is in 2D `n:m` sparse pattern, else False.
    Examples:
        .. code-block:: python

          import numpy as np
          import paddle.fluid.contrib.sparsity as sparsity

          x = np.array([[0, 8, 9, 0],
                        [9, 0, 0, 10],
                        [5, 0, 0, 6],
                        [0, 4, 6, 0]])
          sparsity.check_mask_2d(x, 2, 4) # True

          x = np.array([[0, 8, 0, 9],
                        [9, 0, 0, 10],
                        [0, 5, 0, 6],
                        [0, 4, 6, 0]])
          sparsity.check_mask_2d(x, 2, 4) # False

          # x would be padded to shape (8, 8)
          x = np.array([[0, 8, 0, 9],
                        [9, 0, 7, 0],
                        [0, 5, 0, 6],
                        [3, 0, 6, 0],
                        [1, 1, 0, 1]])
          sparsity.check_mask_2d(x, 2, 4) # True
    r   r#   ZaxisFT)r@   r   r4   r:   r(   sum)r)   r.   r*   r,   r&   r0   sub_maskr   r   r   r     s   )  r   c                    s  t |  \}}t|d  }tt|D ]i}tt|| }t|| }t|}	 fdd|	D }
t	
 }t	
 }tt|	d ddD ]5}|
| }||d  |ks`||d  |kraqJd||d |d f< ||d   d7  < ||d   d7  < qJqt|}d}td|d  D ]%}|  }td|d  D ]}|  }|| |||||f< |d7 }qq|d| jd d| jd f S )a  
    Greedily generate 2D `n:m` sparse pattern mask of the input matrix :attr:`mat`. 
    This function would pad each dimension of :attr:`mat` by zero to be a multiples of :attr:`m` before mask generation.

    2D `n:m` sparse pattern: At least :math:`n \times n` zeros in every :math:`m \times m` block 
    under the constraint of at least :attr:`n` zeros for each row and column.
    Greedily generating: For each :math:`m \times m` block, selecting values to keep in descent order.

    Args:
        mat (nparray): The input matrix.
        n (int): n of `n:m` sparse pattern.
        m (int): m of `n:m` sparse pattern.
    Returns:
        nparray: The 2D `n:m` sparse mask of :attr:`mat`.
    Examples:
        .. code-block:: python

          import numpy as np
          import paddle.fluid.contrib.sparsity as sparsity

          mat = np.array([[9, 8, 3, 7],
                          [9, 2, 1, 10],
                          [5, 1, 3, 6],
                          [2, 4, 6, 1]])
          mask = sparsity.get_mask_2d_greedy(mat, 2, 4)
          # nparray([[1. 1. 0. 0.]
          #          [1. 0. 0. 1.]
          #          [0. 0. 1. 1.]
          #          [0. 1. 1. 0.]])
          sparsity.check_mask_2d(mask, 2, 4) # True
    r$   c                    s    g | ]}t |  |  fqS r   )int).0r    r*   r   r   
<listcomp>a  s    z&get_mask_2d_greedy.<locals>.<listcomp>r#   r   g      ?N)r@   r   Z
zeros_liker(   r2   r%   r4   r:   r3   collectionsCounterr9   r&   )r)   r.   r*   r,   r&   Zmask_paddedidxr0   rC   Zmin_order_1d_indicesZmin_order_2d_indicesZrow_counterZcol_counterr8   Zmatrix_entryr7   r;   r<   r=   r>   r?   r   rF   r   r	   9  s<    




 r	   c                 C   s   d || }|tv rt| S t|}d|d| < ttt| }|| }tttt||}|j	dd| kj	dd|k
 d d}t|jd ||f}||dd  |dd< t  |t|< t  |S )a  
    Compute all vaild 2D `n:m` sparse patterns.

    2D `n:m` sparse pattern: At least :math:`n \times n` zeros in every :math:`m \times m` block 
    under the constraint of at least :attr:`n` zeros for each row and column.

    Args:
        n (int): n of `n:m` sparse pattern.
        m (int): m of `n:m` sparse pattern.
    Returns:
        dictionary: A dictionary with key: *m_n* (string) and value: all vaild 2D `n:m` sparse patterns.
    z{}_{}r#   NrA   r   r$   )format_valid_2d_patternsr   r'   listsetr   r5   ZasarrayrB   r   r(   r9   r&   _valid_2d_patterns_lockacquirerelease)r.   r*   Z	valid_keypatternsZvalidZvalid_patternsr   r   r   _compute_valid_2d_patterns  s.   
rS   c              	   C   s   t ||}t| |\}}t|d||}tjt|||jd || jdd}||dd  |dd< t	|}d}	t
d|d |D ]%}
|
| }t
d|d |D ]}|| }||	 ||
|||f< |	d7 }	qTqF|d| jd d| jd f S )aC  
    Generate 2D `n:m` sparse pattern mask of the input matrix :attr:`mat` 
    to form sparse matrix with maximun L1 norm .This function would pad each 
    dimension of :attr:`mat` by zero to be a multiples of :attr:`m` before mask generation.

    2D `n:m` sparse pattern: At least :math:`n \times n` zeros in every :math:`m \times m` block 
    under the constraint of at least :attr:`n` zeros for each row and column.

    *Note*: L1 norm of sparse matrix from `Best` API is greater than or equal to the one from `Greedy`.

    Args:
        mat (nparray): The input matrix.
        n (int): n of `n:m` sparse pattern.
        m (int): m of `n:m` sparse pattern.
    Returns:
        nparray: The 1D `n:m` sparse mask of :attr:`mat`.
    Examples:
        .. code-block:: python

          import numpy as np
          import paddle.fluid.contrib.sparsity as sparsity

          mat = np.array([[2, 8, 9, 9],
                          [9, 1, 3, 9],
                          [5, 6, 3, 9],
                          [2, 4, 6, 9]])
          mask_greedy = sparsity.get_mask_2d_greedy(mat, 2, 4)
          mask_best = sparsity.get_mask_2d_best(mat, 2, 4)
          print("L1 norm of `greedy` sparse matrix", np.multiply(mat, mask_greedy).sum()) # 56
          print("L1 norm of `best` sparse matrix", np.multiply(mat, mask_best).sum()) # 61
    r$   r   r#   rA   N)rS   r@   r   r1   r(   Zargmaxmatmulr&   Tr9   r2   )r)   r.   r*   rR   r/   r&   r6   Zpmaxr7   r;   r<   r=   r>   r?   r   r   r   r
     s&   
 


 r
   r!      c           	      C   sZ  | j }| j}| t}t|tsJ dt|tt	j
t |jd}t|dkr2|d|d }nlt|dkrC||d |d }n[t|dkrX||d |d  |d }nFt|dkr|g d|d |d  |d  |d }||||d	}||d |d |d |d gg d|S td
t|||||d	}|||S )aV  
    Create `n:m` sparse pattern mask of the input tensor via function given by :attr:`func_name`.
    Currently only support tensor with dimension less than or equal to 4.

    Args:
        tensor (nparray): The input tensor.
        func_name (MaskAlgo, optional): The function name to generate spase mask. Default is `MaskAlgo.MASK_1D`. All options please refer to `MaskAlgo`.
        n (int, optional): n of `n:m` sparse pattern. Default is 2.
        m (int, optional): m of `n:m` sparse pattern. Default is 4.
    Returns:
        nparray: The `n:m` sparse mask of :attr:`tensor` generated by :attr:`func_name`.
    Examples:
        .. code-block:: python

          import numpy as np
          import paddle.fluid.contrib.sparsity as sparsity

          tensor = np.array([[2, 8, 9, 9],
                             [9, 1, 3, 9],
                             [5, 6, 3, 9],
                             [2, 4, 6, 9]])
          mask_1d = sparsity.create_mask(tensor, func_name=sparsity.MaskAlgo.MASK_1D)
          # nparray([[0 0 1 1],
          #          [1 0 0 1],
          #          [0 1 0 1],
          #          [0 0 1 1]])
          mask_2d = sparsity.create_mask(tensor, func_name=sparsity.MaskAlgo.MASK_2D_BEST)
          # nparray([[0 1 1 0],
          #          [1 0 0 1],
          #          [1 1 0 0],
          #          [0 0 1 1]])
    zNfunc_name argumet of create_mask is only accepted as type MaskAlgo. But got {}Nr#   r   r!      rV   r   r#   rW   r!   r.   r*   iThe dimension of input tensor is not supported in create_mask, Only dimension < 4 is supported but got {})r&   dtypeastyper   r   r   rK   typegetattrsysmodulesr   valuer%   r(   	transpose
ValueError)	tensor	func_namer.   r*   r&   r[   tfuncr7   r   r   r   r     s:   !

$
r   c                 C   s
  | j }| t}t|tksJ dt|ttjt	 |j
d}t|dkr0|d|d }nNt|dkrA||d |d }n=t|dkrV||d |d  |d }n(t|dkru|g d|d |d  |d  |d g}n	td	t|||||d
S )a=  
    Check if input tensor is in `n:m` sparse pattern via function given by :attr:`func_name`.
    Currently only support tensor with dimension less than or equal to 4.

    Args:
        tensor (nparray): The input tensor.
        func_name (CheckMethod, optional): The function name to generate spase mask. Default is `CheckMethod.CHECK_1D`. All options please refer to `CheckMethod`.
        n (int, optional): n of `n:m` sparse pattern. Default is 2.
        m (int, optional): m of `n:m` sparse pattern. Default is 4.
    Returns:
        bool: True if tensor pass checking of function given by :attr:`func_name`, else False.
    Examples:
        .. code-block:: python

          import numpy as np
          import paddle.fluid.contrib.sparsity as sparsity

          tensor = np.array([[2, 8, 9, 9],
                             [9, 1, 3, 9],
                             [5, 6, 3, 9],
                             [2, 4, 6, 9]])
          mask_1d = sparsity.create_mask(tensor, func_name=sparsity.MaskAlgo.MASK_1D)
          # nparray([[0 0 1 1],
          #          [1 0 0 1],
          #          [0 1 0 1],
          #          [0 0 1 1]])
          sparsity.check_sparsity(mask_1d, func_name=sparsity.CheckMethod.CHECK_1D) # True
          sparsity.check_sparsity(mask_1d, func_name=sparsity.CheckMethod.CHECK_2D) # False
    zTfunc_name argumet of check_sparsity is only accepted as type CheckMethod. But got {}Nr#   r   r!   rW   rV   rX   rZ   rY   )r&   r\   r   r]   r   rK   r^   r_   r`   r   ra   r%   r(   rb   rc   )rd   re   r.   r*   r&   rf   rg   r   r   r   r     s*   

"
r   ) r   
__future__r   r_   mathrH   numpyr   enumr   	itertoolsr   	threading__all__r   r   r   r-   r   r   r@   r   r	   LockrO   rL   rS   r
   r   r   r   r   r   r   r   r   <module>   s2   
),(+2B&5>