Flow Matching & Diffusion Models · Lecture 1 of 5 · MIT 6.S184

Flow & Diffusion Models

Stable Diffusion, Sora, AlphaFold3, Movie Gen — the same idea underneath all of them. Turn pure noise into a sample of your data by following a differential equation. This lecture builds the machine: ODEs, SDEs, and how to simulate them.

Prerequisites: derivatives & the chain rule, vectors, and what a Gaussian is. A little Python. No prior generative-model knowledge assumed.
10
Chapters
4
Simulations
3
Code Labs

Chapter 0: Why — a Machine That Invents Dogs

The whole class in one sentence. You will learn to build a model that takes a draw of pure random noise and continuously deforms it — following arrows in space, like iron filings following a magnetic field — until what comes out the other end is a brand-new image (or video, or protein) that looks like it belongs in your training set. Flow and diffusion models are exactly this. They power Stable Diffusion, OpenAI's Sora, Meta's Movie Gen, and DeepMind's AlphaFold3.

Type "a photo of a dog" into an image generator and out comes a dog that has never existed. The model did not retrieve it from a database. It generated it — it produced a new object. That is the deceptively simple thing this entire course is about: algorithms that generate new objects.

To engineer something, you must first say precisely what "generate a good dog photo" means. Vague taste won't compile. So Lecture 1 has two jobs, and they map exactly onto the two sections of the original MIT slides:

The two jobs of this lecture.
  • Section 1 — From generation to sampling. Formalize "generate an image" as "draw a sample from a probability distribution." This is the conceptual move that makes everything else math.
  • Section 2 — Flow and diffusion models. Build the actual generative machine out of differential equations — ODEs (deterministic) and SDEs (noisy) — and learn to simulate them on a computer.

By the end you will be able to take a standard Gaussian blob, push it through a hand-written numerical integrator, and watch it become a target shape — the literal skeleton of how Stable Diffusion samples an image. We have no neural network yet (that arrives in Lectures 2–4, where we learn how to train the arrows). Today we build and drive the engine.

Where this sits. This is Lecture 1 of MIT's 6.S184: Generative AI with Stochastic Differential Equations (Holderrieth & Shprints). The series: (1) Flow & Diffusion Models → (2) Flow Matching → (3) Score Matching & Guidance → (4) Architectures & Latent Spaces → (5) Discrete Diffusion. The companion deep-dive on this site is the Introduction to Flow Matching and Diffusion Models lecture-notes Veanor.
What is the single conceptual move that lets us turn the fuzzy goal "generate a good image" into something we can build an algorithm for?

Chapter 1: Generation Is Sampling

Step one is purely about representation. A computer cannot store "a dog" — it stores numbers. So every object we ever want to generate, we first flatten into a vector of real numbers, a point in some space Rd (d-dimensional space):

Everything is a vector.
  • Image: height H × width W × 3 color channels (RGB). A 256×256 photo is a vector of d = 256·256·3 ≈ 196,608 numbers.
  • Video: T frames, each an image — stack them: d = T·H·W·3.
  • Molecule: N atoms, each with 3 coordinates — d = 3N numbers.

So our target object is a point z ∈ Rd. Now: what makes a good z? The slides line up four dog images — "useless," "bad," "wrong animal," "great!" — and ask the key question: those are subjective words. Can we make "good" objective?

The trick: let frequency define quality

Here is the move. Imagine the gigantic distribution of all images on the internet. A crisp, well-lit dog photo is very likely to appear there; a smear of random static is essentially impossible. So we declare:

How good an image is ≈ how likely it is under the data distribution. "Good" is no longer a matter of taste — it is probability mass. We call this distribution the data distribution and write its probability density as pdata. High density = the kind of object we want; near-zero density = garbage.

With that definition, generation becomes sampling: to "generate an object," draw a fresh sample from the data distribution.

z ∼ pdata

Read that as: "z is sampled from p-data." A great dog photo is a high-probability draw; we want our algorithm to land in those high-density regions.

The catch that makes this hard — and is the reason the whole course exists. We do not know the density pdata. Nobody can write down the formula for "how likely is this exact arrangement of 196,608 pixels to be a real internet image." All we ever get is a dataset — a finite pile of samples already drawn from pdata (e.g. images scraped from the web, videos from YouTube, structures from the Protein Data Bank). We must learn to sample new points from a distribution we can only ever see examples of.

Spell out the dataset precisely: it is a finite collection

z1, z2, …, zN   each  zi ∼ pdata

That is the only grip we have on pdata. Training will mean: squeeze as much information as we can out of these samples so that, at the end, we can produce more samples that look like they came from the same source.

Conditioning on a prompt

"A photo of a dog" vs. "a photo of a cat" vs. "a landscape" are different distributions. So we generalize: a condition variable y (the prompt, a class label, a text caption) selects a conditional data distribution pdata(· | y). Generating from a prompt means sampling that conditional distribution:

z ∼ pdata(· | y)
Strategy for the course. We first master unconditional generation (sample pdata with no prompt). Then in Lecture 3 we learn guidance — a clean trick that turns an unconditional model into one that obeys a prompt y. Walk before you run.
Why can't we just write down pdata(z) and sample it directly with a textbook method?

Chapter 2: The Plan — Push Noise Into Data

We can't sample pdata directly. But there is one distribution we can always sample, trivially, as many times as we like: a standard Gaussian. So here is the entire architecture of every flow and diffusion model:

The generative-model recipe. Pick an initial distribution pinit that is easy to sample (almost always the standard Gaussian, written N(0, Id) — mean zero, identity covariance). Then learn a transformation that converts a sample from pinit into a sample from pdata.
X0 ∼ pinit   ⟶[ generative model ]   X1 ∼ pdata

Read it left to right: start at a random Gaussian point X0 at time t = 0; transform it; arrive at a data sample X1 at time t = 1. The whole game is to build that arrow in the middle. The defining choice of flow/diffusion models: the transformation is not one big jump — it is a continuous journey through time, governed by a differential equation.

From a Gaussian blob to a target shape

A cloud of points starts as a standard Gaussian (left, t = 0). Press Generate to transport every point continuously over time t = 0 → 1 toward a target distribution (a ring of clusters — our toy "data"). This is exactly what a trained flow model does to make images; here the arrows are hand-designed so you can see the motion. Notice: every output point lands in a high-density region of the target.

t = 0.00 — pure Gaussian noise. Press Generate.

Two questions now stand between us and a working generator, and they are the rest of this lecture:

  1. What mathematical object describes "transport points continuously through time"? Answer: a differential equation — an ODE for smooth deterministic motion (Chapters 3–5), or an SDE for noisy motion (Chapters 6–8).
  2. How do we run it on a computer? Answer: numerical simulation — tiny time-steps (the Euler method), Chapter 4.
Honest scope check. Today the arrows are given to us (toy examples). The deep question — how do you find the arrows that turn Gaussian noise into dog photos? — is the training problem, and it is the subject of Lecture 2 (Flow Matching) and Lecture 3 (Score Matching). You cannot train what you cannot simulate, so we build the simulator first.
Why do all these models start from a Gaussian pinit rather than some other distribution?

Chapter 3: ODEs, Vector Fields & Flows

"Transport a point continuously through time" is the job of an ordinary differential equation (ODE). Three objects describe the same idea from three angles. Get all three and the rest of the course is downhill.

1. The trajectory

A trajectory is the path a single point takes. It is a function of time:

X : [0, 1] → Rd,    t ↦ Xt

For each time t between 0 and 1, Xt is where the point is. (We use the time window [0,1] by convention — t = 0 is noise, t = 1 is data.)

2. The vector field — the arrows

A vector field u assigns, to every location x and every time t, a velocity — an arrow saying "if you are here at this time, move this way, this fast":

u : Rd × [0, 1] → Rd,    (x, t) ↦ ut(x)

Think of ut(x) as a wind map, or the current in a river that changes over the day. It is the only thing we get to design — later, a neural network is this vector field.

3. The ODE itself — "follow the arrows"

The ODE is the rule connecting the two: at every instant, the trajectory's velocity must equal the arrow at its current location. "Velocity" is the time-derivative, so:

d⁄dt Xt = ut(Xt)      (the ODE)
X0 = x0      (the initial condition)

The first line says "go where the arrow at your feet points." The second pins down where you start. Without a starting point the path is not determined — the same wind map produces a different journey from a different origin.

The flow — bookkeeping for all starting points at once. Ask: "If I start at x0 and follow the arrows, where am I at time t?" The answer is a function called the flow, written ψt(x0) (psi). It packages the solution for every start: ψ0(x0) = x0 (you begin where you begin), and d⁄dt ψt(x0) = utt(x0)) (it obeys the ODE). The trajectory of one point is just Xt = ψt(X0).

So: vector fields define ODEs, whose solutions are flows. Three names, one underlying object. The vector field is the wind; the ODE is the law "drift with the wind"; the flow is the resulting map that warps all of space, like a fluid carrying a grid of dye.

A vector field and the trajectory it produces

Blue arrows are the vector field ut(x). The orange dot starts where you click (or press Drop point) and follows the arrows — that traced path is the ODE trajectory. Switch fields to feel how the arrows dictate the motion. The "contracting" field is u(x) = −θx, the running example we solve by hand next.

field
Click the canvas to drop a point, or press Drop point.

Do solutions even exist? (Yes — relax)

A careful person asks: given a vector field, is there always a trajectory, and is it the only one? The Picard–Lindelöf theorem answers yes, under mild conditions:

Theorem (existence & uniqueness). If the vector field ut(x) is continuously differentiable with bounded derivatives (more generally, Lipschitz), then the ODE has a unique solution — a flow ψt exists, and it is a diffeomorphism (a smooth, smoothly-invertible warp of space).

Why you can stop worrying: we will parameterize u with a neural network, and neural networks always have bounded derivatives. So in every case we care about, the flow exists and is unique. This theorem is good news you can file away, not an obstacle.

Worked example: the linear field, solved exactly

Pick the simplest non-trivial field, a contraction toward the origin: ut(x) = −θx for some θ > 0. Claim: the flow is

ψt(x0) = exp(−θt) · x0

Let's prove it by checking the two defining properties of a flow — every step shown:

Step 1 — initial condition. At t = 0: ψ0(x0) = exp(−θ·0)·x0 = exp(0)·x0 = 1·x0 = x0. ✓ It starts in the right place.

Step 2 — the ODE. Differentiate in t. Using d⁄dt exp(−θt) = −θ·exp(−θt) (chain rule):
d⁄dt ψt(x0) = d⁄dt [exp(−θt) x0] = −θ·exp(−θt)·x0 = −θ·ψt(x0) = utt(x0)). ✓

Both conditions hold, so by uniqueness this is the flow. The point decays exponentially toward 0.

Plug in numbers (we'll reuse these in the code lab): θ = 1, x0 = 2. Then ψt(2) = 2·exp(−t). At t = 1: 2·exp(−1) = 2⁄e ≈ 0.7358. That exact number is our ground truth for testing a numerical integrator.

A vector field, an ODE, and a flow are described as "three descriptions of the same object." Which statement captures the relationship?

Chapter 4: Simulating an ODE — Euler's Method

The linear field had a closed-form flow. Almost nothing else does — a neural-network vector field has no formula for its flow. So we simulate: approximate the continuous journey by many tiny straight steps. The simplest, most intuitive scheme is the Euler method.

The idea is one line of common sense. You are at Xt. The arrow there says "move in direction ut(Xt)." Take a small step of size h in that direction:

Xt+h = Xt + h · ut(Xt)

Here h = 1/n is the step size and n is the number of steps. Repeat from t = 0 up to t = 1. That's it — "small step in the direction of the arrow," over and over.

Where this comes from (and why it's only approximate). The derivative is the limit of a difference quotient: d⁄dt Xt ≈ (Xt+h − Xt)/h for small h. Set that equal to ut(Xt) and rearrange → the Euler update. The arrow is only exactly right at the start of the step; as you move, the true arrow changes, so a finite h leaves an error. Shrink h and the error shrinks — at the cost of more steps.

Worked example: Euler on the linear field, by hand

Field u(x) = −x (so θ = 1), start x0 = 2, integrate to t = 1. The exact answer (Chapter 3) is 0.7358. Watch Euler approach it as we halve the step:

Coarse: h = 0.5 (2 steps).
X0 = 2.
X0.5 = 2 + 0.5·(−2) = 2 − 1 = 1.0.
X1.0 = 1 + 0.5·(−1) = 1 − 0.5 = 0.5.   (true 0.7358 — error 0.236)

Finer: h = 0.25 (4 steps).
2 → 2+0.25(−2)=1.5 → 1.5+0.25(−1.5)=1.125 → 1.125+0.25(−1.125)=0.84375 → 0.84375+0.25(−0.84375)= 0.6328.  (error 0.103)

Halving h roughly halved the error — Euler is a first-order method (error shrinks proportionally to h). That trade-off — accuracy vs. number of steps — is the central practical knob in diffusion sampling, where each step is an expensive neural-network call.

Step size vs. error — Euler chasing the true flow

The smooth teal curve is the exact flow 2·exp(−t). The orange polyline is the Euler approximation. Drag the slider to change the number of steps n (step size h = 1/n). Few steps → jagged, large endpoint error; many steps → the polyline hugs the curve. The readout shows the endpoint error.

steps n4
A better step for free: Heun's method. Euler trusts the arrow at the start of the step. Heun takes a tentative Euler step to peek at the arrow at the end, then steps with the average of the two arrows: first X′ = Xt + h·ut(Xt), then Xt+h = Xt + (h/2)·[ut(Xt) + ut+h(X′)]. Averaging the start and end slopes cancels much of the first-order error (it is second-order). For this course, plain Euler is enough — but real samplers use such higher-order schemes to cut the number of network calls.

Now build the integrator yourself. This is the literal engine of every sampler in the course — the step function you write here is reused, unchanged, all the way to Lecture 4.

In the hand calculation, halving the Euler step size (h: 0.5 → 0.25) roughly halved the endpoint error. What does that tell you about the Euler method?

Chapter 5: Flow Models — an ODE With a Neural-Net Engine

We now assemble the first real generative model. Take the ODE machinery and make two changes that turn it from "a wind map" into "an image generator."

Change 1 — make the vector field learnable. Replace the hand-designed ut(x) with a neural network uθt(x), where θ are the network weights (don't confuse this θ with the linear-field constant). It takes a location x and a time t, and outputs a velocity vector. Training (Lecture 2) is the art of choosing θ so the arrows transport noise to data.
Change 2 — make the start random. An ODE is fully deterministic: fix the start, you fix the whole path. But we need random samples. The fix is delightfully simple — make only the initial point random by drawing it from pinit = N(0, Id). Randomness enters once, at t = 0; the journey afterward is deterministic.

Putting it together, a flow model is the ODE:

X0 ∼ pinit     (random Gaussian start)
d⁄dt Xt = uθt(Xt)     (follow the learned arrows)

and the goal of training is to make the endpoint land on the data distribution:

X1 ∼ pdata
The name is a little misleading — read this twice. It's called a flow model, but the neural network parameterizes the vector field (the arrows), not the flow (the endpoint map). To actually get a sample — to compute the flow — you must simulate the ODE with Euler. There is no shortcut: sampling = run the integrator. That's why "sampling is slow" is the defining complaint about diffusion/flow models, and why people obsess over fewer, bigger steps.

Algorithm 1 — Sampling from a flow model

This is the entire inference procedure. Memorize its shape; everything in the course either trains the network inside it or makes its steps cheaper.

# Sample from a flow model with the Euler method
# Input: trained vector-field network u_theta(x, t); number of steps n
t = 0.0
h = 1.0 / n                      # step size
x = sample_gaussian()            # X_0 ~ p_init = N(0, I)
for i in range(n):
    x = x + h * u_theta(x, t)    # one Euler step along the learned arrows
    t = t + h
return x                        # X_1, a sample of the data distribution

Read it against Algorithm 1 in the slides: draw a Gaussian, then loop n Euler steps, return the endpoint. The only difference from Chapter 4's hand example is that u_theta is a trained network instead of −θx. The "Generate" button in Chapter 2 ran exactly this loop with a toy field.

Data-flow check (Concept + Realization). Shapes: x is a vector in Rd (e.g. the 196,608 numbers of an image). u_theta(x, t) takes that vector plus a scalar time and returns another Rd vector — same shape as x, because it's a velocity for each coordinate. Each Euler step adds h·velocity, nudging all d numbers at once. After n steps the pixel-vector has been transported from "Gaussian static" to "a dog." n is typically 20–1000 network evaluations — that is the inference cost.
In a flow model, what exactly does the neural network compute, and what do you have to do to turn its output into an image?

Chapter 6: Randomness — Brownian Motion

Flow models are deterministic after the start. Diffusion models add randomness at every step. To build them we need the canonical source of continuous randomness: Brownian motion (also called the Wiener process, hence the symbol W — after Norbert Wiener, who taught at MIT).

Picture a stochastic process: a trajectory that is random. Run it twice from the same start and you get two different paths, because the dynamics themselves roll dice. Brownian motion is the continuous-time limit of a random walk — the path a speck of pollen traces as water molecules kick it.

Definition of Brownian motion W. It starts at W0 = 0, has continuous paths, and satisfies two properties:
  • Normal increments: for any 0 ≤ s < t, the jump Wt − Ws is Gaussian with mean 0 and variance equal to the elapsed time, (t − s). Wait longer, spread more — linearly.
  • Independent increments: jumps over non-overlapping time intervals are statistically independent — the future increment doesn't care about the past path.

How do we simulate it? Discretize time into steps of size h. Each step, add a Gaussian kick whose variance is h (matching "variance = elapsed time"). Since standard normal noise ε has variance 1, scaling by √h gives variance h:

Wt+h = Wt + √h · εt,    εt ∼ N(0, Id)
The √h is the whole subtlety — don't write h. Variance adds over independent steps. After n = t/h steps, total variance = n · (variance per step). If each step has variance h, total = (t/h)·h = t. ✓ — exactly the defining law Var[Wt] = t. If you mistakenly scaled by h (variance h² per step), total variance would be (t/h)·h² = t·h → 0 as h shrinks: the path would freeze. The square root is what makes the continuous limit non-degenerate. (Fun fact: Brownian paths are continuous but infinitely long — you could draw one without lifting your pen, but never finish.)
Brownian motion — many walkers spreading like √t

Each thin line is one simulated path of 1-D Brownian motion, all starting at 0. They fan out over time; the dashed envelope is ±√t (one standard deviation), the theoretical spread. Press Resimulate for fresh randomness. The readout reports the empirical variance at the final time — it should sit near t.

Now simulate it yourself and confirm the signature law Var[Wt] = t — the property that defines Brownian motion.

When discretizing Brownian motion, why must the per-step kick be scaled by √h and not h?

Chapter 7: SDEs — ODEs With a Noise Term

A stochastic differential equation (SDE) is just an ODE with Brownian noise stirred in at every step. To see exactly where the noise slots in, we first rewrite the ODE without derivatives (because for a random, jagged path the derivative no longer exists in the usual sense).

Rewriting the ODE as small steps

Start from d⁄dt Xt = ut(Xt). Use the definition of the derivative as a difference quotient and rearrange — every step shown:

d⁄dt Xt = ut(Xt)  ⇔  (1/h)(Xt+h − Xt) = ut(Xt) + Rt(h)
⇔  Xt+h = Xt + h·ut(Xt) + h·Rt(h)

where Rt(h) is an error term that vanishes as h → 0. This just restates Euler: "each instant, take a small step of size h in direction ut." Nothing new — but now there are no derivatives, so we can add randomness.

Adding the noise

Amend the update: take the deterministic step and a Brownian kick, scaled by a diffusion coefficient σt ≥ 0 that controls how much noise to inject:

Xt+h = Xt + h·ut(Xt) [deterministic] + σt(Wt+h − Wt) [stochastic] + h·Rt(h)

This is the Euler–Maruyama update — the SDE analogue of Euler. Since the Brownian increment Wt+h − Wt = √h·ε (Chapter 6), in code it reads:

Xt+h = Xt + h·ut(Xt) + σt·√h·ε,    ε ∼ N(0, Id)

Mathematicians write the same thing in the compact symbolic form (just shorthand for the limit of the update above):

dXt = ut(Xt) dt + σt dWt

Here ut is the drift (the deterministic pull) and σt is the diffusion (the noise strength). Two facts make SDEs easy to live with:

Two key facts.
  • Every ODE is an SDE with σt = 0. Turn the noise off and you recover deterministic flow. So from now on we can speak of SDEs and treat ODEs as the special, noiseless case.
  • Existence & uniqueness still holds (Theorem 5): if the drift is nice (Lipschitz) and σt is continuous, a unique solution process exists. But there is no flow map anymore — Xt isn't determined by X0 alone, because the path keeps rolling dice.

You already wrote the deterministic x + h*drift step (Chapter 4) and the √h Brownian kick (Chapter 6). The next code lab (Chapter 8) snaps them together into Euler–Maruyama — the complete diffusion sampler.

In the SDE update Xt+h = Xt + h·ut(Xt) + σt·√h·ε, what is the role of σt, and what happens when σt = 0?

Chapter 8: Diffusion Models & the Ornstein–Uhlenbeck Process

A diffusion model is a generative model built from an SDE: random Gaussian start, a learned drift uθt, a noise schedule σt, simulated with Euler–Maruyama. To build intuition for how drift and noise interact, we study the cleanest non-trivial SDE — the Ornstein–Uhlenbeck (OU) process.

The OU process. Constant linear drift ut(x) = −θx (a spring pulling toward the origin, θ > 0) and constant diffusion σt = σ. Two forces fight: the drift pulls in toward 0; the noise pushes out. Their balance produces a stationary distribution — a steady-state spread the process settles into and never leaves.
dXt = −θXt dt + σ dWt

The remarkable result: the OU process converges to a Gaussian whose variance is set by the tug-of-war between pull and push:

Xt ⟶[t→∞] N(0, σ²⁄(2θ))

Sanity-check the extremes. More noise σ → wider stationary spread (σ² on top). Stronger pull θ → tighter spread (2θ on the bottom). And if σ = 0 (no noise), the variance is 0 — every path collapses deterministically to the origin, recovering the contracting flow from Chapter 3. Same equation, dial in between.

Worked number. Take θ = 0.5, σ = 1. Stationary variance = σ²⁄(2θ) = 1⁄(2·0.5) = 1⁄1 = 1.0 — standard deviation 1.0. We'll verify this empirically in the code lab by simulating thousands of walkers and measuring their spread once they settle.
OU process — drift vs. noise, and the stationary band

Walkers start spread out; the drift pulls them toward 0 while the noise jostles them. Watch them settle into a steady band whose half-width is the theoretical std √(σ²/2θ) (dashed lines). Drag θ (pull strength) and σ (noise). Set σ = 0 to see the deterministic flow collapse to the origin — an ODE is just an SDE with the noise off.

pull θ0.50
noise σ1.00
Why the OU process matters for real diffusion models. Run OU forward from any data point and it forgets everything, decaying to that simple Gaussian N(0, σ²/2θ) — this is the "noising" / forward process that turns data into noise. Diffusion models learn to run an SDE that reverses it: start from the easy Gaussian and drift back toward data. The OU process is the textbook noising process; the whole training problem (Lecture 3) is learning the drift that undoes it.

Now assemble everything: implement the OU drift and the full Euler–Maruyama step, then verify the stationary variance σ²/(2θ).

ODE sampling vs. SDE sampling

The same target can often be reached by an ODE (deterministic, σ = 0) or an SDE (stochastic, σ > 0). ODE sampling is reproducible and usually needs fewer steps; SDE sampling injects fresh noise each step, which can improve sample diversity and quality and "self-correct" errors. Lecture 3 shows the two are deeply linked through the score function. For now: flow = ODE, diffusion = SDE, and ODE is the σ = 0 corner of the SDE world.

For the OU process with pull θ and noise σ, increasing θ while holding σ fixed does what to the stationary distribution, and why?

Chapter 9: Connections & Cheat Sheet

You built the entire engine of modern generative AI from first principles — with no neural network yet. Here is the whole lecture on one page.

The spine. Generation = sampling pdata (which we only see via examples). Strategy: transport an easy Gaussian pinit to pdata by a differential equation. Flow model = ODE with a learned vector field; sample by Euler. Diffusion model = SDE (ODE + Brownian noise); sample by Euler–Maruyama. ODE is the σ = 0 special case of an SDE.

Cheat sheet

Vector field / ODE / flow. ut(x) = velocity arrow at (x,t). ODE: d⁄dt Xt = ut(Xt), X0 = x0. Flow ψt(x0) = solution for all starts; Xt = ψt(X0). Exists & unique when u is Lipschitz (always true for nets).

Linear example. u(x) = −θx ⇒ ψt(x0) = exp(−θt)·x0.

Euler. Xt+h = Xt + h·ut(Xt), h = 1/n. First-order: error ∝ h.

Brownian motion. W0 = 0; Wt − Ws ~ N(0, (t−s)I), independent increments. Simulate: Wt+h = Wt + √h·ε. Var[Wt] = t.

SDE / Euler–Maruyama. dXt = ut(Xt)dt + σtdWt. Step: Xt+h = Xt + h·ut(Xt) + σt·√h·ε.

OU process. dXt = −θXtdt + σdWt ⇒ stationary N(0, σ²/(2θ)). σ = 0 recovers the deterministic flow.

Sampling (Algorithm 1). Draw X0 ~ N(0,I); loop n Euler(–Maruyama) steps; return X1. The net parameterizes the vector field, not the flow — sampling means simulating.

What's next in this series

Related lessons on this site

The Feynman test. Open the original Lecture 1 slides. Can you now explain every line — "generation as sampling," the vector-field/ODE/flow trio, Euler's update and its error, Brownian motion's √h, the Euler–Maruyama step, and the OU stationary variance — to a friend, from memory, and write the sampler in ten lines of Python? If yes, you've got Lecture 1.
One sentence: what is the relationship between a flow model and a diffusion model?