Flow Components¶
Modular components that FlowMatchingObjective composes, so flow-matching variants
are configuration rather than subclasses.
Every getter (get_path, get_time_sampler, get_coupling, get_source) accepts
either a registry name or an already-constructed instance.
Paths¶
The interpolation between noise and data.
Invariant
A path's velocity() must be the exact time derivative of its interpolate().
If they disagree, training silently regresses a target that does not match the
interpolation the model is shown.
paths
¶
Path Interpolation Strategies for Flow Matching.
Defines how to interpolate between noise (x_0) and data (x_1) and compute the corresponding target velocity fields.
PathInterpolant
¶
Bases: ABC
Abstract base class for interpolation paths in flow matching.
A path defines: 1. How to interpolate: x_t = interpolate(x_0, x_1, t) 2. The target velocity: v_t = velocity(x_0, x_1, t)
The velocity is the derivative dx_t/dt that the model learns to predict.
Source code in flowpde/flows/components/paths.py
interpolate(x_0, x_1, t)
abstractmethod
¶
Compute interpolated point x_t on the path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_0
|
Tensor
|
Starting point (noise), shape (B, *) |
required |
x_1
|
Tensor
|
Ending point (data), shape (B, *) |
required |
t
|
Tensor
|
Time in [0, 1], shape (B, 1) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
x_t |
Tensor
|
Interpolated point, shape (B, *) |
Source code in flowpde/flows/components/paths.py
velocity(x_0, x_1, t)
abstractmethod
¶
Compute target velocity v_t = dx_t/dt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_0
|
Tensor
|
Starting point (noise), shape (B, *) |
required |
x_1
|
Tensor
|
Ending point (data), shape (B, *) |
required |
t
|
Tensor
|
Time in [0, 1], shape (B, 1) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
v_t |
Tensor
|
Target velocity, shape (B, *) |
Source code in flowpde/flows/components/paths.py
__call__(x_0, x_1, t)
¶
Compute both interpolation and velocity.
Returns:
| Type | Description |
|---|---|
(x_t, v_t)
|
Interpolated point and target velocity |
Source code in flowpde/flows/components/paths.py
LinearPath
¶
Bases: PathInterpolant
Linear interpolation path (straight lines).
Path: x_t = (1 - t) * x_0 + t * x_1 Velocity: v_t = x_1 - x_0 (constant along path)
This is the standard path used in: - Flow Matching (Lipman et al., 2023) - Rectified Flow (Liu et al., 2023)
Properties: - Straight lines from noise to data - Constant velocity (simplest to learn) - x_0 at t=0, x_1 at t=1
Source code in flowpde/flows/components/paths.py
interpolate(x_0, x_1, t)
¶
OTConditionalPath
¶
Bases: PathInterpolant
Optimal-transport conditional flow path (Tong et al., 2023).
Two noise schedules are available, and both regress the exact derivative of the path the model is shown.
schedule='constant' (default) — the OT-CFM path of Tong et al.:
x_t = t * x_1 + (1 - t) * x_0 + sigma * eps
v_t = x_1 - x_0
The noise term does not depend on t, so the chord is the conditional velocity. This is what makes sigma > 0 usable: the regression target stays bounded everywhere.
schedule='bridge' — a Brownian-bridge tube that pins both endpoints:
x_t = t * x_1 + (1 - t) * x_0 + sigma * sqrt(t(1-t)) * eps
v_t = x_1 - x_0 + sigma * (1 - 2t) / (2 * sqrt(t(1-t))) * eps
Here the tube width is time dependent, so the chord alone is not the derivative: the second term is required, or the model regresses a target that does not match its own input. That term diverges as t approaches 0 or 1 (it is clamped, but stays large), which is why the constant schedule is the default.
With sigma = 0 both schedules reduce exactly to LinearPath.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sigma
|
float
|
Noise scale (default: 0.0). |
0.0
|
schedule
|
str
|
|
'constant'
|
Source code in flowpde/flows/components/paths.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
interpolate(x_0, x_1, t, noise=None)
¶
x_t on the conditional path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
noise
|
Optional[Tensor]
|
The epsilon to use. Pass the same one to |
None
|
Source code in flowpde/flows/components/paths.py
velocity(x_0, x_1, t, noise=None)
¶
Exact derivative dx_t/dt of the path defined by interpolate.
Source code in flowpde/flows/components/paths.py
__call__(x_0, x_1, t)
¶
Interpolated point and its target velocity, from a single epsilon.
Drawing epsilon twice would hand the model an input from one sample path and the derivative of a different one.
Source code in flowpde/flows/components/paths.py
get_path(path, **kwargs)
¶
Get a path interpolant by name or return if already an instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Union[str, PathInterpolant]
|
Path name ('linear', 'ot_conditional') or PathInterpolant instance |
required |
**kwargs
|
Any
|
Additional arguments for path constructor |
{}
|
Returns:
| Type | Description |
|---|---|
PathInterpolant
|
PathInterpolant instance |
Examples:
>>> path = get_path('linear')
>>> path = get_path('ot_conditional', sigma=0.01)
>>> path = get_path(LinearPath()) # Pass-through
Source code in flowpde/flows/components/paths.py
Time Samplers¶
time_samplers
¶
Time Sampling Strategies for Flow Matching.
Defines distributions for sampling time t ∈ [0, 1] during training. Different sampling strategies can improve training dynamics.
TimeSampler
¶
Bases: ABC
Abstract base class for time sampling distributions.
Different time distributions can affect training: - Uniform: Standard, simple - Logit-normal: Concentrates samples near t=0 and t=1 - Beta: Flexible, can emphasize different regions
Source code in flowpde/flows/components/time_samplers.py
sample(batch_size, device)
abstractmethod
¶
Sample time values in [0, 1].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
batch_size
|
int
|
Number of samples |
required |
device
|
device
|
Device for tensor |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
Time tensor of shape (batch_size, 1) |
Source code in flowpde/flows/components/time_samplers.py
UniformSampler
¶
Bases: TimeSampler
Uniform time sampling: t ~ U(low, high).
Standard sampling strategy for flow matching.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
low
|
float
|
Lower bound (default: 0.0) |
0.0
|
high
|
float
|
Upper bound (default: 1.0) |
1.0
|
Source code in flowpde/flows/components/time_samplers.py
sample(batch_size, device)
¶
LogitNormalSampler
¶
Bases: TimeSampler
Logit-normal time sampling: t = sigmoid(z) where z ~ N(mean, std²).
This distribution concentrates samples near t=0 and t=1, which can help the model learn the behavior at the boundaries better.
Used in Rectified Flow and some diffusion models.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
mean
|
float
|
Mean of the normal distribution (default: 0.0) |
0.0
|
std
|
float
|
Standard deviation (default: 1.0) |
1.0
|
Source code in flowpde/flows/components/time_samplers.py
sample(batch_size, device)
¶
Sample t = sigmoid(z) where z ~ N(mean, std²).
BetaSampler
¶
Bases: TimeSampler
Beta distribution time sampling: t ~ Beta(alpha, beta).
Flexible distribution that can emphasize different regions: - alpha=beta=1: Uniform - alpha=beta=0.5: U-shaped (emphasizes boundaries) - alpha=beta>1: Concentrated around 0.5
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
alpha
|
float
|
First shape parameter (default: 1.0) |
1.0
|
beta
|
float
|
Second shape parameter (default: 1.0) |
1.0
|
Source code in flowpde/flows/components/time_samplers.py
sample(batch_size, device)
¶
get_time_sampler(sampler, **kwargs)
¶
Get a time sampler by name or return if already an instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
sampler
|
Union[str, TimeSampler]
|
Sampler name ('uniform', 'logit_normal', 'beta') or instance |
required |
**kwargs
|
Any
|
Additional arguments for sampler constructor |
{}
|
Returns:
| Type | Description |
|---|---|
TimeSampler
|
TimeSampler instance |
Examples:
>>> sampler = get_time_sampler('uniform')
>>> sampler = get_time_sampler('logit_normal', std=0.5)
>>> sampler = get_time_sampler('beta', alpha=0.5, beta=0.5)
Source code in flowpde/flows/components/time_samplers.py
Couplings¶
couplings
¶
Coupling Strategies for Flow Matching.
Defines how noise samples (x_0) and data samples (x_1) are paired during training. Different couplings can affect training efficiency.
Coupling
¶
Bases: ABC
Abstract base class for noise-data coupling strategies.
A coupling defines how to pair samples from the base distribution (noise, x_0) with samples from the data distribution (x_1).
Standard flow matching uses independent coupling, but optimal transport (OT) couplings can improve training.
Source code in flowpde/flows/components/couplings.py
couple(x_0, x_1)
abstractmethod
¶
Apply coupling strategy to pair noise and data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_0
|
Tensor
|
Noise samples, shape (B, *) |
required |
x_1
|
Tensor
|
Data samples, shape (B, *) |
required |
Returns:
| Type | Description |
|---|---|
(x_0_coupled, x_1_coupled)
|
Paired samples |
Source code in flowpde/flows/components/couplings.py
IndependentCoupling
¶
Bases: Coupling
Independent coupling: no reordering.
Each noise sample x_0[i] is paired with data sample x_1[i] as they appear in the batch. This is the standard approach.
Simple and fast, but may not be optimal for transport.
Source code in flowpde/flows/components/couplings.py
MiniBatchOTCoupling
¶
Bases: Coupling
Mini-batch Optimal Transport coupling.
Reorders the noise samples within each mini-batch to minimize total transport cost (squared Euclidean distance).
This can lead to more efficient training by creating shorter transport paths on average.
Requires scipy for the linear assignment. scipy is a hard dependency of this package, so a missing install is an error rather than a reason to quietly fall back to independent coupling -- that fallback would turn an OT ablation into a duplicate of the baseline with nothing to show for it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cost_fn
|
str
|
Cost function ('euclidean', 'cosine'). Default: 'euclidean' |
'euclidean'
|
Source code in flowpde/flows/components/couplings.py
couple(x_0, x_1)
¶
Compute OT coupling via linear assignment.
Finds permutation of x_0 that minimizes total transport cost.
Source code in flowpde/flows/components/couplings.py
get_coupling(coupling, **kwargs)
¶
Get a coupling strategy by name or return if already an instance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coupling
|
Union[str, Coupling]
|
Coupling name ('independent', 'minibatch_ot') or instance |
required |
**kwargs
|
Any
|
Additional arguments for coupling constructor |
{}
|
Returns:
| Type | Description |
|---|---|
Coupling
|
Coupling instance |
Examples:
>>> coupling = get_coupling('independent')
>>> coupling = get_coupling('minibatch_ot', cost_fn='cosine')
Source code in flowpde/flows/components/couplings.py
Sources¶
Where trajectories start. BatchSource is what makes reflow correct — it consumes
the exact noise z that produced each generated target instead of resampling.
sources
¶
Source Distributions for Flow Matching.
The source distribution supplies \(x_0\), the point a trajectory starts from. It is the fourth pluggable component of the flow-matching objective, alongside the path, the time sampler and the coupling.
Making it configurable is what allows the objective to be trained on precomputed pairs rather than freshly drawn noise. That distinction is the whole mechanism behind reflow: reflow retrains on pairs \((z, \mathrm{ODE}(z))\) generated by the current model, and the straightening only happens if training uses that \(z\) rather than an independently resampled one. Drawing fresh noise silently turns reflow into a no-op.
Available sources:
GaussianSource— standard normal noise (the default).BatchSource— read \(x_0\) from the training batch, falling back to another source when the key is absent (i.e. at inference time).
SourceDistribution
¶
Bases: ABC
Abstract base class for flow source distributions.
A source answers one question: given a target shape and a training batch, where does the trajectory start?
Source code in flowpde/flows/components/sources.py
sample(shape, device, batch=None)
abstractmethod
¶
Draw \(x_0\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
shape
|
Tuple[int, ...]
|
Shape of the target |
required |
device
|
device
|
Device to place the result on. |
required |
batch
|
Optional[Dict[str, Tensor]]
|
The training batch, when one is available. Sources that read precomputed values use it; others ignore it. |
None
|
Returns:
| Type | Description |
|---|---|
Tensor
|
Tensor of shape |
Source code in flowpde/flows/components/sources.py
GaussianSource
¶
Bases: SourceDistribution
Standard Gaussian source, \(x_0 \sim \mathcal{N}(0, \sigma^2 I)\).
The default for flow matching, and what inference uses regardless of how training was configured.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
std
|
float
|
Standard deviation (default: 1.0). |
1.0
|
Source code in flowpde/flows/components/sources.py
BatchSource
¶
Bases: SourceDistribution
Read \(x_0\) from the training batch.
Used for reflow, where the pairing between source and target is fixed in advance and must be preserved: the model is retrained on exactly the trajectories it generated, which is what straightens them.
When the key is absent — most importantly during sampling, where there is
no batch — the fallback source is used instead. So a model trained
with BatchSource still samples from \(\mathcal{N}(0, I)\) at
inference, as it should.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Batch key holding the precomputed |
'x_0'
|
fallback
|
Optional[SourceDistribution]
|
Source used when the key is missing. Defaults to
|
None
|
strict
|
bool
|
When True, raise instead of falling back if a batch is supplied but lacks the key. Catches silently-degraded reflow runs, and is the default for that reason. |
True
|
Source code in flowpde/flows/components/sources.py
get_source(source, **kwargs)
¶
Get a source distribution by name, or pass an instance through.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
Union[str, SourceDistribution]
|
Name ( |
required |
**kwargs
|
Any
|
Forwarded to the constructor when a name is given. |
{}
|
Returns:
| Type | Description |
|---|---|
SourceDistribution
|
A |
Examples:
>>> get_source('gaussian')
GaussianSource(std=1.0)
>>> isinstance(get_source(GaussianSource()), GaussianSource)
True