I am trying to speedup some sparse matrix-matrix multiplications in Python using Numba and it's JIT compiler. Unfortunately it doesn't support the SciPy library as I need it.
My solution is to translate the functions csr_matmat_pass1() and csr_matmat_pass2() from here into Python code. My code seems to work for matrices smaller than ~80x80 and delivers correct results. Here's my solution:
import scipy.sparse as sparse
import numpy as np
def csr_matmat_pass1(n_row, n_col, Ap, Aj, Bp, Bj):
mask = np.ones(n_col, dtype="int") * -1
Cp = np.zeros(n_row+1, dtype="int")
nnz = 0
for i in range(n_row):
row_nnz = 0
for jj in range(Ap[i],Ap[i+1]):
j = Aj[jj]
for kk in range(Bp[j],Bp[j+1]):
k = Bj[kk]
if(mask[k] != i):
mask[k] = i
row_nnz += 1
next_nnz = nnz + row_nnz;
nnz = next_nnz;
Cp[i+1] = nnz;
return Cp
def csr_matmat_pass2(n_row, n_col, Ap, Aj, Ax, Bp, Bj, Bx, Cp):
nextV = np.ones(n_col, dtype="int") * -1
sums = np.zeros(n_col)
nnz = 0
Cp[0] = 0
#preallocate space
sizeC = max(len(Ax),len(Bx))
Cj = np.zeros(sizeC, dtype="int")
Cx = np.zeros(sizeC)
for i in range(n_row):
head = -2
length = 0
jj_start = Ap[i]
jj_end = Ap[i+1]
for jj in range(jj_start,jj_end):
j = Aj[jj]
v = Ax[jj]
kk_start = Bp[j]
kk_end = Bp[j+1]
for kk in range(kk_start,kk_end):
k = Bj[kk]
sums[k] += v*Bx[kk]
if(nextV[k] == -1):
nextV[k] = head
head = k
length += 1
for jj in range(length):
if(sums[head] != 0.0):
Cj[nnz] = head
Cx[nnz] = sums[head]
nnz += 1
temp = head
head = nextV[head]
nextV[temp] = -1
sums[temp] = 0
Cp[i+1] = nnz
return Cp, Cj, Cx
#calculate random sparse matrices A,B
mSize = 50
A = sparse.random(mSize, mSize, 0.01).tocsr()
B = sparse.random(mSize, mSize, 0.01).tocsr()
#calculate sparse C
Cp = csr_matmat_pass1(np.shape(A)[0], np.shape(B)[1], A.indptr, A.indices, B.indptr, B.indices)
Cp, Cj, Cx = csr_matmat_pass2(np.shape(A)[0], np.shape(B)[1], A.indptr, A.indices, A.data, B.indptr, B.indices, B.data, Cp)
#generate numpy sparse matrix from Cx, Cj, Cp
C = sparse.csr_matrix((Cx,Cj,Cp),shape=(nRow,nCol))
diffC = A.dot(B) - C
#validate function -> check if any nonzero element is present. If so -> calc is wrong
if np.any(diffC.todense()): UserWarning('Calculations are wrong')
When increasing the size of the matrices (lets say mSize=100) I get the following error:
line 168, in csr_matmat_pass2 Cj[nnz] = head
IndexError: index 72 is out of bounds for axis 0 with size 72
I assume the error is in my python translation rather than in the C++ code (since it is from the scipy library). Also Cp has greater entries than the size of the matrices A, B. For that reason there must be an error in the translation of csr_matmat_pass1(). Unfortunately I cannot find any syntax errors and don't know why nnz gets bigger than it should.