What is the fastest way to breed with an extremely sparse matrix?

I have a very rare structured matrix. My matrix has exactly one nonzero entry per column. But its huge (10k * 1M) and given in the following form (for example, random uisng values)

rows = np.random.randint(0, 10000, 1000000)
values = np.random.randint(0,10,1000000)

where the rows give us the row number for a nonzero entry in each column. I want fast matrix multiplication with S, and now I do the following: I convert this form to a sparse matrix (S) and make S.dot (X) to multiply with a matrix X (which can be sparse or dense).

S=scipy.sparse.csr_matrix( (values, (rows, scipy.arange(1000000))), shape = (10000,1000000))

For an X of size 1M * 2500 and nnz (X) = 8M, it takes 178 ms to create S and 255 ms for its application. So my question is what is the best way to make SX (where X can be rare or dense), given that my S is described like this. Since creating an S in itself is very time consuming, I was thinking about something crazy. I tried to create something using loops, but it didn't even close. A simple loop procedure looks something like this:

SX = np.zeros((rows.size,X.shape[1])) for i in range(X.shape[0]): SX[rows[i],:]+=values[i]*X[i,:] return SX
Can we make it effective?

Any suggestions are welcome. Thanks

+3
source share
3 answers

Inspired by this post The fastest way to sum over rows of a sparse matrix , I found that the best way to do this is to write loops and ports in numba. Here

`

@njit
def sparse_mul(SX,row,col,data,values,row_map):
    N = len(data)
    for idx in range(N):
        SX[row_map[row[idx]],col[idx]]+=data[idx]*values[row[idx]]
    return SX
X_coo=X.tocoo()
s=row_map.max()+1
SX = np.zeros((s,X.shape[1]))
sparse_mul(SX,X_coo.row,X_coo.col,X_coo.data,values,row_map)`

row_map - . X (1M * 1K), 1% s = 10K , row_map S.dot(A).

+1

№ 1

, , np.bincount - rows, values X , , S -

def sparse_matrix_mult(rows, values, X):
    nrows = rows.max()+1
    ncols = X.shape[1]
    nelem = nrows * ncols

    ids = rows + nrows*np.arange(ncols)[:,None]
    sums = np.bincount(ids.ravel(), (X.T*values).ravel(), minlength=nelem)
    out = sums.reshape(ncols,-1).T
    return out

-

In [746]: import numpy as np
     ...: from scipy.sparse import csr_matrix
     ...: import scipy as sp
     ...: 

In [747]: np.random.seed(1234)
     ...: m,n = 3,4
     ...: rows = np.random.randint(0, m, n)
     ...: values = np.random.randint(2,10,n)
     ...: X = np.random.randint(2, 10, (n,5))
     ...: 

In [748]: S = csr_matrix( (values, (rows, sp.arange(n))), shape = (m,n))

In [749]: S.dot(X)
Out[749]: 
array([[42, 27, 45, 78, 87],
       [24, 18, 18, 12, 24],
       [18,  6,  8, 16, 10]])

In [750]: sparse_matrix_mult(rows, values, X)
Out[750]: 
array([[ 42.,  27.,  45.,  78.,  87.],
       [ 24.,  18.,  18.,  12.,  24.],
       [ 18.,   6.,   8.,  16.,  10.]])

# 2

np.add.reduceat np.bincount -

def sparse_matrix_mult_v2(rows, values, X):
    nrows = rows.max()+1
    ncols = X.shape[1]

    scaled_ar = X*values[:,None]
    sidx = rows.argsort()
    rows_s = rows[sidx]
    cut_idx = np.concatenate(([0],np.flatnonzero(rows_s[1:] != rows_s[:-1])+1))
    sums = np.add.reduceat(scaled_ar[sidx],cut_idx,axis=0)

    out = np.empty((nrows, ncols),dtype=sums.dtype)
    row_idx = rows_s[cut_idx]
    out[row_idx] = sums
    return out

, , . , , -

In [149]: m,n = 1000, 100000
     ...: rows = np.random.randint(0, m, n)
     ...: values = np.random.randint(2,10,n)
     ...: X = np.random.randint(2, 10, (n,2500))
     ...: 

In [150]: S = csr_matrix( (values, (rows, sp.arange(n))), shape = (m,n))

In [151]: %timeit csr_matrix( (values, (rows, sp.arange(n))), shape = (m,n))
100 loops, best of 3: 16.1 ms per loop

In [152]: %timeit S.dot(X)
1 loop, best of 3: 193 ms per loop

In [153]: %timeit sparse_matrix_mult(rows, values, X)
1 loop, best of 3: 4.4 s per loop

In [154]: %timeit sparse_matrix_mult_v2(rows, values, X)
1 loop, best of 3: 2.81 s per loop

, numpy.dot , .


X

X , -

from scipy.sparse import find
def sparse_matrix_mult_sparseX(rows, values, Xs): # Xs is sparse    
    nrows = rows.max()+1
    ncols = Xs.shape[1]
    nelem = nrows * ncols

    scaled_vals = Xs.multiply(values[:,None])
    r,c,v = find(scaled_vals)
    ids = rows[r] + c*nrows
    sums = np.bincount(ids, v, minlength=nelem)
    out = sums.reshape(ncols,-1).T
    return out
+3

As I recall, Knuth TAOP talks about representing a sparse matrix instead as a linked list (for your application) of non-zero values. Maybe something like that? Then move the linked list, not the entire array across each dimension.

0
source

Source: https://habr.com/ru/post/1686572/


All Articles