How to compute scipy sparse matrix determinant without turning it to dense?

前端 未结 3 2034
挽巷
挽巷 2021-02-01 17:19

I am trying to figure out the fastest method to find the determinant of sparse symmetric and real matrices in python. using scipy sparse module but really surprised

3条回答
  •  南笙
    南笙 (楼主)
    2021-02-01 18:24

    You can use scipy.sparse.linalg.splu to obtain sparse matrices for the lower (L) and upper (U) triangular matrices of an M=LU decomposition:

    from scipy.sparse.linalg import splu
    
    lu = splu(M)
    

    The determinant det(M) can be then represented as:

    det(M) = det(LU) = det(L)det(U)
    

    The determinant of triangular matrices is just the product of the diagonal terms:

    diagL = lu.L.diagonal()
    diagU = lu.U.diagonal()
    d = diagL.prod()*diagU.prod()
    

    However, for large matrices underflow or overflow commonly occurs, which can be avoided by working with the logarithms.

    diagL = diagL.astype(np.complex128)
    diagU = diagU.astype(np.complex128)
    logdet = np.log(diagL).sum() + np.log(diagU).sum()
    

    Note that I invoke complex arithmetic to account for negative numbers that might appear in the diagonals. Now, from logdet you can recover the determinant:

    det = np.exp(logdet) # usually underflows/overflows for large matrices
    

    whereas the sign of the determinant can be calculated directly from diagL and diagU (important for example when implementing Crisfield's arc-length method):

    sign = swap_sign*np.sign(diagL).prod()*np.sign(diagU).prod()
    

    where swap_sign is a term to consider the number of permutations in the LU decomposition. Thanks to @Luiz Felippe Rodrigues, it can be calculated:

    swap_sign = minimumSwaps(lu.perm_r)
    
    def minimumSwaps(arr): 
        """
        Minimum number of swaps needed to order a
        permutation array
        """
        # from https://www.thepoorcoder.com/hackerrank-minimum-swaps-2-solution/
        a = dict(enumerate(arr))
        b = {v:k for k,v in a.items()}
        count = 0
        for i in a:
            x = a[i]
            if x!=i:
                y = b[i]
                a[y] = x
                b[x] = y
                count+=1
        return count
    

提交回复
热议问题