Skip to content

Direct Solvers

Direct methods for solving sparse linear systems.

spsolve

1. Basic Usage

import numpy as np
from scipy import sparse
from scipy.sparse import linalg as splinalg

def main():
    A = sparse.csr_matrix([[4, 1, 0],
                           [1, 4, 1],
                           [0, 1, 4]])
    b = np.array([1, 2, 1])

    x = splinalg.spsolve(A, b)

    print(f"Solution: x = {x}")
    print(f"Residual: {np.linalg.norm(A @ x - b):.2e}")

if __name__ == "__main__":
    main()

splu - Sparse LU

1. Factor Once, Solve Multiple

import numpy as np
from scipy import sparse
from scipy.sparse import linalg as splinalg

def main():
    n = 1000
    A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n, n), format='csc')

    # Factor once
    lu = splinalg.splu(A)

    # Solve multiple systems
    for i in range(3):
        b = np.random.randn(n)
        x = lu.solve(b)
        print(f"System {i}: residual = {np.linalg.norm(A @ x - b):.2e}")

if __name__ == "__main__":
    main()

2. Fill-in Management

import numpy as np
from scipy import sparse
from scipy.sparse import linalg as splinalg

def main():
    n = 100
    A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n, n), format='csc')

    lu = splinalg.splu(A)

    print(f"Original nnz: {A.nnz}")
    print(f"L nnz: {lu.L.nnz}")
    print(f"U nnz: {lu.U.nnz}")

if __name__ == "__main__":
    main()

spilu - Incomplete LU

1. Preconditioner

import numpy as np
from scipy import sparse
from scipy.sparse import linalg as splinalg

def main():
    n = 100
    A = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n, n), format='csc')

    # Incomplete LU (for preconditioning)
    ilu = splinalg.spilu(A)

    print(f"Original nnz: {A.nnz}")
    print(f"ILU L nnz: {ilu.L.nnz}")
    print(f"ILU U nnz: {ilu.U.nnz}")

if __name__ == "__main__":
    main()

Performance

1. Sparse vs Dense

import numpy as np
from scipy import sparse, linalg
from scipy.sparse import linalg as splinalg
import time

def main():
    n = 2000
    A_sparse = sparse.diags([-1, 2, -1], [-1, 0, 1], shape=(n, n), format='csc')
    A_dense = A_sparse.toarray()
    b = np.random.randn(n)

    # Dense solve
    start = time.perf_counter()
    x_dense = linalg.solve(A_dense, b)
    dense_time = time.perf_counter() - start

    # Sparse solve
    start = time.perf_counter()
    x_sparse = splinalg.spsolve(A_sparse, b)
    sparse_time = time.perf_counter() - start

    print(f"Dense solve:  {dense_time:.4f} sec")
    print(f"Sparse solve: {sparse_time:.4f} sec")
    print(f"Speedup: {dense_time/sparse_time:.1f}x")

if __name__ == "__main__":
    main()

Summary

Function Description
spsolve(A, b) Direct sparse solve
splu(A) Sparse LU factorization
spilu(A) Incomplete LU (preconditioner)

Use CSC format for direct solvers.