Files

69 lines
2.8 KiB
Python

"""Distilled Flow: samplers for few-step distilled flow models, listed in ComfyUI like any other.
`dflow_ord1` is Euler (same speed as `euler`). `dflow_ord2` is Adams-Bashforth 2: it reuses the last
prediction for a second-order step, with no extra model calls. `dflow_ord2_ab2` fades that second order
in across the run (early noisy steps stay first order, late detail steps get the full second order),
which is gentler on short schedules. A first-order version of the fade would be plain `dflow_ord1`, so it
has no name of its own.
Deterministic, no noise injection. Flow models only (the model predicts a straight path to the picture).
A sound stream riding in the latent is stepped first order, because second order corrupts it.
"""
import comfy.k_diffusion.sampling as ksampling
import comfy.samplers
import torch
ID = "distilled_flow"
TITLE = "Distilled Flow samplers"
STAGE = "sampling"
CATEGORY = "sampling"
STATUS = "experimental"
def _picture_only(new, old):
"""`new` for the picture, `old` for the sound, when the latent carries both."""
if getattr(new, "is_nested", False):
from comfy.nested_tensor import NestedTensor
return NestedTensor((new.unbind()[0], *old.unbind()[1:]))
return new
def make(order, ramp):
@torch.no_grad()
def sample(model, x, sigmas, extra_args=None, callback=None, disable=None):
extra_args = {} if extra_args is None else extra_args
steps = len(sigmas) - 1
ones = x.new_ones([x.shape[0]])
before = before_h = None
for i in range(steps):
sigma, to = sigmas[i], sigmas[i + 1]
denoised = model(x, sigma * ones, **extra_args)
if callback is not None:
callback({"x": x, "i": i, "sigma": sigma, "sigma_hat": sigma, "denoised": denoised})
h = float((sigma - to).abs())
used = denoised
if order >= 2 and before is not None and before_h > 1e-7 and h > 1e-7:
r = min(5.0, max(0.1, h / before_h))
used = _picture_only(denoised * (1 + r / 2) - before * (r / 2), denoised)
if ramp and steps > 1:
used = denoised + (used - denoised) * (i / (steps - 1))
before, before_h = denoised, h
if float(to) == 0:
return used
d = ksampling.to_d(x, sigma, used)
x = x + d * (to - sigma)
return x
return sample
def register():
"""Add every preset to ComfyUI's own sampler list (in place: its Sampler nodes share the list)."""
for name, order, ramp in (("dflow_ord1", 1, False), ("dflow_ord2", 2, False), ("dflow_ord2_ab2", 2, True)):
if name not in comfy.samplers.SAMPLER_NAMES:
setattr(ksampling, f"sample_{name}", make(order, ramp))
comfy.samplers.SAMPLER_NAMES.append(name)
register()