bartduis's picture
Initial public release
e298226
Raw
History Blame Contribute Delete
3.3 kB
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 World Labs.
"""Per-modality flow-matching timestep schedules for RGB + depth inference.
Each modality walks from t = t_max down to t = t_min, warped by the
SD3 / FLUX shift transform
t' = exp(mu) / (exp(mu) + (1 / t - 1) ** sigma)
so larger mu pushes the schedule toward noisier (later) timesteps.
In asymmetric modes one stream is held at the boundary of its noise
range:
"joint" — both schedules are warped linear walks.
"i2d" — RGB held at t = 0 (clean RGB conditioning).
"d2i" — depth held at t = 0; RGB optionally held at t = 1.
"""
from __future__ import annotations
import dataclasses
import math
from typing import Literal
import torch
from torch import Tensor
Mode = Literal["joint", "i2d", "d2i"]
@dataclasses.dataclass
class ScheduleConfig:
rgb_shift_mu: float = 0.0
rgb_shift_sigma: float = 1.0
depth_shift_mu: float = 0.0
depth_shift_sigma: float = 1.0
# Some d2i checkpoints were trained with RGB pinned at full noise; this
# flag mirrors that conditioning at sample time.
d2i_full_noise_rgb: bool = False
def _shift(mu: float, sigma: float, t: Tensor) -> Tensor:
return math.exp(mu) / (math.exp(mu) + (1.0 / t - 1.0) ** sigma)
def _f_alpha(t: Tensor, alpha: float) -> Tensor:
"""Latent-forcing Möbius warp f_α(t) = αt / (1 + (α-1)t). Fixes 0 and 1."""
return (alpha * t) / (1.0 + (alpha - 1.0) * t)
def rollout_timesteps(config: ScheduleConfig, num_steps: int, *,
mode: Mode = "joint",
log2_alpha: float | None = None,
t_max: float = 1.0, t_min: float = 0.0,
device: torch.device | str | None = None,
dtype: torch.dtype = torch.float32) -> tuple[Tensor, Tensor]:
"""Return per-modality timestep tensors of shape `(num_steps + 1,)`.
``log2_alpha`` (joint mode only) tilts the RGB/depth denoising trajectory:
the depth schedule becomes ``f_α(t_rgb)`` with ``α = 2 ** log2_alpha`` on top
of the training time-shift density. ``α > 1`` keeps depth noisier for longer
so RGB resolves first (rgb-first → cleaner depth); ``α < 1`` is depth-first;
``α = 1`` (or ``None``) is the diagonal joint schedule.
"""
if num_steps < 1:
raise ValueError(f"num_steps must be >= 1; got {num_steps}")
linear = torch.linspace(t_max, t_min, num_steps + 1, device=device, dtype=dtype)
t_rgb = _shift(config.rgb_shift_mu, config.rgb_shift_sigma, linear)
t_depth = _shift(config.depth_shift_mu, config.depth_shift_sigma, linear)
if mode == "i2d":
t_rgb = torch.zeros_like(t_rgb)
elif mode == "d2i":
if config.d2i_full_noise_rgb:
t_rgb = torch.ones_like(t_rgb)
t_depth = torch.zeros_like(t_depth)
elif mode == "joint":
if log2_alpha is not None:
# Warp the depth schedule off the (time-shifted) RGB grid, matching
# the final-paper time-shift trajectory: nodes follow the training
# sigmoid density, then the Möbius warp tilts depth vs RGB.
t_depth = _f_alpha(t_rgb, 2.0 ** log2_alpha)
else:
raise ValueError(f"unknown mode {mode!r}; expected joint / i2d / d2i")
return t_rgb, t_depth