comdoleger commited on
Commit
da53977
·
verified ·
1 Parent(s): 4041f8f

Upload extensions_built_in/diffusion_models/hidream/src/schedulers/fm_solvers_unipc.py with huggingface_hub

Browse files
extensions_built_in/diffusion_models/hidream/src/schedulers/fm_solvers_unipc.py ADDED
@@ -0,0 +1,800 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copied from https://github.com/huggingface/diffusers/blob/v0.31.0/src/diffusers/schedulers/scheduling_unipc_multistep.py
2
+ # Convert unipc for flow matching
3
+ # Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
4
+
5
+ import math
6
+ from typing import List, Optional, Tuple, Union
7
+
8
+ import numpy as np
9
+ import torch
10
+ from diffusers.configuration_utils import ConfigMixin, register_to_config
11
+ from diffusers.schedulers.scheduling_utils import (KarrasDiffusionSchedulers,
12
+ SchedulerMixin,
13
+ SchedulerOutput)
14
+ from diffusers.utils import deprecate, is_scipy_available
15
+
16
+ if is_scipy_available():
17
+ import scipy.stats
18
+
19
+
20
+ class FlowUniPCMultistepScheduler(SchedulerMixin, ConfigMixin):
21
+ """
22
+ `UniPCMultistepScheduler` is a training-free framework designed for the fast sampling of diffusion models.
23
+
24
+ This model inherits from [`SchedulerMixin`] and [`ConfigMixin`]. Check the superclass documentation for the generic
25
+ methods the library implements for all schedulers such as loading and saving.
26
+
27
+ Args:
28
+ num_train_timesteps (`int`, defaults to 1000):
29
+ The number of diffusion steps to train the model.
30
+ solver_order (`int`, default `2`):
31
+ The UniPC order which can be any positive integer. The effective order of accuracy is `solver_order + 1`
32
+ due to the UniC. It is recommended to use `solver_order=2` for guided sampling, and `solver_order=3` for
33
+ unconditional sampling.
34
+ prediction_type (`str`, defaults to "flow_prediction"):
35
+ Prediction type of the scheduler function; must be `flow_prediction` for this scheduler, which predicts
36
+ the flow of the diffusion process.
37
+ thresholding (`bool`, defaults to `False`):
38
+ Whether to use the "dynamic thresholding" method. This is unsuitable for latent-space diffusion models such
39
+ as Stable Diffusion.
40
+ dynamic_thresholding_ratio (`float`, defaults to 0.995):
41
+ The ratio for the dynamic thresholding method. Valid only when `thresholding=True`.
42
+ sample_max_value (`float`, defaults to 1.0):
43
+ The threshold value for dynamic thresholding. Valid only when `thresholding=True` and `predict_x0=True`.
44
+ predict_x0 (`bool`, defaults to `True`):
45
+ Whether to use the updating algorithm on the predicted x0.
46
+ solver_type (`str`, default `bh2`):
47
+ Solver type for UniPC. It is recommended to use `bh1` for unconditional sampling when steps < 10, and `bh2`
48
+ otherwise.
49
+ lower_order_final (`bool`, default `True`):
50
+ Whether to use lower-order solvers in the final steps. Only valid for < 15 inference steps. This can
51
+ stabilize the sampling of DPMSolver for steps < 15, especially for steps <= 10.
52
+ disable_corrector (`list`, default `[]`):
53
+ Decides which step to disable the corrector to mitigate the misalignment between `epsilon_theta(x_t, c)`
54
+ and `epsilon_theta(x_t^c, c)` which can influence convergence for a large guidance scale. Corrector is
55
+ usually disabled during the first few steps.
56
+ solver_p (`SchedulerMixin`, default `None`):
57
+ Any other scheduler that if specified, the algorithm becomes `solver_p + UniC`.
58
+ use_karras_sigmas (`bool`, *optional*, defaults to `False`):
59
+ Whether to use Karras sigmas for step sizes in the noise schedule during the sampling process. If `True`,
60
+ the sigmas are determined according to a sequence of noise levels {σi}.
61
+ use_exponential_sigmas (`bool`, *optional*, defaults to `False`):
62
+ Whether to use exponential sigmas for step sizes in the noise schedule during the sampling process.
63
+ timestep_spacing (`str`, defaults to `"linspace"`):
64
+ The way the timesteps should be scaled. Refer to Table 2 of the [Common Diffusion Noise Schedules and
65
+ Sample Steps are Flawed](https://huggingface.co/papers/2305.08891) for more information.
66
+ steps_offset (`int`, defaults to 0):
67
+ An offset added to the inference steps, as required by some model families.
68
+ final_sigmas_type (`str`, defaults to `"zero"`):
69
+ The final `sigma` value for the noise schedule during the sampling process. If `"sigma_min"`, the final
70
+ sigma is the same as the last sigma in the training schedule. If `zero`, the final sigma is set to 0.
71
+ """
72
+
73
+ _compatibles = [e.name for e in KarrasDiffusionSchedulers]
74
+ order = 1
75
+
76
+ @register_to_config
77
+ def __init__(
78
+ self,
79
+ num_train_timesteps: int = 1000,
80
+ solver_order: int = 2,
81
+ prediction_type: str = "flow_prediction",
82
+ shift: Optional[float] = 1.0,
83
+ use_dynamic_shifting=False,
84
+ thresholding: bool = False,
85
+ dynamic_thresholding_ratio: float = 0.995,
86
+ sample_max_value: float = 1.0,
87
+ predict_x0: bool = True,
88
+ solver_type: str = "bh2",
89
+ lower_order_final: bool = True,
90
+ disable_corrector: List[int] = [],
91
+ solver_p: SchedulerMixin = None,
92
+ timestep_spacing: str = "linspace",
93
+ steps_offset: int = 0,
94
+ final_sigmas_type: Optional[str] = "zero", # "zero", "sigma_min"
95
+ ):
96
+
97
+ if solver_type not in ["bh1", "bh2"]:
98
+ if solver_type in ["midpoint", "heun", "logrho"]:
99
+ self.register_to_config(solver_type="bh2")
100
+ else:
101
+ raise NotImplementedError(
102
+ f"{solver_type} is not implemented for {self.__class__}")
103
+
104
+ self.predict_x0 = predict_x0
105
+ # setable values
106
+ self.num_inference_steps = None
107
+ alphas = np.linspace(1, 1 / num_train_timesteps,
108
+ num_train_timesteps)[::-1].copy()
109
+ sigmas = 1.0 - alphas
110
+ sigmas = torch.from_numpy(sigmas).to(dtype=torch.float32)
111
+
112
+ if not use_dynamic_shifting:
113
+ # when use_dynamic_shifting is True, we apply the timestep shifting on the fly based on the image resolution
114
+ sigmas = shift * sigmas / (1 +
115
+ (shift - 1) * sigmas) # pyright: ignore
116
+
117
+ self.sigmas = sigmas
118
+ self.timesteps = sigmas * num_train_timesteps
119
+
120
+ self.model_outputs = [None] * solver_order
121
+ self.timestep_list = [None] * solver_order
122
+ self.lower_order_nums = 0
123
+ self.disable_corrector = disable_corrector
124
+ self.solver_p = solver_p
125
+ self.last_sample = None
126
+ self._step_index = None
127
+ self._begin_index = None
128
+
129
+ self.sigmas = self.sigmas.to(
130
+ "cpu") # to avoid too much CPU/GPU communication
131
+ self.sigma_min = self.sigmas[-1].item()
132
+ self.sigma_max = self.sigmas[0].item()
133
+
134
+ @property
135
+ def step_index(self):
136
+ """
137
+ The index counter for current timestep. It will increase 1 after each scheduler step.
138
+ """
139
+ return self._step_index
140
+
141
+ @property
142
+ def begin_index(self):
143
+ """
144
+ The index for the first timestep. It should be set from pipeline with `set_begin_index` method.
145
+ """
146
+ return self._begin_index
147
+
148
+ # Copied from diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler.set_begin_index
149
+ def set_begin_index(self, begin_index: int = 0):
150
+ """
151
+ Sets the begin index for the scheduler. This function should be run from pipeline before the inference.
152
+
153
+ Args:
154
+ begin_index (`int`):
155
+ The begin index for the scheduler.
156
+ """
157
+ self._begin_index = begin_index
158
+
159
+ # Modified from diffusers.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler.set_timesteps
160
+ def set_timesteps(
161
+ self,
162
+ num_inference_steps: Union[int, None] = None,
163
+ device: Union[str, torch.device] = None,
164
+ sigmas: Optional[List[float]] = None,
165
+ mu: Optional[Union[float, None]] = None,
166
+ shift: Optional[Union[float, None]] = None,
167
+ ):
168
+ """
169
+ Sets the discrete timesteps used for the diffusion chain (to be run before inference).
170
+ Args:
171
+ num_inference_steps (`int`):
172
+ Total number of the spacing of the time steps.
173
+ device (`str` or `torch.device`, *optional*):
174
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
175
+ """
176
+
177
+ if self.config.use_dynamic_shifting and mu is None:
178
+ raise ValueError(
179
+ " you have to pass a value for `mu` when `use_dynamic_shifting` is set to be `True`"
180
+ )
181
+
182
+ if sigmas is None:
183
+ sigmas = np.linspace(self.sigma_max, self.sigma_min,
184
+ num_inference_steps +
185
+ 1).copy()[:-1] # pyright: ignore
186
+
187
+ if self.config.use_dynamic_shifting:
188
+ sigmas = self.time_shift(mu, 1.0, sigmas) # pyright: ignore
189
+ else:
190
+ if shift is None:
191
+ shift = self.config.shift
192
+ sigmas = shift * sigmas / (1 +
193
+ (shift - 1) * sigmas) # pyright: ignore
194
+
195
+ if self.config.final_sigmas_type == "sigma_min":
196
+ sigma_last = ((1 - self.alphas_cumprod[0]) /
197
+ self.alphas_cumprod[0])**0.5
198
+ elif self.config.final_sigmas_type == "zero":
199
+ sigma_last = 0
200
+ else:
201
+ raise ValueError(
202
+ f"`final_sigmas_type` must be one of 'zero', or 'sigma_min', but got {self.config.final_sigmas_type}"
203
+ )
204
+
205
+ timesteps = sigmas * self.config.num_train_timesteps
206
+ sigmas = np.concatenate([sigmas, [sigma_last]
207
+ ]).astype(np.float32) # pyright: ignore
208
+
209
+ self.sigmas = torch.from_numpy(sigmas)
210
+ self.timesteps = torch.from_numpy(timesteps).to(
211
+ device=device, dtype=torch.int64)
212
+
213
+ self.num_inference_steps = len(timesteps)
214
+
215
+ self.model_outputs = [
216
+ None,
217
+ ] * self.config.solver_order
218
+ self.lower_order_nums = 0
219
+ self.last_sample = None
220
+ if self.solver_p:
221
+ self.solver_p.set_timesteps(self.num_inference_steps, device=device)
222
+
223
+ # add an index counter for schedulers that allow duplicated timesteps
224
+ self._step_index = None
225
+ self._begin_index = None
226
+ self.sigmas = self.sigmas.to(
227
+ "cpu") # to avoid too much CPU/GPU communication
228
+
229
+ # Copied from diffusers.schedulers.scheduling_ddpm.DDPMScheduler._threshold_sample
230
+ def _threshold_sample(self, sample: torch.Tensor) -> torch.Tensor:
231
+ """
232
+ "Dynamic thresholding: At each sampling step we set s to a certain percentile absolute pixel value in xt0 (the
233
+ prediction of x_0 at timestep t), and if s > 1, then we threshold xt0 to the range [-s, s] and then divide by
234
+ s. Dynamic thresholding pushes saturated pixels (those near -1 and 1) inwards, thereby actively preventing
235
+ pixels from saturation at each step. We find that dynamic thresholding results in significantly better
236
+ photorealism as well as better image-text alignment, especially when using very large guidance weights."
237
+
238
+ https://arxiv.org/abs/2205.11487
239
+ """
240
+ dtype = sample.dtype
241
+ batch_size, channels, *remaining_dims = sample.shape
242
+
243
+ if dtype not in (torch.float32, torch.float64):
244
+ sample = sample.float(
245
+ ) # upcast for quantile calculation, and clamp not implemented for cpu half
246
+
247
+ # Flatten sample for doing quantile calculation along each image
248
+ sample = sample.reshape(batch_size, channels * np.prod(remaining_dims))
249
+
250
+ abs_sample = sample.abs() # "a certain percentile absolute pixel value"
251
+
252
+ s = torch.quantile(
253
+ abs_sample, self.config.dynamic_thresholding_ratio, dim=1)
254
+ s = torch.clamp(
255
+ s, min=1, max=self.config.sample_max_value
256
+ ) # When clamped to min=1, equivalent to standard clipping to [-1, 1]
257
+ s = s.unsqueeze(
258
+ 1) # (batch_size, 1) because clamp will broadcast along dim=0
259
+ sample = torch.clamp(
260
+ sample, -s, s
261
+ ) / s # "we threshold xt0 to the range [-s, s] and then divide by s"
262
+
263
+ sample = sample.reshape(batch_size, channels, *remaining_dims)
264
+ sample = sample.to(dtype)
265
+
266
+ return sample
267
+
268
+ # Copied from diffusers.schedulers.scheduling_flow_match_euler_discrete.FlowMatchEulerDiscreteScheduler._sigma_to_t
269
+ def _sigma_to_t(self, sigma):
270
+ return sigma * self.config.num_train_timesteps
271
+
272
+ def _sigma_to_alpha_sigma_t(self, sigma):
273
+ return 1 - sigma, sigma
274
+
275
+ # Copied from diffusers.schedulers.scheduling_flow_match_euler_discrete.set_timesteps
276
+ def time_shift(self, mu: float, sigma: float, t: torch.Tensor):
277
+ return math.exp(mu) / (math.exp(mu) + (1 / t - 1)**sigma)
278
+
279
+ def convert_model_output(
280
+ self,
281
+ model_output: torch.Tensor,
282
+ *args,
283
+ sample: torch.Tensor = None,
284
+ **kwargs,
285
+ ) -> torch.Tensor:
286
+ r"""
287
+ Convert the model output to the corresponding type the UniPC algorithm needs.
288
+
289
+ Args:
290
+ model_output (`torch.Tensor`):
291
+ The direct output from the learned diffusion model.
292
+ timestep (`int`):
293
+ The current discrete timestep in the diffusion chain.
294
+ sample (`torch.Tensor`):
295
+ A current instance of a sample created by the diffusion process.
296
+
297
+ Returns:
298
+ `torch.Tensor`:
299
+ The converted model output.
300
+ """
301
+ timestep = args[0] if len(args) > 0 else kwargs.pop("timestep", None)
302
+ if sample is None:
303
+ if len(args) > 1:
304
+ sample = args[1]
305
+ else:
306
+ raise ValueError(
307
+ "missing `sample` as a required keyward argument")
308
+ if timestep is not None:
309
+ deprecate(
310
+ "timesteps",
311
+ "1.0.0",
312
+ "Passing `timesteps` is deprecated and has no effect as model output conversion is now handled via an internal counter `self.step_index`",
313
+ )
314
+
315
+ sigma = self.sigmas[self.step_index]
316
+ alpha_t, sigma_t = self._sigma_to_alpha_sigma_t(sigma)
317
+
318
+ if self.predict_x0:
319
+ if self.config.prediction_type == "flow_prediction":
320
+ sigma_t = self.sigmas[self.step_index]
321
+ x0_pred = sample - sigma_t * model_output
322
+ else:
323
+ raise ValueError(
324
+ f"prediction_type given as {self.config.prediction_type} must be one of `epsilon`, `sample`,"
325
+ " `v_prediction` or `flow_prediction` for the UniPCMultistepScheduler."
326
+ )
327
+
328
+ if self.config.thresholding:
329
+ x0_pred = self._threshold_sample(x0_pred)
330
+
331
+ return x0_pred
332
+ else:
333
+ if self.config.prediction_type == "flow_prediction":
334
+ sigma_t = self.sigmas[self.step_index]
335
+ epsilon = sample - (1 - sigma_t) * model_output
336
+ else:
337
+ raise ValueError(
338
+ f"prediction_type given as {self.config.prediction_type} must be one of `epsilon`, `sample`,"
339
+ " `v_prediction` or `flow_prediction` for the UniPCMultistepScheduler."
340
+ )
341
+
342
+ if self.config.thresholding:
343
+ sigma_t = self.sigmas[self.step_index]
344
+ x0_pred = sample - sigma_t * model_output
345
+ x0_pred = self._threshold_sample(x0_pred)
346
+ epsilon = model_output + x0_pred
347
+
348
+ return epsilon
349
+
350
+ def multistep_uni_p_bh_update(
351
+ self,
352
+ model_output: torch.Tensor,
353
+ *args,
354
+ sample: torch.Tensor = None,
355
+ order: int = None, # pyright: ignore
356
+ **kwargs,
357
+ ) -> torch.Tensor:
358
+ """
359
+ One step for the UniP (B(h) version). Alternatively, `self.solver_p` is used if is specified.
360
+
361
+ Args:
362
+ model_output (`torch.Tensor`):
363
+ The direct output from the learned diffusion model at the current timestep.
364
+ prev_timestep (`int`):
365
+ The previous discrete timestep in the diffusion chain.
366
+ sample (`torch.Tensor`):
367
+ A current instance of a sample created by the diffusion process.
368
+ order (`int`):
369
+ The order of UniP at this timestep (corresponds to the *p* in UniPC-p).
370
+
371
+ Returns:
372
+ `torch.Tensor`:
373
+ The sample tensor at the previous timestep.
374
+ """
375
+ prev_timestep = args[0] if len(args) > 0 else kwargs.pop(
376
+ "prev_timestep", None)
377
+ if sample is None:
378
+ if len(args) > 1:
379
+ sample = args[1]
380
+ else:
381
+ raise ValueError(
382
+ " missing `sample` as a required keyward argument")
383
+ if order is None:
384
+ if len(args) > 2:
385
+ order = args[2]
386
+ else:
387
+ raise ValueError(
388
+ " missing `order` as a required keyward argument")
389
+ if prev_timestep is not None:
390
+ deprecate(
391
+ "prev_timestep",
392
+ "1.0.0",
393
+ "Passing `prev_timestep` is deprecated and has no effect as model output conversion is now handled via an internal counter `self.step_index`",
394
+ )
395
+ model_output_list = self.model_outputs
396
+
397
+ s0 = self.timestep_list[-1]
398
+ m0 = model_output_list[-1]
399
+ x = sample
400
+
401
+ if self.solver_p:
402
+ x_t = self.solver_p.step(model_output, s0, x).prev_sample
403
+ return x_t
404
+
405
+ sigma_t, sigma_s0 = self.sigmas[self.step_index + 1], self.sigmas[
406
+ self.step_index] # pyright: ignore
407
+ alpha_t, sigma_t = self._sigma_to_alpha_sigma_t(sigma_t)
408
+ alpha_s0, sigma_s0 = self._sigma_to_alpha_sigma_t(sigma_s0)
409
+
410
+ lambda_t = torch.log(alpha_t) - torch.log(sigma_t)
411
+ lambda_s0 = torch.log(alpha_s0) - torch.log(sigma_s0)
412
+
413
+ h = lambda_t - lambda_s0
414
+ device = sample.device
415
+
416
+ rks = []
417
+ D1s = []
418
+ for i in range(1, order):
419
+ si = self.step_index - i # pyright: ignore
420
+ mi = model_output_list[-(i + 1)]
421
+ alpha_si, sigma_si = self._sigma_to_alpha_sigma_t(self.sigmas[si])
422
+ lambda_si = torch.log(alpha_si) - torch.log(sigma_si)
423
+ rk = (lambda_si - lambda_s0) / h
424
+ rks.append(rk)
425
+ D1s.append((mi - m0) / rk) # pyright: ignore
426
+
427
+ rks.append(1.0)
428
+ rks = torch.tensor(rks, device=device)
429
+
430
+ R = []
431
+ b = []
432
+
433
+ hh = -h if self.predict_x0 else h
434
+ h_phi_1 = torch.expm1(hh) # h\phi_1(h) = e^h - 1
435
+ h_phi_k = h_phi_1 / hh - 1
436
+
437
+ factorial_i = 1
438
+
439
+ if self.config.solver_type == "bh1":
440
+ B_h = hh
441
+ elif self.config.solver_type == "bh2":
442
+ B_h = torch.expm1(hh)
443
+ else:
444
+ raise NotImplementedError()
445
+
446
+ for i in range(1, order + 1):
447
+ R.append(torch.pow(rks, i - 1))
448
+ b.append(h_phi_k * factorial_i / B_h)
449
+ factorial_i *= i + 1
450
+ h_phi_k = h_phi_k / hh - 1 / factorial_i
451
+
452
+ R = torch.stack(R)
453
+ b = torch.tensor(b, device=device)
454
+
455
+ if len(D1s) > 0:
456
+ D1s = torch.stack(D1s, dim=1) # (B, K)
457
+ # for order 2, we use a simplified version
458
+ if order == 2:
459
+ rhos_p = torch.tensor([0.5], dtype=x.dtype, device=device)
460
+ else:
461
+ rhos_p = torch.linalg.solve(R[:-1, :-1],
462
+ b[:-1]).to(device).to(x.dtype)
463
+ else:
464
+ D1s = None
465
+
466
+ if self.predict_x0:
467
+ x_t_ = sigma_t / sigma_s0 * x - alpha_t * h_phi_1 * m0
468
+ if D1s is not None:
469
+ pred_res = torch.einsum("k,bkc...->bc...", rhos_p,
470
+ D1s) # pyright: ignore
471
+ else:
472
+ pred_res = 0
473
+ x_t = x_t_ - alpha_t * B_h * pred_res
474
+ else:
475
+ x_t_ = alpha_t / alpha_s0 * x - sigma_t * h_phi_1 * m0
476
+ if D1s is not None:
477
+ pred_res = torch.einsum("k,bkc...->bc...", rhos_p,
478
+ D1s) # pyright: ignore
479
+ else:
480
+ pred_res = 0
481
+ x_t = x_t_ - sigma_t * B_h * pred_res
482
+
483
+ x_t = x_t.to(x.dtype)
484
+ return x_t
485
+
486
+ def multistep_uni_c_bh_update(
487
+ self,
488
+ this_model_output: torch.Tensor,
489
+ *args,
490
+ last_sample: torch.Tensor = None,
491
+ this_sample: torch.Tensor = None,
492
+ order: int = None, # pyright: ignore
493
+ **kwargs,
494
+ ) -> torch.Tensor:
495
+ """
496
+ One step for the UniC (B(h) version).
497
+
498
+ Args:
499
+ this_model_output (`torch.Tensor`):
500
+ The model outputs at `x_t`.
501
+ this_timestep (`int`):
502
+ The current timestep `t`.
503
+ last_sample (`torch.Tensor`):
504
+ The generated sample before the last predictor `x_{t-1}`.
505
+ this_sample (`torch.Tensor`):
506
+ The generated sample after the last predictor `x_{t}`.
507
+ order (`int`):
508
+ The `p` of UniC-p at this step. The effective order of accuracy should be `order + 1`.
509
+
510
+ Returns:
511
+ `torch.Tensor`:
512
+ The corrected sample tensor at the current timestep.
513
+ """
514
+ this_timestep = args[0] if len(args) > 0 else kwargs.pop(
515
+ "this_timestep", None)
516
+ if last_sample is None:
517
+ if len(args) > 1:
518
+ last_sample = args[1]
519
+ else:
520
+ raise ValueError(
521
+ " missing`last_sample` as a required keyward argument")
522
+ if this_sample is None:
523
+ if len(args) > 2:
524
+ this_sample = args[2]
525
+ else:
526
+ raise ValueError(
527
+ " missing`this_sample` as a required keyward argument")
528
+ if order is None:
529
+ if len(args) > 3:
530
+ order = args[3]
531
+ else:
532
+ raise ValueError(
533
+ " missing`order` as a required keyward argument")
534
+ if this_timestep is not None:
535
+ deprecate(
536
+ "this_timestep",
537
+ "1.0.0",
538
+ "Passing `this_timestep` is deprecated and has no effect as model output conversion is now handled via an internal counter `self.step_index`",
539
+ )
540
+
541
+ model_output_list = self.model_outputs
542
+
543
+ m0 = model_output_list[-1]
544
+ x = last_sample
545
+ x_t = this_sample
546
+ model_t = this_model_output
547
+
548
+ sigma_t, sigma_s0 = self.sigmas[self.step_index], self.sigmas[
549
+ self.step_index - 1] # pyright: ignore
550
+ alpha_t, sigma_t = self._sigma_to_alpha_sigma_t(sigma_t)
551
+ alpha_s0, sigma_s0 = self._sigma_to_alpha_sigma_t(sigma_s0)
552
+
553
+ lambda_t = torch.log(alpha_t) - torch.log(sigma_t)
554
+ lambda_s0 = torch.log(alpha_s0) - torch.log(sigma_s0)
555
+
556
+ h = lambda_t - lambda_s0
557
+ device = this_sample.device
558
+
559
+ rks = []
560
+ D1s = []
561
+ for i in range(1, order):
562
+ si = self.step_index - (i + 1) # pyright: ignore
563
+ mi = model_output_list[-(i + 1)]
564
+ alpha_si, sigma_si = self._sigma_to_alpha_sigma_t(self.sigmas[si])
565
+ lambda_si = torch.log(alpha_si) - torch.log(sigma_si)
566
+ rk = (lambda_si - lambda_s0) / h
567
+ rks.append(rk)
568
+ D1s.append((mi - m0) / rk) # pyright: ignore
569
+
570
+ rks.append(1.0)
571
+ rks = torch.tensor(rks, device=device)
572
+
573
+ R = []
574
+ b = []
575
+
576
+ hh = -h if self.predict_x0 else h
577
+ h_phi_1 = torch.expm1(hh) # h\phi_1(h) = e^h - 1
578
+ h_phi_k = h_phi_1 / hh - 1
579
+
580
+ factorial_i = 1
581
+
582
+ if self.config.solver_type == "bh1":
583
+ B_h = hh
584
+ elif self.config.solver_type == "bh2":
585
+ B_h = torch.expm1(hh)
586
+ else:
587
+ raise NotImplementedError()
588
+
589
+ for i in range(1, order + 1):
590
+ R.append(torch.pow(rks, i - 1))
591
+ b.append(h_phi_k * factorial_i / B_h)
592
+ factorial_i *= i + 1
593
+ h_phi_k = h_phi_k / hh - 1 / factorial_i
594
+
595
+ R = torch.stack(R)
596
+ b = torch.tensor(b, device=device)
597
+
598
+ if len(D1s) > 0:
599
+ D1s = torch.stack(D1s, dim=1)
600
+ else:
601
+ D1s = None
602
+
603
+ # for order 1, we use a simplified version
604
+ if order == 1:
605
+ rhos_c = torch.tensor([0.5], dtype=x.dtype, device=device)
606
+ else:
607
+ rhos_c = torch.linalg.solve(R, b).to(device).to(x.dtype)
608
+
609
+ if self.predict_x0:
610
+ x_t_ = sigma_t / sigma_s0 * x - alpha_t * h_phi_1 * m0
611
+ if D1s is not None:
612
+ corr_res = torch.einsum("k,bkc...->bc...", rhos_c[:-1], D1s)
613
+ else:
614
+ corr_res = 0
615
+ D1_t = model_t - m0
616
+ x_t = x_t_ - alpha_t * B_h * (corr_res + rhos_c[-1] * D1_t)
617
+ else:
618
+ x_t_ = alpha_t / alpha_s0 * x - sigma_t * h_phi_1 * m0
619
+ if D1s is not None:
620
+ corr_res = torch.einsum("k,bkc...->bc...", rhos_c[:-1], D1s)
621
+ else:
622
+ corr_res = 0
623
+ D1_t = model_t - m0
624
+ x_t = x_t_ - sigma_t * B_h * (corr_res + rhos_c[-1] * D1_t)
625
+ x_t = x_t.to(x.dtype)
626
+ return x_t
627
+
628
+ def index_for_timestep(self, timestep, schedule_timesteps=None):
629
+ if schedule_timesteps is None:
630
+ schedule_timesteps = self.timesteps
631
+
632
+ indices = (schedule_timesteps == timestep).nonzero()
633
+
634
+ # The sigma index that is taken for the **very** first `step`
635
+ # is always the second index (or the last index if there is only 1)
636
+ # This way we can ensure we don't accidentally skip a sigma in
637
+ # case we start in the middle of the denoising schedule (e.g. for image-to-image)
638
+ pos = 1 if len(indices) > 1 else 0
639
+
640
+ return indices[pos].item()
641
+
642
+ # Copied from diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler._init_step_index
643
+ def _init_step_index(self, timestep):
644
+ """
645
+ Initialize the step_index counter for the scheduler.
646
+ """
647
+
648
+ if self.begin_index is None:
649
+ if isinstance(timestep, torch.Tensor):
650
+ timestep = timestep.to(self.timesteps.device)
651
+ self._step_index = self.index_for_timestep(timestep)
652
+ else:
653
+ self._step_index = self._begin_index
654
+
655
+ def step(self,
656
+ model_output: torch.Tensor,
657
+ timestep: Union[int, torch.Tensor],
658
+ sample: torch.Tensor,
659
+ return_dict: bool = True,
660
+ generator=None) -> Union[SchedulerOutput, Tuple]:
661
+ """
662
+ Predict the sample from the previous timestep by reversing the SDE. This function propagates the sample with
663
+ the multistep UniPC.
664
+
665
+ Args:
666
+ model_output (`torch.Tensor`):
667
+ The direct output from learned diffusion model.
668
+ timestep (`int`):
669
+ The current discrete timestep in the diffusion chain.
670
+ sample (`torch.Tensor`):
671
+ A current instance of a sample created by the diffusion process.
672
+ return_dict (`bool`):
673
+ Whether or not to return a [`~schedulers.scheduling_utils.SchedulerOutput`] or `tuple`.
674
+
675
+ Returns:
676
+ [`~schedulers.scheduling_utils.SchedulerOutput`] or `tuple`:
677
+ If return_dict is `True`, [`~schedulers.scheduling_utils.SchedulerOutput`] is returned, otherwise a
678
+ tuple is returned where the first element is the sample tensor.
679
+
680
+ """
681
+ if self.num_inference_steps is None:
682
+ raise ValueError(
683
+ "Number of inference steps is 'None', you need to run 'set_timesteps' after creating the scheduler"
684
+ )
685
+
686
+ if self.step_index is None:
687
+ self._init_step_index(timestep)
688
+
689
+ use_corrector = (
690
+ self.step_index > 0 and
691
+ self.step_index - 1 not in self.disable_corrector and
692
+ self.last_sample is not None # pyright: ignore
693
+ )
694
+
695
+ model_output_convert = self.convert_model_output(
696
+ model_output, sample=sample)
697
+ if use_corrector:
698
+ sample = self.multistep_uni_c_bh_update(
699
+ this_model_output=model_output_convert,
700
+ last_sample=self.last_sample,
701
+ this_sample=sample,
702
+ order=self.this_order,
703
+ )
704
+
705
+ for i in range(self.config.solver_order - 1):
706
+ self.model_outputs[i] = self.model_outputs[i + 1]
707
+ self.timestep_list[i] = self.timestep_list[i + 1]
708
+
709
+ self.model_outputs[-1] = model_output_convert
710
+ self.timestep_list[-1] = timestep # pyright: ignore
711
+
712
+ if self.config.lower_order_final:
713
+ this_order = min(self.config.solver_order,
714
+ len(self.timesteps) -
715
+ self.step_index) # pyright: ignore
716
+ else:
717
+ this_order = self.config.solver_order
718
+
719
+ self.this_order = min(this_order,
720
+ self.lower_order_nums + 1) # warmup for multistep
721
+ assert self.this_order > 0
722
+
723
+ self.last_sample = sample
724
+ prev_sample = self.multistep_uni_p_bh_update(
725
+ model_output=model_output, # pass the original non-converted model output, in case solver-p is used
726
+ sample=sample,
727
+ order=self.this_order,
728
+ )
729
+
730
+ if self.lower_order_nums < self.config.solver_order:
731
+ self.lower_order_nums += 1
732
+
733
+ # upon completion increase step index by one
734
+ self._step_index += 1 # pyright: ignore
735
+
736
+ if not return_dict:
737
+ return (prev_sample,)
738
+
739
+ return SchedulerOutput(prev_sample=prev_sample)
740
+
741
+ def scale_model_input(self, sample: torch.Tensor, *args,
742
+ **kwargs) -> torch.Tensor:
743
+ """
744
+ Ensures interchangeability with schedulers that need to scale the denoising model input depending on the
745
+ current timestep.
746
+
747
+ Args:
748
+ sample (`torch.Tensor`):
749
+ The input sample.
750
+
751
+ Returns:
752
+ `torch.Tensor`:
753
+ A scaled input sample.
754
+ """
755
+ return sample
756
+
757
+ # Copied from diffusers.schedulers.scheduling_dpmsolver_multistep.DPMSolverMultistepScheduler.add_noise
758
+ def add_noise(
759
+ self,
760
+ original_samples: torch.Tensor,
761
+ noise: torch.Tensor,
762
+ timesteps: torch.IntTensor,
763
+ ) -> torch.Tensor:
764
+ # Make sure sigmas and timesteps have the same device and dtype as original_samples
765
+ sigmas = self.sigmas.to(
766
+ device=original_samples.device, dtype=original_samples.dtype)
767
+ if original_samples.device.type == "mps" and torch.is_floating_point(
768
+ timesteps):
769
+ # mps does not support float64
770
+ schedule_timesteps = self.timesteps.to(
771
+ original_samples.device, dtype=torch.float32)
772
+ timesteps = timesteps.to(
773
+ original_samples.device, dtype=torch.float32)
774
+ else:
775
+ schedule_timesteps = self.timesteps.to(original_samples.device)
776
+ timesteps = timesteps.to(original_samples.device)
777
+
778
+ # begin_index is None when the scheduler is used for training or pipeline does not implement set_begin_index
779
+ if self.begin_index is None:
780
+ step_indices = [
781
+ self.index_for_timestep(t, schedule_timesteps)
782
+ for t in timesteps
783
+ ]
784
+ elif self.step_index is not None:
785
+ # add_noise is called after first denoising step (for inpainting)
786
+ step_indices = [self.step_index] * timesteps.shape[0]
787
+ else:
788
+ # add noise is called before first denoising step to create initial latent(img2img)
789
+ step_indices = [self.begin_index] * timesteps.shape[0]
790
+
791
+ sigma = sigmas[step_indices].flatten()
792
+ while len(sigma.shape) < len(original_samples.shape):
793
+ sigma = sigma.unsqueeze(-1)
794
+
795
+ alpha_t, sigma_t = self._sigma_to_alpha_sigma_t(sigma)
796
+ noisy_samples = alpha_t * original_samples + sigma_t * noise
797
+ return noisy_samples
798
+
799
+ def __len__(self):
800
+ return self.config.num_train_timesteps