Delete ddpmx.py
Browse files
ddpmx.py
DELETED
|
@@ -1,597 +0,0 @@
|
|
| 1 |
-
# how to use
|
| 2 |
-
# ddpm = DDPM(Z_train, T=100, n_iter=20_000, key=random.PRNGKey(0))
|
| 3 |
-
#
|
| 4 |
-
# Fast unconditional samples (try 15–30 first)
|
| 5 |
-
# z = ddpm.sample_dpmpp(N=4096, num_steps=20)
|
| 6 |
-
#
|
| 7 |
-
# Fast “refine latents” starting from an intermediate noise level
|
| 8 |
-
#z_ref = ddpm.refine_latents_dpmpp(z0, t_start=30, num_steps=20, add_noise=True)
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
# src/dima/ddpmx.py
|
| 13 |
-
from __future__ import annotations
|
| 14 |
-
|
| 15 |
-
from typing import Any, Optional
|
| 16 |
-
|
| 17 |
-
import jax
|
| 18 |
-
import jax.numpy as jnp
|
| 19 |
-
from jax import random
|
| 20 |
-
|
| 21 |
-
from flax import linen as nn
|
| 22 |
-
from flax.training import train_state
|
| 23 |
-
from flax import struct
|
| 24 |
-
import optax
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
# --- Add near the top of ddpm.py ---
|
| 28 |
-
import numpy as np
|
| 29 |
-
|
| 30 |
-
def _sigma_to_alpha_sigma_t(sigma: jnp.ndarray) -> tuple[jnp.ndarray, jnp.ndarray]:
|
| 31 |
-
"""
|
| 32 |
-
EDM-style sigma parameterization used by many DPM-Solver++ implementations:
|
| 33 |
-
alpha_t = 1 / sqrt(1 + sigma^2)
|
| 34 |
-
sigma_t = sigma * alpha_t
|
| 35 |
-
so that x = alpha_t * x0 + sigma_t * eps
|
| 36 |
-
"""
|
| 37 |
-
alpha_t = 1.0 / jnp.sqrt(1.0 + sigma**2)
|
| 38 |
-
sigma_t = sigma * alpha_t
|
| 39 |
-
return alpha_t, sigma_t
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
def _make_lu_sigma_schedule(
|
| 43 |
-
sigma_start: float,
|
| 44 |
-
sigma_end: float,
|
| 45 |
-
num_steps: int,
|
| 46 |
-
) -> np.ndarray:
|
| 47 |
-
"""
|
| 48 |
-
"Lu" schedule in lambda-space (uniform in lambda = -log(sigma)),
|
| 49 |
-
which is a common default for DPM-Solver samplers.
|
| 50 |
-
"""
|
| 51 |
-
sigma_start = float(max(sigma_start, 1e-12))
|
| 52 |
-
sigma_end = float(max(sigma_end, 1e-12))
|
| 53 |
-
lam_start = -np.log(sigma_start)
|
| 54 |
-
lam_end = -np.log(sigma_end)
|
| 55 |
-
lambdas = np.linspace(lam_start, lam_end, num_steps, dtype=np.float32)
|
| 56 |
-
sigmas = np.exp(-lambdas).astype(np.float32)
|
| 57 |
-
return sigmas
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
# ------------------------------
|
| 61 |
-
# Schedules & embeddings
|
| 62 |
-
# ------------------------------
|
| 63 |
-
def cosine_schedule(T: int, s: float = 0.008):
|
| 64 |
-
"""
|
| 65 |
-
Nichol & Dhariwal cosine schedule.
|
| 66 |
-
|
| 67 |
-
Returns:
|
| 68 |
-
alpha: (T,)
|
| 69 |
-
beta: (T,)
|
| 70 |
-
alpha_bar: (T,)
|
| 71 |
-
"""
|
| 72 |
-
steps = jnp.arange(T + 1, dtype=jnp.float32)
|
| 73 |
-
f = jnp.cos(((steps / T + s) / (1.0 + s)) * jnp.pi / 2.0) ** 2
|
| 74 |
-
alpha_bar_all = f / f[0]
|
| 75 |
-
alpha_bar = alpha_bar_all[1:] # (T,)
|
| 76 |
-
alpha = alpha_bar / jnp.concatenate([jnp.array([1.0], dtype=jnp.float32), alpha_bar[:-1]])
|
| 77 |
-
beta = 1.0 - alpha
|
| 78 |
-
return alpha, beta, alpha_bar
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
def sinusoidal_embedding(t_idx: jnp.ndarray, dim: int) -> jnp.ndarray:
|
| 82 |
-
"""
|
| 83 |
-
t_idx: (B,1) int32
|
| 84 |
-
returns: (B,dim)
|
| 85 |
-
"""
|
| 86 |
-
if t_idx.ndim != 2 or t_idx.shape[1] != 1:
|
| 87 |
-
raise ValueError("t_idx must have shape (B,1)")
|
| 88 |
-
t = t_idx.astype(jnp.float32)
|
| 89 |
-
half = dim // 2
|
| 90 |
-
denom = float(max(half - 1, 1))
|
| 91 |
-
freqs = jnp.exp(-jnp.log(10_000.0) * jnp.arange(half, dtype=jnp.float32) / denom)
|
| 92 |
-
args = t * freqs
|
| 93 |
-
emb = jnp.concatenate([jnp.sin(args), jnp.cos(args)], axis=-1)
|
| 94 |
-
if dim % 2 == 1:
|
| 95 |
-
emb = jnp.pad(emb, ((0, 0), (0, 1)))
|
| 96 |
-
return emb
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
# ------------------------------
|
| 100 |
-
# Model: epsilon predictor
|
| 101 |
-
# ------------------------------
|
| 102 |
-
class EpsMLP(nn.Module):
|
| 103 |
-
"""Simple MLP epsilon-predictor for DDPM in R^D."""
|
| 104 |
-
hidden: int
|
| 105 |
-
t_dim: int
|
| 106 |
-
data_dim: int
|
| 107 |
-
|
| 108 |
-
@nn.compact
|
| 109 |
-
def __call__(self, x: jnp.ndarray, t_idx: jnp.ndarray) -> jnp.ndarray:
|
| 110 |
-
# x: (B,D), t_idx: (B,1)
|
| 111 |
-
t_emb = sinusoidal_embedding(t_idx, self.t_dim)
|
| 112 |
-
t_h = nn.Dense(self.hidden)(t_emb)
|
| 113 |
-
t_h = nn.gelu(t_h)
|
| 114 |
-
|
| 115 |
-
h = nn.Dense(self.hidden)(x)
|
| 116 |
-
h = nn.gelu(h + t_h)
|
| 117 |
-
|
| 118 |
-
t_h2 = nn.Dense(self.hidden)(t_h)
|
| 119 |
-
h = nn.Dense(self.hidden)(h)
|
| 120 |
-
h = nn.gelu(h + t_h2)
|
| 121 |
-
|
| 122 |
-
out = nn.Dense(self.data_dim)(h)
|
| 123 |
-
return out
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
# ------------------------------
|
| 127 |
-
# TrainState with EMA
|
| 128 |
-
# ------------------------------
|
| 129 |
-
@struct.dataclass
|
| 130 |
-
class TrainStateEMA(train_state.TrainState):
|
| 131 |
-
"""Flax TrainState extended with EMA params."""
|
| 132 |
-
ema_params: Any = struct.field(pytree_node=True)
|
| 133 |
-
|
| 134 |
-
def apply_gradients(self, *, grads, ema_decay: float):
|
| 135 |
-
updates, new_opt_state = self.tx.update(grads, self.opt_state, self.params)
|
| 136 |
-
new_params = optax.apply_updates(self.params, updates)
|
| 137 |
-
new_ema = optax.incremental_update(new_params, self.ema_params, step_size=1.0 - ema_decay)
|
| 138 |
-
return self.replace(
|
| 139 |
-
step=self.step + 1,
|
| 140 |
-
params=new_params,
|
| 141 |
-
opt_state=new_opt_state,
|
| 142 |
-
ema_params=new_ema,
|
| 143 |
-
)
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
# ------------------------------
|
| 147 |
-
# DDPM
|
| 148 |
-
# ------------------------------
|
| 149 |
-
class DDPM:
|
| 150 |
-
"""
|
| 151 |
-
DDPM for D-dimensional latents.
|
| 152 |
-
|
| 153 |
-
API expected by your DIMA wrapper:
|
| 154 |
-
- DDPM(Z_train, ...): trains in __init__ (n_iter can be 0 to skip)
|
| 155 |
-
- refine_latents(z0, t_start, key, add_noise) -> z_refined
|
| 156 |
-
- __call__(...) delegates to refine_latents
|
| 157 |
-
- sample(N) -> latent samples
|
| 158 |
-
- attributes: state.params, state.ema_params, T, D, model.hidden, model.t_dim, beta_max, eps
|
| 159 |
-
"""
|
| 160 |
-
|
| 161 |
-
def __init__(
|
| 162 |
-
self,
|
| 163 |
-
Z_iX: jnp.ndarray,
|
| 164 |
-
*,
|
| 165 |
-
T: int = 100,
|
| 166 |
-
hidden_dim: int = 128,
|
| 167 |
-
t_embed_dim: int = 64,
|
| 168 |
-
learning_rate: float = 1e-3,
|
| 169 |
-
n_iter: int = 20_000,
|
| 170 |
-
ema_decay: float = 0.999,
|
| 171 |
-
beta_max: float = 0.02,
|
| 172 |
-
batch_size: Optional[int] = None,
|
| 173 |
-
key: jax.Array = random.PRNGKey(0),
|
| 174 |
-
verbose_every: int = 0,
|
| 175 |
-
eps: float = 1e-5,
|
| 176 |
-
):
|
| 177 |
-
Z_iX = jnp.asarray(Z_iX, dtype=jnp.float32)
|
| 178 |
-
if Z_iX.ndim != 2:
|
| 179 |
-
raise ValueError("Z_iX must be 2D (N,D).")
|
| 180 |
-
|
| 181 |
-
self.D = int(Z_iX.shape[1])
|
| 182 |
-
self.T = int(T)
|
| 183 |
-
self.key = key
|
| 184 |
-
self.ema_decay = float(ema_decay)
|
| 185 |
-
self.batch_size = batch_size
|
| 186 |
-
self.verbose_every = int(verbose_every)
|
| 187 |
-
self.eps = float(eps)
|
| 188 |
-
self.beta_max = float(beta_max)
|
| 189 |
-
|
| 190 |
-
# schedule (cosine, clipped by beta_max)
|
| 191 |
-
alpha, beta, alpha_bar = cosine_schedule(self.T)
|
| 192 |
-
beta = jnp.minimum(beta, self.beta_max)
|
| 193 |
-
alpha = 1.0 - beta
|
| 194 |
-
alpha_bar = jnp.cumprod(alpha)
|
| 195 |
-
|
| 196 |
-
self.alpha_s = alpha.astype(jnp.float32)
|
| 197 |
-
self.beta_s = beta.astype(jnp.float32)
|
| 198 |
-
self.alpha_bar_s = alpha_bar.astype(jnp.float32)
|
| 199 |
-
|
| 200 |
-
# model + optimizer + EMA
|
| 201 |
-
self.model = EpsMLP(hidden=int(hidden_dim), t_dim=int(t_embed_dim), data_dim=self.D)
|
| 202 |
-
|
| 203 |
-
params = self.model.init(
|
| 204 |
-
self.key,
|
| 205 |
-
jnp.zeros((1, self.D), dtype=jnp.float32),
|
| 206 |
-
jnp.zeros((1, 1), dtype=jnp.int32),
|
| 207 |
-
)["params"]
|
| 208 |
-
|
| 209 |
-
tx = optax.adam(float(learning_rate))
|
| 210 |
-
self.state = TrainStateEMA.create(apply_fn=self.model.apply, params=params, tx=tx, ema_params=params)
|
| 211 |
-
|
| 212 |
-
# train
|
| 213 |
-
if int(n_iter) > 0:
|
| 214 |
-
self._train(Z_iX, int(n_iter))
|
| 215 |
-
|
| 216 |
-
# ---------- training ----------
|
| 217 |
-
@staticmethod
|
| 218 |
-
def _loss(params, apply_fn, x_t, t_idx, eps_true):
|
| 219 |
-
eps_pred = apply_fn({"params": params}, x_t, t_idx)
|
| 220 |
-
return jnp.mean((eps_pred - eps_true) ** 2)
|
| 221 |
-
|
| 222 |
-
@staticmethod
|
| 223 |
-
@jax.jit
|
| 224 |
-
def _train_step(
|
| 225 |
-
state: TrainStateEMA,
|
| 226 |
-
x0_batch: jnp.ndarray,
|
| 227 |
-
key: jax.Array,
|
| 228 |
-
alpha_bar_s: jnp.ndarray,
|
| 229 |
-
ema_decay: float,
|
| 230 |
-
eps: float,
|
| 231 |
-
):
|
| 232 |
-
B = x0_batch.shape[0]
|
| 233 |
-
key, k_eps, k_t = random.split(key, 3)
|
| 234 |
-
|
| 235 |
-
eps_noise = random.normal(k_eps, shape=x0_batch.shape) # (B,D)
|
| 236 |
-
t_idx = random.randint(k_t, shape=(B, 1), minval=0, maxval=alpha_bar_s.shape[0])
|
| 237 |
-
|
| 238 |
-
a_bar_t = jnp.take(alpha_bar_s, t_idx.squeeze(-1))[:, None]
|
| 239 |
-
a_bar_t = jnp.clip(a_bar_t, eps, 1.0)
|
| 240 |
-
|
| 241 |
-
x_t = jnp.sqrt(a_bar_t) * x0_batch + jnp.sqrt(1.0 - a_bar_t) * eps_noise
|
| 242 |
-
|
| 243 |
-
def loss_fn(p):
|
| 244 |
-
return DDPM._loss(p, state.apply_fn, x_t, t_idx, eps_noise)
|
| 245 |
-
|
| 246 |
-
loss, grads = jax.value_and_grad(loss_fn)(state.params)
|
| 247 |
-
new_state = state.apply_gradients(grads=grads, ema_decay=ema_decay)
|
| 248 |
-
return new_state, loss, key
|
| 249 |
-
|
| 250 |
-
def _train(self, Z_iX: jnp.ndarray, n_iter: int):
|
| 251 |
-
N = int(Z_iX.shape[0])
|
| 252 |
-
bs = N if (self.batch_size is None) else min(int(self.batch_size), N)
|
| 253 |
-
|
| 254 |
-
for it in range(n_iter):
|
| 255 |
-
if bs >= N:
|
| 256 |
-
batch = Z_iX
|
| 257 |
-
else:
|
| 258 |
-
self.key, k_perm = random.split(self.key)
|
| 259 |
-
idx = random.permutation(k_perm, N)[:bs]
|
| 260 |
-
batch = Z_iX[idx]
|
| 261 |
-
|
| 262 |
-
self.state, loss, self.key = self._train_step(
|
| 263 |
-
self.state,
|
| 264 |
-
batch,
|
| 265 |
-
self.key,
|
| 266 |
-
self.alpha_bar_s,
|
| 267 |
-
self.ema_decay,
|
| 268 |
-
self.eps,
|
| 269 |
-
)
|
| 270 |
-
|
| 271 |
-
if self.verbose_every and (it % self.verbose_every == 0 or it == n_iter - 1):
|
| 272 |
-
print(f"iter {it:6d} loss {float(loss):.6f}", end="\r")
|
| 273 |
-
|
| 274 |
-
if self.verbose_every:
|
| 275 |
-
print("\ntraining complete.")
|
| 276 |
-
|
| 277 |
-
# ---------- diffusion utilities ----------
|
| 278 |
-
@staticmethod
|
| 279 |
-
def _posterior_variance(alpha_s, beta_s, alpha_bar_s, t):
|
| 280 |
-
a_bar_t = alpha_bar_s[t]
|
| 281 |
-
a_bar_prev = jnp.where(t > 0, alpha_bar_s[t - 1], jnp.array(1.0, dtype=alpha_bar_s.dtype))
|
| 282 |
-
return ((1.0 - a_bar_prev) / (1.0 - a_bar_t)) * beta_s[t]
|
| 283 |
-
|
| 284 |
-
@staticmethod
|
| 285 |
-
def _make_sampler_step(params_ema, apply_fn, alpha_s, beta_s, alpha_bar_s, eps: float):
|
| 286 |
-
@jax.jit
|
| 287 |
-
def step(carry, _):
|
| 288 |
-
key, t, x = carry # x: (B,D)
|
| 289 |
-
key, k = random.split(key)
|
| 290 |
-
|
| 291 |
-
alpha_t = jnp.clip(alpha_s[t], eps, 1.0)
|
| 292 |
-
a_bar_t = jnp.clip(alpha_bar_s[t], eps, 1.0)
|
| 293 |
-
|
| 294 |
-
sqrt_alpha = jnp.sqrt(alpha_t)
|
| 295 |
-
sqrt_one_minus_a_bar = jnp.sqrt(jnp.clip(1.0 - a_bar_t, eps, 1.0))
|
| 296 |
-
|
| 297 |
-
B = x.shape[0]
|
| 298 |
-
t_batch = jnp.full((B, 1), t, dtype=jnp.int32)
|
| 299 |
-
eps_pred = apply_fn({"params": params_ema}, x, t_batch) # (B,D)
|
| 300 |
-
|
| 301 |
-
# predict x0
|
| 302 |
-
x0_hat = (x - sqrt_one_minus_a_bar * eps_pred) / jnp.sqrt(a_bar_t)
|
| 303 |
-
|
| 304 |
-
a_bar_prev = jnp.where(t > 0, alpha_bar_s[t - 1], jnp.array(1.0, dtype=alpha_bar_s.dtype))
|
| 305 |
-
denom = jnp.clip(1.0 - a_bar_t, eps, 1.0)
|
| 306 |
-
|
| 307 |
-
coef1 = jnp.sqrt(jnp.clip(a_bar_prev, eps, 1.0)) * beta_s[t] / denom
|
| 308 |
-
coef2 = sqrt_alpha * (1.0 - a_bar_prev) / denom
|
| 309 |
-
|
| 310 |
-
mean = coef1 * x0_hat + coef2 * x
|
| 311 |
-
|
| 312 |
-
beta_tilde = DDPM._posterior_variance(alpha_s, beta_s, alpha_bar_s, t)
|
| 313 |
-
sigma = jnp.sqrt(jnp.clip(beta_tilde, 0.0, 1.0))
|
| 314 |
-
|
| 315 |
-
z = random.normal(k, x.shape)
|
| 316 |
-
z = jnp.where(t == 0, 0.0, z)
|
| 317 |
-
x_prev = mean + sigma * z
|
| 318 |
-
|
| 319 |
-
return (key, t - 1, x_prev), x_prev
|
| 320 |
-
|
| 321 |
-
return step
|
| 322 |
-
|
| 323 |
-
# ---------- API ----------
|
| 324 |
-
def refine_latents(
|
| 325 |
-
self,
|
| 326 |
-
z0: jnp.ndarray,
|
| 327 |
-
t_start: int = 10,
|
| 328 |
-
key: Optional[jax.Array] = None,
|
| 329 |
-
add_noise: bool = True,
|
| 330 |
-
) -> jnp.ndarray:
|
| 331 |
-
"""
|
| 332 |
-
Refine latents by:
|
| 333 |
-
(optional) forward-noise z0 to step t_start
|
| 334 |
-
reverse-diffuse from t_start -> 0 using EMA params.
|
| 335 |
-
"""
|
| 336 |
-
z0 = jnp.asarray(z0, dtype=jnp.float32)
|
| 337 |
-
if z0.ndim != 2 or z0.shape[1] != self.D:
|
| 338 |
-
raise ValueError(f"z0 must have shape (B,{self.D}).")
|
| 339 |
-
if not (0 <= int(t_start) < self.T):
|
| 340 |
-
raise ValueError(f"t_start must be in [0, {self.T-1}]")
|
| 341 |
-
|
| 342 |
-
t_start = int(t_start)
|
| 343 |
-
|
| 344 |
-
if key is None:
|
| 345 |
-
self.key, key = random.split(self.key)
|
| 346 |
-
else:
|
| 347 |
-
# advance internal RNG too
|
| 348 |
-
self.key, _ = random.split(key)
|
| 349 |
-
|
| 350 |
-
# forward diffuse to t_start
|
| 351 |
-
key, k_eps = random.split(key)
|
| 352 |
-
eps_noise = random.normal(k_eps, z0.shape)
|
| 353 |
-
|
| 354 |
-
a_bar_t = jnp.clip(self.alpha_bar_s[t_start], self.eps, 1.0)
|
| 355 |
-
if add_noise:
|
| 356 |
-
z_t = jnp.sqrt(a_bar_t) * z0 + jnp.sqrt(1.0 - a_bar_t) * eps_noise
|
| 357 |
-
else:
|
| 358 |
-
z_t = z0
|
| 359 |
-
|
| 360 |
-
step = self._make_sampler_step(
|
| 361 |
-
self.state.ema_params,
|
| 362 |
-
self.state.apply_fn,
|
| 363 |
-
self.alpha_s,
|
| 364 |
-
self.beta_s,
|
| 365 |
-
self.alpha_bar_s,
|
| 366 |
-
self.eps,
|
| 367 |
-
)
|
| 368 |
-
|
| 369 |
-
(final_key, _, _), trace = jax.lax.scan(
|
| 370 |
-
step,
|
| 371 |
-
(key, t_start, z_t),
|
| 372 |
-
xs=None,
|
| 373 |
-
length=t_start + 1,
|
| 374 |
-
)
|
| 375 |
-
self.key = final_key
|
| 376 |
-
return trace[-1]
|
| 377 |
-
|
| 378 |
-
def __call__(
|
| 379 |
-
self,
|
| 380 |
-
z0: jnp.ndarray,
|
| 381 |
-
t_start: int = 10,
|
| 382 |
-
key: Optional[jax.Array] = None,
|
| 383 |
-
add_noise: bool = True,
|
| 384 |
-
) -> jnp.ndarray:
|
| 385 |
-
return self.refine_latents(z0, t_start=t_start, key=key, add_noise=add_noise)
|
| 386 |
-
|
| 387 |
-
def reverse_from_T(self, x_T: jnp.ndarray) -> jnp.ndarray:
|
| 388 |
-
x_T = jnp.asarray(x_T, dtype=jnp.float32)
|
| 389 |
-
if x_T.ndim != 2 or x_T.shape[1] != self.D:
|
| 390 |
-
raise ValueError(f"x_T must have shape (B,{self.D}).")
|
| 391 |
-
|
| 392 |
-
step = self._make_sampler_step(
|
| 393 |
-
self.state.ema_params,
|
| 394 |
-
self.state.apply_fn,
|
| 395 |
-
self.alpha_s,
|
| 396 |
-
self.beta_s,
|
| 397 |
-
self.alpha_bar_s,
|
| 398 |
-
self.eps,
|
| 399 |
-
)
|
| 400 |
-
self.key, k0 = random.split(self.key)
|
| 401 |
-
(_, _, _), trace = jax.lax.scan(
|
| 402 |
-
step,
|
| 403 |
-
(k0, self.T - 1, x_T),
|
| 404 |
-
xs=None,
|
| 405 |
-
length=self.T,
|
| 406 |
-
)
|
| 407 |
-
return trace[-1]
|
| 408 |
-
|
| 409 |
-
def sample(self, N: int = 10_000) -> jnp.ndarray:
|
| 410 |
-
self.key, k = random.split(self.key)
|
| 411 |
-
noise = random.normal(k, (int(N), self.D))
|
| 412 |
-
return self.reverse_from_T(noise)
|
| 413 |
-
|
| 414 |
-
# --- In __init__ AFTER you compute self.alpha_bar_s ---
|
| 415 |
-
# sigma_in is the "EDM-style" sigma = sigma_t / alpha_t = sqrt((1-a_bar)/a_bar)
|
| 416 |
-
self.sigma_in_train = jnp.sqrt(
|
| 417 |
-
jnp.clip((1.0 - self.alpha_bar_s) / jnp.clip(self.alpha_bar_s, self.eps, 1.0), self.eps, 1e12)
|
| 418 |
-
).astype(jnp.float32)
|
| 419 |
-
|
| 420 |
-
def _make_dpmpp_schedule(self, *, num_steps: int, t_start: int) -> tuple[jnp.ndarray, jnp.ndarray]:
|
| 421 |
-
"""
|
| 422 |
-
Returns:
|
| 423 |
-
sigmas_in: (K+1,) float32, decreasing, last one is 0
|
| 424 |
-
t_cont: (K,) float32, "continuous" time indices to feed the model
|
| 425 |
-
"""
|
| 426 |
-
t_start = int(t_start)
|
| 427 |
-
if not (0 <= t_start < self.T):
|
| 428 |
-
raise ValueError(f"t_start must be in [0, {self.T-1}]")
|
| 429 |
-
if int(num_steps) < 1:
|
| 430 |
-
raise ValueError("num_steps must be >= 1")
|
| 431 |
-
|
| 432 |
-
# Start/end sigmas from your trained VP schedule
|
| 433 |
-
sigma_start = float(self.sigma_in_train[t_start])
|
| 434 |
-
sigma_end = float(self.sigma_in_train[0])
|
| 435 |
-
|
| 436 |
-
# Build sigma schedule (length K) and append final sigma=0
|
| 437 |
-
sigmas_k = _make_lu_sigma_schedule(sigma_start, sigma_end, int(num_steps))
|
| 438 |
-
sigmas = np.concatenate([sigmas_k, np.array([0.0], np.float32)], axis=0)
|
| 439 |
-
|
| 440 |
-
# Map sigma -> continuous t by interpolating in log(sigma) over the training sigmas
|
| 441 |
-
sigma_train = np.array(self.sigma_in_train).astype(np.float32) # (T,)
|
| 442 |
-
log_sig_train = np.log(np.maximum(sigma_train, 1e-12)) # increasing with t
|
| 443 |
-
t_train = np.arange(self.T, dtype=np.float32)
|
| 444 |
-
|
| 445 |
-
# For model calls, we only need times for the K "current" sigmas (exclude final 0)
|
| 446 |
-
log_sig = np.log(np.maximum(sigmas[:-1], 1e-12))
|
| 447 |
-
t_cont = np.interp(log_sig, log_sig_train, t_train).astype(np.float32) # (K,)
|
| 448 |
-
|
| 449 |
-
return jnp.array(sigmas, dtype=jnp.float32), jnp.array(t_cont, dtype=jnp.float32)
|
| 450 |
-
|
| 451 |
-
@staticmethod
|
| 452 |
-
@jax.jit
|
| 453 |
-
def _dpmpp_2m_midpoint_sample(
|
| 454 |
-
params_ema: Any,
|
| 455 |
-
apply_fn: Any,
|
| 456 |
-
x_start: jnp.ndarray, # (B,D)
|
| 457 |
-
sigmas_in: jnp.ndarray, # (K+1,)
|
| 458 |
-
t_cont: jnp.ndarray, # (K,)
|
| 459 |
-
eps: float,
|
| 460 |
-
) -> jnp.ndarray:
|
| 461 |
-
"""
|
| 462 |
-
DPM-Solver++ (2M, midpoint) sampler.
|
| 463 |
-
|
| 464 |
-
Uses:
|
| 465 |
-
- first-order update on step 0
|
| 466 |
-
- second-order midpoint update on steps 1..K-2
|
| 467 |
-
- first-order update on final step K-1 (important when next sigma is 0)
|
| 468 |
-
"""
|
| 469 |
-
# Current/next sigma for each step
|
| 470 |
-
sigma_s = sigmas_in[:-1] # (K,)
|
| 471 |
-
sigma_t = sigmas_in[1:] # (K,)
|
| 472 |
-
|
| 473 |
-
alpha_s, sigma_s_t = _sigma_to_alpha_sigma_t(sigma_s) # both (K,)
|
| 474 |
-
alpha_t, sigma_t_t = _sigma_to_alpha_sigma_t(sigma_t) # both (K,)
|
| 475 |
-
|
| 476 |
-
# lambda = log(alpha) - log(sigma_t); for this parameterization it's ~ -log(sigma_in)
|
| 477 |
-
# Avoid log(0) warnings: allow inf; we will not use 2nd-order formula on final step.
|
| 478 |
-
lambda_s = jnp.log(alpha_s) - jnp.log(sigma_s_t)
|
| 479 |
-
lambda_t = jnp.log(alpha_t) - jnp.log(sigma_t_t)
|
| 480 |
-
|
| 481 |
-
K = t_cont.shape[0]
|
| 482 |
-
is_first = jnp.arange(K) == 0
|
| 483 |
-
is_last = jnp.arange(K) == (K - 1)
|
| 484 |
-
|
| 485 |
-
def step(carry, inp):
|
| 486 |
-
x, m_prev, lam_prev = carry
|
| 487 |
-
(a_s, s_s, a_t, s_t, lam_s, lam_t, t_i, first_i, last_i) = inp
|
| 488 |
-
|
| 489 |
-
B = x.shape[0]
|
| 490 |
-
t_batch = jnp.full((B, 1), t_i, dtype=jnp.float32)
|
| 491 |
-
|
| 492 |
-
# model predicts epsilon
|
| 493 |
-
eps_pred = apply_fn({"params": params_ema}, x, t_batch) # (B,D)
|
| 494 |
-
|
| 495 |
-
# convert epsilon -> x0 (data prediction)
|
| 496 |
-
a_s_b = jnp.clip(a_s, eps, 1.0)
|
| 497 |
-
x0 = (x - s_s * eps_pred) / a_s_b # (B,D)
|
| 498 |
-
|
| 499 |
-
h = lam_t - lam_s
|
| 500 |
-
exp_neg_h = jnp.exp(-h)
|
| 501 |
-
|
| 502 |
-
# 1st-order (DDIM-like in dpmsolver++ form)
|
| 503 |
-
x_first = (s_t / s_s) * x - (a_t * (exp_neg_h - 1.0)) * x0
|
| 504 |
-
|
| 505 |
-
def do_second(_):
|
| 506 |
-
# 2nd-order multistep midpoint
|
| 507 |
-
h0 = lam_s - lam_prev
|
| 508 |
-
# r0 = h0/h; clip to avoid division by 0 if something degenerate happens
|
| 509 |
-
r0 = h0 / jnp.clip(h, 1e-12)
|
| 510 |
-
D1 = (x0 - m_prev) / jnp.clip(r0, 1e-12)
|
| 511 |
-
x_second = (s_t / s_s) * x - (a_t * (exp_neg_h - 1.0)) * (x0 + 0.5 * D1)
|
| 512 |
-
return x_second
|
| 513 |
-
|
| 514 |
-
x_next = jax.lax.cond(first_i | last_i, lambda _: x_first, do_second, operand=None)
|
| 515 |
-
return (x_next, x0, lam_s), x_next
|
| 516 |
-
|
| 517 |
-
# Pack per-step scalars
|
| 518 |
-
xs = (
|
| 519 |
-
alpha_s, sigma_s_t,
|
| 520 |
-
alpha_t, sigma_t_t,
|
| 521 |
-
lambda_s, lambda_t,
|
| 522 |
-
t_cont, is_first, is_last
|
| 523 |
-
)
|
| 524 |
-
|
| 525 |
-
# init prev buffers (unused on first step)
|
| 526 |
-
x0_init = jnp.zeros_like(x_start)
|
| 527 |
-
lam_init = jnp.array(0.0, dtype=jnp.float32)
|
| 528 |
-
|
| 529 |
-
(x_final, _, _), _trace = jax.lax.scan(step, (x_start, x0_init, lam_init), xs)
|
| 530 |
-
return x_final
|
| 531 |
-
|
| 532 |
-
# --- Public API: fast refine/sample methods ---
|
| 533 |
-
def refine_latents_dpmpp(
|
| 534 |
-
self,
|
| 535 |
-
z0: jnp.ndarray,
|
| 536 |
-
*,
|
| 537 |
-
t_start: int = 10,
|
| 538 |
-
num_steps: int = 20,
|
| 539 |
-
key: Optional[jax.Array] = None,
|
| 540 |
-
add_noise: bool = True,
|
| 541 |
-
) -> jnp.ndarray:
|
| 542 |
-
z0 = jnp.asarray(z0, dtype=jnp.float32)
|
| 543 |
-
if z0.ndim != 2 or z0.shape[1] != self.D:
|
| 544 |
-
raise ValueError(f"z0 must have shape (B,{self.D}).")
|
| 545 |
-
if not (0 <= int(t_start) < self.T):
|
| 546 |
-
raise ValueError(f"t_start must be in [0, {self.T-1}]")
|
| 547 |
-
|
| 548 |
-
if key is None:
|
| 549 |
-
self.key, key = random.split(self.key)
|
| 550 |
-
else:
|
| 551 |
-
self.key, _ = random.split(key)
|
| 552 |
-
|
| 553 |
-
# Forward-noise to t_start (same as your original code)
|
| 554 |
-
key, k_eps = random.split(key)
|
| 555 |
-
eps_noise = random.normal(k_eps, z0.shape)
|
| 556 |
-
|
| 557 |
-
a_bar = jnp.clip(self.alpha_bar_s[int(t_start)], self.eps, 1.0)
|
| 558 |
-
if add_noise:
|
| 559 |
-
x_start = jnp.sqrt(a_bar) * z0 + jnp.sqrt(1.0 - a_bar) * eps_noise
|
| 560 |
-
else:
|
| 561 |
-
x_start = z0
|
| 562 |
-
|
| 563 |
-
sigmas_in, t_cont = self._make_dpmpp_schedule(num_steps=int(num_steps), t_start=int(t_start))
|
| 564 |
-
|
| 565 |
-
x_final = self._dpmpp_2m_midpoint_sample(
|
| 566 |
-
self.state.ema_params,
|
| 567 |
-
self.state.apply_fn,
|
| 568 |
-
x_start,
|
| 569 |
-
sigmas_in,
|
| 570 |
-
t_cont,
|
| 571 |
-
self.eps,
|
| 572 |
-
)
|
| 573 |
-
return x_final
|
| 574 |
-
|
| 575 |
-
def reverse_from_T_dpmpp(self, x_T: jnp.ndarray, *, num_steps: int = 20) -> jnp.ndarray:
|
| 576 |
-
x_T = jnp.asarray(x_T, dtype=jnp.float32)
|
| 577 |
-
if x_T.ndim != 2 or x_T.shape[1] != self.D:
|
| 578 |
-
raise ValueError(f"x_T must have shape (B,{self.D}).")
|
| 579 |
-
|
| 580 |
-
sigmas_in, t_cont = self._make_dpmpp_schedule(num_steps=int(num_steps), t_start=self.T - 1)
|
| 581 |
-
|
| 582 |
-
return self._dpmpp_2m_midpoint_sample(
|
| 583 |
-
self.state.ema_params,
|
| 584 |
-
self.state.apply_fn,
|
| 585 |
-
x_T,
|
| 586 |
-
sigmas_in,
|
| 587 |
-
t_cont,
|
| 588 |
-
self.eps,
|
| 589 |
-
)
|
| 590 |
-
|
| 591 |
-
def sample_dpmpp(self, N: int = 10_000, *, num_steps: int = 20) -> jnp.ndarray:
|
| 592 |
-
self.key, k = random.split(self.key)
|
| 593 |
-
x_T = random.normal(k, (int(N), self.D)).astype(jnp.float32)
|
| 594 |
-
return self.reverse_from_T_dpmpp(x_T, num_steps=int(num_steps))
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
__all__ = ["DDPM", "EpsMLP", "cosine_schedule", "sinusoidal_embedding"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|