Diffusion and flow matching

One Lie-group Euler step for frame flow

HardRaw PyTorchAbout 30 min2 public examples plus private tests

FrameFlowRFdiffusion2

Write and run your solution Sign in

Free account with email, Google or GitHub. Your code runs in an isolated PyTorch sandbox.

Your task

Advance positions and rotation frames one step toward predicted endpoints. Leave fixed valid frames unchanged; padded positions return zero and padded rotations return the identity matrix.

Function signature

lie_euler(
    x_t: torch.Tensor,
    x1_hat: torch.Tensor,
    R_t: torch.Tensor,
    R1_hat: torch.Tensor,
    t: torch.Tensor,
    dt: torch.Tensor,
    valid_mask: torch.Tensor,
    diffuse_mask: torch.Tensor,
    rotation_schedule: str = 'linear',
    exp_rate: float = 10.0,
) -> tuple[torch.Tensor, torch.Tensor]

Worked example

Advance only part of the remaining path

Use one valid movable residue with x_t=[0,0,0], x1_hat=[4,0,0], R_t=identity, R1_hat=[[0,-1,0],[1,0,0],[0,0,1]], t=[0.5] and dt=[0.125]. The translation fraction is 0.125/(1−0.5)=1/4, so x_next=[1,0,0]. With rotation_schedule="linear", the 90° endpoint rotation advances by 22.5° about z. With rotation_schedule="exp" and exp_rate=4, the angular fraction is 0.125×4=1/2 and R_next rotates 45°; translation still advances by one quarter.

Parameters

ParameterShape and meaning
x_ttorch.Tensor[B, N, 3][B,N,3] current positions
x1_hattorch.Tensor[B, N, 3]Predicted clean positions
R_ttorch.Tensor[B, N, 3, 3]Current local-to-global rotations
R1_hattorch.Tensor[B, N, 3, 3]Predicted endpoint rotations
ttorch.Tensor[B]Current time in [0,1]
dttorch.Tensor[B]Nonnegative forward \mathrm{step} \le 1-t
valid_masktorch.Tensor[B, N]True marks usable positions; False marks padding.
diffuse_masktorch.Tensor[B, N]Bool; True may update
rotation_schedulestrDefault 'linear'Choose "linear" for the remaining-time rotation fraction or "exp" for the exp_rate-controlled fraction defined below.
exp_ratefloatDefault 10.0Rate of the exponential rotational schedule.

Return

tuple[torch.Tensor, torch.Tensor]

Return these tensors in the stated order.

Required behavior

  • Inputs x_t,x1_hat [B,N,3], R_t,R1_hat [B,N,3,3], t,dt [B], and two bool masks.
  • For 0\le t<1 and 0\le \mathrm{dt}\le 1-t, f=\mathrm{dt}/(1-t), x_{\mathrm{next}}=x_{t}+f\cdot (\mathrm{x1\_hat}-x_{t}). Evaluate this as (1-f)\cdot x_{t}+f\cdot \mathrm{x1\_hat} to avoid overflowing a residual whose endpoints are finite.
  • Set \omega=\operatorname{Log}(R_{t}^{\top} \mathrm{R1\_hat}) in the LOCAL/BODY frame and R_{\mathrm{next}}=R_{t} \operatorname{Exp}(g\cdot \omega). rotation_schedule='linear' (default) uses g=f.
  • The alternate 'exp' uses g=\mathrm{dt}\cdot \mathrm{exp\_rate}, \mathrm{exp\_rate}>0 default 10; translation still uses the linear factor.
  • This exponential rotation scaling corresponds to \mathrm{kappa}(t)=1-\operatorname{exp}(-\mathrm{exp\_rate}\cdot t), whose kappa'/(1-\mathrm{kappa}) is constant; it need not hit the predicted rotational endpoint at t=1.
  • In the linear variant \mathrm{dt}=1-t reaches both supplied endpoints exactly (up to roundoff) for proper rotations away from the cut locus. At t=1 only \mathrm{dt}=0 is valid: return the current valid frames unchanged, and never evaluate 1/(1-t).
  • Valid fixed residues always retain their CURRENT x_t/R_t; invalid positions become zero and invalid rotations identity. This is a positive-time flow step; do not insert the minus sign of a reverse-time diffusion integrator. No \epsilon, stochastic noise or post-update orthogonalization.
  • The only keys are rotation_schedule and exp_rate (used only for 'exp'). All masks are bool: valid_mask=True denotes a real residue/atom; diffuse_mask=True denotes a movable one. Their conjunction selects updates. B,N (or A) are positive; an all-false mask, not a zero-length tensor, represents an empty example.
  • All supplied numbers, including padding, are finite and share dtype/device; floating tensors are float32 or float64. Rotations act on column vectors and map local coordinates to the global frame.
  • For v=(x,y,z), \operatorname{hat}(v)=\begin{pmatrix}0&-z&y\\z&0&-x\\-y&x&0\end{pmatrix}. \operatorname{Exp}(v)=I+A\operatorname{hat}(v)+B\operatorname{hat}(v)^2, where q=v\cdot v, A=\frac{\sin\sqrt q}{\sqrt q}, B=\frac{1-\cos\sqrt q}{q}.
  • For q<10^{-4} use A=1-\frac q6+\frac{q^2}{120}-\frac{q^3}{5040} and B=\frac12-\frac q{24}+\frac{q^2}{720}-\frac{q^3}{40320}; at zero \operatorname{Exp}(0)=I.
  • For \operatorname{Log}(R), set v=\frac12\operatorname{vee}(R-R^\top), q=v\cdot v, s=\sqrt{q}, c=\frac{\operatorname{tr}R-1}{2}, \theta=\operatorname{atan2}(s,c). Ordinarily \operatorname{Log}(R)=\frac{\theta}{s}v. If q<10^{-8} and c>0 use v\left(1+\frac q6+\frac{3q^2}{40}+\frac{5q^3}{112}\right).
  • If q<10^{-6} and c<0, recover an axis from P=\frac{R+R^\top-2cI}{2(1-c)}: choose the largest diagonal (\mathrm{ties} x, \mathrm{then} y, \mathrm{then} z), take its positive square root, and divide its row by that component; normalize the axis.
  • Orient it to have nonnegative dot with v when q>10^{-20}; otherwise keep the chosen component positive. Return \theta\cdot \mathrm{axis}.
  • For dormant branches clamp 1-c below by 10^{-6} and diagonal square-root arguments below by 10^{-12}; these floors do not alter the selected near-\pi axis. Evaluate every inactive square root/division safely before torch.where. R must be a proper \operatorname{SO}(3) matrix; no projection/repair is requested.
  • At \pi axis sign is inherently ambiguous: \operatorname{exp}(\operatorname{log}(R)) is the geometric invariant. No global continuous logarithm exists at the \pi cut locus.

Why this matters

Positions move by a linear interpolation factor. Rotations move by a local angular increment composed on the right. Translation and rotation may use different schedules, but both outputs are complete next frames.

Starter code

You write the body of this function in the BioTorch workspace. Your code runs in an isolated PyTorch sandbox; Run checks the public examples and Submit grades the full test suite, including gradients.

import torch


def lie_euler(
    x_t: torch.Tensor,  # (B, N, 3)
    x1_hat: torch.Tensor,  # (B, N, 3)
    R_t: torch.Tensor,  # (B, N, 3, 3)
    R1_hat: torch.Tensor,  # (B, N, 3, 3)
    t: torch.Tensor,  # (B,)
    dt: torch.Tensor,  # (B,)
    valid_mask: torch.Tensor,  # (B, N)
    diffuse_mask: torch.Tensor,  # (B, N)
    rotation_schedule: str = 'linear',
    exp_rate: float = 10.0,
) -> tuple[torch.Tensor, torch.Tensor]:
    """Return tuple[torch.Tensor, torch.Tensor]:
    
    x_next: (B, N, 3).
    R_next: (B, N, 3, 3)."""
    # Implement the operation. Inputs and test setup are supplied by BioTorch.
    raise NotImplementedError("Implement lie_euler")

Ready to implement it?

3 progressive hints, a solution walkthrough, and grading that checks values, shapes and gradients are waiting in the workspace.

Write and run your solution Sign in

Free account with email, Google or GitHub. Your code runs in an isolated PyTorch sandbox.

Sources and further reading

A primitive exercise, not an end-to-end reproduction of the named models. No affiliation or endorsement is implied.