Download src/flow/solvers.py from kaupane/Inko: direct link, hf CLI and curl.
- Browser
- Download file 3.95 kB
-
https://huggingface.co/spaces/kaupane/Inko/resolve/main/src/flow/solvers.py
- Command line
-
hf download hf://spaces/kaupane/Inko/src/flow/solvers.py
-
curl -L -o solvers.py https://huggingface.co/spaces/kaupane/Inko/resolve/main/src/flow/solvers.py
3.95 kB
| """ | |
| ODE and SDE solvers for sampling from flow matching and diffusion models. | |
| """ | |
| from abc import ABC, abstractmethod | |
| from typing import Callable, Optional, Tuple, Union, List | |
| import torch | |
| class Solver(ABC): | |
| """Abstract base class for solvers.""" | |
| def step( | |
| self, x: torch.Tensor, t: float, dt: float, model_fn: Callable | |
| ) -> torch.Tensor: | |
| pass | |
| class Euler(Solver): | |
| """Euler method for ODEs.""" | |
| def step( | |
| self, x: torch.Tensor, t: float, dt: float, model_fn: Callable | |
| ) -> torch.Tensor: | |
| # Accumulate outside the model's mixed precision: small increments | |
| # disappear in BF16, and rounded times corrupt high-frequency features. | |
| if x.dtype in (torch.float16, torch.bfloat16): | |
| x = x.float() | |
| t_tensor = torch.tensor(t, device=x.device, dtype=torch.float32).expand(x.shape[0]) | |
| v = model_fn(x, t_tensor).to(x.dtype) | |
| return x + v * dt | |
| class Heun(Solver): | |
| """Heun's method (Improved Euler) for ODEs.""" | |
| def step( | |
| self, x: torch.Tensor, t: float, dt: float, model_fn: Callable | |
| ) -> torch.Tensor: | |
| if x.dtype in (torch.float16, torch.bfloat16): | |
| x = x.float() | |
| t_tensor = torch.tensor(t, device=x.device, dtype=torch.float32).expand(x.shape[0]) | |
| v1 = model_fn(x, t_tensor).to(x.dtype) | |
| x_guess = x + v1 * dt | |
| t_next = t + dt | |
| t_next_tensor = torch.tensor(t_next, device=x.device, dtype=torch.float32).expand( | |
| x.shape[0] | |
| ) | |
| v2 = model_fn(x_guess, t_next_tensor).to(x.dtype) | |
| return x + 0.5 * (v1 + v2) * dt | |
| def sample_ode( | |
| model_fn: Callable, | |
| z0: torch.Tensor, | |
| steps: int = 100, | |
| solver: str = "euler", | |
| solver_instance: Optional[Solver] = None, | |
| t_start: float = 0.0, | |
| t_end: float = 1.0, | |
| device: Optional[Union[str, torch.device]] = None, | |
| return_intermediates: bool = False, | |
| time_shift: Optional[float] = None, | |
| progress_callback: Optional[Callable[[int, int], None]] = None, | |
| ) -> Union[torch.Tensor, Tuple[torch.Tensor, List[torch.Tensor]]]: | |
| """Sample with FP32 times and at least FP32 integration state. | |
| The model may use autocast and return lower-precision velocities. Cast the | |
| final samples to the decoder's dtype at the decode boundary. | |
| Args: | |
| time_shift: If provided, the uniform timestep schedule is shifted via | |
| t' = t / (s - (s - 1) * t) (SD3 Eq. 23 adapted to this repo's | |
| t=0-noise convention, see flow.paths.apply_time_shift). When None, | |
| the shift is automatically computed from z0's spatial shape using | |
| the same resolution-dependent formula used in training. | |
| """ | |
| from .paths import resolution_time_shift, shift_timesteps | |
| if solver_instance is not None: | |
| s = solver_instance | |
| elif solver == "euler": | |
| s = Euler() | |
| elif solver == "heun": | |
| s = Heun() | |
| else: | |
| raise ValueError(f"Unknown solver: {solver}") | |
| if time_shift is None: | |
| time_shift = resolution_time_shift(z0) | |
| target_device = torch.device(device) if device is not None else z0.device | |
| x = z0.to(target_device) | |
| if x.dtype in (torch.float16, torch.bfloat16): | |
| x = x.float() | |
| # Build shifted timestep schedule | |
| uniform_ts = torch.linspace(t_start, t_end, steps + 1, dtype=torch.float32) | |
| shifted_ts = [shift_timesteps(u, z0, time_shift=time_shift).item() for u in uniform_ts] | |
| intermediates = [] | |
| if return_intermediates: | |
| intermediates.append(x.cpu()) | |
| for i in range(steps): | |
| if progress_callback is not None: | |
| progress_callback(i + 1, steps) | |
| t = shifted_ts[i] | |
| dt = shifted_ts[i + 1] - shifted_ts[i] | |
| x = s.step(x, t, dt, model_fn) | |
| if return_intermediates: | |
| intermediates.append(x.cpu()) | |
| if return_intermediates: | |
| return x, intermediates | |
| return x | |