sparse 3d matrix/array in Python?

后端 未结 6 1914
借酒劲吻你
借酒劲吻你 2020-12-02 15:13

In scipy, we can construct a sparse matrix using scipy.sparse.lil_matrix() etc. But the matrix is in 2d.

I am wondering if there is an existing data structure for sp

6条回答
  •  感动是毒
    2020-12-02 15:55

    I also need 3D sparse matrix for solving the 2D heat equations (2 spatial dimensions are dense, but the time dimension is diagonal plus and minus one offdiagonal.) I found this link to guide me. The trick is to create an array Number that maps the 2D sparse matrix to a 1D linear vector. Then build the 2D matrix by building a list of data and indices. Later the Number matrix is used to arrange the answer back to a 2D array.

    [edit] It occurred to me after my initial post, this could be handled better by using the .reshape(-1) method. After research, the reshape method is better than flatten because it returns a new view into the original array, but flatten copies the array. The code uses the original Number array. I will try to update later.[end edit]

    I test it by creating a 1D random vector and solving for a second vector. Then multiply it by the sparse 2D matrix and I get the same result.

    Note: I repeat this many times in a loop with exactly the same matrix M, so you might think it would be more efficient to solve for inverse(M). But the inverse of M is not sparse, so I think (but have not tested) using spsolve is a better solution. "Best" probably depends on how large the matrix is you are using.

    #!/usr/bin/env python3
    # testSparse.py
    # profhuster
    
    import numpy as np
    import scipy.sparse as sM
    import scipy.sparse.linalg as spLA
    from array import array
    from numpy.random import rand, seed
    seed(101520)
    
    nX = 4
    nY = 3
    r = 0.1
    
    def loadSpNodes(nX, nY, r):
        # Matrix to map 2D array of nodes to 1D array
        Number = np.zeros((nY, nX), dtype=int)
    
        # Map each element of the 2D array to a 1D array
        iM = 0
        for i in range(nX):
            for j in range(nY):
                Number[j, i] = iM
                iM += 1
        print(f"Number = \n{Number}")
    
        # Now create a sparse matrix of the "stencil"
        diagVal = 1 + 4 * r
        offVal = -r
        d_list = array('f')
        i_list = array('i')
        j_list = array('i')
        # Loop over the 2D nodes matrix
        for i in range(nX):
            for j in range(nY):
                # Recall the 1D number
                iSparse = Number[j, i]
                # populate the diagonal
                d_list.append(diagVal)
                i_list.append(iSparse)
                j_list.append(iSparse)
                # Now, for each rectangular neighbor, add the 
                # off-diagonal entries
                # Use a try-except, so boundry nodes work
                for (jj,ii) in ((j+1,i),(j-1,i),(j,i+1),(j,i-1)):
                    try:
                        iNeigh = Number[jj, ii]
                        if jj >= 0 and ii >=0:
                            d_list.append(offVal)
                            i_list.append(iSparse)
                            j_list.append(iNeigh)
                    except IndexError:
                        pass
        spNodes = sM.coo_matrix((d_list, (i_list, j_list)), shape=(nX*nY,nX*nY))
        return spNodes
    
    
    MySpNodes = loadSpNodes(nX, nY, r)
    print(f"Sparse Nodes = \n{MySpNodes.toarray()}")
    b = rand(nX*nY)
    print(f"b=\n{b}")
    x = spLA.spsolve(MySpNodes.tocsr(), b)
    print(f"x=\n{x}")
    print(f"Multiply back together=\n{x * MySpNodes}")
    

提交回复
热议问题