Files
vllm-omni/vllm_omni/diffusion/models/mammoth_moda2/schedulers.py
T
1612948062 add support for MammothModa2 model (#336)
Signed-off-by: HonestDeng <2958906959@qq.com>
Signed-off-by: iwzbi <iwzbi@zju.edu.cn>
Signed-off-by: iwzbi <wzbi@zju.edu.cn>
Signed-off-by: Divyansh Singhvi <divyanshsinghvi@gmail.com>
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
Signed-off-by: yinpeiqi <yinpeiqi809@gmail.com>
Signed-off-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Signed-off-by: 汪志鹏 <wangzhipeng628@gmail.com>
Co-authored-by: catcat <108673086+iwzbi@users.noreply.github.com>
Co-authored-by: iwzbi <iwzbi@zju.edu.cn>
Co-authored-by: Hongsheng Liu <liuhongsheng4@huawei.com>
Co-authored-by: dsinghvi <divyanshsinghvi@gmail.com>
Co-authored-by: Canlin Guo <canlinguosdu@gmail.com>
Co-authored-by: Peiqi Yin <60515999+yinpeiqi@users.noreply.github.com>
Co-authored-by: 汪志鹏 <wangzhipeng628@gmail.com>
2026-03-04 22:52:56 +08:00

138 lines
5.2 KiB
Python

# Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
# SPDX-License-Identifier: Apache-2.0
#
# This file has been modified by ByteDance Ltd. on 2025-09-30.
#
# Original file was released under Apache-2.0, with the full license text
# available at https://www.apache.org/licenses/LICENSE-2.0
#
# This modified file is released under the same license.
#
# --- Upstream header preserved below ---
#
# Copyright 2024 Stability AI, Katherine Crowson and The HuggingFace Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from dataclasses import dataclass
import numpy as np
import torch
@dataclass
class FlowMatchEulerDiscreteSchedulerOutput:
prev_sample: torch.FloatTensor
class FlowMatchEulerDiscreteScheduler:
order = 1
def __init__(self, num_train_timesteps: int = 1000, dynamic_time_shift: bool = True):
self.num_train_timesteps = int(num_train_timesteps)
self.dynamic_time_shift = bool(dynamic_time_shift)
timesteps = torch.linspace(0, 1, self.num_train_timesteps + 1, dtype=torch.float32)[:-1]
self.timesteps = timesteps
self._timesteps = torch.cat([timesteps, torch.ones(1, dtype=timesteps.dtype)])
self._step_index: int | None = None
self._begin_index: int | None = None
@property
def step_index(self) -> int | None:
return self._step_index
@property
def begin_index(self) -> int | None:
return self._begin_index
def set_begin_index(self, begin_index: int = 0) -> None:
self._begin_index = int(begin_index)
def index_for_timestep(self, timestep: torch.Tensor, schedule_timesteps: torch.Tensor | None = None) -> int:
schedule_timesteps = self._timesteps if schedule_timesteps is None else schedule_timesteps
indices = (schedule_timesteps == timestep).nonzero()
pos = 1 if len(indices) > 1 else 0
return int(indices[pos].item())
def set_timesteps(
self,
num_inference_steps: int | None = None,
device: str | torch.device | None = None,
timesteps: list[float] | None = None,
num_tokens: int | None = None,
) -> None:
if timesteps is None:
if num_inference_steps is None:
raise ValueError("`num_inference_steps` must be provided when `timesteps` is None.")
timesteps_np = np.linspace(0, 1, num_inference_steps + 1, dtype=np.float32)[:-1]
if self.dynamic_time_shift and num_tokens is not None:
m = np.sqrt(float(num_tokens)) / 40.0
timesteps_np = timesteps_np / (m - m * timesteps_np + timesteps_np)
else:
timesteps_np = np.asarray(timesteps, dtype=np.float32)
timesteps_t = torch.from_numpy(timesteps_np).to(dtype=torch.float32, device=device)
self.timesteps = timesteps_t
self._timesteps = torch.cat([timesteps_t, torch.ones(1, device=timesteps_t.device, dtype=timesteps_t.dtype)])
self._step_index = None
self._begin_index = None
def _init_step_index(self, timestep: float | torch.Tensor) -> None:
if self.begin_index is None:
if isinstance(timestep, torch.Tensor):
timestep = timestep.to(self.timesteps.device)
else:
timestep = torch.tensor(timestep, device=self.timesteps.device, dtype=self.timesteps.dtype)
self._step_index = self.index_for_timestep(timestep)
else:
self._step_index = self._begin_index
def step(
self,
model_output: torch.FloatTensor,
timestep: float | torch.FloatTensor,
sample: torch.FloatTensor,
generator: torch.Generator | None = None, # noqa: ARG002 - kept for API compatibility
return_dict: bool = True,
) -> FlowMatchEulerDiscreteSchedulerOutput | tuple[torch.FloatTensor]:
if isinstance(timestep, (int, torch.IntTensor, torch.LongTensor)):
raise ValueError(
"Integer timesteps (e.g. a loop index from enumerate) are not supported; "
"pass a value from `scheduler.timesteps` instead.",
)
if self.step_index is None:
self._init_step_index(timestep)
assert self._step_index is not None
sample_fp32 = sample.to(torch.float32)
t = self._timesteps[self._step_index]
t_next = self._timesteps[self._step_index + 1]
prev_sample = sample_fp32 + (t_next - t) * model_output
prev_sample = prev_sample.to(model_output.dtype)
self._step_index += 1
if not return_dict:
return (prev_sample,)
return FlowMatchEulerDiscreteSchedulerOutput(prev_sample=prev_sample)
def __len__(self) -> int:
return self.num_train_timesteps