
    _-Ph                    h   d Z ddlmZ ddlZddlmZmZmZm	Z	m
Z
mZmZmZmZ ddlmZ ddl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mZ d Zd1dZd Zd2dZ d2dZ!d2dZ"d1dZ#d Z$d Z%d3dZ&d2dZ'd Z(d Z)	 	 d4d Z*d2d!Z+d" Z,d1d#Z-d$ Z.d% Z/d& Z0d5d(Z1d) Z2d* Z3d+ Z4d, Z5d- Z6d3d.Z7d/ Z8d0 Z9dS )6z$General utility functions for pyamg.    )warnN)	
isspmatrixisspmatrix_csrisspmatrix_cscisspmatrix_bsr
csr_matrix
csc_matrix
bsr_matrix
coo_matrixeye)eigvals)csr_scale_rowsbsr_scale_rowscsr_scale_columnsbsr_scale_columns)make_system)upcast   )amg_core   )linalgc                 >    t          |           r| j        d         S dS )z"Return the block size of a matrix.r   r   )r   	blocksizeAs    P/var/www/html/test/jupyter/venv/lib/python3.11/site-packages/pyamg/util/utils.pyget_blocksizer      s#    a {1~1    c                 V   | j         d         j        t          j                            j        d         d          z  g | | j        fdi| n>fd}|                     |                    dd                    } |f||d	| t          j	                  S )
a  Profile a particular multilevel object.

    Parameters
    ----------
    ml : multilevel
        Fully constructed multilevel object
    accel : function pointer
        Pointer to a valid Krylov solver (e.g. gmres, cg)

    Returns
    -------
    residuals : array
        Array of residuals for each iteration

    See Also
    --------
    multilevel.psolve, multilevel.solve

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse import spdiags, csr_matrix
    >>> from scipy.sparse.linalg import cg
    >>> from pyamg.classical import ruge_stuben_solver
    >>> from pyamg.util.utils import profile_solver
    >>> n=100
    >>> e = np.ones((n,1)).ravel()
    >>> data = [ -1*e, 2*e, -1*e ]
    >>> A = csr_matrix(spdiags(data,[-1,0,1],n,n))
    >>> b = A*np.ones(A.shape[0])
    >>> ml = ruge_stuben_solver(A, max_coarse=10)
    >>> res = profile_solver(ml,accel=cg)

    r   r   N	residualsc           	                               t          j        t          j                  t          j        | z            z
                       d S N)appendr   normnpravel)xr   br    s    r   callbackz profile_solver.<locals>.callbackG   s@    V[!rx!}})DEEFFFFFr   cycleV)r*   )Mr)   )
levelsr   r%   randomrandshapesolveaspreconditionergetasarray)mlaccelkwargsr)   r,   r   r(   r    s        @@@r   profile_solverr8      s    F 		!A	BINN171:q)))AI}22i262222	G 	G 	G 	G 	G 	G 	Gfjj#&>&>??a5a(55f555:i   r   c           	         t          |           r|                                 S t          j        |           dk    rt	          d          t          t          j        |           t          j        t          |                     t          j        t          |           dz             ft          |           t          |           f          S )a  Return a diagonal.

    If A is a sparse matrix (e.g. csr_matrix or csc_matrix)
       - return the diagonal of A as an array

    Otherwise
       - return a csr_matrix with A on the diagonal

    Parameters
    ----------
    A : sparse matrix or 1d array
        General sparse matrix or array of diagonal entries

    Returns
    -------
    B : array or sparse matrix
        Diagonal sparse is returned as csr if A is dense otherwise return an
        array of the diagonal

    Examples
    --------
    >>> import numpy as np
    >>> from pyamg.util.utils import diag_sparse
    >>> d = 2.0*np.ones((3,)).ravel()
    >>> print(diag_sparse(d).toarray())
    [[2. 0. 0.]
     [0. 2. 0.]
     [0. 0. 2.]]

    r   z&input diagonal array expected to be 1d)	r   diagonalr%   ndim
ValueErrorr   r4   arangelenr   s    r   diag_sparser?   O   s    > !}} zz||	wqzzQABBBrz!}}biA&7&7yQ**,.1!ffc!ff-=? ? ?r   Tc                    t          j        |          }| j        \  }}t          |           st	          d          |t          |          k    rt	          d          |rM|                                 } t          j        | j        t          | j
        |j
                            | _        nt          j        || j
                  }t          |           r$t          ||| j        | j        | j        |           nt          |           rb| j        \  }}t#          t%          ||z            t%          ||z            ||| j        | j        t          j        | j                  |           not'          |           r)t)          j        ||| j        | j        | j        |           n7| j        }t/          t1          |           |                              |          } | S )ah  Scale the sparse rows of a matrix.

    Parameters
    ----------
    A : sparse matrix
        Sparse matrix with M rows
    v : array_like
        Array of M scales
    copy : {True,False}
        - If copy=True, then the matrix is copied to a new and different return
          matrix (e.g. B=scale_rows(A,v))
        - If copy=False, then the matrix is overwritten deeply (e.g.
          scale_rows(A,v,copy=False) overwrites A)

    Returns
    -------
    A : sparse matrix
        Scaled sparse matrix in original format

    See Also
    --------
    scipy.sparse._sparsetools.csr_scale_rows, scale_columns

    Notes
    -----
    - if A is a csc_matrix, the transpose A.T is passed to scale_columns
    - if A is not csr, csc, or bsr, it is converted to csr and sent
      to scale_rows

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse import spdiags
    >>> from pyamg.util.utils import scale_rows
    >>> n=5
    >>> e = np.ones((n,1)).ravel()
    >>> data = [ -1*e, 2*e, -1*e ]
    >>> A = spdiags(data,[-1,0,1],n,n-1).tocsr()
    >>> B = scale_rows(A,5*np.ones((A.shape[0],1)))

    z scale rows needs a sparse matrix#scale vector has incompatible shapedtype)r%   r&   r0   r   r<   r>   copyr4   datar   rC   r   r   indptrindicesr   r   r   intr   r   csc_scale_rowsformat
scale_rowsr   asformatr   vrD   r,   NRCfmts           r   rK   rK   x   s   T 	A7DAqa== =;<<<CFF{{>??? )FFHHAF&!'*B*BCCCJq(((a 
7q!QXqy!&!<<<<			 7{1s1Q3xxQqS1a19x''	, 	, 	, 	,			 71ah	161EEEEhz!}}a((11#66Hr   c                    t          j        |          }| j        \  }}t          |           st	          d          |t          |          k    rt	          d          |rM|                                 } t          j        | j        t          | j
        |j
                            | _        nt          j        || j
                  }t          |           r$t          ||| j        | j        | j        |           nt          |           rb| j        \  }}t#          t%          ||z            t%          ||z            ||| j        | j        t          j        | j                  |           not'          |           r)t)          j        ||| j        | j        | j        |           n7| j        }t/          t1          |           |                              |          } | S )a  Scale the sparse columns of a matrix.

    Parameters
    ----------
    A : sparse matrix
        Sparse matrix with N rows
    v : array_like
        Array of N scales
    copy : {True,False}
        - If copy=True, then the matrix is copied to a new and different return
          matrix (e.g. B=scale_columns(A,v))
        - If copy=False, then the matrix is overwritten deeply (e.g.
          scale_columns(A,v,copy=False) overwrites A)

    Returns
    -------
    A : sparse matrix
        Scaled sparse matrix in original format

    See Also
    --------
    scipy.sparse._sparsetools.csr_scale_columns, scale_rows

    Notes
    -----
    - if A is a csc_matrix, the transpose A.T is passed to scale_rows
    - if A is not csr, csc, or bsr, it is converted to csr and sent to
      scale_rows

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse import spdiags
    >>> from pyamg.util.utils import scale_columns
    >>> n=5
    >>> e = np.ones((n,1)).ravel()
    >>> data = [ -1*e, 2*e, -1*e ]
    >>> A = spdiags(data,[-1,0,1],n,n-1).tocsr()
    >>> print(scale_columns(A,5*np.ones((A.shape[1],1))).toarray())
    [[10. -5.  0.  0.]
     [-5. 10. -5.  0.]
     [ 0. -5. 10. -5.]
     [ 0.  0. -5. 10.]
     [ 0.  0.  0. -5.]]

    z#scale columns needs a sparse matrixrA   rB   )r%   r&   r0   r   r<   r>   rD   r4   rE   r   rC   r   r   rF   rG   r   r   r   rH   r   r   csc_scale_columnsrJ   scale_columnsr   rL   rM   s           r   rU   rU      s   ^ 	A7DAqa== @>???CFF{{>??? )FFHHAF&!'*B*BCCCJq(((a 
:!Q!)QVQ????			 :{1#ac((C!HHaAHai(16**A	/ 	/ 	/ 	/			 :"1a19afaHHHHh*Q--++44S99Hr   c                 6   t          |           st          |           st          |           r| j        d         | j        d         k    rt	          d          t          |           }|dk    }| j        t          k    r"t          j	        t          |                    }nt          j	        |          }t          j        |          }d||         z  ||<   t          | ||          }t          ||d          }|||fS t          t          |                     S )ad  Scale the matrix symmetrically.

        A = D^{-1/2} A D^{-1/2}

    where D=diag(A).

    The left multiplication is accomplished through scale_rows and the right
    multiplication is done through scale columns.

    Parameters
    ----------
    A : sparse matrix
        Sparse matrix with N rows
    copy : {True,False}
        - If copy=True, then the matrix is copied to a new and different return
          matrix (e.g. B=symmetric_rescaling(A))
        - If copy=False, then the matrix is overwritten deeply (e.g.
          symmetric_rescaling(A,copy=False) overwrites A)

    Returns
    -------
    D_sqrt : array
        Array of sqrt(diag(A))
    D_sqrt_inv : array
        Array of 1/sqrt(diag(A))
    DAD    : csr_matrix
        Symmetrically scaled A

    Notes
    -----
    - if A is not csr, it is converted to csr and sent to scale_rows

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse import spdiags
    >>> from pyamg.util.utils import symmetric_rescaling
    >>> n=5
    >>> e = np.ones((n,1)).ravel()
    >>> data = [ -1*e, 2*e, -1*e ]
    >>> A = spdiags(data,[-1,0,1],n,n).tocsr()
    >>> Ds, Dsi, DAD = symmetric_rescaling(A)
    >>> print(DAD.toarray())
    [[ 1.  -0.5  0.   0.   0. ]
     [-0.5  1.  -0.5  0.   0. ]
     [ 0.  -0.5  1.  -0.5  0. ]
     [ 0.   0.  -0.5  1.  -0.5]
     [ 0.   0.   0.  -0.5  1. ]]

    r   r   zexpected square matrix      ?rD   F)r   r   r   r0   r<   r?   rC   complexr%   sqrtabs
zeros_likerK   rU   symmetric_rescalingr   )r   rD   DmaskD_sqrt
D_sqrt_invDADs          r   r]   r]     s	   f a 'N1-- '1B1B '71:##5666NNAv7gWSVV__FF WQZZF]6**
vd|+
4JT222C%888z3&&z!}}---r   c                    t          | d          \  }}} t          |j        d                   D ]<}t          j        |dd|f                   t          j        |          z  |dd|f<   =t          | d          rs| j        dk    rh|t          d          t          |j        d                   D ]<}t          j        |dd|f                   t          j        |          z  |dd|f<   =| ||gS )a  Scale the matrix symmetrically.

        A = D^{-1/2} A D^{-1/2}

    where D=diag(A).  The left multiplication is accomplished through
    scale_rows and the right multiplication is done through scale columns.

    The candidates B and BH are scaled accordingly::

        B = D^{1/2} B
        BH = D^{1/2} BH

    Parameters
    ----------
    A : {sparse matrix}
        Sparse matrix with N rows
    B : {array}
        N x m array
    BH : {None, array}
        If A.symmetry == 'nonsymmetric, then BH must be an N x m array.
        Otherwise, BH is ignored.

    Returns
    -------
    Appropriately scaled A, B and BH, i.e.,
    A = D^{-1/2} A D^{-1/2},  B = D^{1/2} B,  and BH = D^{1/2} BH

    Notes
    -----
    - if A is not csr, it is converted to csr and sent to scale_rows

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse import spdiags
    >>> from pyamg.util.utils import symmetric_rescaling_sa
    >>> n=5
    >>> e = np.ones((n,1)).ravel()
    >>> data = [ -1*e, 2*e, -1*e ]
    >>> A = spdiags(data,[-1,0,1],n,n).tocsr()
    >>> B = e.copy().reshape(-1,1)
    >>> [DAD, DB, DBH] = symmetric_rescaling_sa(A,B,BH=None)
    >>> print(DAD.toarray())
    [[ 1.  -0.5  0.   0.   0. ]
     [-0.5  1.  -0.5  0.   0. ]
     [ 0.  -0.5  1.  -0.5  0. ]
     [ 0.   0.  -0.5  1.  -0.5]
     [ 0.   0.   0.  -0.5  1. ]]
    >>> print(DB)
    [[1.41421356]
     [1.41421356]
     [1.41421356]
     [1.41421356]
     [1.41421356]]

    FrX   r   NsymmetrynonsymmetriczBH should be an n x m array)r]   ranger0   r%   r&   hasattrrd   r<   )r   BBHr`   _is         r   symmetric_rescaling_sarl   Z  s   t 'qu555LFAq 171: 5 5(1QQQT7##BHV$4$44!!!Q$q* ?:''z !>???28A;'' ? ?8Bqqq!tH--bhv.>.>>111a4q":r   c                     t          | |          }t          |          }t          |          D ]:}t          j        ||                   rt          j        ||         g          ||<   ;|S )a0  Upcast variables to a type.

    Loop over all elements of varlist and convert them to upcasttype
    The only difference with pyamg.util.utils.to_type(...), is that scalars
    are wrapped into (1,0) arrays.  This is desirable when passing
    the numpy complex data type to C routines and complex scalars aren't
    handled correctly

    Parameters
    ----------
    upcast_type : data type
        e.g. complex, float64 or complex128
    varlist : list
        list may contain arrays, mat's, sparse matrices, or scalars
        the elements may be float, int or complex

    Returns
    -------
    Returns upcast-ed varlist to upcast_type

    Notes
    -----
    Useful when harmonizing the types of variables, such as
    if A and b are complex, but x,y and z are not.

    Examples
    --------
    >>> import numpy as np
    >>> from pyamg.util.utils import type_prep
    >>> from scipy.sparse.sputils import upcast
    >>> x = np.ones((5,1))
    >>> y = 2.0j*np.ones((5,1))
    >>> z = 2.3
    >>> varlist = type_prep(upcast(x.dtype, y.dtype), [x, y, z])

    )to_typer>   rf   r%   isscalararrayupcast_typevarlistnvrk   s       r   	type_prepru     si    J k7++G	WB2YY 0 0;wqz"" 	071:,//GAJNr   c                 f   t          |          }t          |          D ]}t          j        ||                   r&t          j        ||         g|           d         ||<   B	 ||         j        | k    r||                             |           ||<   s# t          $ r t          d           Y w xY w|S )a  Loop over all elements of varlist and convert them to upcasttype.

    Parameters
    ----------
    upcast_type : data type
        e.g. complex, float64 or complex128
    varlist : list
        list may contain arrays, mat's, sparse matrices, or scalars
        the elements may be float, int or complex

    Returns
    -------
    Returns upcast-ed varlist to upcast_type

    Notes
    -----
    Useful when harmonizing the types of variables, such as
    if A and b are complex, but x,y and z are not.

    Examples
    --------
    >>> import numpy as np
    >>> from pyamg.util.utils import to_type
    >>> from scipy.sparse.sputils import upcast
    >>> x = np.ones((5,1))
    >>> y = 2.0j*np.ones((5,1))
    >>> varlist = to_type(upcast(x.dtype, y.dtype), [x, y])

    r   zFailed to cast in to_type)	r>   rf   r%   ro   rp   rC   astypeAttributeErrorr   rq   s       r   rn   rn     s    @ 
WB2YY 2 2 ;wqz"" 	271:,<<Q?GAJJ21:#{22!(!2!2;!?!?GAJ! 2 2 20111112 Ns   "/BB.-B.Fc                    t          |           s<t          |           s-t          |           st          d           t	          |           } |                                  |dk    rP| j        }|                    |                                          t          j
        |j        d         f          z  }nc|dk    rI|                     |                                           t          j
        | j        d         f          z  }n|                                 }|r*t          j        |          }|dk    }d||         z  ||<   |S |S )a  Return the diagonal or inverse of diagonal for A, (A.H A) or (A A.H).

    Parameters
    ----------
    A   : {dense or sparse matrix}
        e.g. array, matrix, csr_matrix, ...
    norm_eq : {0, 1, 2}
        0 ==> D = diag(A)
        1 ==> D = diag(A.H A)
        2 ==> D = diag(A A.H)
    inv : {True, False}
        If True, D = 1.0/D

    Returns
    -------
    diagonal, D, of appropriate system

    Notes
    -----
    This function is especially useful for its fast methods
    of obtaining diag(A A.H) and diag(A.H A).  Dinv is zero
    wherever D is zero

    Examples
    --------
    >>> from pyamg.util.utils import get_diagonal
    >>> from pyamg.gallery import poisson
    >>> A = poisson( (5,), format='csr' )
    >>> D = get_diagonal(A)
    >>> print(D)
    [2. 2. 2. 2. 2.]
    >>> D = get_diagonal(A, norm_eq=1, inv=True)
    >>> print(D)
    [0.2        0.16666667 0.16666667 0.16666667 0.2       ]

    z$Implicit conversion to sparse matrixr   r   r           rW   )r   r   r   r   r   sort_indicesTmultiply	conjugater%   onesr0   r:   r\   )r   norm_eqinvAtr^   Dinvr_   s          r   get_diagonalr     s&   L 1 !2!2 nQ6G6G 3444qMM NN!|| S[[(("'28A;.*A*AA	AZZ&&(>(>>JJLL
 }QCx1T7]T
Hr   c                    t          |           st          d          | j        d         | j        d         k    rt          d          t	          j        | j        d         |          dk    rt          d          t          | d          rg|re| j        j        d         |k    rN| j        j        d         |k    r8| j        j        d         t          | j        d         |z            k    r| j        S nvt          | d          rf|sd| j	        j        d         |k    rN| j	        j        d         |k    r8| j	        j        d         t          | j        d         |z            k    r| j	        S t          |           st          | ||f	          } | j        ||fk    r|                     ||f	          } |                                 } t	          j        t          | j        d         |z            ||f| j        
          }t	          j        d| j        j        d         dz             | j        | j        f}t          | j        d         |z            t          | j        d         |z            f}t)          ||                                          }|dz  }|dk    }||         }|j        dk    r| j        |ddddf         ||ddddf<   |rm|j        d         dk     r@t/          j        |                                |j        d         |j        d         d           nt5          j        |           || _        n|| _	        |S )a  Return the block diagonal of A, in array form.

    Parameters
    ----------
    A : csr_matrix
        assumed to be square
    blocksize : int
        square block size for the diagonal
    inv_flag : bool
        if True, return the inverse of the block diagonal

    Returns
    -------
    block_diag : array
        block diagonal of A in array form,
        array size is (A.shape[0]/blocksize, blocksize, blocksize)

    Examples
    --------
    >>> from numpy import arange
    >>> from scipy.sparse import csr_matrix
    >>> from pyamg.util.utils import get_block_diag
    >>> A = csr_matrix(arange(36).reshape(6,6))
    >>> block_diag_inv = get_block_diag(A, blocksize=2, inv_flag=False)
    >>> print(block_diag_inv)
    [[[ 0.  1.]
      [ 6.  7.]]
    <BLANKLINE>
     [[14. 15.]
      [20. 21.]]
    <BLANKLINE>
     [[28. 29.]
      [34. 35.]]]
    >>> block_diag_inv = get_block_diag(A, blocksize=2, inv_flag=True)

    Expected sparse matrixr   r   Expected square matrix(blocksize and A.shape must be compatibleblock_D_invr   block_Dr   rB   r0   r   N   r|   )r   	TypeErrorr0   r<   r%   modrg   r   rH   r   r   r
   r   tobsrasfptypezerosrC   r=   rG   rF   r   r:   rE   r   
pinv_arrayr&   r   )r   r   inv_flag
block_diagAAIJr0   diag_entriesnonzero_masks           r   get_block_diagr   C  sK   J a== 20111wqzQWQZ1222	vagaj)$$))CDDD q-   	X 	M"i//M"i//M"c!'!*Y*>&?&???= 	I		  IOA)++IOA)++IOA#agaj&:";";;;9 ! <qY	$:;;;{y),,,GGy)4G55 	


A3qwqz)344iK !) ) )J Ia+A-..	18DDI%&&AGAJy,@(A(ABEd%00099;;LAL2%L-LT!!)*aaa0B)C
<AAA%& 
A""
 0 0 2 2J4DQ4G * 0 3S: : : : j)))"	r   c                    |dk    r| S t          j        | j        d         |          dk    rt          d          |                     ||f          } |                                  t          j        | j        j                  | j        | j        f}t          | j        d         | j
        d         z            t          | j        d         | j
        d         z            f}t          ||          S )a  Amalgamate matrix A.

    Parameters
    ----------
    A : csr_matrix
        Matrix to amalgamate
    blocksize : int
        blocksize to use while amalgamating

    Returns
    -------
    A_amal : csr_matrix
        Amalgamated  matrix A, first, convert A to BSR with square blocksize
        and then return a CSR matrix of ones using the resulting BSR indptr and
        indices

    Notes
    -----
    inverse operation of unamal for square matrices

    Examples
    --------
    >>> from numpy import array
    >>> from scipy.sparse import csr_matrix
    >>> from pyamg.util.utils import amalgamate
    >>> row = array([0,0,1])
    >>> col = array([0,2,1])
    >>> data = array([1,2,3])
    >>> A = csr_matrix( (data,(row,col)), shape=(4,4) )
    >>> A.toarray()
    array([[1, 0, 2, 0],
           [0, 3, 0, 0],
           [0, 0, 0, 0],
           [0, 0, 0, 0]])
    >>> amalgamate(A,2).toarray()
    array([[1., 1.],
           [0., 0.]])

    r   r   zIncompatible blocksizer   r   )r%   r   r0   r<   r   r{   r   rG   rF   rH   r   r   )r   r   subIr0   s       r   
amalgamater     s    P A~~	vagaj)$$))1222	9i011ANNGAIO$$ai:DAKN*++AKN*++-Ed%((((r   c                     t          j        | j        j        d         ||f          }|| j        | j        f}|| j        d         z  || j        d         z  f}t          ||          S )a`  Unamalgamate a CSR A with blocks of 1's.

    This operation is equivalent to
    replacing each entry of A with ones(rows_per_block, cols_per_block), i.e., this
    is equivalent to setting all of A's nonzeros to 1 and then doing a
    Kronecker product between A and ones(rows_per_block, cols_per_block).

    Parameters
    ----------
    A : csr_matrix
        Amalgamted matrix
    rows_per_block : int
        Give A blocks of size (rows_per_block, cols_per_block)
    cols_per_block : int
        Give A blocks of size (rows_per_block, cols_per_block)

    Returns
    -------
    A : bsr_matrix
        Returns A.data[:] = 1, followed by a Kronecker product of A and
        ones(rows_per_block, cols_per_block)

    Examples
    --------
    >>> from numpy import array
    >>> from scipy.sparse import csr_matrix
    >>> from pyamg.util.utils import unamal
    >>> row = array([0,0,1,2,2,2])
    >>> col = array([0,2,2,0,1,2])
    >>> data = array([1,2,3,4,5,6])
    >>> A = csr_matrix( (data,(row,col)), shape=(3,3) )
    >>> A.toarray()
    array([[1, 0, 2],
           [0, 0, 3],
           [4, 5, 6]])
    >>> unamal(A,2,2).toarray()
    array([[1., 1., 0., 0., 1., 1.],
           [1., 1., 0., 0., 1., 1.],
           [0., 0., 0., 0., 1., 1.],
           [0., 0., 0., 0., 1., 1.],
           [1., 1., 1., 1., 1., 1.],
           [1., 1., 1., 1., 1., 1.]])

    r   r   r   )r%   r   rG   r0   rF   r
   )r   rows_per_blockcols_per_blockrE   blockIr0   s         r   unamalr     sg    Z 7AIOA&GHHDAIqx(FAGAJ&qwqz(ABEfE****r    |center-c                 
   d}t          | t                    r| d         }| d         } g }| D ]}	t          t          |	          t          |          z
            D ]}
|                    d           t          |	          D ]0\  }}t          |          ||         k    rt          |          ||<   1t          |          D ]\  }}
||xx         |z  cc<   t          |          t          |          t          |          dz
  z  z   }t          |          dk    r@|                    d          }|D ]#}|t          	                    ||          dz   z  }$|dz  }|
                                }|dk    rt          j	        }|dk    rt          j        }|dk    rt          j        }|rt          | d         |          D ]$\  }}| |t          |          |          |z   z  }%| d         g k    r-|dt          |                                                    dz   }t          |          dk    rd	}||t          t!          j        t%          |          t%          t          |                    z                      z  dz   z  }| dd         } | D ]p}	t          |	|          D ]$\  }}| |t          |          |          |z   z  }%|	g k    r.|dt          |                                                    dz   }k|dz  }q|S )
a  Print a table from a list of lists representing the rows of a table.

    Parameters
    ----------
    table : list
        list of lists, e.g. a table with 3 columns and 2 rows could be
        [ ['0,0', '0,1', '0,2'], ['1,0', '1,1', '1,2'] ]
    title : string
        Printed centered above the table
    delim : string
        character to delimit columns
    centering : {'left', 'right', 'center'}
        chooses justification for columns
    col_padding : int
        number of blank spaces to add to each column
    header : {True, False}
        Does the first entry of table contain column headers?
    headerchar : {string}
        character to separate column headers from rest of table

    Returns
    -------
    string representing table that's ready to be printed

    Notes
    -----
    The string for the table will have correctly justified columns
    with extra padding added into each column entry to ensure columns align.
    The characters to delimit the columns can be user defined.  This
    should be useful for printing convergence data from tests.


    Examples
    --------
    >>> from pyamg.util.utils import print_table
    >>> table = [ ['cos(0)', 'cos(pi/2)', 'cos(pi)'], ['0.0', '1.0', '0.0'] ]
    >>> table1 = print_table(table)                 # string to print
    >>> table2 = print_table(table, delim='||')
    >>> table3 = print_table(table, headerchar='*')
    >>> table4 = print_table(table, col_padding=6, centering='left')

    
r   r   r   r   rightleftN )
isinstancetuplerf   r>   r#   	enumeratesumsplitstrr   lowerrjustljustziprstriprH   r%   ceilfloat)tabletitledelim	centeringcol_paddingheader
headerchar	table_str	colwidthsrowrj   jrrk   ttwidthtelmt	elmtwidths                     r   print_tabler     sP   X I % aa I & &s3xx#i..011 	! 	!AR     cNN 	& 	&DAq1vv	!$$"1vv	!	&
 )$$ $ $1!# )nns5zz3y>>!+;<<G 5zzA~~D!! 	7 	7AAw//$66IIT	 !!IHJ	GI	FI	 "58Y77 	A 	AOD)3t99i885@@II8r>>!,CJJ;,/66884?I z??aJZguS__'='==>>??@BFG 	G	 abb	  "3	22 	A 	AOD)3t99i885@@II"99!,CJJ;,/66884?IIIIr   c           
         g dg}g dg}g }t          | j                  D ]%\  }}|j                                        }|du r|                                 |j        dd         |j        dd         z
  }|dk                                    d         }	|                                }|j        dd         |j        dd         z
  }
|
dk                                    d         }t          j	        |	|          }|
                                }||ddf         dd|f         }n|
                                }t          |          }t          j        |          }t          t          j        |                    }t!          t          j        |                    }t!          |t          j        |          dk              j                  }t!          |t          j        |          dk             j                  }|                    t'          |          |d	|d	t'          |          t'          |          |d
g           t          t          j        |                    }t!          t          j        |                    }t!          |t          j        |          dk              j                  }t!          |t          j        |          dk             j                  }|                    t'          |          |d	|d	t'          |          t'          |          |d
g           |                    |           't+          t-          |                     t+          t-          |                     |S )a  Examine a multilevel hierarchy's spectrum.

    Parameters
    ----------
    mg { pyamg multilevel hierarchy }
        e.g. generated with smoothed_aggregation_solver(...) or
        ruge_stuben_solver(...)

    Returns
    -------
    leveleigs : list
        List of eigenvalues per level

    Notes
    -----
    This can be useful for troubleshooting and when examining how your
    problem's nature changes from level to level

    A table is printed to standard out detailing the spectrum of each level in mg

    Examples
    --------
    >>> from pyamg import smoothed_aggregation_solver
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import hierarchy_spectrum
    >>> A = poisson( (1,), format='csr' )
    >>> ml = smoothed_aggregation_solver(A)
    >>> leveleigs = hierarchy_spectrum(ml)
    <BLANKLINE>
     Level | min(re(eig)) | max(re(eig)) | num re(eig) < 0 | num re(eig) > 0 | cond_2(A)
    -------------------------------------------------------------------------------------
       0   |    2.000     |    2.000     |        0        |        1        |  1.00e+00
    <BLANKLINE>
    <BLANKLINE>
     Level | min(im(eig)) | max(im(eig)) | num im(eig) < 0 | num im(eig) > 0 | cond_2(A)
    -------------------------------------------------------------------------------------
       0   |    0.000     |    0.000     |        0        |        0        |  1.00e+00
    <BLANKLINE>

    )Levelzmin(re(eig))zmax(re(eig))znum re(eig) < 0znum re(eig) > 0	cond_2(A))r   zmin(im(eig))zmax(im(eig))znum im(eig) < 0znum im(eig) > 0r   Tr   r   r   Nrz   z1.3fz1.2e)r   r-   r   tocsreliminate_zerosrF   nonzerotocscr%   union1dtoarrayr   r   condminrealmaxr0   r#   r   imagprintr   )mgfilter_entries
real_table
imag_table	leveleigsrk   levelr   nnz_per_rownonzero_rowsnnz_per_colnonzero_colsnonzero_rowcolsec
lambda_min
lambda_maxnum_negnum_poss                      r   hierarchy_spectrumr   y  s%   R3 3 3 4J3 3 3 4J Ibi(( ! !5GMMOOT!!(1R4.18ABB<7K'1,5577:L		A(1R4.18ABB<7K'1,5577:L j|DDO		A/111$%aaa&89AA		AAJJKNN__
__
a

S()/00a

S()/003q66j#7#7J9M9Mw<<W!{{D 	E 	E 	E __
__
a

S()/00a

S()/003q66j#7#7J9M9Mw<<W!{{D 	E 	E 	E 		+j
!
!"""	+j
!
!"""r   c                 r   |dk    rd}n|dv rd}nt          d| d          t          |j                  | k    s0t          |j                  | k    st          |j                  | k    rt          d|            t          j        t          j        | |z  |f                    }t          |           D ]}}||z  }|dk    rd||<   |dk    rAt          dd          D ]0}	t          d	d          D ]}
|	|
k    rd|||	z   |
f<   d
|||	z   |
f<   1|dv rt          d	d          D ]0}	t          d	d          D ]}
|	|
k    rd|||	z   |
f<   d
|||	z   |
f<   1t          d	d          D ]}	t          dd          D ]n}
|	|
dz
  k    rd
|||	z   |
f<   |	|
z   dk    r||         |||	z   |
f<   0|	|
z   dk    r||         |||	z   |
f<   J|	|
z   dk    r||         |||	z   |
f<   dd
|||	z   |
f<   od	}	d}
|||	z   |
fxx         dz  cc<   d}	d}
|||	z   |
fxx         dz  cc<   d}	d}
|||	z   |
fxx         dz  cc<   |S )aO  Convert 2D or 3D coordinates into Rigid body modes.

    For use as near nullspace modes in elasticity AMG solvers.

    Parameters
    ----------
    nnodes : int
        Number of nodes
    ndof :
        Number of dofs per node
    x,y,z : array_like
        Coordinate vectors

    Returns
    -------
    rbm : array
        An array of size (nnodes*ndof) x (1 | 6) containing the 6 rigid
        body modes

    Examples
    --------
    >>> import numpy as np
    >>> from pyamg.util.utils import coord_to_rbm
    >>> a = np.array([0,1,2])
    >>> coord_to_rbm(3,6,a,a,a)
    array([[ 1.,  0.,  0.,  0.,  0., -0.],
           [ 0.,  1.,  0., -0.,  0.,  0.],
           [ 0.,  0.,  1.,  0., -0.,  0.],
           [ 0.,  0.,  0.,  1.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  1.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  1.],
           [ 1.,  0.,  0.,  0.,  1., -1.],
           [ 0.,  1.,  0., -1.,  0.,  1.],
           [ 0.,  0.,  1.,  1., -1.,  0.],
           [ 0.,  0.,  0.,  1.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  1.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  1.],
           [ 1.,  0.,  0.,  0.,  2., -2.],
           [ 0.,  1.,  0., -2.,  0.,  2.],
           [ 0.,  0.,  1.,  2., -2.,  0.],
           [ 0.,  0.,  0.,  1.,  0.,  0.],
           [ 0.,  0.,  0.,  0.,  1.,  0.],
           [ 0.,  0.,  0.,  0.,  0.,  1.]])

    r   )      r   zncoord_to_rbm(...) only supports 1, 3 or 6 PDEs per spatial location,i.e. ndof = [1 | 3 | 6]. You have entered .zXcoord_to_rbm(...) requires coordinate vectors of equal length.  Length must be nnodes = rW   r   r   rz         g      r   )r<   r   r0   r%   rp   r   rf   )nnodesndofr'   yznumcolsrbmnodedofiijjs              r   coord_to_rbmr     s2   ^ qyy	 5-15 5 5 6 6 	6 	AGCLLF$:$:AGPV@V@V F=CF F G G 	G (28VD['233
4
4Cf .$ .$Tk199CI199Aqkk . .1++ . .BRxx*-CFBJ*-CFBJ	. 6>>Aqkk . .1++ . .BRxx*-CFBJ*-CFBJ	. Aqkk 2 21++ 2 2Bbd||*-CFBJrEa<<./gCB
OO e\\./gCB
OO e\\./gCB
OO.1CB
OO2 BBB
OOOt#OOOBBB
OOOt#OOOBBB
OOOt#OOOJr   c                 	   | j         d         }| j         d         |j         d         k    rt          d          | j         d         |j         d         k    rt          d          t          |          r~d}|j        d         }|j        d         }t	          ||z            }	t          |           st          d          || j        d         k    s|| j        d         k    rt          d          nUt          |          r7d}d}d}t	          ||z            }	t          |           st          d          nt          d          t          |j                   dk    r|                    dd          }|j         d         | j         d         k    rt          d	          t          |j                   dk    r|                    dd          }|j         d         | j         d         k    rt          d
          |j         d         |j         d         k    rt          d          |j         d         }
| j        t          k    r%t          j
        | j        t                    | _        |j        t          k    r%t          j
        |j        t                    |_        |j        t          k    r%t          j
        |j        t                    |_        | j        |j        k    s| j        |j        k    rt          d          |t          ||          }|                                }d|j        dd<   |                     |          } |                                 } |                                }t          j        t          j        |j        j         | j                  | j        f          | _        t          j        |j        | j        f          | _        t          j        |j        | j        f          | _        |r|                     ||f          } n|                                 } | |z  |z
  }t1          j        |||	|
t          j        t          j        |                    t          j        |          t          j        |          | j        | j        t          j        | j                  
  
         |                                  | S )a	  Filter the matrix A according to the matrix graph of C.

    Ensure that the new, filtered A satisfies:  A_new*B = Bf.

    A : {csr_matrix, bsr_matrix}
        n x m matrix to filter
    C : {csr_matrix, bsr_matrix}
        n x m matrix representing the couplings in A to keep
    B : {array}
        m x k array of near nullspace vectors
    Bf : {array}
        n x k array of near nullspace vectors to place in span(A)
    BtBinv : {None, array}
        3 dimensional array such that,
        BtBinv[i] = pinv(B_i.H Bi), and B_i is B restricted
        to the neighborhood (with respect to the matrix graph
        of C) of dof of i.  If None is passed in, this array is
        computed internally.

    Returns
    -------
    A : sparse matrix updated such that sparsity structure of A now matches
    that of C, and that the relationship A*B = Bf holds.

    Notes
    -----
    This procedure allows for certain important modes (i.e., Bf) to be placed
    in span(A) by way of row-wise l2-projections that enforce the relationship
    A*B = Bf.  This is useful for maintaining certain modes (e.g., the
    constant) in the span of prolongation.

    This routine is primarily used in
    pyamg.aggregation.smooth.energy_prolongation_smoother, where it is used to
    generate a suitable initial guess for the energy-minimization process, when
    root-node style SA is used.  Essentially, the tentative prolongator, T, is
    processed by this routine to produce fine-grid nullspace vectors when
    multiplying coarse-grid nullspace vectors, i.e., T*B = Bf.  This is
    possible for any arbitrary vectors B and Bf, so long as the sparsity
    structure of T is rich enough.

    When generating initial guesses for root-node style prolongation operators,
    this function is usually called before pyamg.uti.utils.scale_T

    Examples
    --------
    >>> from numpy import ones, array
    >>> from scipy.sparse import csr_matrix
    >>> from pyamg.util.utils import filter_operator
    >>> A = array([ [1.,1,1],[1,1,1],[0,1,0],[0,1,0],[0,0,1],[0,0,1]])
    >>> C = array([ [1.,1,0],[1,1,0],[0,1,0],[0,1,0],[0,0,1],[0,0,1]])
    >>> B = ones((3,1))
    >>> Bf = ones((6,1))
    >>> filter_operator(csr_matrix(A), csr_matrix(C), B, Bf).toarray()
    array([[0.5, 0.5, 0. ],
           [0.5, 0.5, 0. ],
           [0. , 1. , 0. ],
           [0. , 1. , 0. ],
           [0. , 0. , 1. ],
           [0. , 0. , 1. ]])

    r   zA and C must be the same sizer   Tz&A and C must either both be CSR or BSRz%A and C must have same BSR blocksizesFr   z+A and Bf must have the same first dimensionzZA and B must have matching dimensions such                          that A*B is computablezEB and Bf must have the same second                          dimensionrB   z"A, B and Bf must of the same dtypeN)r0   r<   r   r   rH   r   r>   reshaperC   r%   rp   rE   r   r   compute_BtBinvrD   r}   tocoohstackr   r   colr   r   r   satisfy_constraints_helperr~   r&   rF   rG   r   )r   rQ   rh   BfBtBinvNfineisBSRr   r   NnodesNullDimdiffs               r   filter_operatorr  G  sF   ~ GAJEwqzQWQZ8999wqzQWQZ8999a CQQU>)**a   	GEFFFak!n,,.AKPQN2R2RDEEE 3S 
		 CU>)**a   	GEFFF	G ABBB
28}}ZZA	x{agaj  FGGG
17||qIIb!wqzQWQZ 2 3 3 	3 	wqzRXa[   % & & 	& gajGw#~~!&...w#~~!&...	x3(27%000	1728 3 3<===
 ~1%% 	
AAF111I	

1A			A			AYQW===qvFGGAFIquaen%%AEIquaen%%AE GG^^455GGII Q38D '(.(*RXa[[(A(A(*(*(8(8!(()	28AF3C3CE E E Hr   c                    t          |           st          d          | j        d         | j        d         k    rt          d          t          |          st          d          |j        d         |j        d         k    rt          d          t          |          st          d          |j        d         |j        d         k    rt          d          |j        d         |j        d         k    s|j        d         | j        d         k    rt          d	          |j        dk    r;|j        | z  }|j        dk    rt          j        |j                   | |z  } || z  |z   } | S )
a
  Scale T with a block diagonal matrix.

    Helper function that scales T with a right multiplication by a block
    diagonal inverse, so that T is the identity at C-node rows.

    Parameters
    ----------
    T : {bsr_matrix}
        Tentative prolongator, with square blocks in the BSR data structure,
        and a non-overlapping block-diagonal structure
    P_I : {bsr_matrix}
        Interpolation operator that carries out only simple injection from the
        coarse grid to fine grid Cpts nodes
    I_F : {bsr_matrix}
        Identity operator on Fpts, i.e., the action of this matrix zeros
        out entries in a vector at all Cpts, leaving Fpts untouched

    Returns
    -------
    T : {bsr_matrix}
        Tentative prolongator scaled to be identity at C-pt nodes

    Examples
    --------
    >>> from scipy.sparse import csr_matrix, bsr_matrix
    >>> import numpy as np
    >>> from pyamg.util.utils import scale_T
    >>> T = np.array([[ 1.0,  0.,   0. ],
    ...               [ 0.5,  0.,   0. ],
    ...               [ 0. ,  1.,   0. ],
    ...               [ 0. ,  0.5,  0. ],
    ...               [ 0. ,  0.,   1. ],
    ...               [ 0. ,  0.,   0.25 ]])
    >>> P_I = np.array([[ 0.,  0.,   0. ],
    ...                 [ 1.,  0.,   0. ],
    ...                 [ 0.,  1.,   0. ],
    ...                 [ 0.,  0.,   0. ],
    ...                 [ 0.,  0.,   0. ],
    ...                 [ 0.,  0.,   1. ]])
    >>> I_F = np.array([[ 1.,  0.,  0.,  0.,  0.,  0.],
    ...                 [ 0.,  0.,  0.,  0.,  0.,  0.],
    ...                 [ 0.,  0.,  0.,  0.,  0.,  0.],
    ...                 [ 0.,  0.,  0.,  1.,  0.,  0.],
    ...                 [ 0.,  0.,  0.,  0.,  1.,  0.],
    ...                 [ 0.,  0.,  0.,  0.,  0.,  0.]])
    >>> scale_T(bsr_matrix(T), bsr_matrix(P_I), bsr_matrix(I_F)).toarray()
    array([[2. , 0. , 0. ],
           [1. , 0. , 0. ],
           [0. , 1. , 0. ],
           [0. , 0.5, 0. ],
           [0. , 0. , 4. ],
           [0. , 0. , 1. ]])

    Notes
    -----
    This routine is primarily used in
    pyamg.aggregation.smooth.energy_prolongation_smoother, where it is used to
    generate a suitable initial guess for the energy-minimization process, when
    root-node style SA is used.  This function, scale_T, takes an existing
    tentative prolongator and ensures that it injects from the coarse-grid to
    fine-grid root-nodes.

    When generating initial guesses for root-node style prolongation operators,
    this function is usually called after pyamg.uti.utils.filter_operator

    This function assumes that the eventual coarse-grid nullspace vectors
    equal coarse-grid injection applied to the fine-grid nullspace vectors.

    Expected BSR matrix Tr   r   z(Expected BSR matrix T with square blockszExpected BSR matrix P_Iz*Expected BSR matrix P_I with square blockszExpected BSR matrix I_Fz*Expected BSR matrix I_F with square blocksz.Expected identical blocksize in I_F, P_I and T)r   r   r   nnzr|   r   r   rE   )r|   P_II_Fr^   s       r   scale_Tr    sm   L ! 1/000{1~Q''BCCC# 31222
}Q3=+++DEEE# 31222
}Q3=+++DEEEaCM!,,,aAKN**HIII w{{E!G5199af%%% aC ECKHr   c           	         t          |           st          |           st          d          t          |          st          d          t          |          st          d          |j        d         |j        d         k    rt          d          | j        d         | j        d         k    rt          d          |j        d         | j        d         k    rt          d          |j        d         |j        d         k    r |j        d         dk    rt          d	          t          |           r| j        d         dk    rp| j        d         }t          j        ||z  |          }t          d|          D ]9}|t          t          ||j        d         |                    xx         |z  cc<   :nd}|}t          j	        |t          
          }|j        d         |j        d         k    r |j        d         |k    rt          d          ||j        d         k    rt          d          |j        d         t          |j        d         |z            k    rt          d          |j        d         }t          | j        d         | j        d         d          }|                                }	d|	j        |<   |	                                 ||	z
  }|                                 |	j                                        }
|j        dk    r7|                                }t          j        |j        d         dz             }nDt          j        d|j        j        
          }t          j        |dz   f|j        j        
          }t-          |j                                        ||f|j        d         |f          }|                    |j                  }t          |           r5|                    | j                  }|	                    | j                  }	n,|                    d          }|	                    d          }	||	|||
dS )aa	  Return C and F pts.

    Helper function that returns a dictionary of sparse matrices and arrays
    which allow us to easily operate on Cpts and Fpts separately.

    Parameters
    ----------
    A : {csr_matrix, bsr_matrix}
        Operator
    Cnodes : {array}
        Array of all root node indices.  This is an array of nodal indices,
        not degree-of-freedom indices.  If the blocksize of T is 1, then
        nodal indices and degree-of-freedom indices coincide.
    AggOp : {csr_matrix}
        Aggregation operator corresponding to A
    T : {bsr_matrix}
        Tentative prolongator based on AggOp

    Returns
    -------
    Dictionary containing these parameters:

    P_I : {bsr_matrix}
        Interpolation operator that carries out only simple injection from the
        coarse grid to fine grid Cpts nodes
    I_F : {bsr_matrix}
        Identity operator on Fpts, i.e., the action of this matrix zeros
        out entries in a vector at all Cpts, leaving Fpts untouched
    I_C : {bsr_matrix}
        Identity operator on Cpts nodes, i.e., the action of this matrix zeros
        out entries in a vector at all Fpts, leaving Cpts untouched
    Cpts : {array}
        An array of all root node dofs, corresponding to the F/C splitting
    Fpts : {array}
        An array of all non root node dofs, corresponding to the F/C splitting

    Examples
    --------
    >>> from numpy import array
    >>> from pyamg.util.utils import get_Cpt_params
    >>> from pyamg.gallery import poisson
    >>> from scipy.sparse import csr_matrix, bsr_matrix
    >>> A = poisson((10,), format='csr')
    >>> Cpts = array([3, 7])
    >>> AggOp = ([[ 1., 0.], [ 1., 0.],
    ...           [ 1., 0.], [ 1., 0.],
    ...           [ 1., 0.], [ 0., 1.],
    ...           [ 0., 1.], [ 0., 1.],
    ...           [ 0., 1.], [ 0., 1.]])
    >>> AggOp = csr_matrix(AggOp)
    >>> T = AggOp.copy().tobsr()
    >>> params = get_Cpt_params(A, Cpts, AggOp, T)
    >>> params['P_I'].toarray()
    array([[0., 0.],
           [0., 0.],
           [0., 0.],
           [1., 0.],
           [0., 0.],
           [0., 0.],
           [0., 0.],
           [0., 1.],
           [0., 0.],
           [0., 0.]])

    Notes
    -----
    The principal calling routine is
    aggregation.smooth.energy_prolongation_smoother,
    which uses the Cpt_param dictionary for root-node style
    prolongation smoothing

    zExpected BSR or CSR matrix AzExpected CSR matrix AggOpr  r   r   z*Expected square blocksize for BSR matrix TzExpected square matrix Az[Expected compatible dimensions for T and A,                         T.shape[0] = A.shape[0]zRNumber of columns in AggOp must equal number                             of CnodesrB   z+Expected number of Cpts to match T.shape[1]z'Expected identical blocksize in A and TzUNumber of rows in AggOp must equal number of                          fine-grid nodescsrrJ   rz   r   r   )r   r   r   )r  r  I_CCptsFpts)r   r   r   r   r0   r%   repeatrf   listrp   rH   r<   r   rD   rE   r   rG   r  r=   r   rC   rF   r	   r   )r   CnodesAggOpr|   r   r  kncoarser  r  r  rG   rF   r  s                 r   get_Cpt_paramsr  P  s+   R ! 8^A%6%6 86777%   53444! 1/000{1~Q''DEEEwqzQWQZ2333wqzQWQZ 2 3 3 	3|A%+a.((;q>A ( ) ) ) a Q[^a//KN	y6)955q)$$ 	@ 	@AeAtz!}i8899:::a?::::	@ 	8D$$$D z!}
""71:	!!JKKKAKN""BCCC{1~QWQZ	12222 + , , 	, gajG
agaj!'!*U
3
3
3C
((**CCHTN
)C ;D
 w{{))++7=+A-..(4qy777719,ahn===
chmmoow7IaL'24 4 4C
))AK
 
 C a *ii$$ii$$ii&i))ii&i))s3dKKKr   c                    t          |          st          |          st          d          |j        d         | j        d         k    rt          d          t          |          r|j        d         }|j        d         }nd}d}|j        d         }|j        d         }| j        d         }t          ||z            }t          j        |||f| j                  }t          t          |dz                       }	t          j        ||	f| j                  }
d}t          |          D ]}t          ||          D ]}t          j        t          j        t          j        | dd|f                                       t          j        t          j        | dd|f                             z  |
dd|f<   |dz   }t          j        |||t          j        t          j        |
                    |	t          j        t          j        |                    |j        |j                   |                    d                                          }t)          j        |           |S )a  Create block inverses.

    Helper function that creates inv(B_i.T B_i) for each block row i in C,
    where B_i is B restricted to the sparsity pattern of block row i.

    Parameters
    ----------
    B : {array}
        (M,k) array, typically near-nullspace modes for coarse grid, i.e., B_c.
    C : {csr_matrix, bsr_matrix}
        Sparse NxM matrix, whose sparsity structure (i.e., matrix graph)
        is used to determine BtBinv.

    Returns
    -------
    BtBinv : {array}
        BtBinv[i] = inv(B_i.T B_i), where B_i is B restricted to the nonzero
        pattern of block row i in C.

    Examples
    --------
    >>> from numpy import array
    >>> from scipy.sparse import bsr_matrix
    >>> from pyamg.util.utils import compute_BtBinv
    >>> T = array([[ 1.,  0.],
    ...            [ 1.,  0.],
    ...            [ 0.,  .5],
    ...            [ 0.,  .25]])
    >>> T = bsr_matrix(T)
    >>> B = array([[1.],[2.]])
    >>> compute_BtBinv(B, T)
    array([[[1.  ]],
    <BLANKLINE>
           [[1.  ]],
    <BLANKLINE>
           [[0.25]],
    <BLANKLINE>
           [[0.25]]])

    Notes
    -----
    The principal calling routines are
    aggregation.smooth.energy_prolongation_smoother, and
    util.utils.filter_operator.

    BtBinv is used in the prolongation smoothing process that incorporates B
    into the span of prolongation with row-wise projection operators.  It is
    these projection operators that BtBinv is part of.

    z'Expected bsr_matrix or csr_matrix for Cr   r   z*Expected matching dimensions such that C*BrB   N)r   r   r   )r   r   r   r0   r   rH   r%   r   rC   r   rf   r~   r&   r4   r   calc_BtBrF   rG   	transposerD   r   r   )rh   rQ   r   r   Ncoarser  r	  r  r  BsqColsBsqcounterrk   r   s                 r   r   r     sF   f ! C^A%6%6 CABBBwqzQWQZDEEE a QQgajGGAJEgajG~%&&F Xvw0@@@F%	""##G
(GW%QW
5
5
5CG7^^ " "q'"" 	" 	"A l28BJqAw4G4G+H+HIIAaaadG,,--.C7
OkGG	"
 gv~hrz#//rx
6(:(:;;h	+ + + i((--//F
fMr   RQ?c                    |                                  }t          j        |j                  |_        t	          |dd          }|||t          j        |j        d         f|          z  |z
  z  k    }t          |          }|dk    rMt          j        |t                    }|
                    d|          }t          j        |d          }||k    }t          |j        d         |j        d         d	          }d
|j        |<   ||z  |z  }d|j        |<   d
|j        t          j        |dk              d         <   ||z   }~|S )a  Eliminate diagonally dominance.

    Helper function that eliminates diagonally dominant rows and cols from A
    in the separate matrix C.  This is useful because it eliminates nodes in C
    which we don't want coarsened.  These eliminated nodes in C just become
    the rows and columns of the identity.

    Parameters
    ----------
    A : {csr_matrix, bsr_matrix}
        Sparse NxN matrix
    C : {csr_matrix}
        Sparse MxM matrix, where M is the number of nodes in A.  M=N if A
        is CSR or is BSR with blocksize 1.  Otherwise M = N/blocksize.
    theta : {float}
        determines diagonal dominance threshold

    Returns
    -------
    C : {csr_matrix}
        C updated such that the rows and columns corresponding to diagonally
        dominant rows in A have been eliminated and replaced with rows and
        columns of the identity.

    Notes
    -----
    Diagonal dominance is defined as
    :math:`\| (e_i, A) - a_{ii} \|_1  <  \\theta a_{ii}`
    that is, the 1-norm of the off diagonal elements in row i must be less than
    theta times the diagonal element.

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import eliminate_diag_dom_nodes
    >>> A = poisson( (4,), format='csr' )
    >>> C = eliminate_diag_dom_nodes(A, A.copy(), 1.1)
    >>> C.toarray()
    array([[ 1.,  0.,  0.,  0.],
           [ 0.,  2., -1.,  0.],
           [ 0., -1.,  2.,  0.],
           [ 0.,  0.,  0.,  1.]])

    r   F)r   r   rB   r   r   )axisr  r  rz   rW   )rD   r%   r[   rE   r   r   r0   r   rp   rH   r   r   r   where)r   rQ   thetaA_absD_absdiag_dom_rowsbsizeIds           r   eliminate_diag_dom_nodesr0  C  sX   \ FFHHE
##EJu555EeU27EKN3D9>,@ ,@ ,@ &@BG&H I JM %  Eqyyc:::%--b%88}1555%. 
QWQZE	2	2	2B BGM
QA BGM/2BGBH]a'((+,	BAHr   c                 v   t          |           st          d          | j        d         | j        d         k    rt          d| j                   t	          |           } | j        | j        k    }| j        |         | _        | j        |         | _        | j        |         | _        |                                 S )a  Remove the diagonal of the matrix S.

    Parameters
    ----------
    S : csr_matrix
        Square matrix

    Returns
    -------
    S : csr_matrix
        Strength matrix with the diagonal removed

    Notes
    -----
    This is needed by all the splitting routines which operate on matrix graphs
    with an assumed zero diagonal


    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import remove_diagonal
    >>> A = poisson( (4,), format='csr' )
    >>> C = remove_diagonal(A)
    >>> C.toarray()
    array([[ 0., -1.,  0.,  0.],
           [-1.,  0., -1.,  0.],
           [ 0., -1.,  0., -1.],
           [ 0.,  0., -1.,  0.]])

    expected csr_matrixr   r   zexpected square matrix, shape=)	r   r   r0   r<   r   r   r  rE   r   )Sr_   s     r   remove_diagonalr4    s    @ ! /-...wqzQWQZC!'CCDDD1A5AE>DE$KAEE$KAEVD\AF7799r   c                 D   t          |           st          d          t          j        | j        d         f| j                  }t          j        | j        d         || j        | j	        | j
                   d||dk             z  ||dk    <   t          | |d          } | S )a{  Scale each row in S by it's largest in magnitude entry.

    Parameters
    ----------
    S : csr_matrix

    Returns
    -------
    S : csr_matrix
        Each row has been scaled by it's largest in magnitude entry

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import scale_rows_by_largest_entry
    >>> A = poisson( (4,), format='csr' )
    >>> A.data[1] = 5.0
    >>> A = scale_rows_by_largest_entry(A)
    >>> A.toarray()
    array([[ 0.4,  1. ,  0. ,  0. ],
           [-0.5,  1. , -0.5,  0. ],
           [ 0. , -0.5,  1. , -0.5],
           [ 0. ,  0. , -0.5,  1. ]])

    r2  r   rB   rW   TrX   )r   r   r%   r   r0   rC   r   maximum_row_valuerF   rG   rE   rK   )r3  largest_row_entrys     r   scale_rows_by_largest_entryr8    s    4 ! /-... !'!*ag>>>qwqz+< xAF< < < 	 1Q 677 '1,-1'd333AHr   c                     t           t                    r5 d         dk    r	 g d}d}nJ fdt          |dz
            D              n*t           t                    r4 dk    rt	          d           fdt          |dz
            D              nt           t
                    rt           d         t                    r' d         d         dk    rt                     dz   }d}nt                     |dz
  k     rE|dz
  t                     z
  } fd	t          |          D             }                     |           n. d t          |dz
            D              nt	          d          || fS )a]  Turn parameter into a list per level.

    Helper function to preprocess the strength and aggregation parameters
    passed to smoothed_aggregation_solver and rootnode_solver.

    Parameters
    ----------
    to_levelize : {string, tuple, list}
        Parameter to preprocess, i.e., levelize and convert to a level-by-level
        list such that entry i specifies the parameter at level i
    max_levels : int
        Defines the maximum number of levels considered
    max_coarse : int
        Defines the maximum coarse grid size allowed

    Returns
    -------
    (max_levels, max_coarse, to_levelize) : tuple
        New max_levels and max_coarse values and then the parameter list
        to_levelize, such that entry i specifies the parameter choice at level
        i.  max_levels and max_coarse are returned, because they may be updated
        if strength or aggregation set a predefined coarsening and possibly
        change these values.

    Notes
    -----
    This routine is needed because the user will pass in a parameter option
    such as smooth='jacobi', or smooth=['jacobi', None], and this option must
    be "levelized", or converted to a list of length max_levels such that entry
    [i] in that list is the parameter choice for level i.

    The parameter choice in to_levelize can be a string, tuple or list.  If
    it is a string or tuple, then that option is assumed to be the
    parameter setting at every level.  If to_levelize is inititally a list,
    if the length of the list is less than max_levels, the last entry in the
    list defines that parameter for all subsequent levels.


    Examples
    --------
    >>> from pyamg.util.utils import levelize_strength_or_aggregation
    >>> strength = ['evolution', 'classical']
    >>> levelize_strength_or_aggregation(strength, 4, 10)
    (4, 10, ['evolution', 'classical', 'classical'])

    r   
predefinedr   c                     g | ]}S  r<  .0rk   to_levelizes     r   
<listcomp>z4levelize_strength_or_aggregation.<locals>.<listcomp>  s    DDD1;DDDr   r   zpredefined to_levelize requires a user-provided CSR matrix representing strength or aggregation i.e., ("predefined", {"C" : CSR_MAT}).c                     g | ]}S r<  r<  r=  s     r   r@  z4levelize_strength_or_aggregation.<locals>.<listcomp>  s    @@@q{@@@r   r   c                      g | ]
}d          S r   r<  r=  s     r   r@  z4levelize_strength_or_aggregation.<locals>.<listcomp>+  s    ===QR===r   Nc                     g | ]}d i fS r"   r<  r>  rk   s     r   r@  z4levelize_strength_or_aggregation.<locals>.<listcomp>/  s    ???abz???r   zinvalid to_levelize)r   r   rf   r   r<   r  r>   extend)r?  
max_levels
max_coarsemlztoexts   `    r    levelize_strength_or_aggregationrK    s   ^ +u%% 0q>\))&-KJJJDDDDjl0C0CDDDKK	K	%	% 0,&& F G G G A@@@E*Q,,?,?@@@	K	&	& 0k"ou-- 
	*OA,..[))A-JJJ ;*Q,.. 1ns;'7'77====%**===""5)))		??5A+>+>???.///z;..r   c                     t           t                    r*t           d         t                    rt                      t           t          t          f          r fdt	          |          D              nt           t                    rVt                     |k     rB|t                     z
  } fdt	          |          D             }                     |           n d t	          |          D               S )a  Turn parameter in to a list per level.

    Helper function to preprocess the smooth and improve_candidates
    parameters passed to smoothed_aggregation_solver and rootnode_solver.

    Parameters
    ----------
    to_levelize : {string, tuple, list}
        Parameter to preprocess, i.e., levelize and convert to a level-by-level
        list such that entry i specifies the parameter at level i
    max_levels : int
        Defines the maximum number of levels considered

    Returns
    -------
    to_levelize : list
        The parameter list such that entry i specifies the parameter choice
        at level i.

    Notes
    -----
    This routine is needed because the user will pass in a parameter option
    such as smooth='jacobi', or smooth=['jacobi', None], and this option must
    be "levelized", or converted to a list of length max_levels such that entry
    [i] in that list is the parameter choice for level i.

    The parameter choice in to_levelize can be a string, tuple or list.  If
    it is a string or tuple, then that option is assumed to be the
    parameter setting at every level.  If to_levelize is inititally a list,
    if the length of the list is less than max_levels, the last entry in the
    list defines that parameter for all subsequent levels.

    Examples
    --------
    >>> from pyamg.util.utils import levelize_smooth_or_improve_candidates
    >>> improve_candidates = ['gauss_seidel', None]
    >>> levelize_smooth_or_improve_candidates(improve_candidates, 4)
    ['gauss_seidel', None, None, None]

    r   c                     g | ]}S r<  r<  r=  s     r   r@  z9levelize_smooth_or_improve_candidates.<locals>.<listcomp>i  s    >>>q{>>>r   c                      g | ]
}d          S rC  r<  r=  s     r   r@  z9levelize_smooth_or_improve_candidates.<locals>.<listcomp>m  s    999[_999r   Nc                     g | ]}d i fS r"   r<  rE  s     r   r@  z9levelize_smooth_or_improve_candidates.<locals>.<listcomp>p  s    ===abz===r   )r   r   r  r   rf   r>   rF  )r?  rG  rI  rJ  s   `   r   %levelize_smooth_or_improve_candidatesrP  6  s   \ +u%% ,k!ne,, 	,{++K+U|,, >>>>>E*,=,=>>>	K	&	& >{j((s;///C9999eCjj999Eu%%%		==5+<+<===r   c           
      x   t          |           st          d          d}t          |           r| j        }| j        }|dk     s|dk    rt          d          |                                                                 } |                                 }| xj        | j        d         z  c_        |xj        | j        d         z  c_        t          j
        | j        d         || j        | j        | j        |j        |j        |j                   |j        d|j        d         xx         |j        d         z  cc<   t          |j        d|j        d                  |j        d|j        d                  |j        f|j                  }~ |d	k    r|                    |          }n|                    |          }|S )
a  Filter each column of A with tol.

    i.e., drop all entries in column k where
        abs(A[i,k]) < tol max( abs(A[:,k]) )

    Parameters
    ----------
    A : sparse_matrix

    theta : float
        In range [0,1) and defines drop-tolerance used to filter the columns
        of A

    Returns
    -------
    A_filter : sparse_matrix
        Each column has been filtered by dropping all entries where
        abs(A[i,k]) < tol max( abs(A[:,k]) )

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import filter_matrix_columns
    >>> from numpy import array
    >>> from scipy.sparse import csr_matrix
    >>> A = csr_matrix( array([[ 0.24,  1.  ,  0.  ],
    ...                        [-0.5 ,  1.  , -0.5 ],
    ...                        [ 0.  ,  0.49,  1.  ],
    ...                        [ 0.  ,  0.  , -0.5 ]]) )
    >>> filter_matrix_columns(A, 0.5).toarray()
    array([[ 0. ,  1. ,  0. ],
           [-0.5,  1. , -0.5],
           [ 0. ,  0. ,  1. ],
           [ 0. ,  0. , -0.5]])

    Sparse matrix input neededr   r   rW   theta must be in [0,1)Nr   r   bsr)r   r<   r   r   rJ   rD   r   rG   r0   r   $classical_strength_of_connection_absrF   rE   r	   r   rL   )r   r*  r   AformatA_filters        r   filter_matrix_columnsrX  u  s   J a== 75666Ia  K	hG		u||1222 	
AvvxxHIIII
"1!'!*2723(23)23&2:/2:2B2:-A A A )hob))***hnQ.??***8=)=(/"*=)=>#+,@X_R-@,@A#?,3;>C C CH 	
%>>),,$$W--Or   c           
      V   t          |           st          d          d}t          |           r| j        }| j        }|                                 } |dk     s|dk    rt          d          |ret          j        | j        d         || j	        | j
        | j        |           |                                  |dk    r|                     |          } dS |                                 }| xj
        | j        d         z  c_
        |xj
        | j        d         z  c_
        t          j        | j        d         || j	        | j
        | j        |j	        |j
        |j                   |j
        d|j	        d	         xx         |j        d         z  cc<   t!          |j        d|j	        d	                  |j
        d|j	        d	                  |j	        f|j        
          }|dk    r|                    |          }n|                    |          }| xj
        | j        d         z  c_
        |S )a  Filter each row of A with tol.

    i.e., drop all entries in row k where
        abs(A[i,k]) < tol max( abs(A[:,k]) )

    Parameters
    ----------
    A : sparse_matrix

    theta : float
        In range [0,1) and defines drop-tolerance used to filter the row of A
    diagonal : bool
        If True, filter by diagonal entry. Otherwise, filter by maximum absolute
        value in row.  This is in place.
    lump : bool
        If True, instead of removing entries, lump them to diagonal. Preserves
        row sum of matrix.

    Returns
    -------
    A_filter : sparse_matrix
        Each row has been filtered by dropping all entries where
        abs(A[i,k]) < tol max( abs(A[:,k]) )
        If `diagonal == True`, then no return (None).

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import filter_matrix_rows
    >>> from numpy import array
    >>> from scipy.sparse import csr_matrix
    >>> A = csr_matrix( array([[ 0.24, -0.5 ,  0.  ,  0.  ],
    ...                        [ 1.  ,  1.  ,  0.49,  0.  ],
    ...                        [ 0.  , -0.5 ,  1.  , -0.5 ]])  )
    >>> filter_matrix_rows(A, 0.5).toarray()
    array([[ 0. , -0.5,  0. ,  0. ],
           [ 1. ,  1. ,  0. ,  0. ],
           [ 0. , -0.5,  1. , -0.5]])

    rR  r   r   rW   rS  rT  r   Nr   r   )r   r<   r   r   rJ   r   r   filter_matrix_rowsr0   rF   rG   rE   r   r   rD   rU  r   rL   )r   r*  r:   lumpr   rV  rW  s          r   rZ  rZ    s9   R a== 75666Ia  K	hG			A		u||1222  #AGAJqx$%Iqvt	= 	= 	=	e),,At vvxxHIIII
" 1!'!*2723(23)23&2:/2:2B2:-A A A )hob))***hnQ.??***8=)=(/"*=)=>#+,@X_R-@,@A#?,3;>C C CH %>>),,$$W--IIIIOr   c                    t          |           st          d          d}t          |           r| j        }t	          |           r|                                 } | j        }|                                 } t          |          }t          j
        | j        d         || j        | j        | j                   |                                  |dk    r|                     |          } n|                     |          } | S )ao  Truncate the rows of A by keeping only the largest in magnitude entries in each row.

    Parameters
    ----------
    A : sparse_matrix

    nz_per_row : int
        Determines how many entries in each row to keep

    Returns
    -------
    A : sparse_matrix
        Each row has been truncated to at most nz_per_row entries

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import truncate_rows
    >>> from numpy import array
    >>> from scipy.sparse import csr_matrix
    >>> A = csr_matrix( array([[-0.24, -0.5 ,  0.  ,  0.  ],
    ...                        [ 1.  , -1.1 ,  0.49,  0.1 ],
    ...                        [ 0.  ,  0.4 ,  1.  ,  0.5 ]])  )
    >>> truncate_rows(A, 2).toarray()
    array([[-0.24, -0.5 ,  0.  ,  0.  ],
           [ 1.  , -1.1 ,  0.  ,  0.  ],
           [ 0.  ,  0.  ,  1.  ,  0.5 ]])

    rR  r   r   rT  )r   r<   r   r   r   rD   rJ   r   rH   r   truncate_rows_csrr0   rF   rG   rE   r   r   rL   )r   
nz_per_rowr   rV  s       r   truncate_rowsr_     s    < a== 75666Ia  K	a FFHHhG			AZJ qwqz:qx y!&2 2 2 %GGIJJwHr   c           	      d   t          |           st          d          | j        d         | j        d         k    rt          d          t	          j        | j        d         |          dk    rt          d          t          |           st          | ||f          } | j        ||fk    r| 	                    ||f          } | j        d         |z  }t          | |d          }t          |t	          j        d|          t	          j        d|dz             f||g| j        	          }|| z  |fS )
a1  Get inverse of block diagonal of A and scale A, A = D^{-1}A.

    Parameters
    ----------
    A : csr or bsr_matrix
        Matrix to scale by block inverse.
    blocksize : int
        Blocksize of matrix.

    Returns
    -------
    tuple, (D^{-1}*A, D^{-1})

    Notes
    -----
    This is not a symmetric scaling.

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.util.utils import scale_block_inverse
    >>> A = poisson((4,), format='csr')
    >>> A, Dinv = scale_block_inverse(A, 2)
    >>> A.toarray()
    array([[ 1.        ,  0.        , -0.33333333,  0.        ],
           [ 0.        ,  1.        , -0.66666667,  0.        ],
           [ 0.        , -0.66666667,  1.        ,  0.        ],
           [ 0.        , -0.33333333,  0.        ,  1.        ]])
    r   r   r   r   r   r   T)r   r   r   )r   r0   )r   r   r0   r<   r%   r   r   r
   r   r   r   r=   )r   r   N_blockr   scales        r   scale_block_inverserc  V  sC   < a== 20111wqzQWQZ1222	vagaj)$$))CDDD ! <qY	$:;;;{y),,,GGy)4G55 gaj9$GATBBBDbi733RYq'!)5L5LM"+Y!7qwH H HE19er   r"   )T)FF)r   r   r   r   Tr   )r&  ):__doc__warningsr   numpyr%   scipy.sparser   r   r   r   r   r	   r
   r   r   scipy.linalgr   scipy.sparse._sparsetoolsr   r   r   r   !scipy.sparse.linalg._isolve.utilsr   scipy.sparse._sputilsr   r   r   r   r   r8   r?   rK   rU   r]   rl   ru   rn   r   r   r   r   r   r   r   r  r  r  r   r0  r4  r8  rK  rP  rX  rZ  r_  rc  r<  r   r   <module>rl     s   * *          H H H H H H H H H H H H H H H H H H H H H H            M M M M M M M M M M M M : 9 9 9 9 9 ( ( ( ( ( (              /! /! /! /!d&? &? &?RF F F FRK K K K\H. H. H. H.VH H H HV+ + +\. . .b< < < <~Y Y Y Yx3) 3) 3)l0+ 0+ 0+f MN(+n n n nbU U U Ups s slY Y Y Yxj j jZRL RL RLj[ [ [|E E E EP, , ,^& & &RP/ P/ P/f< < <~L L L^Y Y Y Yx3 3 3l0 0 0 0 0r   