A Gram matrix is a matrix of the structure X @ X.T
which of course is symmetrical. When dealing with dense matrices, the numpy.dot
product implementation is intelligent enough to recognize the self-multiplication to exploit the symmetry and thus speed up the computations (see this). However, no such effect can be observed when using scipy.sparse
matrices:
random.seed(0)
X = random.randn(5,50)
X[X < 1.5] = 0
X = scipy.sparse.csr_matrix(X)
print(f'sparsity of X: {100 * (1 - X.count_nonzero() / prod(X.shape)):5.2f} %')
# sparsity of X: 92.00 %
%timeit X @ X.T
# 248 µs ± 10.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
X2 = X.copy()
%timeit X @ X2.T
# 251 µs ± 9.38 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
So I was wondering: What is the fastest way to compute a sparse Gram matrix in Python? Notably, it is sufficient to only compute the lower (or equivalently, the upper) triangle.
I've read multiple times, that using the skyline format is very efficient for symmetrical matrices, however, scipy doesn't support the skyline format. Instead, people were pointing towards pysparse many times, but it seems that pysparse has been discontinued a long time ago and there is no support for Python 3. At least, my Anaconda rejects to install pysparse due to compatibility issues with Python 3.