AnonMLuser's picture
Anonymous artefact release
e0eb79a verified
Raw
History Blame Contribute Delete
3.46 kB
"""Noise schedule functions for MDLM diffusion.
All functions operate on PyTorch tensors and are pure (no global state).
Convention: alpha(t) is the fraction of tokens that remain *unmasked*.
- alpha(0) = 1.0 (fully clean)
- alpha(1) = 0.0 (fully masked)
"""
from __future__ import annotations
import math
from collections.abc import Callable
import torch
from torch import Tensor
def linear_schedule(t: Tensor) -> Tensor:
"""Linear noise schedule: alpha(t) = 1 - t.
Args:
t: Diffusion time in [0, 1]. Any shape.
Returns:
Retention probability alpha_t, same shape as *t*.
"""
return 1.0 - t
def cosine_schedule(t: Tensor) -> Tensor:
"""Cosine noise schedule: alpha(t) = cos(pi/2 * t).
MDLM Appendix E.1 eq (92) ("Cosine"); the same function the craftax
repo names "cosine".
Args:
t: Diffusion time in [0, 1]. Any shape.
Returns:
Retention probability alpha_t, same shape as *t*.
"""
return torch.cos(t * (math.pi / 2.0))
def cosine_sq_schedule(t: Tensor) -> Tensor:
"""Cosine-squared noise schedule: alpha(t) = cos(pi/2 * t)^2.
MDLM Appendix E.1 eq (91) ("Cosine Squared", after Nichol & Dhariwal).
Previously registered under the name "cosine" in this repo; renamed so
the label "cosine" denotes the same function in both repos.
Args:
t: Diffusion time in [0, 1]. Any shape.
Returns:
Retention probability alpha_t, same shape as *t*.
"""
return torch.cos(t * (math.pi / 2.0)) ** 2
def linear_schedule_deriv(t: Tensor) -> Tensor:
"""Analytic d(alpha)/dt for the linear schedule."""
return torch.full_like(t, -1.0)
def cosine_schedule_deriv(t: Tensor) -> Tensor:
"""Analytic d(alpha)/dt for the cosine schedule."""
return -(math.pi / 2.0) * torch.sin(t * (math.pi / 2.0))
def cosine_sq_schedule_deriv(t: Tensor) -> Tensor:
"""Analytic d(alpha)/dt for the cosine-squared schedule."""
return -(math.pi / 2.0) * torch.sin(t * math.pi)
_SCHEDULE_MAP: dict[str, Callable[[Tensor], Tensor]] = {
"linear": linear_schedule,
"cosine": cosine_schedule,
"cosine_sq": cosine_sq_schedule,
}
_DERIV_BY_FN: dict[Callable[[Tensor], Tensor], Callable[[Tensor], Tensor]] = {
linear_schedule: linear_schedule_deriv,
cosine_schedule: cosine_schedule_deriv,
cosine_sq_schedule: cosine_sq_schedule_deriv,
}
def get_schedule_deriv_for(
schedule_fn: Callable[[Tensor], Tensor],
) -> Callable[[Tensor], Tensor]:
"""Analytic derivative for a registered schedule function.
The NELBO weight uses the analytic d(alpha)/dt as stated in
MDLM eq (10) / Shi eq (4).
Raises:
KeyError: If *schedule_fn* is not a registered schedule.
"""
if schedule_fn not in _DERIV_BY_FN:
raise KeyError(
"No analytic derivative registered for "
f"{getattr(schedule_fn, '__name__', schedule_fn)!r}"
)
return _DERIV_BY_FN[schedule_fn]
def get_schedule(name: str) -> Callable[[Tensor], Tensor]:
"""Look up a noise schedule by name.
Args:
name: One of ``"linear"``, ``"cosine"``, ``"cosine_sq"``.
Returns:
The schedule function ``alpha(t)``.
Raises:
KeyError: If *name* is not registered.
"""
if name not in _SCHEDULE_MAP:
raise KeyError(
f"Unknown schedule '{name}'. Available: {list(_SCHEDULE_MAP.keys())}"
)
return _SCHEDULE_MAP[name]