NumPy Cheatsheet

Linear Algebra

Use this NumPy reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Matrix Multiplication

import numpy as np

A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

# Matrix multiplication (dot product for 2-D)
A @ B                    # preferred syntax (PEP 465)
np.matmul(A, B)          # equivalent — no scalar broadcasting
np.dot(A, B)             # same for 2-D; for N-D behaves differently

# Dot product for 1-D arrays (inner product)
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.dot(a, b)             # 32  (scalar)
a @ b                    # 32

# Outer product
np.outer(a, b)           # (3, 3) matrix
np.outer(a, b) == a[:, None] * b[None, :]  # True

# Inner product (generalized)
np.inner(A, B)           # sum over last axis of A and last axis of B

# Batch matrix multiply (3-D: batch × M × K  @  batch × K × N)
batch_A = np.random.rand(10, 3, 4)
batch_B = np.random.rand(10, 4, 5)
batch_A @ batch_B        # (10, 3, 5)

# einsum — expressive contraction notation
np.einsum("ij,jk->ik", A, B)        # matrix multiply
np.einsum("i,i->", a, b)            # dot product
np.einsum("ij->ji", A)              # transpose
np.einsum("ij,ij->ij", A, B)        # element-wise multiply
np.einsum("ijk,ikl->ijl", batch_A, batch_B)  # batch matmul
np.einsum("ii->i", A)               # diagonal
np.einsum("ii", A)                  # trace
np.einsum("ij->i", A)               # row sums

numpy.linalg — Core Module

from numpy import linalg as la
# or: import numpy.linalg as la

Decompositions

Eigendecomposition

vals, vecs = la.eig(A)      # eigenvalues, eigenvectors (cols of vecs)
vals                         # array of eigenvalues (may be complex)
vecs[:, 0]                   # first eigenvector

vals = la.eigvals(A)         # eigenvalues only (faster)

# Symmetric / Hermitian matrices (real eigenvalues guaranteed)
vals, vecs = la.eigh(A)     # eigenvalues sorted ascending
vals = la.eigvalsh(A)        # eigenvalues only

Singular Value Decomposition

U, S, Vh = la.svd(A)               # A = U @ np.diag(S) @ Vh
U, S, Vh = la.svd(A, full_matrices=False)  # economy / thin SVD
S = la.svdvals(A)                    # singular values only (NumPy ≥ 2.0)

# Reconstruct: A ≈ U @ np.diag(S) @ Vh  (full) or  (U * S) @ Vh (thin)
k = 2
A_approx = (U[:, :k] * S[:k]) @ Vh[:k, :]   # rank-k approximation

QR Decomposition

Q, R = la.qr(A)              # A = Q @ R
Q, R = la.qr(A, mode="reduced")   # economy form (default for non-square)
Q, R = la.qr(A, mode="complete")  # full Q
Q, R = la.qr(A, mode="r")         # R only

Cholesky Decomposition

# A must be symmetric positive-definite
L = la.cholesky(A)           # A = L @ L.T.conj()
# NumPy returns lower-triangular L

LU-like via SciPy

NumPy does not expose LU directly; use scipy.linalg.lu for that.

Solving Systems

# Solve Ax = b
A = np.array([[2.0, 1.0], [1.0, 3.0]])
b = np.array([5.0, 10.0])

x = la.solve(A, b)                   # exact solution (A must be square)
la.solve(A, np.stack([b, b], axis=1))  # multiple right-hand sides

x, res, rank, sv = la.lstsq(A, b, rcond=None)  # least-squares (A can be non-square)
# res = residuals (sum of squared residuals), rank = effective rank, sv = singular values

# Triangular solve
np.linalg.solve(np.tril(A), b)   # use la.solve; no dedicated triangular variant in NumPy

Inverse, Determinant, and Rank

la.inv(A)          # matrix inverse (A must be square and non-singular)
la.pinv(A)         # Moore-Penrose pseudo-inverse (works for any shape)
la.det(A)          # determinant (scalar)
la.slogdet(A)      # (sign, log|det|) — numerically stable for large matrices
la.matrix_rank(A)  # effective rank (using SVD + tolerance)
la.matrix_rank(A, tol=1e-10)   # custom tolerance

Norms and Condition Number

la.norm(a)                     # L2 norm of vector
la.norm(a, ord=1)              # L1 norm
la.norm(a, ord=np.inf)        # max absolute value
la.norm(a, ord=-np.inf)       # min absolute value

la.norm(A)                     # Frobenius norm (default for matrices)
la.norm(A, ord="fro")          # same
la.norm(A, ord=2)              # spectral norm (largest singular value)
la.norm(A, ord=1)              # max column sum
la.norm(A, ord=np.inf)        # max row sum
la.norm(A, ord=-1)             # min column sum
la.norm(A, ord="nuc")          # nuclear norm (sum of singular values)

la.norm(A, axis=0)             # per-column norm → shape (n,)
la.norm(A, axis=1, keepdims=True)   # per-row norm → shape (m, 1)

la.cond(A)          # condition number (ratio of largest to smallest singular value)
la.cond(A, p=1)     # condition number in 1-norm

Utility Functions

np.trace(A)               # sum of diagonal elements
np.trace(A, offset=1)     # sum of k-th diagonal
np.diagonal(A)            # extract main diagonal as view
np.diagonal(A, offset=1)  # k-th diagonal
np.fill_diagonal(A, 5)    # fill main diagonal in-place (no return value)

# Triangular extraction
np.triu(A)           # upper triangle (zeros below diagonal)
np.tril(A)           # lower triangle (zeros above diagonal)
np.triu(A, k=1)      # above main diagonal only
np.tril(A, k=-1)     # below main diagonal only

# Kronecker product
np.kron(A, B)        # block matrix

# Cross product (3-D vectors)
np.cross(a, b)       # a × b
np.cross(a, b, axisa=0, axisb=0)  # specify which axis holds the vectors

Common Patterns

Projecting onto a subspace

Q, _ = la.qr(A)               # orthonormal basis
proj = Q @ (Q.T @ b)          # project vector b onto column space of A

Batch solving (many systems with same A)

A = np.random.rand(4, 4)
B = np.random.rand(4, 100)    # 100 right-hand sides
X = la.solve(A, B)            # X.shape == (4, 100)

Positive-definite check

def is_pos_def(M):
    try:
        la.cholesky(M)
        return True
    except la.LinAlgError:
        return False

Null space (approximate, via SVD)

def null_space(A, tol=1e-10):
    _, S, Vh = la.svd(A, full_matrices=True)
    return Vh[np.sum(S > tol):].T

Error Handling

try:
    la.inv(singular_matrix)
except la.LinAlgError as e:
    print(e)   # "Singular matrix"

try:
    la.cholesky(non_posdef)
except la.LinAlgError as e:
    print(e)   # "Matrix is not positive definite"