The cloud’s law of motion Fokker–Planck, the probability-flow ODE, and the reverse SDE.
One PDE rules the cloud
Motivation
One SDE path is jagged and unrepeatable. The cloud of paths is a density p_t(\mathbf x) that evolves deterministically.
Drift transports it, diffusion smooths it.
Two payoffs: a deterministic twin ODE, and a reverse SDE.
Both need one unknown: the score\nabla\log p_t.
01
From paths to densities
the two viewpoints and the vector-calculus toolkit
One particle, or the whole cloud
Two viewpoints
The Lagrangian view follows one realization; the Eulerian view tracks the time-marginal p_t(\mathbf x), which averages over all noise and is therefore reproducible. A hundred thousand OU walkers land on the analytic Gaussians:
t=0.1: cloud mean +1.8121 (analytic +1.8097), std 0.4275 (analytic 0.4258)
t=0.5: cloud mean +1.2137 (analytic +1.2131), std 0.7987 (analytic 0.7951)
t=2.0: cloud mean +0.2750 (analytic +0.2707), std 0.9889 (analytic 0.9908)
Three facts from vector calculus
The toolkit
Divergence\nabla\cdot\mathbf v: net flux per unit volume.
Laplacian\Delta h: how far h sits below its neighborhood average.
By parts\int h\,(\nabla\cdot\mathbf v) = -\int \nabla h\cdot\mathbf v.
These three identities are the entire machinery behind everything that follows.
02
The Fokker–Planck equation
derivation, term reading, the OU check
Itô + by parts → a PDE
Derivation
Take a test function \phi: Itô gives d\phi=(\nabla\phi\cdot\mathbf f+\tfrac12 g^2\Delta\phi)dt+\text{noise}. Take \mathbb E[\cdot]=\int(\cdot)\,p_t, kill the Itô term, and integrate by parts onto p_t:
For \mathbf f = -\nabla V this PDE is itself a gradient flow: steepest descent of the free energy \int pV + \tfrac12 g^2\!\int p\log p in the Wasserstein geometry (Jordan–Kinderlehrer–Otto), the same transport geometry that powers flow matching.
Drift transports, diffusion smooths
Reading the PDE
-\nabla\cdot(\mathbf f p) streams probability along the drift; \tfrac12 g^2\Delta p raises p wherever it dips below its neighborhood. With \mathbf f=\mathbf 0 it is the heat equation, variance growing linearly.
The Ornstein–Uhlenbeck check
A closed-form solution
A Gaussian \mathcal N(m(t),v(t)) solves the OU Fokker–Planck iff
\dot m = -\theta m, \qquad \dot v = \sigma^2 - 2\theta v
(match coefficients of (x-m)^0,(x-m)^1,(x-m)^2). The steady state gives \mathcal N(0,\sigma^2/2\theta); numerically the residual is tiny:
def ou_density(x, t, x0=2.0): m, v = ou_mean_var(x0, t)return np.exp(-(x - m)**2/ (2* v)) / np.sqrt(2* np.pi * v)xs = np.linspace(-4.0, 5.0, 4001)dx = xs[1] - xs[0]for t in ts_show: eps =1e-5 dpdt = (ou_density(xs, t + eps) - ou_density(xs, t - eps)) / (2* eps) p = ou_density(xs, t) transport = np.gradient(theta * xs * p, dx) # -d/dx(f p), f = -theta x diffusion =0.5* sigma**2* np.gradient(np.gradient(p, dx), dx) residual = (dpdt - (transport + diffusion))[5:-5] # trim one-sided edge stencilsprint(f't={t}: max|residual| = {np.abs(residual).max():.2e}, 'f'max|dp/dt| = {np.abs(dpdt).max():.2e}')
This ODE has the same time-marginals as the SDE, by uniqueness of the linear transport PDE. With the exact score and divergence, it is smooth, invertible, and has an exact likelihood identity.
One cloud, two dynamics
The experiment
Same marginals, different motion: SDE paths cross and rattle, ODE trajectories glide and never cross. The clouds are statistically indistinguishable (\mathrm{KS} < 0.009):
\mathbf s_t(\mathbf x)=\nabla_{\mathbf x}\log p_t(\mathbf x) points uphill on the log-density and vanishes at modes. Gaussian: -(\mathbf x-\boldsymbol\mu)/\sigma^2, a spring to the mean; a mixture gives responsibility-weighted springs.
Why scores beat densities
Normalizer-invariance
For p=\tilde p/Z, \nabla\log p = \nabla\log\tilde p since \nabla\log Z = 0. An energy model p\propto e^{-E} has score -\nabla E, no intractable Z:
A network can represent the score without ever normalizing, which is why score matching works where density estimation fails.
05
Time reversal
Anderson’s theorem and the factor of two
Anderson’s reverse-time SDE
Time reversal
Running the diffusion backward from the terminal Gaussian:
d\mathbf X = \bigl[\mathbf f - g(t)^2\,\nabla\log p_t(\mathbf X)\bigr]dt + g(t)\,d\bar{\mathbf W}.
A Bayesian Euler step shows why: the marginal p_t acts as a prior that bends each reversed step toward where the data was.
The factor of two, drawn
Time reversal
One dial covers every sampler: \mathbf b_\lambda = \mathbf f - \tfrac{1+\lambda^2}{2}g^2\nabla\log p_t with noise \lambda g, all sharing the marginals p_t. More injected noise, more drift correction: the factor slides from \tfrac12 g^2 (\lambda=0, the PF-ODE) to g^2 (\lambda=1, Anderson):
Noise back into data
Generation
Zero training: the closed-form mixture score re-grows both modes from noise; swap in a trained network and this loop is a diffusion model:
max|p_T - N(0,1)| at T=3: 0.0015 (vs peak 0.3974)
mass split: 0.500 / 0.500 (target 0.5 / 0.5)
left mode: mean -1.999, std 0.260 (target -2.000, 0.250)
right mode: mean +2.001, std 0.261 (target +2.000, 0.250)
KS(generated, exact p0 sample) = 0.0172
Recap
Wrap-up
Fokker–Planck, \partial_t p = -\nabla\cdot(\mathbf f p)+\tfrac12 g^2\Delta p: drift transports, diffusion smooths.
Transport identity (plus sign!) makes it a continuity equation.
PF-ODE: same marginals as the SDE, deterministic, exact likelihood identity for the exact score and divergence.
Score \nabla\log p_t: normalizer-free, points to data, learnable.
Reverse SDE drift corrects by g^2\nabla\log p_t, twice the ODE’s.
The single unknown in all three: the score.
Next: learn that score via score matching, diffusion, and flow matching.