Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Module 1b: Statistical Linear Algebra and Numerical Methods

Advanced Mathematical Foundations for Computational Astrophysics | ASTR 596

San Diego State University

Quick Navigation Guide

📚 Choose Your Learning Path

🏃 Fast Track

Starting Project 4? Read sections marked 🔴

🚶 Standard Path

Preparing for Projects 4-5? Read 🔴 and 🟡

🧗 Complete Path

Want mastery? Read all sections including 🟢

  • Complete module with:

  • Advanced decompositions

  • Numerical implementations

  • ML connections

🎯 Navigation by Project Needs


Learning Objectives

By the end of this module, you will be able to:


Prerequisites from Module 0a


Module Overview

Building on Module 0a’s foundations, this module explores how linear algebra enables statistical computation and machine learning. We cover positive definite matrices that ensure physical validity (Project 4), covariance structures that encode correlations (Project 5), and advanced numerical methods that keep statistical computations stable despite finite precision (all projects). The module culminates by showing how these concepts unite classical physics with modern machine learning (Final Project).

Part 1: Positive Definite Matrices and Statistical Foundations

Priority: 🟡 Important - Essential for Projects 4-5

1.1 Quadratic Forms and Energy {#part-5-positive-definite}

A quadratic form is:

Q(x)=xTAx=i,jAijxixjQ(\vec{x}) = \vec{x}^T A \vec{x} = \sum_{i,j} A_{ij} x_i x_j

These appear as energy expressions throughout physics:

TypeFormulaPhysical Meaning
Kinetic EnergyT=12vTMvT = \frac{1}{2}\vec{v}^T M \vec{v}M is mass matrix
Potential EnergyV=12xTKxV = \frac{1}{2}\vec{x}^T K \vec{x}K is stiffness matrix
Statistical Distanced2=(xμ)TΣ1(xμ)d^2 = (\vec{x}-\vec{\mu})^T \Sigma^{-1} (\vec{x}-\vec{\mu})Mahalanobis distance

Physical Example: The kinetic energy of a rotating rigid body

T=12ωTIωT = \frac{1}{2}\vec{\omega}^T I \vec{\omega}

where ω\vec{\omega} is angular velocity and II is the moment of inertia tensor. This quadratic form is always positive (energy can’t be negative), making II positive definite. The eigenvectors of II are the principal axes - spin around these and the object doesn’t wobble!

1.2 Positive Definiteness: Ensuring Physical Reality {#positive-definiteness}

A symmetric matrix is positive definite if its quadratic form is always positive. This property ensures physical validity—energies are positive, distances are non-negative, and covariance matrices represent valid uncertainties.

Four Equivalent Tests for Positive Definiteness:

  1. ✅ All eigenvalues > 0

  2. ✅ All leading principal minors > 0

  3. ✅ Has Cholesky decomposition A=LLTA = LL^T (requires strict positive definiteness)

  4. ✅ Can write as A=BTBA = B^T B for some invertible matrix BB

Quick check: Positive definite matrices always have positive diagonal elements. Proof: For standard basis vector ei\vec{e}_i, we have eiTAei=Aii>0\vec{e}_i^T A \vec{e}_i = A_{ii} > 0.

Important Distinction:

1.3 Covariance Matrices: The Bridge to Statistics {#covariance-matrices}

Priority: 🟡 Important - Foundation for Projects 4-5

Before diving into matrices, let’s understand what covariance actually tells us:

Intuitive Understanding:

Mathematical Definition:

Cov(X,Y)=E[(XμX)(YμY)]=E[XY]E[X]E[Y]\text{Cov}(X, Y) = E[(X - \mu_X)(Y - \mu_Y)] = E[XY] - E[X]E[Y]

This measures the average product of deviations from the mean. When both variables are above (or below) their means together, the products are positive, giving positive covariance.

Relationship to Correlation:

Correlation=ρXY=Cov(X,Y)σXσY\text{Correlation} = \rho_{XY} = \frac{\text{Cov}(X,Y)}{\sigma_X \sigma_Y}

Correlation is just normalized covariance! It ranges from -1 to +1, making it easier to interpret than raw covariance.

The Covariance Matrix:

For multiple variables X1,...,XnX_1, ..., X_n, we organize all pairwise covariances into a matrix:

Σij=Cov(Xi,Xj)=E[(Xiμi)(Xjμj)]\Sigma_{ij} = \text{Cov}(X_i, X_j) = E[(X_i - \mu_i)(X_j - \mu_j)]

Structure and Properties:

Why Positive Semi-Definite?

For any linear combination of variables Y=aTXY = a^T X:

Var(Y)=aTΣa0\text{Var}(Y) = a^T \Sigma a \geq 0

Variance can’t be negative! This forces Σ\Sigma to be positive semi-definite. If it has negative eigenvalues, you could construct a linear combination with negative variance—physically impossible!

1.4 The Multivariate Gaussian Distribution {#multivariate-gaussian}

Priority: 🟡 Important - Core of Projects 4-5

The multivariate Gaussian extends the familiar bell curve to multiple dimensions. In 1D, you know the Gaussian as the bell-shaped ex2e^{-x^2} curve. In higher dimensions, it becomes an ellipsoidal cloud of probability.

The Formula Decoded:

p(x)=1(2π)n/2Σ1/2exp(12(xμ)TΣ1(xμ))p(\vec{x}) = \frac{1}{(2\pi)^{n/2}|\Sigma|^{1/2}} \exp\left(-\frac{1}{2}(\vec{x}-\vec{\mu})^T\Sigma^{-1}(\vec{x}-\vec{\mu})\right)

Let’s understand each piece intuitively:

The Exponent: 12(xμ)TΣ1(xμ)-\frac{1}{2}(\vec{x}-\vec{\mu})^T\Sigma^{-1}(\vec{x}-\vec{\mu})

The Normalization: (2π)n/2Σ1/2(2\pi)^{n/2}|\Sigma|^{1/2}

Geometric Picture:

1.5 Cholesky Decomposition: The Matrix Square Root

Priority: 🟡 Important - Essential for Project 5

Every positive definite matrix can be factored as:

A=LLTA = LL^T

where LL is lower triangular with positive diagonal entries.

Uniqueness Property: The Cholesky decomposition is unique — for any positive definite matrix AA, there exists exactly one lower triangular matrix LL with positive diagonal entries such that A=LLTA = LL^T.

This uniqueness is crucial for numerical methods: no matter which algorithm you use (whether the classical Cholesky-Banachiewicz or the Cholesky-Crout variant), you’ll always get the same LL matrix (up to numerical precision). This deterministic property makes Cholesky decomposition ideal for reproducible scientific computing.

Why uniqueness matters: When you use Cholesky in your MCMC proposals (Project 4) or GP implementations (Project 5), you’re guaranteed consistent results across different machines and libraries. This isn’t true for eigendecomposition, where eigenvectors can differ by sign or be arbitrarily chosen when eigenvalues are repeated.

Intuitive Understanding: Think of Cholesky as finding the “square root” of a matrix

Why Lower Triangular? The triangular structure means each variable depends only on previous ones:

This sequential dependency structure makes computation efficient!

Two Key Applications:

1. Solving Linear Systems (faster than computing inverse):

# Problem: Solve Ax = b where A is positive definite

# Slow way: x = inv(A) @ b (NEVER DO THIS!)
# - Computing inverse: O(n³) operations
# - Numerically unstable
# - Loses sparsity structure

# Fast way using Cholesky:
L = np.linalg.cholesky(A)      # A = LL^T
# Now Ax = b becomes LL^T x = b

# Step 1: Solve Ly = b (forward substitution)
y = np.linalg.solve(L, b)

# Step 2: Solve L^T x = y (back substitution)
x = np.linalg.solve(L.T, y)

# Why faster? Triangular systems solve in O(n²) not O(n³)!

2. Generating Correlated Random Variables (Projects 4-5):

# Generate samples from N(mu, Sigma)

# The magic: If z ~ N(0, I), then Lz ~ N(0, LL^T) = N(0, Sigma)
L = np.linalg.cholesky(Sigma)
z = np.random.randn(n)          # Standard normal (uncorrelated)
x = mu + L @ z                  # Has mean mu, covariance Sigma

# Why does this work?
# - z has covariance I (independent unit variance)
# - Lz has covariance L·I·L^T = LL^T = Sigma
# - Adding mu shifts the mean without changing covariance

Progressive Problems: Positive Definiteness


Part 2: Advanced Topics for Your Projects

Priority: 🟢 Enrichment - Read as needed for specific projects

2.1 Singular Value Decomposition - The Swiss Army Knife {#svd-swiss-army}

Every matrix has a singular value decomposition:

A=UΣVTA = U\Sigma V^T

where:

SVD for Non-Square Matrices: For an m×nm \times n matrix AA:

Only min(m,n)\min(m,n) singular values exist; the rest of Σ\Sigma is padded with zeros. This is crucial for understanding rank and dimensionality reduction.

Geometric Intuition: Any matrix transformation can be broken into three steps:

  1. Rotate (by VTV^T): Align input to principal axes

  2. Stretch (by Σ\Sigma): Scale along each axis by σᵢ

  3. Rotate (by UU): Align to output space

This means ANY linear transformation—no matter how complex—is just rotate-stretch-rotate! This decomposition is unique (up to sign ambiguities) and always exists.

Understanding Rank Through SVD: The rank of a matrix equals the number of non-zero singular values. This tells you the true dimensionality:

The Pseudoinverse and Least Squares:

The pseudoinverse A+=VΣ+UTA^+ = V\Sigma^+ U^T where Σ+\Sigma^+ inverts non-zero singular values:

# Computing pseudoinverse via SVD
U, s, Vt = np.linalg.svd(A, full_matrices=False)

# Threshold small singular values (regularization)
threshold = 1e-10 * s.max()
s_inv = np.where(s > threshold, 1/s, 0)

# Construct pseudoinverse
A_pseudo = Vt.T @ np.diag(s_inv) @ U.T

# Now x = A_pseudo @ b gives:
# - Least-squares solution if overdetermined
# - Minimum-norm solution if underdetermined

Why SVD Beats Eigendecomposition:

PropertyEigendecompositionSVD
Works forSquare matrices onlyANY matrix shape
RequiresDiagonalizableAlways works
VectorsMay be complexAlways real for real matrices
ValuesCan be negative/complexAlways non-negative real
Numerical stabilityCan be unstableVery stable algorithms

The Deep Connection: For any matrix AA:

2.2 Block Matrices and the Schur Complement

Large systems often have natural block structure:

M=(ABCD)M = \begin{pmatrix} A & B \\ C & D \end{pmatrix}

Physical Motivation: Different parts of your system interact differently.

Concrete Example: Triple Star System Consider a close binary with a distant third star: $$\begin{pmatrix} F_{11} & F_{12} & F_{13} \ F_{21} & F_{22} & F_{23} \ F_{31} & F_{32} & F_{33} \end{pmatrix} =

(0strongweakstrong0weakweakweak0)\begin{pmatrix} \begin{array}{cc|c} 0 & \text{strong} & \text{weak} \\ \text{strong} & 0 & \text{weak} \\ \hline \text{weak} & \text{weak} & 0 \end{array} \end{pmatrix}

The 2×2 upper-left block represents strong binary interactions, while off-diagonal blocks show weak coupling to the distant star.

The Schur Complement in Action:

Let’s solve (ABCD)(xy)=(fg)\begin{pmatrix} A & B \\ C & D \end{pmatrix} \begin{pmatrix} x \\ y \end{pmatrix} = \begin{pmatrix} f \\ g \end{pmatrix}

Instead of solving the full system, we can eliminate yy:

  1. From bottom equation: y=D1(gCx)y = D^{-1}(g - Cx)

  2. Substitute into top: Ax+BD1(gCx)=fAx + BD^{-1}(g - Cx) = f

  3. Rearrange: (ABD1C)x=fBD1g(A - BD^{-1}C)x = f - BD^{-1}g

The matrix S=ABD1CS = A - BD^{-1}C is the Schur complement—it’s the “effective AA” after accounting for yy’s influence through DD.

Physical Intuition: Think of the Schur complement as “marginalizing out” variables. It’s like projecting 3D motion onto 2D while accounting for the 3rd dimension’s influence. When you eliminate variables, their effect doesn’t disappear—it gets encoded in the effective interactions between remaining variables.

Simple 2×2 Example:

(3124)=(ABCD)\begin{pmatrix} 3 & 1 \\ 2 & 4 \end{pmatrix} = \begin{pmatrix} A & B \\ C & D \end{pmatrix}

Schur complement of DD: S=3(1)(1/4)(2)=30.5=2.5S = 3 - (1)(1/4)(2) = 3 - 0.5 = 2.5

This 2.5 is the “effective resistance” in the first variable after the second adjusts optimally.

Why It Matters for Project 5 (GPs): When you add a new observation to a Gaussian Process:

2.3 The Jacobian Matrix: Local Linear Approximation {#jacobian-matrix}

For vector function f:RnRm\vec{f}: \mathbb{R}^n \to \mathbb{R}^m:

Jij=fixjJ_{ij} = \frac{\partial f_i}{\partial x_j}

Intuitive Understanding: Near any point, nonlinear functions look linear. The Jacobian is that linear approximation:

f(x+δx)f(x)+Jδx\vec{f}(\vec{x} + \delta\vec{x}) \approx \vec{f}(\vec{x}) + J\cdot\delta\vec{x}

Concrete 2D Example: For the transformation f(r,θ)=(rcosθ,rsinθ)f(r, \theta) = (r\cos\theta, r\sin\theta) (polar to Cartesian):

J=(xrxθyryθ)=(cosθrsinθsinθrcosθ)J = \begin{pmatrix} \frac{\partial x}{\partial r} & \frac{\partial x}{\partial \theta} \\ \frac{\partial y}{\partial r} & \frac{\partial y}{\partial \theta} \end{pmatrix} = \begin{pmatrix} \cos\theta & -r\sin\theta \\ \sin\theta & r\cos\theta \end{pmatrix}

The determinant J=r|J| = r tells you area scaling—this is why polar area elements are rdrdθr\,dr\,d\theta!

In Project 2 (N-body Stability): Near an equilibrium configuration s0\vec{s}_0, perturbations δs\delta\vec{s} evolve as:

d(δs)dt=Js0δs\frac{d(\delta\vec{s})}{dt} = J|_{\vec{s}_0} \cdot \delta\vec{s}

The eigenvalues of JJ determine the fate:

Example: For a star orbiting at Lagrange point L4:

2.4 Matrix Exponentials: Solving Linear Evolution {#matrix-exponentials}

The matrix exponential solves any linear ODE system:

dxdt=Ax    x(t)=eAtx(0)\frac{d\vec{x}}{dt} = A\vec{x} \implies \vec{x}(t) = e^{At}\vec{x}(0)

Three Ways to Understand eAte^{At}:

1. Series Definition (like scalar exponential):

eAt=I+At+(At)22!+(At)33!+...e^{At} = I + At + \frac{(At)^2}{2!} + \frac{(At)^3}{3!} + ...

2. Through Eigenvalues (if AA is diagonalizable): If A=PDP1A = PDP^{-1} where DD is diagonal with eigenvalues:

eAt=PeDtP1=P(eλ1teλ2t)P1e^{At} = Pe^{Dt}P^{-1} = P\begin{pmatrix} e^{\lambda_1 t} & & \\ & e^{\lambda_2 t} & \\ & & \ddots \end{pmatrix}P^{-1}

3. Physical Interpretation: eAte^{At} is the propagator—it evolves the system from time 0 to time tt.

Practical Computation:

# Never use the series directly—it's inefficient and can be inaccurate
# Instead, use specialized algorithms:

import scipy.linalg as la

A = np.array([[0, 1], [-1, -0.1]])  # Damped oscillator
t = 5.0

# Method 1: Direct computation (best for dense matrices)
expAt = la.expm(A * t)

# Method 2: Via eigendecomposition (good for understanding)
eigvals, eigvecs = la.eig(A)
D = np.diag(np.exp(eigvals * t))
expAt_eig = eigvecs @ D @ la.inv(eigvecs)

# Method 3: For solving ODEs, use ODE solvers instead!
# They're more efficient than computing e^{At} explicitly

Example: Damped Harmonic Oscillator: For x¨+γx˙+ω02x=0\ddot{x} + \gamma\dot{x} + \omega_0^2 x = 0, rewrite as first-order system:

ddt(xv)=(01ω02γ)(xv)\frac{d}{dt}\begin{pmatrix} x \\ v \end{pmatrix} = \begin{pmatrix} 0 & 1 \\ -\omega_0^2 & -\gamma \end{pmatrix} \begin{pmatrix} x \\ v \end{pmatrix}

The eigenvalues λ=γ±γ24ω022\lambda = \frac{-\gamma \pm \sqrt{\gamma^2 - 4\omega_0^2}}{2} determine behavior:

2.5 Matrix Norms: How Big is a Matrix?

Priority: 🟢 Enrichment - Crucial for understanding stability and convergence

Matrix norms measure “size” but different norms capture different aspects:

Frobenius Norm (total “energy”):

AF=i,jaij2=trace(ATA)||A||_F = \sqrt{\sum_{i,j} |a_{ij}|^2} = \sqrt{\text{trace}(A^T A)}

Spectral Norm (worst-case amplification):

A2=maxx=1Ax=σmax||A||_2 = \max_{||\vec{x}||=1} ||A\vec{x}|| = \sigma_{\max}

Why Spectral Norm Matters:

  1. Stability: System xn+1=Axnx_{n+1} = Ax_n is stable iff A2<1||A||_2 < 1

  2. Error amplification: Input error ϵ\epsilon → output error ≤ A2ϵ||A||_2 \cdot \epsilon

  3. Condition number: κ=A2A12=σmax/σmin\kappa = ||A||_2 \cdot ||A^{-1}||_2 = \sigma_{\max}/\sigma_{\min}

  4. Convergence rate: Iterations converge like (A2)n(||A||_2)^n

Example: Why Deep Networks Are Hard to Train: Consider a 10-layer network where each layer multiplies by matrix WW:

This is why techniques like batch normalization and careful initialization are crucial.

2.6: Numerical Implementation Examples


Part 3: Numerical Reality - When Mathematics Meets Silicon

Priority: 🔴 Essential - Critical for debugging all projects

7.1 Condition Numbers Revisited: Statistical Context {#condition-numbers}

Building on Module 0a’s introduction to condition numbers, let’s explore their specific implications for statistical computations:

Condition Numbers in Statistical Methods:

For covariance matrices, the condition number has special meaning:

κ(Σ)=λmaxλmin\kappa(\Sigma) = \frac{\lambda_{\max}}{\lambda_{\min}}

This ratio tells you:

MCMC Implications (Project 4):

# Poorly conditioned proposal covariance slows convergence
Sigma_bad = np.array([[1, 0.999], [0.999, 1]])  # κ ≈ 2000
# MCMC needs ~κ steps to explore the distribution

# Well-conditioned after reparameterization
L = np.linalg.cholesky(Sigma_bad)
# Sample in transformed space where κ = 1, then transform back

GP Implications (Project 5):

# Kernel matrices become ill-conditioned when:
# 1. Data points are very close (duplicate measurements)
# 2. Length scale is too small (overfitting)
# 3. Noise term is too small (numerical instability)

# Check your GP's condition number:
kappa = np.linalg.cond(K)
if kappa > 1e12:
    print("Warning: GP may give nonsense predictions!")
    # Solutions:
    # - Increase noise term σ²
    # - Remove duplicate points
    # - Adjust length scale

7.2 When Statistical Computations Fail

Understanding failure modes specific to statistical methods helps you recognize and fix problems:

Covariance Matrix Not Positive Semi-Definite:

# Computing sample covariance with insufficient data
n_samples = 3
n_features = 5  # More features than samples!

X = np.random.randn(n_samples, n_features)
Sigma = np.cov(X.T)  # Shape: (5, 5)

# This is rank-deficient: rank ≤ min(n_samples-1, n_features) = 2
print(f"Rank: {np.linalg.matrix_rank(Sigma)}")  # 2, not 5!

# Cholesky will fail:
try:
    L = np.linalg.cholesky(Sigma)
except np.linalg.LinAlgError:
    print("Covariance is singular!")

    # Fix: Regularization (add prior belief about variance)
    Sigma_reg = Sigma + 0.01 * np.eye(n_features)
    L = np.linalg.cholesky(Sigma_reg)  # Now works

Numerical Underflow in Likelihoods:

# Computing likelihood of many data points
log_likelihoods = []
for x in data:
    # BAD: Probability underflows to zero
    p = multivariate_normal.pdf(x, mu, Sigma)

    # GOOD: Work in log space
    log_p = multivariate_normal.logpdf(x, mu, Sigma)
    log_likelihoods.append(log_p)

# Sum log-likelihoods instead of multiplying probabilities
total_log_likelihood = np.sum(log_likelihoods)

Cholesky Decomposition Failures:

def safe_cholesky(A, max_tries=5):
    """
    Robust Cholesky with automatic regularization.
    """
    jitter = 1e-6

    for i in range(max_tries):
        try:
            L = np.linalg.cholesky(A + jitter * np.eye(len(A)))
            return L
        except np.linalg.LinAlgError:
            if i == max_tries - 1:
                # Last resort: eigenvalue thresholding
                eigvals, eigvecs = np.linalg.eigh(A)
                eigvals = np.maximum(eigvals, jitter)
                A_fixed = eigvecs @ np.diag(eigvals) @ eigvecs.T
                return np.linalg.cholesky(A_fixed)
            else:
                jitter *= 10  # Increase regularization

    return None

7.3 Statistical-Specific Numerical Guidelines


Part 4: Bridge to Machine Learning

Priority: 🟡 Important - Helps see the big picture

8.1 From Classical to Statistical to Learning

Linear algebra provides the mathematical continuity across your entire journey. Watch how the same mathematical objects evolve in meaning as you progress through different computational paradigms:

The Evolution of Mathematical Objects:

Mathematical ObjectClassical Physics (Projects 1-3)Statistical Methods (Projects 4-5)Machine Learning (Final Project)
VectorsPositions, velocities, forcesParameter samples, data pointsFeature vectors, gradients
MatricesTransformations, rotationsCovariance, kernelsWeight matrices, Jacobians
EigenvaluesStability, oscillation modesConvergence rates, principal componentsLearning rates, network dynamics
Dot productsWork, projectionsCorrelations, similaritiesAttention scores, kernels
NormsDistances, magnitudesErrors, uncertaintiesLoss functions, regularization
DecompositionsSolving dynamicsStatistical inferenceNetwork compression, analysis

This isn’t coincidence—it’s the deep unity of mathematics. The same linear algebra that predicts planetary orbits also powers Google’s search algorithm and ChatGPT’s language understanding.

8.2 Matrix Calculus for Neural Networks {#matrix-calculus-nn}

Priority: 🟡 Important for Final Project

Your final project requires understanding how gradients flow through matrix operations. Here’s the essential matrix calculus:

Basic Matrix Derivatives:

For scalar function ff of matrix WW:

fW is a matrix with [fW]ij=fWij\frac{\partial f}{\partial W} \text{ is a matrix with } \left[\frac{\partial f}{\partial W}\right]_{ij} = \frac{\partial f}{\partial W_{ij}}

Key Results You’ll Need:

  1. Linear layer: f(W)=Wxf(W) = W\vec{x}

    W(Wx)=xT\frac{\partial}{\partial W}(W\vec{x}) = \vec{x}^T
  2. Quadratic form: f(W)=xTWxf(W) = \vec{x}^T W \vec{x}

    W(xTWx)=xxT\frac{\partial}{\partial W}(\vec{x}^T W \vec{x}) = \vec{x}\vec{x}^T
  3. Frobenius norm: f(W)=WF2=trace(WTW)f(W) = ||W||_F^2 = \text{trace}(W^T W)

    WWF2=2W\frac{\partial}{\partial W}||W||_F^2 = 2W

Chain Rule for Matrices:

For composition f(g(W))f(g(W)):

fWij=k,lfgklgklWij\frac{\partial f}{\partial W_{ij}} = \sum_{k,l} \frac{\partial f}{\partial g_{kl}} \frac{\partial g_{kl}}{\partial W_{ij}}

This is what happens during backpropagation—gradients flow backward through the network via chain rule!

Example: Simple Two-Layer Network:

# Forward pass:
# Layer 1: z₁ = W₁x + b₁
# Activation: a₁ = relu(z₁)
# Layer 2: z₂ = W₂a₁ + b₂
# Loss: L = ||z₂ - y||²

# Backward pass (what JAX computes automatically):
# ∂L/∂z₂ = 2(z₂ - y)
# ∂L/∂W₂ = (∂L/∂z₂) @ a₁ᵀ
# ∂L/∂a₁ = W₂ᵀ @ (∂L/∂z₂)
# ∂L/∂z₁ = (∂L/∂a₁) ⊙ relu'(z₁)  # Element-wise product
# ∂L/∂W₁ = (∂L/∂z₁) @ xᵀ

# The pattern: gradients with respect to weights = (upstream gradient) @ (input)ᵀ

8.3 Connecting Everything: The Big Picture

The techniques in this module power every major simulation and discovery in modern astrophysics:

Your Learning Journey:

  1. Classical (Projects 1-3): Deterministic systems, exact solutions

  2. Statistical (Projects 4-5): Uncertainty quantification, probabilistic inference

  3. Learning (Final Project): Pattern discovery, function approximation

The Mathematical Thread:

Why This Matters: Every breakthrough in computational astrophysics builds on these foundations:


Numerical Recipes Quick Reference


Glossary of Terms

All terms introduced in this module, arranged alphabetically:


Main Takeaways


Essential NumPy/SciPy Reference for Module 0b

Priority: 🔴 Essential - Keep this open while coding Projects 4-6

Statistical Linear Algebra Functions

Task

Function

When to Use

Project

Cholesky decomposition

np.linalg.cholesky(A)

Positive definite systems

P4-5

Check positive definite

np.linalg.eigvalsh(A).min() > 0

Before Cholesky

P4-5

Sample covariance

np.cov(X.T)

Compute from data

P4

Multivariate normal

scipy.stats.multivariate_normal

Sampling, likelihood

P4-5

Log probability

.logpdf() not .pdf()

Avoid underflow

P4-5

Pseudoinverse

np.linalg.pinv(A)

Rank-deficient systems

P5

SVD

np.linalg.svd(A)

Decomposition, PCA

All

Matrix exponential

scipy.linalg.expm(A)

Time evolution

P2,5

Force symmetry

(A + A.T) / 2

Fix rounding errors

P4-5

Add regularization

A + lambda * np.eye(n)

Improve conditioning

P4-5

Numerical Stability Patterns

Problem

Solution

Code Pattern

Cholesky fails

Add jitter

K + 1e-6 * np.eye(n)

Negative eigenvalues

Threshold to zero

np.maximum(eigvals, 0)

Probability underflow

Work in log space

Use logpdf, sum logs

Ill-conditioned

Regularize

Add to diagonal

Not symmetric

Force symmetry

(A + A.T) / 2

Different scales

Standardize

(X - mean) / std


Quick Reference Card


With these foundations, you’re equipped to tackle the statistical and machine learning challenges ahead!