Module Organization¶
SciPy provides linear algebra functionality across multiple modules.
Mental Model
SciPy's linear algebra lives in three places: scipy.linalg for dense matrices, scipy.sparse for sparse matrix classes, and scipy.sparse.linalg for sparse solvers. Knowing which module to import is half the battle -- dense operations assume the full matrix fits in memory, while sparse operations work only with the nonzero entries.
Core Modules¶
1. scipy.linalg¶
Dense matrix operations and decompositions.
```python from scipy import linalg
Key functions¶
linalg.lu, linalg.qr, linalg.svd¶
linalg.eig, linalg.eigh¶
linalg.solve, linalg.inv¶
linalg.expm, linalg.logm¶
```
2. scipy.sparse¶
Sparse matrix classes and construction.
```python from scipy import sparse
Sparse matrix formats¶
sparse.csr_matrix, sparse.csc_matrix¶
sparse.coo_matrix, sparse.lil_matrix¶
sparse.dia_matrix, sparse.dok_matrix¶
```
3. scipy.sparse.linalg¶
Sparse linear algebra operations.
```python from scipy.sparse import linalg as splinalg
Sparse solvers¶
splinalg.spsolve, splinalg.splu¶
splinalg.cg, splinalg.gmres¶
splinalg.eigs, splinalg.eigsh¶
```
Module Overview¶
1. Hierarchy¶
scipy
├── linalg # Dense operations
│ ├── decompositions (lu, qr, svd, cholesky, schur)
│ ├── eigenvalues (eig, eigh, eigvals)
│ ├── solvers (solve, solve_triangular, solve_banded)
│ ├── matrix functions (expm, logm, sqrtm)
│ └── special matrices (toeplitz, circulant, companion)
│
├── sparse # Sparse matrix classes
│ ├── csr_matrix, csc_matrix
│ ├── coo_matrix, lil_matrix
│ └── construction functions
│
└── sparse.linalg # Sparse operations
├── direct solvers (spsolve, splu)
├── iterative solvers (cg, gmres, bicg)
└── eigensolvers (eigs, eigsh)
2. Import Patterns¶
```python import numpy as np from scipy import linalg from scipy import sparse from scipy.sparse import linalg as splinalg
def main(): # Dense matrix A_dense = np.array([[4, 1], [1, 3]])
# Dense operations via scipy.linalg
L = linalg.cholesky(A_dense, lower=True)
print("Cholesky L:")
print(L)
print()
# Sparse matrix
A_sparse = sparse.csr_matrix(A_dense)
# Sparse operations via scipy.sparse.linalg
b = np.array([1, 2])
x = splinalg.spsolve(A_sparse, b)
print(f"Sparse solve: x = {x}")
if name == "main": main() ```
Function Categories¶
1. Decompositions¶
| Function | Module | Description |
|---|---|---|
lu |
linalg |
LU factorization |
qr |
linalg |
QR factorization |
svd |
linalg |
Singular value decomposition |
cholesky |
linalg |
Cholesky decomposition |
schur |
linalg |
Schur decomposition |
hessenberg |
linalg |
Hessenberg form |
2. Eigenvalue Problems¶
| Function | Module | Description |
|---|---|---|
eig |
linalg |
General eigenvalues |
eigh |
linalg |
Hermitian eigenvalues |
eigvals |
linalg |
Eigenvalues only |
eigs |
sparse.linalg |
Sparse eigenvalues |
eigsh |
sparse.linalg |
Sparse Hermitian eigenvalues |
3. Linear Solvers¶
| Function | Module | Description |
|---|---|---|
solve |
linalg |
Dense system solver |
solve_triangular |
linalg |
Triangular solver |
solve_banded |
linalg |
Banded matrix solver |
spsolve |
sparse.linalg |
Sparse direct solver |
cg |
sparse.linalg |
Conjugate gradient |
gmres |
sparse.linalg |
GMRES iterative |
4. Matrix Functions¶
| Function | Module | Description |
|---|---|---|
expm |
linalg |
Matrix exponential |
logm |
linalg |
Matrix logarithm |
sqrtm |
linalg |
Matrix square root |
funm |
linalg |
General matrix function |
Practical Usage¶
1. Dense Workflow¶
```python import numpy as np from scipy import linalg
def main(): A = np.random.randn(100, 100) A = A @ A.T # Make symmetric positive definite b = np.random.randn(100)
# Solve using Cholesky
c, low = linalg.cho_factor(A)
x = linalg.cho_solve((c, low), b)
print(f"Solution norm: {np.linalg.norm(x):.4f}")
print(f"Residual: {np.linalg.norm(A @ x - b):.2e}")
if name == "main": main() ```
2. Sparse Workflow¶
```python import numpy as np from scipy import sparse from scipy.sparse import linalg as splinalg
def main(): # Create sparse matrix n = 1000 diags = [-np.ones(n-1), 2*np.ones(n), -np.ones(n-1)] A = sparse.diags(diags, [-1, 0, 1], format='csr')
b = np.ones(n)
# Solve sparse system
x = splinalg.spsolve(A, b)
print(f"Solution norm: {np.linalg.norm(x):.4f}")
print(f"Residual: {np.linalg.norm(A @ x - b):.2e}")
if name == "main": main() ```
Summary¶
1. Quick Reference¶
| Task | Module |
|---|---|
| Dense decompositions | scipy.linalg |
| Dense solvers | scipy.linalg |
| Matrix functions | scipy.linalg |
| Sparse matrices | scipy.sparse |
| Sparse solvers | scipy.sparse.linalg |
| Sparse eigenvalues | scipy.sparse.linalg |
Exercises¶
Exercise 1.
Write a script that imports scipy.linalg, scipy.sparse, and scipy.sparse.linalg. Create a \(5 \times 5\) dense SPD matrix, solve \(Ax = b\) using linalg.cho_factor and linalg.cho_solve. Then convert \(A\) to a sparse CSR matrix and solve the same system with splinalg.spsolve. Verify both solutions agree (norm of difference below \(10^{-12}\)).
Solution to Exercise 1
import numpy as np
from scipy import linalg, sparse
from scipy.sparse import linalg as splinalg
np.random.seed(0)
B = np.random.randn(5, 5)
A = B @ B.T + 5 * np.eye(5)
b = np.random.randn(5)
# Dense Cholesky solve
c, low = linalg.cho_factor(A)
x_dense = linalg.cho_solve((c, low), b)
# Sparse solve
A_sparse = sparse.csr_matrix(A)
x_sparse = splinalg.spsolve(A_sparse, b)
diff = np.linalg.norm(x_dense - x_sparse)
print(f"Dense solution: {x_dense}")
print(f"Sparse solution: {x_sparse}")
print(f"Difference norm: {diff:.2e}")
Exercise 2.
Use scipy.linalg functions to perform the following pipeline on a random \(6 \times 6\) matrix (use np.random.seed(8)): (1) compute the LU decomposition, (2) compute the QR decomposition, (3) compute the eigenvalues. Print the shapes of all decomposition outputs and the eigenvalues.
Solution to Exercise 2
import numpy as np
from scipy import linalg
np.random.seed(8)
A = np.random.randn(6, 6)
# LU decomposition
P, L, U = linalg.lu(A)
print(f"LU: P {P.shape}, L {L.shape}, U {U.shape}")
# QR decomposition
Q, R = linalg.qr(A)
print(f"QR: Q {Q.shape}, R {R.shape}")
# Eigenvalues
eigenvalues = linalg.eigvals(A)
print(f"Eigenvalues: {eigenvalues}")
Exercise 3.
Create a \(500 \times 500\) sparse tridiagonal matrix using scipy.sparse.diags. Solve \(Ax = b\) with \(b = \mathbf{1}\) using three different approaches: splinalg.spsolve, splinalg.cg, and dense linalg.solve (after converting to dense). Print the residual norm for each method and compare.
Solution to Exercise 3
import numpy as np
from scipy import linalg, sparse
from scipy.sparse import linalg as splinalg
n = 500
A_sparse = sparse.diags([-1, 2, -1], [-1, 0, 1],
shape=(n, n), format='csr')
b = np.ones(n)
# Method 1: spsolve
x1 = splinalg.spsolve(A_sparse, b)
r1 = np.linalg.norm(A_sparse @ x1 - b)
# Method 2: CG
x2, info = splinalg.cg(A_sparse, b)
r2 = np.linalg.norm(A_sparse @ x2 - b)
# Method 3: dense solve
A_dense = A_sparse.toarray()
x3 = linalg.solve(A_dense, b)
r3 = np.linalg.norm(A_dense @ x3 - b)
print(f"spsolve residual: {r1:.2e}")
print(f"CG residual: {r2:.2e}")
print(f"Dense residual: {r3:.2e}")