Algorithms¶
Algorithms define the learning paradigm -- how the policy is trained and how it generates actions at inference time. They are decoupled from the decoder architecture: the algorithm orchestrates the decoder without knowing its internals. Certain pairings are naturally constrained by their mathematical formulation (e.g., timestep-conditioned decoders require a generative algorithm that provides timesteps).
All algorithms inherit from DecodingAlgorithm and implement these methods:
| Method | Purpose | Actions required? |
|---|---|---|
forward() |
Training pass | Yes (ground-truth actions) |
predict() |
Inference pass | No |
get_targets() |
Provide regression targets for the loss module | Yes |
class DecodingAlgorithm(nn.Module, abc.ABC):
@abstractmethod
def forward(
self,
network: ActionDecoder,
features: dict[str, torch.Tensor],
actions: dict[str, torch.Tensor] | None = None,
) -> dict[str, torch.Tensor]: ...
@abstractmethod
def predict(
self,
network: ActionDecoder,
features: dict[str, torch.Tensor],
) -> dict[str, torch.Tensor]: ...
def get_targets(
self,
algorithm_output: dict[str, torch.Tensor | dict[str, torch.Tensor]],
ground_truth_actions: dict[str, torch.Tensor],
) -> dict[str, torch.Tensor]:
"""Return the correct regression targets for the loss module.
Default returns ground-truth actions (correct for BC).
"""
return ground_truth_actions
@property
def predicts_in_action_space(self) -> bool:
"""Whether the network output lives in the action space."""
return True
The forward() / predict() contract keeps the two concerns apart: training logic (noise injection, flow interpolation, latent encoding) lives in the algorithm, while the neural network computation lives in the decoder.
Algorithm Targets¶
Different algorithms predict different quantities. The loss module must compare predictions against the correct target, not raw ground-truth actions. Policy.compute_loss() calls algorithm.get_targets() to obtain the right regression target:
| Algorithm | get_targets() returns |
predicts_in_action_space |
|---|---|---|
BehavioralCloning |
Ground-truth actions | True |
FlowMatching |
Velocity field | False |
Diffusion (epsilon) |
Noise | False |
Diffusion (sample) |
Denoised sample | True |
Diffusion (velocity) |
Velocity | False |
The predicts_in_action_space property enables loss-algorithm compatibility validation. Classification losses (e.g., BCE for gripper) require action-space predictions -- pairing them with Flow Matching (which predicts velocity fields) is caught at initialization by ExperimentValidator.
BehavioralCloning¶
The simplest algorithm. Directly predicts actions from observations via supervised learning. Both forward() and predict() delegate to the decoder network without modification.
class BehavioralCloning(DecodingAlgorithm):
def forward(self, network, features, actions=None):
return network(features=features, actions=actions)
def predict(self, network, features):
return network(features=features, actions=None)
Diffusion¶
Generative modeling via Denoising Score Matching. Trains the network to denoise actions at various noise levels, then generates actions through iterative denoising at inference.
Training (forward):
- Sample random timesteps
tfrom[0, num_train_timesteps] - Add noise to ground-truth actions:
x_t = sqrt(alpha_t) * x_0 + sqrt(1 - alpha_t) * epsilon - Pass noisy actions + timesteps to the decoder
- Compute target based on
prediction_type(epsilon, sample, or velocity)
Inference (predict):
- Initialize actions from pure noise
x_T ~ N(0, I) - Iteratively denoise using the learned model for
num_inference_stepssteps - Return the final denoised actions
x_0
Key parameters:
| Parameter | Default | Description |
|---|---|---|
scheduler_type |
"ddim" |
Scheduler type ("ddpm" or "ddim") |
num_train_timesteps |
100 |
Diffusion steps during training |
num_inference_steps |
10 |
Denoising steps during inference |
beta_schedule |
"squaredcos_cap_v2" |
Noise schedule shape |
prediction_type |
"epsilon" |
Network prediction target ("epsilon", "sample", "velocity") |
Encoder caching
When used with DiTBlockActionTransformer, the diffusion algorithm automatically enables encoder caching during inference. The observation encoder runs once and its output is reused across all denoising steps.
FlowMatching¶
Simulation-free generative modeling via conditional flow matching. Trains the network to regress velocity fields that transport samples from noise to actions, then integrates the learned field as an ODE at inference.
Training (forward):
- Sample time
tfrom[0, 1]using the configured timestep sampler - Interpolate between noise
x_0and ground-truth actionsx_1at timet - Compute the conditional velocity field
u_t - Train the network to predict
u_t
Inference (predict):
- Initialize from noise
z ~ N(0, I) - Integrate the learned velocity field from
t=0tot=1using an ODE solver - Return the final trajectory
Key parameters:
| Parameter | Default | Description |
|---|---|---|
sigma |
0.0 |
Noise level for CFM (0 = deterministic straight conditional path) |
num_inference_steps |
10 |
Number of ODE integration steps |
ode_solver |
"euler" |
Solver type ("euler", "heun", "rk4") |
timestep_sampler |
"beta" |
Sampling strategy ("uniform", "logit_normal", "beta") |
beta_alpha / beta_beta |
1.5 / 1.0 |
Shape parameters for Beta distribution sampler |
max_timestep |
0.999 |
Upper bound for timestep sampling |
reverse_flow_convention |
False |
When True, reverses the flow convention (noise at t=1, data at t=0) |
Timestep sampling
The beta sampler (from Pi0) biases training towards later timesteps where the signal-to-noise ratio is higher, improving sample quality. The logit_normal sampler provides similar control via logit_mean and logit_std.
VariationalAlgorithm¶
A compositional wrapper that adds variational inference to any base algorithm. Instead of predicting actions directly from observations, it introduces a latent variable z that captures multi-modal action distributions:
Where:
p(a|z,s)is the base algorithm's decoderq(z|a,s)is the posterior encoder (training only)p(z|s)is the prior (inference)
Architecture¶
VariationalAlgorithm(
base_algorithm=<any DecodingAlgorithm>,
posterior_encoder=<PosteriorLatentEncoder>,
prior=<PriorLatentEncoder | None>,
sampling_from_prior_probability=0.0,
)
The wrapper composes three independent components:
| Component | Role | Available at |
|---|---|---|
Posterior encoder q(z\|a,s) |
Encodes ground-truth actions into latent z |
Training only |
Prior p(z\|s) |
Samples latent z without access to actions |
Training + Inference |
| Base algorithm | Decodes actions given z and observations |
Training + Inference |
Training Flow¶
observations + actions
|
+-----------+-----------+
| |
Posterior q(z|a,s) Prior p(z|s)
| |
z_posterior z_prior
| |
+-------select----------+
|
features + z
|
Base Algorithm
|
predictions
- Posterior encoding:
z ~ q(z|a,s)-- encode ground-truth actions into latent space - Prior training: train the prior to match posterior samples (for learned priors)
- Stochastic mixing: with probability
sampling_from_prior_probability, usezfrom the prior instead of the posterior during training - Decoding: augment features with
zand delegate to the base algorithm
During validation, the prior sample is always used for action decoding (matching inference behavior), while the posterior is still computed for loss (KL term).
Inference Flow¶
- Sample latent:
z ~ p(z|s)from the prior - Decode: pass
features + zto the base algorithm'sforward()withactions=None
Posterior Encoder¶
All posterior encoders inherit from PosteriorLatentEncoder, which defines the encode(actions, observations) interface. Custom posterior encoders can be created by subclassing it.
VAETransformerEncoder -- Gaussian posterior encoder. A transformer encoder that processes action chunks and observation tokens, using a learnable CLS token to predict the mean and log-variance of a conditional Gaussian posterior. The latent z is sampled via the reparameterization trick.
Supports a deterministic mode (no reparameterization) for use with non-KL regularizers such as MMD or Optimal Transport losses.
VQPosteriorEncoder -- discrete posterior encoder. It maps actions and observations through a transformer encoder, quantizes the latent with residual vector quantization, and exposes codebook indices plus continuous/quantized latents for VQ commitment and prior losses.
Prior Types¶
All priors inherit from PriorLatentEncoder, which defines forward(target_latents, observations) for training and sample_prior(batch_size, observations) for inference. Custom priors can be created by subclassing it — any learned network that maps observations to a latent distribution can serve as a prior, enabling probabilistic student-teacher schemes where the prior learns to approximate the posterior without access to actions.
| Prior | Type | Description |
|---|---|---|
GaussianPrior |
Fixed | Standard N(0, I). No trainable parameters. Default when prior=None. |
PriorTransformerEncoder |
Learned | Transformer encoder conditioned on observations. Predicts mu and logvar of a conditional Gaussian p(z\|s). |
DiTPrior |
Learned | DiT-style transformer trained via diffusion or flow matching to denoise latent samples. Supports both "diffusion" and "flow_matching" algorithm types. |
VampPrior |
Learned | Variational Mixture of Posteriors. Defines K learnable pseudo-inputs passed through the posterior encoder to form a mixture-of-Gaussians prior. |
UniformCodebookPrior |
Fixed | Samples residual VQ code indices uniformly and decodes them through the posterior-owned codebook. |
CodebookPrior |
Learned | Predicts categorical logits over each residual VQ codebook layer from observations, then decodes sampled indices through the shared codebook. |
GaussianPrior auto-creation
If prior=None is passed, a GaussianPrior is automatically created with the same latent_dimension as the posterior encoder.
VampPrior initialization
VampPrior and VQ codebook priors require access to posterior-owned state. The VariationalAlgorithm automatically calls prior.wire_posterior(posterior_encoder) during initialization for priors that implement that protocol.
Example Combinations¶
BC + VAE (standard VAE, as in ACT):
VariationalAlgorithm(
base_algorithm=BehavioralCloning(),
posterior_encoder=VAETransformerEncoder(...),
prior=None, # Auto-creates GaussianPrior N(0, I)
)
BC + Learned Conditional Prior:
VariationalAlgorithm(
base_algorithm=BehavioralCloning(),
posterior_encoder=VAETransformerEncoder(...),
prior=PriorTransformerEncoder(...),
)
Flow Matching + DiT Prior:
VariationalAlgorithm(
base_algorithm=FlowMatching(...),
posterior_encoder=VAETransformerEncoder(...),
prior=DiTPrior(algorithm_type="flow_matching", ...),
)
VariationalAlgorithm(
base_algorithm=Diffusion(...),
posterior_encoder=VAETransformerEncoder(...),
prior=VampPrior(num_components=10, ...),
)
Source Locations¶
| Component | Path |
|---|---|
DecodingAlgorithm |
src/versatil/models/decoding/algorithm/base.py |
BehavioralCloning |
src/versatil/models/decoding/algorithm/behavior_cloning.py |
Diffusion |
src/versatil/models/decoding/algorithm/diffusion.py |
FlowMatching |
src/versatil/models/decoding/algorithm/flow_matching.py |
VariationalAlgorithm |
src/versatil/models/decoding/algorithm/variational.py |
VAETransformerEncoder |
src/versatil/models/decoding/latent/posterior/transformer_encoder.py |
VQPosteriorEncoder |
src/versatil/models/decoding/latent/posterior/vq_encoder.py |
GaussianPrior |
src/versatil/models/decoding/latent/prior/gaussian_prior.py |
PriorTransformerEncoder |
src/versatil/models/decoding/latent/prior/transformer_encoder.py |
DiTPrior |
src/versatil/models/decoding/latent/prior/dit_prior.py |
VampPrior |
src/versatil/models/decoding/latent/prior/vamp_prior.py |
UniformCodebookPrior |
src/versatil/models/decoding/latent/prior/uniform_codebook_prior.py |
CodebookPrior |
src/versatil/models/decoding/latent/prior/codebook_prior.py |