Graphs, text features and discretised PDEs produce matrices far too big to store densely.
A sparse matrix stores only its non-zero entries. A 100,000 x 100,000 matrix with 5 non-zeros per row needs about 6 MB sparse versus 80 GB dense.
Formats suit different jobs:
- COO (
coo_array): (row, col, value) triplets, easy to build; duplicates are summed on conversion. - CSR (
csr_array): fast row slicing and matrix-vector products; the default for computation. - CSC: fast column operations, preferred by some solvers.
- LIL / DOK: efficient for incremental construction, then convert with
.tocsr().
Build in COO (or with sparse.diags_array, sparse.eye_array, sparse.random_array), then convert to CSR for computing. SciPy now recommends the array classes (csr_array) over the older matrix classes (csr_matrix): with arrays, * is element-wise and @ is matrix multiplication, as in NumPy.
Linear algebra lives in scipy.sparse.linalg:
spsolve(A, b): direct solve,- iterative solvers
cg(symmetric positive definite),gmres,bicgstab,minresfor very large systems, eigsh/eigs: a few eigenvalues,svds: a truncated SVD.
Avoid .toarray() on large matrices, and avoid changing the sparsity pattern of a CSR matrix element by element, which is slow and triggers a SparseEfficiencyWarning.
Example
import numpy as np
from scipy import sparse
from scipy.sparse.linalg import spsolve, cg
n = 100_000
# tridiagonal matrix of a 1-D Laplacian: 2 on the diagonal, -1 beside it
A = sparse.diags_array([-1.0, 2.0, -1.0], offsets=[-1, 0, 1], shape=(n, n)).tocsr()
print(A.nnz, A.format) # 299998 csr
print(round(A.data.nbytes / 1e6, 1), "MB") # 2.4 MB (dense would be 80,000 MB)
b = np.ones(n)
x = spsolve(A, b)
print(round(x[n // 2], 1)) # 1250024999.6 (exact: 1250025000; ill-conditioned)
rows, cols, vals = [0, 1, 1], [0, 0, 2], [5.0, 3.0, 4.0]
M = sparse.coo_array((vals, (rows, cols)), shape=(2, 3)).tocsr()
print(M.toarray())
# [[5. 0. 0.]
# [3. 0. 4.]]Remember
- Build in COO/LIL, compute in CSR/CSC
- Prefer
csr_arrayovercsr_matrix:@is matmul,*element-wise - Use
spsolveor iterative solvers fromscipy.sparse.linalg
Watch out for
- Calling
.toarray()on a huge sparse matrix and running out of memory.