• 12 min read
  • Cross-industry
  • Oct 2026

Branching Flows: variable-length flow matching

Author · Theodor Mosetti Björk
Image of 3izi created using Protein Workshop. Credit: https://www.rcsb.org/news/5764422099cccf72e74ca304.

Introduction

Diffusion and flow matching have taken over generative modelling for anything that is not text. They produce images, video, molecules and protein structures by transporting a simple noise distribution onto a data distribution along a learned velocity field. The machinery is elegant, the samplers are fast, and the results speak for themselves.

They also share a limitation that is easy to miss, because it is built into the mathematics rather than bolted on afterwards. A flow moves a fixed set of elements. The same points that start as noise at t=0t = 0 arrive as data at t=1t = 1. Nothing is ever created, and nothing is ever destroyed. So the number of elements in the output has to be decided before the model starts running: how many atoms in the molecule, how many residues in the protein, how many tokens in the sequence.

For protein design that is a genuine problem. If you are designing a binder against a target, the number of residues in the binder is one of the things you wanted the model to tell you. In practice it gets handled by sweeping over candidate lengths, sampling many designs at each one, and filtering afterwards.

Branching Flows removes the constraint. Elements evolve over forests of binary trees: each one can split into two, or die off, at rates the model learns alongside the ordinary velocity field. The number of elements becomes an output of the generative process rather than an input to it. This post walks through how it works, starting from flow matching itself.

Note

About this post. Branching Flows is research from the Murrell Lab at Karolinska Institutet, by Lukas Billera, Hedwig Nora Nordlinder, Jack Collier Ryder, Anton Oresten, Aron Stålmarck, Theodor Mosetti Björk and Ben Murrell. It is not a Modulai project. Our colleague Theodor Mosetti Björk is a co-author on it, from his time at the Murrell Lab, before he joined Modulai, and he presented the work at one of our internal learning breakfasts. This write-up follows that talk, and the credit for the research belongs with the lab.

Flow matching

The setup is a transport problem. We have a simple distribution p0p_0 that we can sample from, usually N(0,I)\mathcal{N}(0, I), and a data distribution p1p_1 that we cannot. We want a map that carries one onto the other, and we want to learn it from samples alone.

Flow matching does this with a time-dependent velocity field. Each sample follows a smooth path, governed by an ordinary differential equation:

ddt ψt(x)=ut(ψt(x)),ψ0(x)=x \frac{d}{dt}\,\psi_t(x) = u_t\big(\psi_t(x)\big), \qquad \psi_0(x) = x

The flow ψt\psi_t pushes the base distribution forward, pt=[ψt]# p0p_t = [\psi_t]_\# \, p_0, and we want the result to be p1p_1 when t=1t = 1. The density and the velocity are tied together by the continuity equation:

∂t pt+∇⋅(pt ut)=0 \partial_t\, p_t + \nabla \cdot \big(p_t\, u_t\big) = 0

Time here runs from noise at t=0t = 0 to data at t=1t = 1. In much of the diffusion literature the convention runs the other way, which is worth keeping in mind when comparing papers.

The obvious objective is to regress a network onto the true velocity field:

LFM(θ)=Et, x∼pt∥utθ(x)−ut(x)∥2 \mathcal{L}_{\mathrm{FM}}(\theta) = \mathbb{E}_{t,\, x \sim p_t} \left\| u^\theta_t(x) - u_t(x) \right\|^2

We cannot optimise this directly. We know neither utu_t nor the probability path ptp_t it induces, and if we knew utu_t there would be nothing left to learn.

The conditional flow matching trick

The way out, from Lipman and colleagues, is to specify the path ourselves instead of inferring it from the field. Pick a path conditioned on both endpoints, for example a straight line:

xt=(1−t) x0+t x1,ut(xt∣x0,x1)=x1−x0 x_t = (1 - t)\, x_0 + t\, x_1, \qquad u_t(x_t \mid x_0, x_1) = x_1 - x_0

The conditional velocity ut(xt∣x0,x1)u_t(x_t | x_0, x_1) is just the derivative of that line. Sampling ptp_t is now easy, and it is hierarchical: draw x0x_0 from the noise and x1x_1 from the data, then take a point on the line between them. The choice of the endpoint conditional path induces:

pt(x)=∫pt(x∣x0,x1) p(x0,x1) dx0 dx1,ut(x)=E[ ut(xt∣x0,x1)∣xt=x ] p_t(x) = \int p_t(x \mid x_0, x_1)\, p(x_0, x_1)\, dx_0\, dx_1, \qquad u_t(x) = \mathbb{E}\big[\, u_t(x_t \mid x_0, x_1) \mid x_t = x \,\big]

Training then regresses against the conditional velocity, and the key result is that this has the same gradient as the intractable objective:

LCFM(θ)=Et, (x0,x1), xt∥utθ(xt)−ut(xt∣x0,x1)∥2,∇θLCFM=∇θLFM \mathcal{L}_{\mathrm{CFM}}(\theta) = \mathbb{E}_{t,\,(x_0, x_1),\, x_t} \left\| u^\theta_t(x_t) - u_t(x_t \mid x_0, x_1) \right\|^2, \qquad \nabla_\theta \mathcal{L}_{\mathrm{CFM}} = \nabla_\theta \mathcal{L}_{\mathrm{FM}}

This pattern is worth internalising, because Branching Flows reuses it exactly: choose a conditional process you can write down, take an expectation to recover the marginal object, and train against the conditional target.

Sampling

Once utθu^\theta_t is trained, generating a sample is just solving an ODE. Draw x0∼p0x_0 \sim p_0, then integrate x˙=utθ(x)\dot{x} = u^\theta_t(x) from t=0t = 0 to t=1t = 1, in practice with Euler steps x←x+Δt utθ(x)x \leftarrow x + \Delta t\, u^\theta_t(x).

Four scatter plots showing Gaussian noise transported into a two-moons shape at t = 0, 0.6, 0.85 and 1
Figure 1:A flow carrying Gaussian noise onto a two-moons distribution. Each point follows its own trajectory, and the orange segments show the learned velocity field at that instant. The count of points at t = 1 is exactly the count at t = 0.

Discrete flow matching

The same recipe works when the state is categorical, with a continuous-time Markov chain in place of an ODE. Rather than drifting along a velocity field, the state jumps:

P(xt+Δt=y∣xt=x)=δxy+Qt(y∣x) Δt P(x_{t + \Delta t} = y \mid x_t = x) = \delta_{xy} + Q_t(y \mid x)\, \Delta t

The rate matrix QtQ_t has Qt(y∣x)≥0Q_t(y \mid x) \ge 0 for y≠xy \ne x and rows summing to zero, and the density evolves by the Kolmogorov forward equation, ∂t pt(y)=∑xQt(y∣x) pt(x)\partial_t\, p_t(y) = \sum_x Q_t(y \mid x)\, p_t(x).

Structurally nothing has changed. You still pick conditional paths and learn the marginal object by taking an expectation: a velocity in the continuous case, rates in the discrete one. Sampling becomes a sequence of jumps, where at each step you move to yy with probability Qtθ(y∣x) Δt.Q^\theta_t(y \mid x)\, \Delta t.

Use cases of Flow Matching

Here are some examples of how flow matching models are used today.

1. Image generation models. We transport a noisy image to a clean image from the data distribution. This uses a continuous state space: each pixel holds continuous values (think one value for each RGB channel).

Flow Matching for image models

2. Diffusion language models. We transport masked tokens to text. This uses a discrete state space of tokens.

Flow Matching for language modeling

3. Protein design. We generate a new protein. This combines two state spaces: the discrete amino acid identities and the continuous residue positions in Euclidean space (sometimes extended with residue orientations on a manifold, such as SO(3)).

Flow Matching for protein design

Note that in all three of these applications, the state space was fixed and known before the flow began. In the image example, we already knew the number of pixels; in the language model, we had already laid out a fixed number of masked tokens; and in the protein design model, we started with a point cloud of a fixed number of amino acid residues. No change in dimensionality occurred during the flow. We had to specify it beforehand! This is an important limitation of flow matching.

The variable-length problem

Flow matching works on a fixed state space: x0x_0 and x1x_1 have the same dimensions. That’s fine for applications like image generation, where we usually know the pixel dimensions of the image we want to create beforehand. In protein design, however, it’s more of an issue. We might not know a priori how long a binder should be; ideally the model itself should decide the length at sampling time. This is what we mean by the variable-length problem. The usual approach in protein design is still to train a fixed-length model and sweep across different lengths, which is somewhat ad hoc. Letting the model decide the length itself is the main idea behind Branching Flows.

Another way to see the variable-length problem is through infilling. Suppose you have a prefix A and a suffix B, and you need to generate what goes between them. This is a generative task conditioned on the flanking regions, and plenty of models can already do it, as long as we specify the length beforehand. Masked transformers fill in [MASK] positions; diffusion and flow models denoise the span. Both need the number of missing positions up front, and in infilling that number is not a minor detail. Consider a sentence with a gap of unknown size:

We shipped  [ ? ? ? ]  on time.          L = ?

We shipped  exactly  on time.            L = 1   we made it
We shipped  nowhere near  on time.       L = 2   we did not

The main takeaway is that the length decides which meanings are reachable at all. Choosing a length is not a technical detail; it is part of deciding what the output says. Sweeping over lengths by hand is therefore an ad hoc workaround for a choice that should ideally be made by the model itself. The same is true in protein design: the length of a binder is itself a design variable with functional consequences.

Branching Flows

Branching Flows is a framework that extends flow matching to variable-length generation, where the model itself decides the length. Here’s an example of what that looks like:

Atom trajectories branching from one element at t = 0 into a complete molecule at t = 1
Figure 2:Unconditional QM9 generation. A single element at t = 0 branches repeatedly and the branches diverge into a whole molecule by t = 1. Colour tracks the atom type, with orange marking the mask state. Figure from Billera et al., arXiv:2511.09465.

Rather than inserting new elements at arbitrary positions, which would force the model to learn a distribution over where each one belongs, Branching Flows grows the sequence by splitting: an existing element duplicates in place, and the base process then moves the two copies apart. Elements can also be deleted, and both splits and deletions happen at rates the model learns. Because a new element always starts exactly where its parent was, the construction works for discrete, continuous and manifold states, or any mix of them. That matters for molecules and proteins, where each element has both a position and a label (an atom or amino acid type).

Everything evolves together. The learned field utθu^θ_t reads the whole state and outputs velocities for the atom positions, rates for the discrete atom types, and on top of that the split and deletion rates. This means the model can correct itself as it goes: if it needs more elements, one splits into two, and if an atom is badly placed, it can be deleted. Length becomes an output of the model, not an input.

How Branching Flows is trained

As covered earlier, flow matching trains on simple endpoint-conditional paths rather than the intractable marginal one. Branching Flows does the same, but here x0x_0 and x1x_1 have different lengths, so the path must also decide which elements split, which are deleted, and when. The authors handle this by conditioning on a richer latent variable than just the endpoints.

1. Sample Z

Alongside a data point x1∼q,x_1 \sim q, we sample z=(x1+∅,x0,T,A)z = (x_1^{+\emptyset}, x_0, \mathcal{T}, \mathcal{A}):

  • x0x_0: the initial noise elements, often just a few ones.
  • x1+∅x_1^{+\emptyset}: the data with extra “to-be-deleted” copies inserted.
  • T\mathcal{T}: a forest of binary trees linking each root in x0x_0 to the leaves of x1+∅x_1^{+\emptyset}, built by repeatedly merging random adjacent pairs, which preserves the sequence order.
  • A\mathcal{A}: an anchor for every node. A surviving leaf’s anchor is its data element, and each internal anchor is a weighted average of its children’s anchors (a mask token for discrete components).

2. Conditional paths

Given zz, each element follows the base process towards the anchor of the node ahead of it. It splits in place at a rate proportional to its number of remaining splits, after which each copy heads for its own child’s anchor. Elements on branches ending in a to-be-deleted leaf can also be deleted. Both rates diverge as t→1t → 1, so every scheduled event has happened by the end, and the path terminates exactly at x1x_1. The figure below shows one such conditional path.

A forest of binary trees from two roots at time zero to five leaves at time one, with anchors and a deletion branch
Figure 3:The conditional path. Two noise elements at t = 0 become five leaves at t = 1, one of which is a to-be-deleted element on the red dashed branch. Green anchors steer each branch toward where it has to arrive. Figure 2 from Billera et al., arXiv:2511.09465.

Between events, nothing unusual happens: each element does ordinary flow matching towards the next anchor, exactly as if that anchor were x1x_1. The branching sits on top of whatever base process you choose, whether that’s a plain ODE, an Ornstein–Uhlenbeck process, a Riemannian flow or discrete flow matching. Branching Flows extends the flow matching design space rather than replacing it.

Because zz is fixed, everything in the figure is known in advance: how many more times each element will split, whether it will be deleted, and which anchor it is heading for. The model never gets to see zz, so it has to learn to predict these quantities from the current state alone. That is exactly what it is trained to do.

3. Training targets and loss

For each element at time tt, the model predicts its expected number of remaining splits, its probability of eventually being deleted, and the usual base-process output. The loss is a sum of Bregman divergences: Poisson-style for splits, cross-entropy for deletions, and the base-process loss (for example, squared error or discrete flow matching). As in ordinary flow matching, training on these conditional targets is enough for the model to learn the right overall process. At sampling time, it only needs the current state xtx_t, not zz.

What Branching Flows can do

Now let's see the model in practice. We demonstrate four tasks:

  1. Unconditional protein design — we start from a few masked amino acids and let the model run.
  2. Binder design — we fix a target protein and design a second protein that binds to it.
  3. Infix sampling — a protein complex is missing a section, which we fill back in.
  4. Unconditional small-molecule generation (QM9) — we start from a single masked atom and generate a molecule.

We show one sampling video each. We begin with an element drawn from the noise distribution and integrate it according to our base process, together with the splits and deletions, which is what the videos below show. In each video, the left panel shows the flow (the sample being integrated), and the right panel shows the endpoint prediction: the trained model works by predicting the endpoint and then rescaling it to a velocity.

Unconditional protein design. We start from a few masked amino acids and sample a monomer with three beta-sheet-looking domains.
Binder design. We start from a fixed target structure (blue, the conditioning) and a single masked residue (red), then grow a binder that develops a protein–protein interface against the target.
Infix sampling. We start from a protein complex with two chains, one red and one blue, but part of the red chain is missing. We condition on the complex together with the incomplete red structure, start from a single red residue, and generate the segment that completes the red protein.
Unconditional small-molecule generation (QM9). We start from a single masked atom, then generate a small molecule.

Plug and Play

For anyone who already has a trained flow matching model, the most immediately useful part of the paper is how easily Branching Flows can be added to it.

A pretrained trunk feeding its existing heads alongside two newly added split and delete heads
Figure 4:Take a pretrained fixed-length model, fine-tune Branching Flows in place of flow matching, and add two heads. Each new head is a small MLP emitting one scalar per element: a count and a probability.

As Figure 4 shows, no new architecture is needed. You take any pretrained fixed-length flow matching model and add two heads, one for splits and one for deletions. Then you fine-tune the whole network with Branching Flows and add a split-and-delete step to the sampling loop. The authors report that three days on a single NVIDIA RTX 6000 Ada was enough, and they did this both with their own ChainStorm model and with NVIDIA’s La Proteina.

Practical guidance: three choices helped the authors: using a stochastic base process with learned drift and fixed noise, placing most splits early so new elements had time to settle, and predicting each element’s endpoint rather than its drift directly, which proved more stable. The authors’ open-source code is available on https://github.com/MurrellGroup/BranchingFlows.jl .

Closing thoughts

Branching Flows extends flow matching by making the number of elements part of the generative process itself. This opens the door to variable-length generation, including infilling problems where even the number of missing elements is unknown. Existing models don’t need to be rebuilt from scratch: adding split and deletion heads and fine-tuning is enough to bring them over. Taken together, this meaningfully expands what we can ask flow-based models to generate.

References (3)

  1. Billera, L., Nordlinder, H.N., Ryder, J.C., Oresten, A., Stålmarck, A., Mosetti Björk, T., Murrell, B. Branching Flows: Discrete, Continuous, and Manifold Flow Matching with Splits and Deletions. arXiv:2511.09465, 2025.

    arXiv
  2. Lipman, Y., Chen, R.T.Q., Ben-Hamu, H., Nickel, M. and Le, M. Flow Matching for Generative Modeling. ICLR 2023.

    arXiv
  3. Gat, I., Remez, T., Shaul, N., Kreuk, F., Chen, R.T.Q., Synnaeve, G., Adi, Y. and Lipman, Y. Discrete Flow Matching. NeurIPS 2024.

    arXiv

Learn more

Related content