Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions src/diffusers/schedulers/scheduling_dpmsolver_multistep_inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,19 @@ class DPMSolverMultistepInverseScheduler(SchedulerMixin, ConfigMixin):
The threshold value for dynamic thresholding. Valid only when `thresholding=True` and
`algorithm_type="dpmsolver++"`.
algorithm_type (`str`, defaults to `dpmsolver++`):
Algorithm type for the solver; can be `dpmsolver`, `dpmsolver++`, `sde-dpmsolver` or `sde-dpmsolver++`. The
`dpmsolver` type implements the algorithms in the [DPMSolver](https://huggingface.co/papers/2206.00927)
paper, and the `dpmsolver++` type implements the algorithms in the
[DPMSolver++](https://huggingface.co/papers/2211.01095) paper. It is recommended to use `dpmsolver++` or
`sde-dpmsolver++` with `solver_order=2` for guided sampling like in Stable Diffusion.
Algorithm type for the solver; can be `dpmsolver` or `dpmsolver++`. The `dpmsolver` type implements the
algorithms in the [DPMSolver](https://huggingface.co/papers/2206.00927) paper, and the `dpmsolver++` type
implements the algorithms in the [DPMSolver++](https://huggingface.co/papers/2211.01095) paper. Stochastic
variants (`sde-dpmsolver`, `sde-dpmsolver++`) are not supported: inversion is deterministic.
solver_type (`str`, defaults to `midpoint`):
Solver type for the second-order solver; can be `midpoint` or `heun`. The solver type slightly affects the
sample quality, especially for a small number of steps. It is recommended to use `midpoint` solvers.
lower_order_final (`bool`, defaults to `True`):
Whether to use lower-order solvers in the final steps. Only valid for < 15 inference steps. This can
stabilize the sampling of DPMSolver for steps < 15, especially for steps <= 10.
euler_at_final (`bool`, defaults to `False`):
Whether to use Euler's method in the final step. It is a trade-off between numerical stability and detail
richness. This can stabilize the sampling of the SDE variant of DPMSolver for small number of inference
steps, but sometimes may result in blurring.
Whether to use Euler's method (first-order) in the final step. It is a trade-off between numerical
stability and detail richness for a small number of inference steps, but sometimes may result in blurring.
use_karras_sigmas (`bool`, *optional*, defaults to `False`):
Whether to use Karras sigmas for step sizes in the noise schedule during the sampling process. If `True`,
the sigmas are determined according to a sequence of noise levels {σi}.
Expand Down Expand Up @@ -174,7 +172,7 @@ def __init__(
thresholding: bool = False,
dynamic_thresholding_ratio: float = 0.995,
sample_max_value: float = 1.0,
algorithm_type: Literal["dpmsolver", "dpmsolver++", "sde-dpmsolver", "sde-dpmsolver++"] = "dpmsolver++",
algorithm_type: Literal["dpmsolver", "dpmsolver++"] = "dpmsolver++",
solver_type: Literal["midpoint", "heun"] = "midpoint",
lower_order_final: bool = True,
euler_at_final: bool = False,
Expand All @@ -188,6 +186,11 @@ def __init__(
timestep_spacing: Literal["linspace", "leading", "trailing"] = "linspace",
steps_offset: int = 0,
):
if algorithm_type in ["sde-dpmsolver", "sde-dpmsolver++"]:
raise ValueError(
f"DPMSolverMultistepInverseScheduler does not support algorithm_type='{algorithm_type}' because "
"inversion is deterministic; use 'dpmsolver++' or 'dpmsolver' instead."
)
if self.config.use_beta_sigmas and not is_scipy_available():
raise ImportError("Make sure to install scipy if you want to use beta sigmas.")
if (
Expand All @@ -203,8 +206,11 @@ def __init__(
raise ValueError(
"Only one of `config.use_beta_sigmas`, `config.use_exponential_sigmas`, `config.use_karras_sigmas` can be used."
)
if algorithm_type in ["dpmsolver", "sde-dpmsolver"]:
deprecation_message = f"algorithm_type {algorithm_type} is deprecated and will be removed in a future version. Choose from `dpmsolver++` or `sde-dpmsolver++` instead"
if algorithm_type == "dpmsolver":
deprecation_message = (
"algorithm_type dpmsolver is deprecated and will be removed in a future version. "
"Choose `dpmsolver++` instead."
)
deprecate(
"algorithm_types dpmsolver and sde-dpmsolver",
"1.0.0",
Expand Down Expand Up @@ -244,12 +250,7 @@ def __init__(
self.init_noise_sigma = 1.0

# settings for DPM-Solver
if algorithm_type not in [
"dpmsolver",
"dpmsolver++",
"sde-dpmsolver",
"sde-dpmsolver++",
]:
if algorithm_type not in ["dpmsolver", "dpmsolver++"]:
if algorithm_type == "deis":
self.register_to_config(algorithm_type="dpmsolver++")
else:
Expand Down
24 changes: 24 additions & 0 deletions tests/schedulers/test_scheduler_dpm_multi_inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,3 +271,27 @@ def test_beta_sigmas(self):

def test_exponential_sigmas(self):
self.check_over_configs(use_exponential_sigmas=True)

def test_rejects_stochastic_algorithm_types(self):
# Inversion uses increasing sigmas, so h = lambda_t - lambda_s is negative.
# The SDE variance terms are only real for h > 0 and produce NaN under inversion.
for algorithm_type in ["sde-dpmsolver", "sde-dpmsolver++"]:
with self.assertRaises(ValueError) as ctx:
DPMSolverMultistepInverseScheduler(algorithm_type=algorithm_type)
message = str(ctx.exception)
self.assertIn("DPMSolverMultistepInverseScheduler", message)
self.assertIn(algorithm_type, message)
self.assertIn("deterministic", message)

for algorithm_type in ["dpmsolver", "dpmsolver++"]:
scheduler = DPMSolverMultistepInverseScheduler(
num_train_timesteps=1000,
beta_start=0.00085,
beta_end=0.012,
algorithm_type=algorithm_type,
)
scheduler.set_timesteps(10)
sample = torch.randn(1, 4, 8, 8)
model_output = torch.randn(1, 4, 8, 8)
prev = scheduler.step(model_output, scheduler.timesteps[0], sample).prev_sample
self.assertFalse(torch.isnan(prev).any())
Loading