Flow Matching & Rectified Flows

Flow matching trains a velocity field by regression: on the straight path x_t = (1-t)x0 + t x1 the label is x1 - x0, and the learned field is the average label E[x1 - x0 | x_t].

Advanced · Graduate mathematics · about 34 minutes

Reading map and next steps

Intuition

The idea in plain words, before the symbols.

Flow matching trains a time-dependent velocity field that moves points from a noise distribution toward a data distribution. We begin with a straight path between one noise point and one data point, separating its velocity label from the remaining displacement. The derivation shows that where many paths pass through the same point at the same time, least squares learns the average of their labels; a separate one-dimensional calculation checks which error more sampling steps reduce. This notebook uses least squares and ordinary differential equations.

Imagine starting with a cloud of noise points and wanting to morph it into a cloud shaped like your data. If you knew a time-dependent velocity field v(x,t)v(x,t) that pushes particles, you could integrate the ODE dx/dt=v(x,t)dx/dt=v(x,t) from t=0t=0 to t=1t=1 and watch noise turn into samples.

The first idea to get right is the supervised label. For one chosen noise/data pair, a straight conditional path uses the current point xt=(1−t)x0+tx1x_t=(1-t)x_0+t x_1 but trains on the constant velocity ut=x1−x0u_t=x_1-x_0. The arrow from xtx_t to x1x_1 is only the remaining displacement, not the training target.

The second idea is what the network learns from many pairs. Paths from different pairs pass through the same point at the same time, and the network sees only the point and the time, not the pair. Least squares therefore learns the average of their labels there.

The third idea is sampling. A trained field is only half of generation: a sample comes from following that field from a fresh noise point with a numerical solver, and the solver never sees a pair. The investigation after the demo, which error do more sampling steps reduce?, uses a different coupling in one dimension, where every quantity has a closed form. It keeps apart four things that are easy to merge: one pair's training label, the field that averages many such labels, a deliberate error in the field being followed, and the numerical error of the solver's steps. More steps act only on the last.

Study prompts for this section: Intuition
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Mathematics

Definitions, assumptions and the derivation.

Generative flow and regression target

We model a trajectory xtx_t with a neural velocity field vθv_\theta, then train that field to match a target velocity utu_t:

dxtdt=vθ(xt,t),L=Et,x0,x1 ∥vθ(xt,t)−ut∥2.\frac{dx_t}{dt}=v_\theta(x_t,t),\qquad \mathcal L=\mathbb E_{t,x_0,x_1}\,\big\|v_\theta(x_t,t)-u_t\big\|^2.

Here tt is drawn uniformly from [0,1][0,1], x0x_0 from the noise distribution and x1x_1 from the data. Regressing on these per-pair (conditional) targets has the same gradients as regressing on the marginal field described below, which cannot be computed directly. Lipman et al. (2023) prove this for paths conditioned on the data point x1x_1, and Tong et al. (2024) for paths conditioned on a pair (x0,x1)(x_0,x_1), as here.

Rectified flow (straight paths)

Choose a straight interpolation between noise x0x_0 and data x1x_1. Differentiating the path gives the supervised velocity label:

xt=(1−t)x0+tx1,ut=ddtxt=x1−x0,x1−xt=(1−t)(x1−x0).x_t=(1-t)x_0+t x_1,\qquad u_t=\frac{d}{dt}x_t=x_1-x_0,\qquad x_1-x_t=(1-t)(x_1-x_0).

The remaining displacement x1−xtx_1-x_t is shorter than the training label except at t=0t=0.

So the useful identity is:

xt+(1−t)ut=x1.x_t + (1-t)u_t = x_1.

For a single straight conditional pair, the velocity label stays fixed as tt changes; the remaining integration time shrinks.

What the network learns from many pairs

At each point and time, the squared loss is minimized by the conditional mean of the label:

v⋆(x,t)=E[ x1−x0  ∣  xt=x ].v^\star(x,t)=\mathbb E\big[\,x_1-x_0 \;\big|\; x_t=x\,\big].

Liu et al. (2023) call the ODE driven by v⋆v^\star the rectified flow. It carries the noise distribution onto the data distribution, but its paths are generally not straight: where straight pair-paths cross, v⋆v^\star averages their directions, and the ODE paths bend instead of crossing. Their reflow step retrains on (noise, sample) pairs produced by the previous flow, which straightens the paths and allows fewer integration steps.

Which pairs are used also matters. With independent pairs, each x0x_0 drawn without regard to x1x_1, the learned flow moves noise to data but is not in general the optimal-transport map between the two distributions. The "optimal transport" path in Lipman et al. (2023) refers to each conditional straight path, not to an optimal pairing of the distributions. Tong et al. (2024) pair noise and data within each minibatch by solving a small optimal-transport problem (OT-CFM); this gives straighter flows that are cheaper to integrate, and it approximates dynamic optimal transport when the true optimal pairing is available.

A different coupling: independent Gaussian endpoints

The equations above hold for any pairing of x0x_0 with x1x_1. The first Python example below fixes one: the one-dimensional affine pairing x1=2x0+1x_1=2x_0+1, for which xt=(1+t)x0+tx_t=(1+t)x_0+t. For each t∈[0,1]t\in[0,1] that map is invertible in x0x_0, since 1+t≥11+t\ge1, so a state and time determine the pair and its label, u=(xt+1)/(1+t)u=(x_t+1)/(1+t). The demo instead pairs synthetic two-dimensional Gaussian noise points with two-moons data points. Now take a one-dimensional coupling that is neither: draw the endpoints independently, X0∼N(0,1)X_0\sim\mathcal N(0,1) and X1∼N(0,4)X_1\sim\mathcal N(0,4), where 4 is the target variance and 2 its standard deviation. Keep the straight interpolation Xt=(1−t)X0+tX1X_t=(1-t)X_0+tX_1 for t∈[0,1]t\in[0,1] and the paired label U=X1−X0U=X_1-X_0. Many pairs now pass through the same point at the same time, each with its own label. Neither earlier example is wrong; each uses a different coupling.

q(t)=Var⁡Xt=(1−t)2+4t2=5(t−15)2+45,Var⁡U=5,Cov⁡(U,Xt)=5t−1.q(t)=\operatorname{Var}X_t=(1-t)^2+4t^2=5\left(t-\tfrac15\right)^2+\tfrac45,\qquad \operatorname{Var}U=5,\qquad \operatorname{Cov}(U,X_t)=5t-1.

Here qq is a variance, with minimum 4/54/5 at t=1/5t=1/5; the standard deviation is s(t)=q(t)s(t)=\sqrt{q(t)}, with minimum 4/5≈0.894\sqrt{4/5}\approx0.894. Because (U,Xt)(U,X_t) is jointly Gaussian with mean zero, conditioning on Xt=xX_t=x gives the marginal field and the spread of the labels compatible with that point:

u(x,t)=E[U∣Xt=x]=(5t−1) xq(t),Var⁡(U∣Xt=x)=5−(5t−1)2q(t)=4q(t),u(x,t)=\mathbb E[U\mid X_t=x]=\frac{(5t-1)\,x}{q(t)},\qquad \operatorname{Var}(U\mid X_t=x)=5-\frac{(5t-1)^2}{q(t)}=\frac{4}{q(t)},

using 5q(t)−(5t−1)2=45q(t)-(5t-1)^2=4. Take the pair (x0,x1)=(1,3)(x_0,x_1)=(1,3). At t=0.5t=0.5 its state is 2, its label 2, its remaining displacement 1, and u(2,0.5)=2.4u(2,0.5)=2.4. At t=0.2t=0.2 its state is 1.4 and its label is still 2, but u(1.4,0.2)=0u(1.4,0.2)=0, with label variance 5. The field is the mean over pairs through that point, not over all pairs. The mean is exact; the spread is variation among compatible labels, not uncertainty about the mean.

Following the field, dz/dt=u(z,t)dz/dt=u(z,t), has the exact solution Zt=s(t)Z0Z_t=s(t)Z_0, because s′(t)/s(t)=q′(t)/(2q(t))=(5t−1)/q(t)s'(t)/s(t)=q'(t)/(2q(t))=(5t-1)/q(t). So ZtZ_t and XtX_t have the same law N(0,q(t))\mathcal N(0,q(t)) at every time: the one-dimensional case of the marginal-preserving flow of Liu, Gong and Liu (2023, §3.1, Theorem 3.3). They are different paths with different couplings. The flow pairs Z1=2Z0Z_1=2Z_0, whereas training drew X1X_1 independently of X0X_0, so the flow's destination is not the training partner of its start: a drawn pair has X1=2X0X_1=2X_0 with probability zero. For Z0>0Z_0>0 the flow moves toward zero until t=0.2t=0.2 and away from it afterwards; it is not affine in time, although every training path is.

At t=1t=1 two conditionings differ. Given X1=x1X_1=x_1, the state XtX_t has variance (1−t)2(1-t)^2, and the pair-conditional field (x1−x)/(1−t)(x_1-x)/(1-t) is undefined at t=1t=1; it is not evaluated or clamped there. The label UU is still defined, and conditioning on X1=xX_1=x gives u(x,1)=xu(x,1)=x with label variance 1. The marginal field is regular on all of [0,1][0,1], and one endpoint has no weight in the uniform-time integrals below. Lipman et al.'s Gaussian conditional paths keep a positive endpoint width (their Eqs. 20–23), so this zero-width endpoint rests on the calculation here, not on their construction.

Following a supplied field

A trained model supplies some field. Here the field is chosen instead: vε(x,t)=u(x,t)+εxv_\varepsilon(x,t)=u(x,t)+\varepsilon x for a fixed ε\varepsilon, a stipulated error rather than the result of training. Its exact trajectory from z0=x0z_0=x_0 is eεts(t)x0e^{\varepsilon t}s(t)x_0, which ends at 2eεx02e^{\varepsilon}x_0. Explicit Euler with NN uniform steps, h=1/Nh=1/N and tk=k/Nt_k=k/N, evaluates the field at its own numerical state:

z0=x0,zk+1=zk+h vε(zk,tk),k=0,…,N−1.z_0=x_0,\qquad z_{k+1}=z_k+h\,v_\varepsilon(z_k,t_k),\qquad k=0,\dots,N-1.

The reference endpoint is 2x02x_0, the exact flow of the true field uu. The endpoint discrepancy splits into signed parts:

zN−2x0⏟total=(zN−2eεx0)⏟numerical: Euler minus exact supplied field+(2eεx0−2x0)⏟field-induced.\underbrace{z_N-2x_0}_{\text{total}} =\underbrace{\big(z_N-2e^{\varepsilon}x_0\big)}_{\text{numerical: Euler minus exact supplied field}} +\underbrace{\big(2e^{\varepsilon}x_0-2x_0\big)}_{\text{field-induced}}.

With x0=1x_0=1 and ε=0\varepsilon=0 the field is exact and only numerical error remains: N=8N=8 ends at 1.659138 and N=16N=16 at 1.822732, against 2. With ε=0.2\varepsilon=0.2 the supplied field's exact endpoint is 2.442806, a field-induced error of +0.442806. The numerical errors are −0.443552 at N=8N=8 and −0.234936 at N=16N=16, so the totals are −0.000746 and +0.207870. Refinement reduced the numerical error, as it should, and in doing so removed error that had been cancelling the field's. Signed errors add; absolute distances do not, since 0.443552+0.442806≠0.0007460.443552+0.442806\ne0.000746. None of this makes eight steps better in general. With ε=−0.2\varepsilon=-0.2 both errors are negative and nothing cancels.

A separate counterexample: with ε=0\varepsilon=0 and N=1N=1, the single step is z1=x0+u(x0,0)=x0−x0=0z_1=x_0+u(x_0,0)=x_0-x_0=0 for every seed. Every training path is straight, yet one Euler step of the marginal field collapses every sample to zero. Straight conditional paths do not by themselves make one-step sampling accurate.

The field is linear in xx, so zN=mNx0z_N=m_Nx_0 for a number mNm_N and the sampler's output law is N(0,mN2)\mathcal N(0,m_N^2). Its exact 2-Wasserstein distance to the target N(0,4)\mathcal N(0,4) is ∣∣mN∣−2∣\big||m_N|-2\big|, including when mN<0m_N<0 reflects every seed. That is a population distance for this Gaussian family. It is not the error of one seed, and it is not an image-quality measure.

What the training loss can and cannot see

Use the unhalved squared loss with tt uniform on [0,1][0,1]. Conditioned on one state and time, the expected loss of the supplied field against the paired labels is

E[(vε(x,t)−U)2∣Xt=x]=ε2x2+4q(t).\mathbb E\big[(v_\varepsilon(x,t)-U)^2\mid X_t=x\big]=\varepsilon^2x^2+\frac{4}{q(t)}.

Only after averaging over Xt∼N(0,q(t))X_t\sim\mathcal N(0,q(t)) does x2x^2 become q(t)q(t). With ∫01q dt=5/3\int_0^1q\,dt=5/3 and ∫014/q dt=2[arctan⁡5t−12]01=2(arctan⁡2+arctan⁡12)=π\int_0^1 4/q\,dt=2\big[\arctan\tfrac{5t-1}{2}\big]_0^1=2\big(\arctan2+\arctan\tfrac12\big)=\pi:

LCFM(ε)=π+53ε2,LFM(ε)=53ε2,dLCFMdε=dLFMdε=103ε.\mathcal L_{\mathrm{CFM}}(\varepsilon)=\pi+\tfrac53\varepsilon^2,\qquad \mathcal L_{\mathrm{FM}}(\varepsilon)=\tfrac53\varepsilon^2,\qquad \frac{d\mathcal L_{\mathrm{CFM}}}{d\varepsilon}=\frac{d\mathcal L_{\mathrm{FM}}}{d\varepsilon}=\tfrac{10}{3}\varepsilon.

These are exact population values for this family; they match the equal-gradient statement of Lipman et al.'s Theorem 2. Their printed proof assumes bounded fields, which a linear field is not, so the equality here rests on the explicit calculation, in which every second moment is finite. The floor π\pi is the spread of labels compatible with a state, not measurement noise, numerical error or an unavoidable endpoint error. Individual sample gradients need not agree: for the pair (1, 3) at t=0.2t=0.2, the CFM sample derivative at ε=0\varepsilon=0 is 2(u−U)x=−28/52(u-U)x=-28/5, while the FM sample derivative and both population derivatives are zero. Neither loss depends on NN. Neither is an average along a generated trajectory or over one seed.

The floor belongs to the coupling. The dependent coupling X1=2X0X_1=2X_0 has the same endpoint marginals but Xt=(1+t)X0X_t=(1+t)X_0, field u(x,t)=x/(1+t)u(x,t)=x/(1+t) and zero label variance, so its CFM floor is 0 and its intermediate law differs. It is also distinct from this page's first Python example, x1=2x0+1x_1=2x_0+1. Endpoint data alone do not determine one field, and raw CFM losses under different couplings do not rank model quality.

Source statements, derivations and judgment

Source statements. Lipman et al. (2023; arXiv:2210.02747v2): Eq. 8 aggregates conditional fields into a marginal field, Eq. 9 defines the CFM objective, and Theorem 2 states that the FM and CFM losses differ by a parameter-independent constant and have equal gradients. In §6.2 and Figure 7 they measure the per-pixel mean squared error of ImageNet-32 solutions at up to 100 function evaluations against 1,000-evaluation solutions over 256 seeds, using the midpoint method for that numerical error, and they report FID separately. Liu, Gong and Liu (2023; arXiv:2209.03003v1): §2.1, Eq. 1 regresses X1−X0X_1-X_0 along interpolated pairs and allows an independent coupling. §3.1, Definitions 3.1–3.2 and Theorem 3.3 define the expected-velocity field and its marginal-preserving flow. §5 uses Euler and adaptive RK45, Table 1 counts function evaluations as network calls, and Figure 8 varies the number of Euler steps against image metrics.

Derivations here. The Gaussian field, label variance, exact flows, Euler values, loss identities and Wasserstein distance above are this page's own calculations for one chosen family; neither paper reports these numbers. Nothing here reproduces either paper's sampler, image quality, speed or memory measurements, and nothing here trains a model or performs reflow.

Judgment. For a trained model the true field and flow are unknown. Freezing the field, seeds and numerical precision, then comparing a refinement or an independently implemented solver, measures the numerical sensitivity of integrating that field. Lipman et al.'s 1,000-evaluation comparison is of this kind, and a high-evaluation reference is itself numerical, not ground truth. A separately specified criterion on target data, such as FID for images or the exact distance above, evaluates the output distribution, where field and solver errors combine. Neither comparison alone identifies every error. Held-out CFM risk differences between two fields cancel the unknown floor in expectation only under the same coupling, path and time law; finite-sample uncertainty remains, and a smaller field risk need not give better samples.

Study prompts for this section: Mathematics
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Code

The same calculation as runnable code, in the notation of the derivation.

import numpy as np

rng = np.random.default_rng(0)
x0 = rng.standard_normal(5)

# A simple deterministic "data" mapping for the toy example
x1 = 2.0 * x0 + 1.0

for t in [0.0, 0.25, 0.5, 0.75, 1.0]:
    xt = (1 - t) * x0 + t * x1
    u = x1 - x0
    remaining = x1 - xt

    # For this toy mapping, the exact velocity field can be written as v(xt,t):
    # xt = (1+t)x0 + t  =>  x0 = (xt - t)/(1+t)  =>  u = x0 + 1 = (xt + 1)/(1+t)
    v = (xt + 1.0) / (1.0 + t)
    print(
        "t=", t,
        "max|u-v|=", float(np.max(np.abs(u - v))),
        "remaining/full=", round(float(np.linalg.norm(remaining) / np.linalg.norm(u)), 2),
    )

In this toy, each x1x_1 is a function of its x0x_0, so the point xtx_t identifies its pair and the field v⋆v^\star equals the per-pair label exactly: max|u-v| is zero up to rounding. With independent pairs it would be an average instead, and remaining/full would still fall as 1−t1-t.

A second code block implements the independent-coupling example from the Math section: the field uu, the label variance, the perturbed field vεv_\varepsilon, its exact flow, explicit Euler and the signed endpoint decomposition. The helper that draws the investigation below computes the same values in TypeScript. Each function checks its inputs (a time in [0,1][0,1], a finite state, ε∈[−0.25,0.25]\varepsilon\in[-0.25,0.25], a seed in [−3,3][-3,3] and an integer N∈[1,128]N\in[1,128]) and raises an error rather than return a number outside them or after an overflow. Numbers are converted to binary64 Python floats before any arithmetic, as JavaScript numbers are, so NumPy's fixed-width integers cannot wrap; q, u, label_var and v also accept a NumPy array of real numbers, copied to float64, so that the quadrature can run on an array of times. The loss identity is checked by deterministic quadrature against π\pi to a stated tolerance, never by sampling. The block needs NumPy 2.0 or later, for np.trapezoid.

import numbers
import numpy as np

# A second coupling: independent endpoints X0 ~ N(0, 1) and X1 ~ N(0, 4).
# 4 is the target variance; its standard deviation is 2. X_t = (1 - t) X0 + t X1, label U = X1 - X0.

INF = float("inf")            # a state only needs to be finite

def check(value, lo, hi, name, integer=False, vector=False):
    # Returns the value the arithmetic will use. A real number (Python int or float, NumPy integer or
    # floating scalar) becomes a binary64 Python float before its bounds are checked, so fixed-width
    # integers never enter the arithmetic; an integer above 2**53 rounds as a JavaScript number would.
    # A step count (integer=True) becomes a Python int. With vector=True a NumPy integer or floating
    # array is also accepted: it is copied to float64 (the caller's array is untouched) and every
    # element is checked, so the quadrature below can pass an array of times.
    if vector and isinstance(value, np.ndarray):
        if value.dtype.kind not in "iuf":
            raise TypeError(f"{name} must be an array of real numbers")
        if value.size == 0:
            raise ValueError(f"{name} is an empty array")
        value = value.astype(np.float64)
        if not (np.isfinite(value).all() and ((lo <= value) & (value <= hi)).all()):
            raise ValueError(f"{name} has an element that is not finite or is outside [{lo}, {hi}]")
        return value
    kind = numbers.Integral if integer else numbers.Real
    if isinstance(value, (bool, np.bool_)) or not isinstance(value, kind):
        raise TypeError(f"{name} must be {'an integer' if integer else 'a real number'}")
    if integer:
        if not lo <= value <= hi:
            raise ValueError(f"{name} = {value} is outside the supported interval [{lo}, {hi}]")
        return int(value)
    try:
        number = float(value)
    except OverflowError:     # a Python int beyond the binary64 range
        number = INF
    if not (np.isfinite(number) and lo <= number <= hi):
        raise ValueError(f"{name} = {value} is not finite or is outside the supported interval [{lo}, {hi}]")
    return number

def finite(value, name):      # a result that overflowed is refused, never returned
    if not np.isfinite(value).all():
        raise FloatingPointError(f"non-finite {name}")
    return value

def q(t):                     # Var(X_t): a variance, not a standard deviation
    t = check(t, 0, 1, "t", vector=True)
    return (1 - t) * (1 - t) + 4 * t * t

def u(x, t):                  # E[U | X_t = x]: the marginal field, by Gaussian conditioning
    x = check(x, -INF, INF, "x", vector=True)
    t = check(t, 0, 1, "t", vector=True)
    return finite((5 * t - 1) * x / q(t), "u(x, t)")

def label_var(t):             # Var(U | X_t = x) = 5 - (5t - 1)^2 / q(t) = 4 / q(t)
    return 4 / q(t)

def pair_velocity(x, x1, t):  # given X1 = x1: (x1 - x) / (1 - t), defined only for t < 1
    x = check(x, -INF, INF, "x")
    x1 = check(x1, -INF, INF, "x1")
    t = check(t, 0, 1, "t")
    if t == 1:
        raise ValueError("undefined at t = 1, where X_t given X1 is a point mass")
    return finite((x1 - x) / (1 - t), "pair velocity")

def v(x, t, eps):             # a chosen perturbation of u, not a trained field
    x = check(x, -INF, INF, "x", vector=True)
    eps = check(eps, -0.25, 0.25, "eps")
    return finite(u(x, t) + eps * x, "v(x, t)")

def exact(x0, t, eps=0.0):    # the exact trajectory of v from x0; eps = 0 is the reference s(t) x0
    x0 = check(x0, -3, 3, "x0")
    t = check(t, 0, 1, "t")
    eps = check(eps, -0.25, 0.25, "eps")
    return np.exp(eps * t) * np.sqrt(q(t)) * x0

def euler(x0, eps, n):        # n uniform explicit left-endpoint steps: h = 1/n, t_k = k/n
    x0 = check(x0, -3, 3, "x0")
    eps = check(eps, -0.25, 0.25, "eps")
    n = check(n, 1, 128, "n", integer=True)
    h, z = 1 / n, x0
    for k in range(n):
        z = z + h * v(z, k / n, eps)          # the field is evaluated at the numerical state z_k
        if not np.isfinite(z):
            raise FloatingPointError(f"non-finite Euler state after step {k}")
    return z

print("pair (1, 3): t, x_t, label U, remaining, u(x_t, t), Var(U | X_t = x_t)")
for t in (0.2, 0.5):
    xt = (1 - t) * 1 + t * 3
    print(t, f"{xt:.6f}", 3 - 1, f"{3 - xt:.6f}", f"{u(xt, t):.6f}", f"{label_var(t):.6f}")
assert u(3.0, 1.0) == 3.0 and label_var(1.0) == 1.0   # at t = 1: u(x, 1) = x with label variance 1

print("seed 1: eps, N, Euler, exact supplied, numerical, field-induced, total")
for eps in (0.0, 0.2, -0.2):
    for n in (8, 16):
        z, supplied, reference = euler(1.0, eps, n), exact(1.0, 1.0, eps), exact(1.0, 1.0)
        numerical, field, total = z - supplied, supplied - reference, z - reference
        assert abs(numerical + field - total) < 1e-12   # signed errors add; absolute distances need not
        print(eps, n, f"{z:.6f} {supplied:.6f} {numerical:+.6f} {field:+.6f} {total:+.6f}")

seeds = np.array([-3.0, -1.0, 0.5, 2.0, 3.0])     # the sampler is linear: z_N = m_N x0 for every seed
assert all(euler(s, 0.0, 1) == 0.0 for s in seeds)  # one step of the true field collapses every seed to 0
m8 = euler(1.0, 0.0, 8)
assert np.allclose([euler(s, 0.0, 8) for s in seeds], m8 * seeds, rtol=1e-14, atol=0)
print("W2 from N(0, m_N^2) to N(0, 4) at eps = 0:", "N=1", abs(abs(euler(1.0, 0.0, 1)) - 2), "N=8", f"{abs(abs(m8) - 2):.6f}")

t = np.linspace(0.0, 1.0, 100_001)                # deterministic quadrature, an approximation; no sampling
for eps in (0.0, 0.2, -0.2):
    k = (5 * t - 1) / q(t) + eps                  # v is the linear field k x
    raw = (k * (1 - t) + 1) ** 2 + 4 * (k * t - 1) ** 2   # E[(k X_t - U)^2] by independence of X0, X1
    assert np.allclose(raw, eps**2 * q(t) + label_var(t), rtol=0, atol=1e-12)
    cfm, fm = np.trapezoid(raw, t), np.trapezoid(eps**2 * q(t), t)
    assert abs(cfm - (np.pi + 5 * eps**2 / 3)) < 1e-8 and abs(fm - 5 * eps**2 / 3) < 1e-8
    print(f"eps={eps:+.1f}: CFM ~ {cfm:.8f} vs pi + 5eps^2/3 = {np.pi + 5 * eps**2 / 3:.8f}; FM ~ {fm:.8f}")
Study prompts for this section: Code
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Demo

Change an input, predict the result, and see which quantities respond.

ExploreChange one control at a time and name what responds.An optional observation guide follows the demo.

Explore Flow Matching & Rectified Flows

Loading the interactive demo…

Observation guide

Optional and ungraded. Opening a guide saves nothing.

Choose what to inspect in Flow Matching & Rectified Flows, then open its guide.

Use the demo to predict the conditional velocity label for one paired example, or reveal it directly; a prediction is optional. The pairings are synthetic and chosen to show the label; they are not an optimal-transport assignment. The aggregate field sketch shows how labels from many pairs combine into one field.

After the demo, a separate investigation follows the independent-coupling field from the Math section. Its first view sets one pair's label beside the field value at the same point and the flow from a seed; its second follows a deliberately perturbed field with explicit Euler and splits the endpoint error into signed numerical and field-induced parts. Every result is shown at once, in tables as well as figures. Its controls change only the investigation: the observation guide below still records the demo's state only.

Study prompts for this section: Demo
Study prompts

Each button copies a prompt about this section to your clipboard, for an AI assistant you already use. Nothing is sent by this site.

Practice

For noise N(0, 1) and data N(2, 1), compare one pair's label with the field the network learns at the same point, then move to a later time and predict again.

Loading the exercise…
In focus

Concept: Flow Matching & Rectified Flows

What is the smallest example of Flow Matching & Rectified Flows you can work through by hand, and what does it show?

BeforeDiffusion, Score-Based Models & Flow MatchingNextOverparameterization & Generalization (Double Descent)
DetailsGenerative Models

After the first pass

Go deeper

Optional material for a second reading. Sources are always shown; the other panels open from their headings or from these links, and closing one keeps what you wrote or revealed.
What to look for in each sectionChoose a section and what you expect to see in it, then read short advice.

Section by section

Choose what to look for in each section

Flow matching trains a velocity field by regression: on the straight path x_t = (1-t)x0 + t x1 the label is x1 - x0, and the learned field is the average label E[x1 - x0 | x_t].

Choose what to look for (no demo yet)01 / Intuition
Illustration of particles transported by a learned velocity field from noise into a target distribution.
What to look for

Start with the picture, metaphor, or geometric mechanism.

Choose first

Choose what you expect to see in this section of Flow Matching & Rectified Flows.

Questions to ask of the pictureChoose a part of the picture and what you expect, then read short advice.

Looking at the picture

Questions to ask of the picture

Flow matching trains a velocity field by regression: on the straight path x_t = (1-t)x0 + t x1 the label is x1 - x0, and the learned field is the average label E[x1 - x0 | x_t].

4 of 4 sections writtenNo live demo yet
Question

Which part of the picture should you look at first?

Choose first

Pick the part of the picture you expect to explain Flow Matching & Rectified Flows.

Sources

References for this page

A reference shows where an idea comes from; it does not vouch for every step on this page.
paper · 2023Flow Matching for Generative ModelingLipman et al.
Why it is cited

Trains continuous normalizing flows by regressing a velocity field on targets conditioned on a data point, and proves this has the same gradients as regressing on the marginal field (Theorem 2; ICLR 2023).

Open source
paper · 2023Flow Straight and Fast: Learning to Generate and Transfer Data with Rectified FlowLiu, Gong, and Liu
Why it is cited

Defines rectified flow with the straight interpolation X_t = tX_1 + (1-t)X_0 and least-squares target X_1 - X_0, and straightens the learned paths by repeated rectification (ICLR 2023).

Open source
paper · 2024Improving and generalizing flow-based generative models with minibatch optimal transportTong et al.
Why it is cited

Extends the gradient equivalence to targets conditioned on a noise-data pair, and pairs noise and data by minibatch optimal transport (OT-CFM) for simpler flows that are faster to integrate (TMLR 2024).

Open source
paper · 2024Flow Matching Guide and CodeLipman et al.
Why it is cited

A tutorial-length review of flow matching: probability paths, conditional and marginal velocity fields, couplings, and a reference implementation (arXiv).

Open source
What each claim rests on2 claims, each with its sources, where it appears on this page, and its caveats.

Claims and sources

Flow matching trains a velocity field by regression: on the straight path x_t = (1-t)x0 + t x1 the label is x1 - x0, and the learned field is the average label E[x1 - x0 | x_t].

Each claim checked against the cited passages

The checks were made by this site, not by an independent reviewer.

Flow matching uses regression of a neural ODE velocity field against a target vector field; in the rectified-flow straight interpolation x_t=(1-t)x_0+t x_1, the per-example least-squares label is x_1-x_0, not the remaining displacement x_1-x_t.
What the sources support

Lipman et al. define flow matching as squared-error regression of a neural ODE velocity field on a target field, with a conditional version that regresses on per-pair targets. Liu et al. define rectified flo...

Where to see it on this page
Equation 1
dxtdt=vθ(xt,t),L=Et,x0,x1 ∥vθ(xt,t)−ut∥2.\frac{dx_t}{dt}=v_\theta(x_t,t),\qquad \mathcal L=\mathbb E_{t,x_0,x_1}\,\big\|v_\theta(x_t,t)-u_t\big\|^2.
Equation 2
xt=(1−t)x0+tx1,ut=ddtxt=x1−x0,x1−xt=(1−t)(x1−x0).x_t=(1-t)x_0+t x_1,\qquad u_t=\frac{d}{dt}x_t=x_1-x_0,\qquad x_1-x_t=(1-t)(x_1-x_0).
Caveat

The code and demo show only the per-pair straight-path label. A trained v_theta(x,t) averages labels over all pairs through x at time t. Nothing here tests optimal-transport optimality, solver error, one-ste...

StatusChecked against the cited passages.

Lipman et al. support continuous normalizing flows given by ODE vector fields, and flow matching and conditional flow matching as squared-error regression onto a target or conditional vector field. Liu et al. support the rectified-flow straight path X_t=tX_1+(1-t)X_0 and the least-squares target X_1-X_0. The page's equations, code and demo compare that constant label with the remaining displacement X_1-X_t=(1-t)(X_1-X_0); no claim about optimal transport or performance was checked.

For independent X0 ~ N(0, 1), X1 ~ N(0, 4) on straight paths, u(x, t) = (5t - 1)x / q(t), q = (1 - t)^2 + 4t^2, with label variance 4 / q(t); its flow matches each marginal law, not the coupling. With the perturbed field u + εx, the Euler endpoint error is numerical plus field-induced; more steps reduce only the first.
What the sources support

Lipman et al. (arXiv:2210.02747v2): Eqs. 8–9, Theorem 2, Eqs. 20–23 (positive endpoint width), §6.2 and Figure 7 (mean squared error against 1,000-evaluation midpoint solutions over 256 seeds; FID reported s...

Caveat

A worked one-dimensional calculation with a chosen field: no trained model, reflow, solver recommendation, image-quality or speed result, or paper reproduction. Lipman et al. prove the loss equality for boun...

StatusChecked against the cited passages.

Checked against the two cited papers, in the versions the sources list: Lipman et al. Eqs. 8–9, Theorem 2 (its proof assumes bounded fields), Eqs. 20–23, §6.2 and Figure 7; Liu, Gong and Liu Eq. 1, Definitions 3.1–3.2 and Theorem 3.3, whose conditions this field meets, §5, Table 1 and Figure 8. The field, the label variance (5q − (5t − 1)² = 4), the exact flow and the Euler and loss values were recomputed separately; for ε in [−0.25, 0.25], more steps shrank the numerical part at every N from 1 to 128.

Explain it without the pageWrite your own explanation before asking for feedback. Your draft and any hints stay while this page is open.

Practice · Flow Matching & Rectified Flows

Try the idea in your own words

Flow matching trains a velocity field by regression: on the straight path x_t = (1-t)x0 + t x1 the label is x1 - x0, and the learned field is the average label E[x1 - x0 | x_t].

Concept · Selected for practice

Flow Matching & Rectified Flows

What it rests on: Sources: Flow Matching for Generative Modeling; Flow Straight and Fast: Learning to Generate and Transfer Data with Rectified Flow; Improving and g...

Context and links
Choose a task

Explain the mechanism

For Flow Matching & Rectified Flows: What is the smallest example of Flow Matching & Rectified Flows you can work through by hand, and what does it show? Explain your answer, including what changes, why, and which assumption matters.

No answer yet

A rough first thought is enough. Your draft stays when you change tasks.

Your draft stays on this page and is cleared when you leave or reload.

A little help · Explain

Open one hint at a time. These are suggestions, not your answer or a grade.

0 of 3 hints shown for this question.

    Clearing your answer keeps this help record. Outside help is your own report; this page cannot check it.

    Where am I stuck? (optional)
    Your own description, not an automatic diagnosis

    Choose one, or leave this unspecified. Select it again to clear it.

    Take your draft to a feedback conversation

    No AI feedback runs here. You can copy a prompt to use elsewhere; nothing is sent automatically. Review the text before sharing, and leave out private information.

    Write an attempt before copying a feedback prompt.

    A draft, or an AI reply to it, is not a test of what you have learned. To check that, try a different case later without help.

    Ask about this pagePick one item on this page and copy a prompt about it into an AI assistant you already use. Notes you write here stay in this browser.
    Ask about this pageClose
    ConceptFlow Matching & Rectified FlowsSources: Flow Matching for Generative Modeling; Flow Straight and Fast: Learning to Generate and Transfer Data with Rectified Flow; Improving and g...assert u(3.0, 1.0) == 3.0 and label_var(1.0) == 1.0 # at t = 1: u(x, 1) = x with label variance 1

    Your question

    Choose what your question is about

    Pick the idea, equation, source, code, claim, misconception or demo state you want to ask about. The prompt you copy includes it, so the answer can stay on that item.
    Next local actionNo local draft saved yet

    Open the draft below to save one note and next action in this browser.

    conceptGenerative Models

    Flow Matching & Rectified Flows

    Starting question

    What is the smallest example of Flow Matching & Rectified Flows you can work through by hand, and what does it show?

    SourcesCheck the 4 cited sources listed under Sources.Notes can be saved for this item
    Points of view on this item

    Fixed prompts generated from this item. They are not comments from people or independent reviews.

    Learner’s first stepAsk what would make "Flow Matching & Rectified Flows" feel predictable rather than familiar.
    Assumption

    The 4 cited sources must support this exact item, not just the surrounding topic.

    Source-checking summary

    Connect the definition to one equation, piece of code or demo before widening the discussion.

    Proposed experiment

    Change one input, then check that the same relationship holds in the mathematics, the code and the demo.

    Next action

    You can state the mechanism in your own words

    Evidence4 checks
    ObservationChecking for a saved observation
    ActionReady for one action
    PromptLearner prompt ready to copy
    Go to this item
    01ObservationChecking what this browser has saved
    02EvidenceChecking for a saved observation
    03SourcesCheck the 4 cited sources listed under Sources.
    04Next stepSave one next action
    Local action draftNo local draft saved yetOpen when you are ready to write one next action
    Local action draft

    This draft stays in this browser and is attached to this item only.

    No local draft saved.
    Evidence to inspect
    • What the 4 cited sources say about this exact item
    • The definition, its prerequisites, and a contrasting concept
    • The equation or code that makes the concept concrete
    • One demo state that shows what stays the same as the inputs change
    What would resolve this
    • You can state the mechanism in your own words
    • You can name the prerequisite that would clear up the confusion
    • You can predict how the result changes when one input changes
    Reference idconcept/concept-notebook/generative-models/flow-matching concept:generative-models/flow-matching