stock

Conditional Flow Matching with ODE Solvers

Jason Stock|12/5/2025|5 minutes

Conditional Flow Matching (CFM): a simulation-free training objective for continuous normalizing flows.​ We explore a few different flow matching variants and ODE solvers on a simple dataset. This repo was inspired and adapted by the awesome work in TorchCFM and Torchdyn. 😄

img

Background

Training: consider a smooth time-varying vector field u:[0,1]×RdRdu\,:\,[0, 1] \times \mathbb{R}^d \to \mathbb{R}^d that governs the dynamics of an ordinary differential equation (ODE), dx=ut(x)dtdx = u_t(x)\,dt. The probability path pt(x)p_t(x) can be generated by transporting mass1 along the vector field ut(x)u_t(x) between distributions over time, following the continuity equation

pt=(ptut).\frac{\partial p}{\partial t} = -\nabla \cdot (p_t u_t).

However, the target distributions pt(x)p_t(x) and the vector field ut(x)u_t(x) are intractable in practice. Therefore, we assume the probability path can be expressed as a marginal over latent variables:

pt(x)=pt(xz)q(z)dz,p_t(x) = \int p_t(x | z) q(z)\, dz,

where pt(xz)=N(xμt(z),σt2I)p_t(x | z) = \mathcal{N}\left(x | \mu_t(z), \sigma_t^2 I\right) is the conditional probability path, with a latent zz sampled from a prior distribution q(z)q(z). The dynamics of the conditional probability path are now governed by a conditional vector field ut(xz)u_t(x | z). We approximate this using a neural network, parameterizing the time-dependent vector field vθ:[0,1]×RdRdv_\theta\,:\,[0,1] \times \mathbb{R}^d \to \mathbb{R}^d. We train the network by regressing the conditional flow matching loss:

LCFM(θ)=Et,q(z),pt(xz)vθ(t,x)ut(xz)2,L_{\text{CFM}}(\theta) = \mathrm{E}_{t, q(z), p_t(x | z)} \lVert v_\theta(t, x) - u_t(x | z) \rVert^2,

such that tU(0,1),  zq(z),  and  xtpt(xz)t \sim U(0,1), \; z \sim q(z), \; \text{and} \; x_t \sim p_t(x|z). But, how do we compute ut(xz)u_t(x|z)? Well, assuming a Gaussian probability path, we have a unique vector field (Theorem 3; Lipman et al. 2023) given by,

ut(xz)=σ˙t(z)σt(z)(xμt(z))+μ˙t(z),u_t(x | z) = \frac{\dot{\sigma}_t (z)}{\sigma_t (z)}\,\left(x - \mu_t(z)\right) + \dot{\mu}_t(z),

where μ˙\dot{\mu} and σ˙\dot{\sigma} are the time derivatives of the mean and standard deviation. If we consider z(x0,x1)\mathbf{z} \equiv (\mathbf{x}_0, \mathbf{x}_1) and q(z)=q0(x0)q1(x1)q(z) = q_0(x_0)q_1(x_1) with

μt(z)=tx1+(1t)x0,σt(z)=σ>0,\begin{align} \mu_t(z) &= tx_1 + (1 - t) x_0, \\ \sigma_t(z) &= \sigma_{> 0}, \end{align}

then we have independent conditional flow matching (Tong et al. 2023) with the resulting conditional probability path and vector field

pt(xz)=N(xtx1+(1t)x0,σ2),ut(xz)=x1x0.\begin{align} p_t(x | z) &= \mathcal{N}\left(x | tx_1 + (1 - t) x_0, \sigma^2\right), \\ u_t(x | z) &= x_1 - x_0. \end{align}

Alternatively, the variance-preserving stochastic interpolant (Albergo & Vanden-Eijnden 2023) has the form

μt(z)=cos(πt/2)x0+sin(πt/2)x1andσt(z)=0,ut(xz)=π2(cos(πt/2)x1sin(πt/2)x0).\begin{align} \mu_t(z) = \cos \left(\pi t / 2\right)x_0 + \sin \left(\pi t / 2 \right)x_1 \quad\text{and}\quad \sigma_t(z) = 0,\\ u_t(x | z) = \frac{\pi}{2} \left( \cos\left(\pi t / 2\right) x_1 - \sin\left(\pi t / 2\right) x_0 \right). \end{align}

Sampling: now that we have our vector field, we can sample from our prior xq0(x)\mathbf{x} \sim q_0(\mathbf{x}), and run a forward ODE solver (e.g., fixed Euler or higher-order, adaptive Dormand–Prince) generally defined by

xt+Δ=xt+vθ(t,xt)Δ,\mathbf{x}_{t+\Delta} = \mathbf{x}_{t} + v_\theta (t, \mathbf{x}_t) \Delta,

for tt steps between 00 and 11.

Running

Run with default params and save the result in media/*.png:

python main.py --method vp --solver dopri5
  • main.py: training and sampling
  • models.py: neural net definition
  • datasets.py: generate prior and target data
  • cfm.py: flow matching variants
  • odeint.py: adaptive and fixed numerical integrators
  • solver.py: solver definition for integrator

Dependencies

Install the dependencies (optimized for Apple silicon; yay for MLX!):

pip install -r requirements.txt

Footnotes

  1. this is a very important thing!