o
    * iPW                     @  s  d Z ddlm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
mZmZ ddlZer4ddlm
Z g ZG dd deZG d	d
 d
eZd.ddZdd Zd/ddZd0ddZdd Zd/ddZd0d d!Ze ai ad"d# Zd0d$d%Zej d&d'fd1d*d+Z!ej"d&d'fd2d,d-Z#dS )3z#
Utilities of Auto SParsity (ASP).
    )annotationsN)Enum)permutations)TYPE_CHECKINGAnyc                   @  s   e Zd ZdZdZdZdZdS )MaskAlgoz
    A collection of all mask generating algorithms.
    There currently are three algorithms, `MASK_1D`, `MASK_2D_GREEDY` and `MASK_2D_BEST`
    get_mask_1dget_mask_2d_greedyget_mask_2d_bestN)__name__
__module____qualname____doc__MASK_1DZMASK_2D_GREEDYZMASK_2D_BEST r   r   e/home/app/PaddleOCR-VL-test/.venv_paddleocr/lib/python3.10/site-packages/paddle/incubate/asp/utils.pyr   $   s
    r   c                   @  s&   e Zd ZdZdZdZed
ddZd	S )CheckMethodzz
    A collection of all sparsity checking approaches.
    There currently are two methods, `CHECK_1D` and `CHECK_2D`
    check_mask_1dcheck_mask_2d	mask_algor   returnc                 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.incubate.asp import CheckMethod, MaskAlgo
                >>> print(CheckMethod.get_checking_method(MaskAlgo.MASK_1D))
                CheckMethod.CHECK_1D
                >>> print(CheckMethod.get_checking_method(MaskAlgo.MASK_2D_GREEDY))
                CheckMethod.CHECK_2D
                >>> print(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)r   r   r   r   get_checking_method8   s   
zCheckMethod.get_checking_methodN)r   r   r   r   )r   r   r   r   r   r   staticmethodr   r   r   r   r   r   /   s    r   xnpt.NDArray[Any]r   floatc                 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]])
            >>> out = paddle.incubate.asp.calculate_density(x)
            >>> print(out)
            0.625

    r   )flattenr   npnonzerosize)r   Zx_flattenedr   r   r   calculate_densityV   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_1dr   s   "r0   r,   nintr-   boolc                 C  sf   t | jdkrt| d| jd |\}}nt| |\}}|D ]}t|d j|| kr0 dS qdS )a  
    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.incubate.asp as sparsity

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

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

          >>> # x would be padded to shape (2, 8)
          >>> x = np.array([[0, 1, 0, 4, 6],
          ...               [1, 0, 0, 1, 7]])
          >>> y = sparsity.check_mask_1d(x, 2, 4)
          >>> print(y)
          True
    r&   r   FT)r(   r)   r0   r+   r    r!   r"   )r,   r1   r-   mat_flattenr)   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.incubate.asp as sparsity
          >>> mat = np.array([[0, 1, 5, 4],
          ...                 [2, 7, 3, 6]])
          >>> mask = sparsity.get_mask_1d(mat, 2, 4)
          >>> print(mask)
          [[0 0 1 1]
          [0 1 0 1]]
          >>> y = sparsity.check_mask_1d(mask, 2, 4)
          >>> print(y)
          True
    r   Nr&   )	r0   r    	ones_likeranger)   argsortabsolutetolistr+   )
r,   r1   r-   r4   r)   mask_flattenmaskir5   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 )a3  
    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+   r7   squeeze)r,   r-   Zremainder_0Zremainder_1Z	new_shaper/   r4   curr_idx	row_startrow_end	col_startcol_endr5   r   r   r   _reshape_2d   s*   ""
 

rE   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.incubate.asp as sparsity

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

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

          >>> # 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]])
          >>> y = sparsity.check_mask_2d(x, 2, 4)
          >>> print(y)
          True
    r   r&   ZaxisFT)rE   r    r9   r?   r+   sum)r,   r1   r-   r/   r)   r5   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.incubate.asp 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)
          >>> print(mask)
          [[1. 1. 0. 0.]
          [1. 0. 0. 1.]
          [0. 0. 1. 1.]
          [0. 1. 1. 0.]]
          >>> y = sparsity.check_mask_2d(mask, 2, 4)
          >>> print(y)
          True
    r'   c                   s    g | ]}t |  |  fqS r   )r2   ).0r   r-   r   r   
<listcomp>{  s    z&get_mask_2d_greedy.<locals>.<listcomp>r&   r   g      ?N)rE   r    Z
zeros_liker+   r7   r(   r9   r?   r8   collectionsCounterr>   r)   )r,   r1   r-   r/   r)   Zmask_paddedidxr5   rH   Zmin_order_1d_indicesZmin_order_2d_indicesZrow_counterZcol_counterr=   Zmatrix_entryr<   r@   rA   rB   rC   rD   r   rJ   r   r	   N  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 valid 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 valid 2D `n:m` sparse patterns.
    _r&   NrF   r   r'   )_valid_2d_patternsr    r*   listsetr   r:   ZasarrayrG   r!   r+   r>   r)   _valid_2d_patterns_lockacquirerelease)r1   r-   Z	valid_keypatternsZvalidZvalid_patternsr   r   r   _compute_valid_2d_patterns  s&   
rW   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 )a  
    Generate 2D `n:m` sparse pattern mask of the input matrix :attr:`mat`
    to form sparse matrix with maximum 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.incubate.asp 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())
          L1 norm of `greedy` sparse matrix 56.0
          >>> print("L1 norm of `best` sparse matrix", np.multiply(mat, mask_best).sum())
          L1 norm of `best` sparse matrix 61.0
    r'   r   r&   rF   N)rW   rE   r    r6   r+   Zargmaxmatmulr)   Tr>   r7   )r,   r1   r-   rV   r4   r)   r;   Zpmaxr<   r@   rA   rB   rC   rD   r   r   r   r
     s$   
"

 r
   r$      tensor	func_namec           	      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 )aI  
    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 sparse 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.incubate.asp 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)
          >>> print(mask_1d)
          [[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)
          >>> print(mask_2d)
          [[0 1 1 0]
          [1 0 0 1]
          [1 1 0 0]
          [0 0 1 1]]
    zMfunc_name argument of create_mask is only accepted as type MaskAlgo. But got Nr&   r   r$      rZ   r   r&   r]   r$   r1   r-   gThe dimension of input tensor is not supported in create_mask, Only dimension < 4 is supported but got )r)   dtypeastyper   r   r   typegetattrsysmodulesr   valuer(   r+   	transpose
ValueError)	r[   r\   r1   r-   r)   ra   tfuncr<   r   r   r   create_mask  s>   (
 
rl   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 sparse 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.incubate.asp 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)
          >>> print(mask_1d)
          [[0 0 1 1]
          [1 0 0 1]
          [0 1 0 1]
          [0 0 1 1]]
          >>> y = sparsity.check_sparsity(mask_1d, func_name=sparsity.CheckMethod.CHECK_1D)
          >>> print(y)
          True
          >>> y = sparsity.check_sparsity(mask_1d, func_name=sparsity.CheckMethod.CHECK_2D)
          >>> print(y)
          True
    zSfunc_name argument of check_sparsity is only accepted as type CheckMethod. But got Nr&   r   r$   r]   rZ   r^   r`   r_   )r)   rb   r   rc   r   rd   re   rf   r   rg   r(   r+   rh   ri   )r[   r\   r1   r-   r)   rj   rk   r   r   r   check_sparsityH  s0   (
rm   )r   r   r   r   )r,   r   r1   r2   r-   r2   r   r3   )r,   r   r1   r2   r-   r2   r   r   )
r[   r   r\   r   r1   r2   r-   r2   r   r   )
r[   r   r\   r   r1   r2   r-   r2   r   r3   )$r   
__future__r   rL   re   	threadingenumr   	itertoolsr   typingr   r   numpyr    Znumpy.typingZnpt__all__r   r   r#   r0   r   r   rE   r   r	   LockrS   rP   rW   r
   r   rl   r   rm   r   r   r   r   <module>   sB   
'

2*
+
9I
):N