Math — Advanced Linear Algebra
A progressive journey from manually computing determinants with Python lists to classifying matrix definiteness with NumPy — building deep intuition for the linear algebra pipeline: determinant → minor → cofactor → adjugate → inverse → definiteness.
Learning Objectives
| # | Concept |
|---|---|
| 1 | Compute the determinant of any square matrix recursively using Laplace expansion (no NumPy) |
| 2 | Build a matrix of minors by extracting submatrices and computing their determinants |
| 3 | Apply the checkerboard sign pattern to convert minors into cofactors |
| 4 | Compute the adjugate (classical adjoint) as the transpose of the cofactor matrix |
| 5 | Find the inverse by dividing the adjugate by the determinant — handle singular matrices |
| 6 | Classify symmetric matrices by definiteness using NumPy eigenvalue sign analysis |
| 7 | Combine all concepts into a pipeline: det → minor → cofactor → adjugate → inverse |
Task-by-Task Reference
Each task below highlights the unique challenge it posed and the new technique introduced — techniques from earlier tasks are not repeated. Tasks 0–4 use only Python lists (no NumPy); Task 5 introduces NumPy.
Task 0 — Determinant (0-determinant.py)
Challenge: Compute the determinant of a square matrix of arbitrary size using only Python lists — implementing Laplace expansion from scratch with proper input validation.
Approach: The function handles three base cases (0×0 → 1, 1×1 → the value, 2×2 → ) and recursively expands along the first row for larger matrices. For each column , it builds the submatrix by skipping row 0 and column , then accumulates .
New techniques introduced:
| Technique | Purpose |
|---|---|
matrix == [[]] → return 1 | Base case: 0×0 matrix determinant is 1 |
ad - bc for 2×2 | Direct formula avoids recursion overhead |
matrix[row][:col] + matrix[row][col+1:] | Slice-based column removal (no np.delete) |
(-1) ** col alternating sign | Laplace expansion sign pattern for first-row expansion |
Recursive determinant(submatrix) | Recursively reduce problem to smaller submatrices |
| Input validation chain | isinstance(list) → all(isinstance(row, list)) → square check |
Key takeaway: The determinant is the foundation of the entire linear algebra pipeline. Laplace expansion recursively breaks an n×n problem into n problems of size (n−1)×(n−1) — pure divide and conquer.
Task 1 — Matrix of Minors (1-minor.py)
Challenge: Build a full matrix of minors — for every position , compute the determinant of the submatrix formed by deleting row and column .
Approach: Reuses determinant() from Task 0. Implements special-case shortcuts for 1×1 ([[1]]) and 2×2 (reverse rows and columns). For 3×3+, iterates over all pairs, builds each submatrix by skipping row and column , computes its determinant, then reassembles results into the minor matrix shape using list slicing temp[i*n:(i+1)*n].
New techniques introduced:
| Technique | Purpose |
|---|---|
continue to skip row | Cleanly omit a row from submatrix construction |
for k in range(n): if k == i: continue | Build submatrix excluding one specific row |
Row-slicing reassembly temp[i*n:(i+1)*n] | Convert flat list of minor values into 2D matrix rows |
2×2 minor shortcut matrix[row][::-1] | Direct formula avoids submatrix/det overhead for small case |
Key takeaway: The minor matrix generalizes the determinant — instead of one number, you compute a determinant for every position . Each minor tells you "how much does this position matter?"
Task 2 — Cofactor Matrix (2-cofactor.py)
Challenge: Convert the matrix of minors into a cofactor matrix by applying the checkerboard sign pattern .
Approach: Calls minor() from Task 1, then iterates over every element multiplying by . The sign pattern creates alternating signs like a chess board: at even , at odd .
New techniques introduced:
| Technique | Purpose |
|---|---|
(-1) ** (row + col) | Checkerboard sign pattern — positive at even positions, negative at odd |
cofactor = sign × minor | Mathematical relationship: |
| Cumulative task dependency | Task 2 imports minor() from Task 1, which uses determinant() from Task 0 |
Key takeaway: The cofactor is just a signed minor — same values, alternating signs. The sign pattern is what turns minors into the building blocks of inverses.
Task 3 — Adjugate Matrix (3-adjugate.py)
Challenge: Compute the adjugate (classical adjoint) — the transpose of the cofactor matrix. This is the final preprocessing step before the inverse.
Approach: Calls cofactor() from Task 2, then transposes by iterating with cofact[col][row] (swapped indices). Reassembles into 2D using row slicing.
New techniques introduced:
| Technique | Purpose |
|---|---|
cofact[col][row] (swapped indices) | Manual matrix transpose — iterates columns-first, rows-second |
| Adjugate = transpose of cofactor | — swap rows and columns of the cofactor matrix |
| Full pipeline dependency | adjugate() → cofactor() → minor() → determinant() |
Key takeaway: The adjugate is the transpose of the cofactor matrix — a simple operation that completes the preprocessing chain. The inverse is now just one division away: .
Task 4 — Inverse (4-inverse.py)
Challenge: Compute the matrix inverse by dividing the adjugate by the determinant — and detect singular (non-invertible) matrices.
Approach: Calls adjugate() and determinant(). If , returns None (singular). Otherwise divides every element of the adjugate by the determinant: adju[row][col] / det.
New techniques introduced:
| Technique | Purpose |
|---|---|
det == 0 → return None | Singular matrix detection — no inverse exists |
adju[row][col] / det | Inverse formula: |
| Scalar division of every element | Building a new matrix by scaling each entry |
| Full pipeline: det → minor → cofactor → adjugate → inverse | Complete linear algebra chain, all from Python lists |
Key takeaway: The inverse is the culmination of the entire pipeline — every previous step (determinant, minor, cofactor, adjugate) was building toward this. If , the matrix is singular and has no inverse.
Task 5 — Definiteness (5-definiteness.py)
Challenge: Classify a symmetric matrix by its definiteness (positive/negative definite, semi-definite, or indefinite) — introducing NumPy and eigenvalue analysis.
Approach: Validates input as np.ndarray, checks symmetry with np.allclose(matrix, matrix.T), then uses np.linalg.eigvalsh() to get eigenvalues. Classifies by sign: all → positive definite, all → positive semi-definite, all → negative definite, all → negative semi-definite, mixed → indefinite.
New techniques introduced:
| Technique | Purpose |
|---|---|
isinstance(matrix, np.ndarray) | Require NumPy array input (vs Python lists in Tasks 0–4) |
np.allclose(matrix, matrix.T) | Check matrix symmetry with floating-point tolerance |
np.linalg.eigvalsh(A) | Efficient eigenvalue computation for symmetric/Hermitian matrices |
np.all(eigvals > tol) | Classify definiteness by eigenvalue sign analysis |
| Five-way classification | Positive/negative definite, semi-definite, indefinite |
Tolerance tol = 1e-10 | Handle floating-point noise near zero |
Key takeaway: Definiteness tells you the "shape" of a matrix — eigenvalues are the key. All positive eigenvalues = positive definite (bowl-shaped), mixed signs = indefinite (saddle-shaped). This concept is critical for optimization and ML.
Bonus — Matrix Quiz (quiz.py)
Challenge: A comprehensive 10-question quiz that tests all six concepts using NumPy's linalg module as a verification tool — applying the theory to concrete 3×3 matrices.
Approach: Implements NumPy-based helper functions (minor_matrix, cofactor_matrix, adjugate_matrix, classify_definiteness) and runs each concept against multiple-choice options using np.allclose(). Also explores eigenvalue power iteration () and eigenpair verification.
New techniques introduced:
| Technique | Purpose |
|---|---|
np.linalg.det() / np.linalg.inv() / np.linalg.eig() | NumPy verification of manual implementations |
np.allclose(result, option) | Match computed results to multiple-choice answers |
| Eigenvalue power iteration | when is an eigenvector |
np.arange()[:, None] broadcasting | Efficient sign pattern generation without loops |
np.delete(A, i, axis=0) | NumPy alternative to the manual submatrix construction |
Key takeaway: The quiz validates your manual implementations against NumPy's battle-tested
linalgmodule — and shows how eigenvalues unlock efficient matrix powers without computing directly.
Technique Inventory
| Task | New technique summarized | Category |
|---|---|---|
| 0 | Recursive Laplace expansion, 2×2 shortcut, (-1)^col sign | Determinant (Manual) |
| 1 | Submatrix by continue skip, flat-to-2D reassembly | Minors (Manual) |
| 2 | Checkerboard sign on minors | Cofactors (Manual) |
| 3 | Manual transpose matrix[col][row] of cofactor | Adjugate (Manual) |
| 4 | Inverse = adjugate / det, singular → None | Inverse (Manual) |
| 5 | np.linalg.eigvalsh(), symmetry check, definiteness classification | Definiteness (NumPy) |
| quiz | np.linalg.det/inv/eig, power iteration, np.delete() | Verification (NumPy) |