dagloop5 commited on
Commit
93e29c9
·
verified ·
1 Parent(s): 62b9248

Update pk_workflow.py

Browse files
Files changed (1) hide show
  1. pk_workflow.py +59 -2
pk_workflow.py CHANGED
@@ -175,6 +175,47 @@ SCHEDULE_SIGMA_FUNCS = {
175
  "normal": normal_sigmas,
176
  }
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
  class use_schedule:
180
  """Set each scheduler's shift for one request, and — for anything but `native` — force its sigma grid onto
@@ -187,11 +228,13 @@ class use_schedule:
187
  are shared, request-spanning objects that must not carry one request's shift into the next.
188
  """
189
 
190
- def __init__(self, pipe, steps: int, schedule_name: str, video_shift: float, audio_shift: float, threshold_noise: float = 0.025):
191
  self.pipe = pipe
192
  self.attr_names = ["scheduler", "audio_scheduler"]
193
  self.shifts = [float(video_shift), float(audio_shift)]
194
  self.schedule_name = schedule_name
 
 
195
  self.steps = int(steps)
196
  self.threshold_noise = float(threshold_noise)
197
  self._originals: dict = {}
@@ -215,11 +258,25 @@ class use_schedule:
215
  return _f(_s, None, device, _grid)
216
 
217
  scheduler.set_timesteps = forced
 
 
 
 
 
 
 
 
 
 
 
 
218
  return self
219
 
220
  def __exit__(self, *_):
221
  for attr_name, original in self._originals.items():
222
- getattr(self.pipe, attr_name).__dict__.pop("set_timesteps", None)
 
 
223
  setattr(self.pipe, attr_name, original)
224
  return False
225
 
 
175
  "normal": normal_sigmas,
176
  }
177
 
178
+ def _euler_ancestral_step(scheduler, generator, model_output, timestep, sample, eta: float = 1.0, s_noise: float = 1.0):
179
+ """Ports k-diffusion's `sample_euler_ancestral_RF` — the flow-matching branch `sample_euler_ancestral`
180
+ dispatches to for `CONST`-style model sampling, which is what MiniMax-H3's `[0, 1]` sigma space is — onto one
181
+ `MiniMaxH3Scheduler.step()` call. Single model evaluation, same shape as `step()` itself, with fresh
182
+ ancestral noise injected each step instead of a plain Euler blend. Mirrors `step()`'s own care around
183
+ recomputing `sigma_from_timestep` from `timestep` rather than reading `self.sigmas` at the current index, for
184
+ the same numerical-consistency reason documented there.
185
+ """
186
+ if scheduler._step_index is None:
187
+ scheduler._step_index = scheduler.index_for_timestep(timestep) if scheduler._begin_index is None else scheduler._begin_index
188
+
189
+ if not isinstance(timestep, torch.Tensor):
190
+ timestep = torch.tensor(timestep, dtype=sample.dtype)
191
+ sigma_from_timestep = 1 - timestep.to(device=sample.device, dtype=sample.dtype)
192
+ while sigma_from_timestep.ndim < sample.ndim:
193
+ sigma_from_timestep = sigma_from_timestep.unsqueeze(-1)
194
+ denoised = sample + sigma_from_timestep * model_output
195
+
196
+ compute_dtype = torch.float32 if sample.dtype in (torch.float16, torch.bfloat16) else sample.dtype
197
+ sigma = scheduler.sigmas[scheduler._step_index].to(device=sample.device, dtype=compute_dtype)
198
+ sigma_next = scheduler.sigmas[scheduler._step_index + 1].to(device=sample.device, dtype=compute_dtype)
199
+ x = sample.to(dtype=compute_dtype)
200
+ denoised = denoised.to(dtype=compute_dtype)
201
+
202
+ if sigma_next == 0:
203
+ prev_sample = denoised
204
+ else:
205
+ downstep_ratio = 1 + (sigma_next / sigma - 1) * eta
206
+ sigma_down = sigma_next * downstep_ratio
207
+ alpha_next = 1 - sigma_next
208
+ alpha_down = 1 - sigma_down
209
+ renoise_coeff = (sigma_next**2 - sigma_down**2 * alpha_next**2 / alpha_down**2).clamp_min(0).sqrt()
210
+ ratio = sigma_down / sigma
211
+ prev_sample = ratio * x + (1 - ratio) * denoised
212
+ if eta > 0:
213
+ noise = torch.randn(x.shape, dtype=x.dtype, device=x.device, generator=generator)
214
+ prev_sample = (alpha_next / alpha_down) * prev_sample + noise * s_noise * renoise_coeff
215
+
216
+ prev_sample = prev_sample.to(dtype=sample.dtype)
217
+ scheduler._step_index += 1
218
+ return prev_sample
219
 
220
  class use_schedule:
221
  """Set each scheduler's shift for one request, and — for anything but `native` — force its sigma grid onto
 
228
  are shared, request-spanning objects that must not carry one request's shift into the next.
229
  """
230
 
231
+ def __init__(self, pipe, steps: int, schedule_name: str, video_shift: float, audio_shift: float, sampler_name: str = "euler", seed: int = 0, threshold_noise: float = 0.025):
232
  self.pipe = pipe
233
  self.attr_names = ["scheduler", "audio_scheduler"]
234
  self.shifts = [float(video_shift), float(audio_shift)]
235
  self.schedule_name = schedule_name
236
+ self.sampler_name = sampler_name
237
+ self.seed = int(seed)
238
  self.steps = int(steps)
239
  self.threshold_noise = float(threshold_noise)
240
  self._originals: dict = {}
 
258
  return _f(_s, None, device, _grid)
259
 
260
  scheduler.set_timesteps = forced
261
+
262
+ if self.sampler_name == "euler_ancestral":
263
+ # Separate `torch.Generator` per scheduler (offset seeds) so video and audio ancestral noise don't
264
+ # correlate — each generator advances across every step call to *that* scheduler over the request.
265
+ for offset, attr_name in enumerate(self.attr_names):
266
+ scheduler = getattr(self.pipe, attr_name)
267
+ generator = torch.Generator(device="cpu").manual_seed(self.seed + offset)
268
+
269
+ def stepped(model_output, timestep, sample, return_dict=True, _s=scheduler, _g=generator, **_kwargs):
270
+ return (_euler_ancestral_step(_s, _g, model_output, timestep, sample),)
271
+
272
+ scheduler.step = stepped
273
  return self
274
 
275
  def __exit__(self, *_):
276
  for attr_name, original in self._originals.items():
277
+ current = getattr(self.pipe, attr_name)
278
+ current.__dict__.pop("set_timesteps", None)
279
+ current.__dict__.pop("step", None)
280
  setattr(self.pipe, attr_name, original)
281
  return False
282