SciPy Cheatsheet
Linear Algebra
Use this SciPy reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Core Import
from scipy import linalg import numpy as np # Or direct imports from scipy.linalg import ( solve, lstsq, inv, det, norm, eig, eigh, eigvals, eigvalsh, svd, svdvals, lu, qr, cholesky, ldl, expm, logm, sqrtm, funm, solve_triangular, solve_banded, block_diag, circulant, companion, kron, tril, triu )
Prefer
scipy.linalgovernumpy.linalg— scipy uses LAPACK directly and supports more options, faster routines, and additional decompositions.
Solving Linear Systems
solve — Square Systems
from scipy.linalg import solve A = np.array([[3, 1], [1, 2]], dtype=float) b = np.array([9, 8], dtype=float) x = solve(A, b) # solves A @ x = b # x ≈ [2, 3] # Multiple right-hand sides (b as 2-D) B = np.random.rand(2, 5) X = solve(A, B) # shape (2, 5) # Key options x = solve(A, b, lower=False, # A is upper triangular (skip factorization) overwrite_a=False, # allow overwriting A for memory efficiency overwrite_b=False, check_finite=True, # set False to skip input checking (faster) assume_a='gen') # 'gen', 'sym', 'her', 'pos' # Symmetric positive definite → faster x = solve(A, b, assume_a='pos')
solve_triangular
from scipy.linalg import solve_triangular R = np.triu(np.random.rand(4, 4)) + np.eye(4) # upper triangular b = np.random.rand(4) x = solve_triangular(R, b) # default: upper x = solve_triangular(R, b, lower=True) # treat as lower x = solve_triangular(R, b, trans='T') # solve R.T @ x = b x = solve_triangular(R, b, unit_diagonal=True)
solve_banded
from scipy.linalg import solve_banded # ab[0] = upper diagonal, ab[1] = main, ab[2] = lower (for l=1, u=1) ab = np.array([[0, 1, 1, 1], # upper diag (first element unused) [4, 4, 4, 4], # main diagonal [1, 1, 1, 0]]) # lower diag (last element unused) b = np.array([1, 2, 3, 4], dtype=float) x = solve_banded((1, 1), ab, b) # (l, u) = number of sub/superdiagonals
lstsq — Least-Squares (Overdetermined)
from scipy.linalg import lstsq A = np.random.rand(10, 3) b = np.random.rand(10) x, residuals, rank, sv = lstsq(A, b) # x — solution minimizing ||A@x - b|| # residuals — sum of squared residuals (empty if rank < N) # rank — effective rank of A # sv — singular values
Matrix Factorizations
LU Decomposition
from scipy.linalg import lu, lu_factor, lu_solve P, L, U = lu(A) # A = P @ L @ U # Efficient repeated solves with same A lu_and_piv = lu_factor(A) # factorize once x = lu_solve(lu_and_piv, b) # solve many times x2 = lu_solve(lu_and_piv, b2)
QR Decomposition
from scipy.linalg import qr, qr_update, qr_delete, qr_insert Q, R = qr(A) # full QR: Q (m×m), R (m×n) Q, R = qr(A, mode='economic') # thin QR: Q (m×n), R (n×n) R_only = qr(A, mode='r') # R only (faster) Q, R, P = qr(A, pivoting=True) # column-pivoted: A[:,P] = Q @ R
Cholesky Decomposition
from scipy.linalg import cholesky, cho_factor, cho_solve # A must be symmetric positive definite L = cholesky(A, lower=True) # A = L @ L.T (lower triangular) U = cholesky(A, lower=False) # A = U.T @ U (upper triangular) # Efficient solves c, low = cho_factor(A, lower=True) x = cho_solve((c, low), b)
LDL Decomposition
from scipy.linalg import ldl # For symmetric (not necessarily positive definite) L, D, perm = ldl(A) # A[np.ix_(perm,perm)] = L @ D @ L.T
SVD
from scipy.linalg import svd, svdvals U, s, Vh = svd(A) # full SVD: A = U @ diag(s) @ Vh U, s, Vh = svd(A, full_matrices=False) # thin/economy SVD s_only = svdvals(A) # just singular values (faster) # Reconstruct A_reconstructed = U @ np.diag(s) @ Vh # Truncated SVD (use scipy.sparse.linalg for large matrices) from scipy.sparse.linalg import svds U_k, s_k, Vh_k = svds(A, k=5) # top-5 singular values/vectors
Schur Decomposition
from scipy.linalg import schur, rsf2csf T, Z = schur(A) # A = Z @ T @ Z.H (T quasi-upper triangular) T, Z = schur(A, output='complex') # force complex Schur form
Eigenvalues & Eigenvectors
from scipy.linalg import eig, eigh, eigvals, eigvalsh # General (non-symmetric) eigenvalues, eigenvectors = eig(A) eigenvalues = eigvals(A) # eigenvalues only (faster) # Symmetric/Hermitian (real eigenvalues, orthonormal eigenvectors) eigenvalues, eigenvectors = eigh(A) # sorted ascending eigenvalues = eigvalsh(A) # Generalized eigenvalue problem: A @ v = lambda * B @ v eigenvalues, eigenvectors = eig(A, b=B) eigenvalues, eigenvectors = eigh(A, b=B) # symmetric A, B # Subset (faster for large matrices) eigenvalues, eigenvectors = eigh(A, subset_by_index=[0, 4]) # first 5 eigenvalues, eigenvectors = eigh(A, subset_by_value=[-1, 1]) # in [-1,1] # Left and right eigenvectors eigenvalues, vl, vr = eig(A, left=True, right=True)
Matrix Operations
from scipy.linalg import inv, det, norm, matrix_rank, pinv inv_A = inv(A) # inverse (avoid if possible — use solve) d = det(A) # determinant r = matrix_rank(A) # numerical rank A_pinv = pinv(A) # Moore-Penrose pseudoinverse # Norms n1 = norm(A, ord=1) # max column sum n2 = norm(A, ord=2) # spectral norm (= largest singular value) nf = norm(A, 'fro') # Frobenius ni = norm(A, np.inf) # max row sum # Condition number cond = norm(A) * norm(inv(A)) # or faster: cond = np.linalg.cond(A)
Matrix Functions
from scipy.linalg import expm, logm, sqrtm, signm, funm, cosm, sinm eA = expm(A) # matrix exponential e^A lA = logm(A) # matrix logarithm sA = sqrtm(A) # matrix square root sgA = signm(A) # matrix sign # General matrix function fA = funm(A, np.cos) # cos(A) via Schur decomposition # Trigonometric cA = cosm(A) sA = sinm(A)
Special Matrix Construction
from scipy.linalg import block_diag, circulant, companion, toeplitz, hankel, hadamard, hilbert, leslie # Block diagonal BD = block_diag(A1, A2, A3) # Circulant (from first column) C = circulant([1, 2, 3, 4]) # Companion matrix of polynomial # For x^3 - 6x^2 + 11x - 6 = 0: M = companion([1, -6, 11, -6]) # Toeplitz T = toeplitz([1, 2, 3], [1, 4, 5]) # (col, row) vectors # Hilbert (ill-conditioned test matrix) H = hilbert(5) # Hadamard W = hadamard(8) # n must be power of 2
Solving Structured Systems
# Kronecker product from scipy.linalg import kron K = kron(A, B) # Kronecker product # Solve with preconditioning (large systems — see also scipy.sparse.linalg) from scipy.sparse.linalg import spsolve, cg, gmres from scipy.sparse import csr_array # For large, sparse A: A_sparse = csr_array(A) x = spsolve(A_sparse, b) # Iterative solvers x, info = cg(A_sparse, b, tol=1e-8, maxiter=1000) # Conjugate Gradient (SPD) x, info = gmres(A_sparse, b, tol=1e-8) # GMRES (general)
Common Gotchas
Never compute
inv(A) @ bto solveA @ x = b. Usesolve(A, b)— it is 3x faster, numerically stabler, and avoids explicitly forming the inverse.
eigvseigh: For symmetric/Hermitian matrices,eighis faster, returns real eigenvalues, and guarantees orthonormal eigenvectors.eigon a symmetric matrix may still return tiny imaginary parts.
svdvssvds:linalg.svdcomputes all singular values (dense). For large matrices and only a few singular values, usescipy.sparse.linalg.svds.
Overflow in
expm: IfAhas large entries,expmcan overflow. Consider scaling and squaring manually, or check the norm ofAfirst.
norm(A)withoutordreturns the Frobenius norm for matrices but the 2-norm for vectors — be explicit withord=.