diff --git a/CHANGELOG.md b/CHANGELOG.md index d5ff54114..87532ed10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ # Change Log for SD.Next -## Update for 2026-02-02 +## Update for 2026-02-04 + +### Highlights for 2026-02-04 + +Refresh release two weeks after prior release, yet we still somehow managed to pack in ~140 commits with new features, models and fixes! +For full list of changes, see full changelog. + +[ReadMe](https://github.com/vladmandic/automatic/blob/master/README.md) | [ChangeLog](https://github.com/vladmandic/automatic/blob/master/CHANGELOG.md) | [Docs](https://vladmandic.github.io/sdnext-docs/) | [WiKi](https://github.com/vladmandic/automatic/wiki) | [Discord](https://discord.com/invite/sd-next-federal-batch-inspectors-1101998836328697867) | [Sponsor](https://github.com/sponsors/vladmandic) + +### Details for 2026-02-04 - **Models** - [Tongyi-MAI Z-Image Base](https://tongyi-mai.github.io/Z-Image-blog/) diff --git a/TODO.md b/TODO.md index 1a6cedd90..1dbbe1a7c 100644 --- a/TODO.md +++ b/TODO.md @@ -1,96 +1,123 @@ # TODO -## Project Board - -- - ## Internal -- Feature: Flow-match `res4lyf` schedulers -- Feature: Move `nunchaku` models to refernce instead of internal decision +Project board: + - Update: `transformers==5.0.0` -- Feature: Unify *huggingface* and *diffusers* model folders -- Reimplement `llama` remover for Kanvas - Deploy: Create executable for SD.Next -- Feature: Integrate natural language image search - [ImageDB](https://github.com/vladmandic/imagedb) -- Feature: Remote Text-Encoder support -- Refactor: move sampler options to settings to config -- Refactor: [GGUF](https://huggingface.co/docs/diffusers/main/en/quantization/gguf) -- Feature: LoRA add OMI format support for SD35/FLUX.1 -- Refactor: remove `CodeFormer` -- Refactor: remove `GFPGAN` -- UI: Lite vs Expert mode -- Video tab: add full API support -- Control tab: add overrides handling -- Engine: `TensorRT` acceleration +- Deploy: Lite vs Expert mode - Engine: [mmgp](https://github.com/deepbeepmeep/mmgp) - Engine: [sharpfin](https://github.com/drhead/sharpfin) instead of `torchvision` +- Engine: `TensorRT` acceleration +- Feature: Auto handle scheduler `prediction_type` +- Feature: Cache models in memory +- Feature: Control tab: add overrides handling +- Feature: Integrate natural language image search + [ImageDB](https://github.com/vladmandic/imagedb) +- Feature: LoRA add OMI format support for SD35/FLUX.1 +- Feature: Multi-user support +- Feature: Remote Text-Encoder support +- Feature: Settings profile manager +- Feature: Video tab: add full API support +- Refactor: Unify *huggingface* and *diffusers* model folders +- Refactor: Move `nunchaku` models to refernce instead of internal decision +- Refactor: [GGUF](https://huggingface.co/docs/diffusers/main/en/quantization/gguf) +- Refactor: move sampler options to settings to config +- Refactor: remove `CodeFormer` +- Refactor: remove `GFPGAN` +- Reimplement `llama` remover for Kanvas ## Modular - Switch to modular pipelines - Feature: Transformers unified cache handler - Refactor: [Modular pipelines and guiders](https://github.com/huggingface/diffusers/issues/11915) -- [MagCache](https://github.com/lllyasviel/FramePack/pull/673/files) -- [SmoothCache](https://github.com/huggingface/diffusers/issues/11135) -## Features - -- [Flux.2 TinyVAE](https://huggingface.co/fal/FLUX.2-Tiny-AutoEncoder) -- [IPAdapter composition](https://huggingface.co/ostris/ip-composition-adapter) -- [IPAdapter negative guidance](https://github.com/huggingface/diffusers/discussions/7167) -- [STG](https://github.com/huggingface/diffusers/blob/main/examples/community/README.md#spatiotemporal-skip-guidance) -- [Video Inpaint Pipeline](https://github.com/huggingface/diffusers/pull/12506) -- [Sonic Inpaint](https://github.com/ubc-vision/sonic) - -### New models / Pipelines +## New models / Pipelines TODO: Investigate which models are diffusers-compatible and prioritize! -- [Bria FiboEdit](https://github.com/huggingface/diffusers/commit/d7a1c31f4f85bae5a9e01cdce49bd7346bd8ccd6) -- [LTXVideo 0.98 LongMulti](https://github.com/huggingface/diffusers/pull/12614) -- [Cosmos-Predict-2.5](https://huggingface.co/nvidia/Cosmos-Predict2.5-2B) -- [NewBie Image Exp0.1](https://github.com/huggingface/diffusers/pull/12803) -- [Sana-I2V](https://github.com/huggingface/diffusers/pull/12634#issuecomment-3540534268) +### Text-to-Image - [Bria FIBO](https://huggingface.co/briaai/FIBO) -- [Bytedance Lynx](https://github.com/bytedance/lynx) -- [ByteDance OneReward](https://github.com/bytedance/OneReward) -- [ByteDance USO](https://github.com/bytedance/USO) -- [Chroma Radiance](https://huggingface.co/lodestones/Chroma1-Radiance) - [Chroma Zeta](https://huggingface.co/lodestones/Zeta-Chroma) -- [DiffSynth Studio](https://github.com/modelscope/DiffSynth-Studio) -- [DiffusionForcing](https://github.com/kwsong0113/diffusion-forcing-transformer) -- [Dream0 guidance](https://huggingface.co/ByteDance/DreamO) -- [HunyuanAvatar](https://huggingface.co/tencent/HunyuanVideo-Avatar) -- [HunyuanCustom](https://github.com/Tencent-Hunyuan/HunyuanCustom) -- [Inf-DiT](https://github.com/zai-org/Inf-DiT) -- [Krea Realtime Video](https://huggingface.co/krea/krea-realtime-video) -- [LanDiff](https://github.com/landiff/landiff) -- [Liquid](https://github.com/FoundationVision/Liquid) -- [LongCat-Video](https://huggingface.co/meituan-longcat/LongCat-Video) -- [LucyEdit](https://github.com/huggingface/diffusers/pull/12340) +- [Chroma Radiance](https://huggingface.co/lodestones/Chroma1-Radiance) +- [Liquid](https://github.com/FoundationVision/Liquid) - [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO) -- [Magi](https://github.com/SandAI-org/MAGI-1)(https://github.com/huggingface/diffusers/pull/11713) -- [Ming](https://github.com/inclusionAI/Ming) + +### Image-to-Image +- [Meituan LongCat-Image-Edit-Turbo](https://huggingface.co/meituan-longcat/LongCat-Image-Edit-Turbo) +- [VIBE Image-Edit](https://huggingface.co/iitolstykh/VIBE-Image-Edit) +- [Bria FiboEdit (diffusers)](https://github.com/huggingface/diffusers/commit/d7a1c31f4f85bae5a9e01cdce49bd7346bd8ccd6) +- [LucyEdit (diffusers PR)](https://github.com/huggingface/diffusers/pull/12340) +- [SD3 UltraEdit](https://github.com/HaozheZhao/UltraEdit) +- [Step1X-Edit](https://github.com/stepfun-ai/Step1X-Edit) +- [OneReward (mask-guided / object removal)](https://github.com/bytedance/OneReward) + +### Text-to-Video +- [OpenMOSS MOVA](https://huggingface.co/OpenMOSS-Team/MOVA-720p) +- [Wan family (Wan2.1 / Wan2.2 variants)](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B) + - example: [Wan2.1-T2V-14B-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid) + - distill / step-distill examples: [Wan2.1-StepDistill-CfgDistill](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-StepDistill-CfgDistill) +- [Krea Realtime Video](https://huggingface.co/krea/krea-realtime-video) +- [MAGI-1 (autoregressive video)](https://github.com/SandAI-org/MAGI-1) +- [MUG-V 10B (video generation)](https://huggingface.co/MUG-V/MUG-V-inference) +- [Ovi (audio/video generation)](https://github.com/character-ai/Ovi) + +### Image-to-Video - [MUG-V 10B](https://huggingface.co/MUG-V/MUG-V-inference) -- [Ovi](https://github.com/character-ai/Ovi) -- [Phantom HuMo](https://github.com/Phantom-video/Phantom) -- [SD3 UltraEdit](https://github.com/HaozheZhao/UltraEdit) -- [SelfForcing](https://github.com/guandeh17/Self-Forcing) -- [SEVA](https://github.com/huggingface/diffusers/pull/11440) -- [Step1X](https://github.com/stepfun-ai/Step1X-Edit) -- [Wan-2.2 Animate](https://github.com/huggingface/diffusers/pull/12526) -- [Wan-2.2 S2V](https://github.com/huggingface/diffusers/pull/12258) -- [WAN-CausVid-Plus t2v](https://github.com/goatWu/CausVid-Plus/) -- [WAN-CausVid](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-CausVid) -- [WAN-StepDistill](https://huggingface.co/lightx2v/Wan2.1-T2V-14B-StepDistill-CfgDistill) -- [Wan2.2-Animate-14B](https://huggingface.co/Wan-AI/Wan2.2-Animate-14B) -- [WAN2GP](https://github.com/deepbeepmeep/Wan2GP) +- [HunyuanVideo-Avatar / HunyuanCustom](https://huggingface.co/tencent/HunyuanVideo-Avatar) +- [Sana Image→Video (Sana-I2V)](https://github.com/huggingface/diffusers/pull/12634#issuecomment-3540534268) +- [Wan-2.2 S2V (diffusers PR)](https://github.com/huggingface/diffusers/pull/12258) -### Migration +### Video Editing / Long-Video / Animation Tooling +- [LongCat-Video](https://huggingface.co/meituan-longcat/LongCat-Video) +- [LTXVideo / LTXVideo LongMulti (diffusers PR)](https://github.com/huggingface/diffusers/pull/12614) +- [DiffSynth-Studio (ModelScope)](https://github.com/modelscope/DiffSynth-Studio) +- [Phantom (Phantom HuMo)](https://github.com/Phantom-video/Phantom) +- [CausVid-Plus / WAN-CausVid-Plus](https://github.com/goatWu/CausVid-Plus/) +- [Wan2GP (workflow/GUI for Wan)](https://github.com/deepbeepmeep/Wan2GP) -#### Asyncio +### Multimodal +- [Cosmos-Predict-2.5 (NVIDIA)](https://huggingface.co/nvidia/Cosmos-Predict2.5-2B) +- [Liquid (unified multimodal generator)](https://github.com/FoundationVision/Liquid) +- [Lumina-DiMOO](https://huggingface.co/Alpha-VLLM/Lumina-DiMOO) +- [Ming (inclusionAI)](https://github.com/inclusionAI/Ming) +- [Magi (SandAI)](https://github.com/SandAI-org/MAGI-1) +- [DreamO (ByteDance) — image customization framework](https://huggingface.co/ByteDance/DreamO) + +### Other/Unsorted +- [DiffusionForcing](https://github.com/kwsong0113/diffusion-forcing-transformer) +- [Self-Forcing](https://github.com/guandeh17/Self-Forcing) +- [SEVA](https://github.com/huggingface/diffusers/pull/11440) +- [ByteDance USO](https://github.com/bytedance/USO) +- [ByteDance Lynx](https://github.com/bytedance/lynx) +- [LanDiff](https://github.com/landiff/landiff) +- [MagCache](https://github.com/huggingface/diffusers/pull/12744) +- [SmoothCache](https://github.com/huggingface/diffusers/issues/11135) +- [STG](https://github.com/huggingface/diffusers/blob/main/examples/community/README.md#spatiotemporal-skip-guidance) +- [Video Inpaint Pipeline](https://github.com/huggingface/diffusers/pull/12506) +- [Sonic Inpaint](https://github.com/ubc-vision/sonic) +- [BoxDiff](https://github.com/huggingface/diffusers/pull/7947) +- [Make-It-Count](https://github.com/Litalby1/make-it-count) +- [FreeCustom](https://github.com/aim-uofa/FreeCustom) +- [ControlNeXt](https://github.com/dvlab-research/ControlNeXt/) +- [MS-Diffusion](https://github.com/MS-Diffusion/MS-Diffusion) +- [UniRef](https://github.com/FoundationVision/UniRef) +- [AnyDoor](https://github.com/ali-vilab/AnyDoor) +- [AnyText2](https://github.com/tyxsspa/AnyText2) +- [DragonDiffusion](https://github.com/MC-E/DragonDiffusion) +- [DenseDiffusion](https://github.com/naver-ai/DenseDiffusion) +- [FlashFace](https://github.com/ali-vilab/FlashFace) +- [PowerPaint](https://github.com/open-mmlab/PowerPaint) +- [IC-Light](https://github.com/lllyasviel/IC-Light) +- [ReNO](https://github.com/ExplainableML/ReNO) +- [LoRAdapter](https://github.com/CompVis/LoRAdapter) +- [LivePortrait](https://github.com/KwaiVGI/LivePortrait) + +## Migration + +### Asyncio - Policy system is deprecated and will be removed in **Python 3.16** - [Python 3.14 removals - asyncio](https://docs.python.org/3.14/whatsnew/3.14.html#id10) @@ -102,7 +129,7 @@ TODO: Investigate which models are diffusers-compatible and prioritize! - [asyncio.run](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.run) - [asyncio.Runner](https://docs.python.org/3.14/library/asyncio-runner.html#asyncio.Runner) -#### rmtree +### rmtree - `onerror` deprecated and replaced with `onexc` in **Python 3.12** ``` python diff --git a/installer.py b/installer.py index 21766ea05..84790cac0 100644 --- a/installer.py +++ b/installer.py @@ -665,7 +665,7 @@ def check_diffusers(): t_start = time.time() if args.skip_all: return - sha = 'd4f97d19210d4abc32dac78fe7080cd8f5f0809c' # diffusers commit hash + sha = '430c557b6a66a3c2b5740fb186324cb8a9f0f2e9' # diffusers commit hash # if args.use_rocm or args.use_zluda or args.use_directml: # sha = '043ab2520f6a19fce78e6e060a68dbc947edb9f9' # lock diffusers versions for now pkg = pkg_resources.working_set.by_key.get('diffusers', None) @@ -811,6 +811,7 @@ def install_rocm_zluda(): torch_command = os.environ.get('TORCH_COMMAND', f'torch torchvision --index-url https://rocm.nightlies.amd.com/{device.therock}') else: check_python(supported_minors=[12], reason='ROCm: Windows preview python==3.12 required') + # torch 2.8.0a0 is the last version with rocm 6.4 support torch_command = os.environ.get('TORCH_COMMAND', '--no-cache-dir https://repo.radeon.com/rocm/windows/rocm-rel-6.4.4/torch-2.8.0a0%2Bgitfc14c65-cp312-cp312-win_amd64.whl https://repo.radeon.com/rocm/windows/rocm-rel-6.4.4/torchvision-0.24.0a0%2Bc85f008-cp312-cp312-win_amd64.whl') else: #check_python(supported_minors=[10, 11, 12, 13, 14], reason='ROCm backend requires a Python version between 3.10 and 3.13') diff --git a/modules/api/xyz_grid.py b/modules/api/xyz_grid.py index 2d2011dae..569ae98b0 100644 --- a/modules/api/xyz_grid.py +++ b/modules/api/xyz_grid.py @@ -2,7 +2,7 @@ from typing import List def xyz_grid_enum(option: str = "") -> List[dict]: - from scripts.xyz import xyz_grid_classes + from scripts.xyz import xyz_grid_classes # pylint: disable=no-name-in-module options = [] for x in xyz_grid_classes.axis_options: _option = { diff --git a/modules/res4lyf/TASK.md b/modules/res4lyf/TASK.md deleted file mode 100644 index a74429e6c..000000000 --- a/modules/res4lyf/TASK.md +++ /dev/null @@ -1,24 +0,0 @@ -# TASK: Schedulers - -## Notes - -This is a codebase for diffusion schedulers implemented for `diffusers` library and ported from `res4lyf` repository at - -Ported schedulers codebase is in `modules/res4lyf`, do not modify any other files - -## Testing - -All schedulers were tested using prediction type `epsilon` and `StableDiffusionXLPipeline` pipeline for *text2image*: WORKING GOOD! - -Shifting focus to testing prediction type `flow_prediction` and `ZImagePipeline` pipeline for *text2image* - -## Results - -- so far all tested schedules produce blocky/pixelated and unresolved output - -## TODO - -- [x] focus on a single scheduler only. lets pick abnorsett_2m (Fixed: Implemented AB update branch) -- [x] validate config params: is this ok? (Validated: Config is correct for Flux/SD3 with new patch) - config={'num_train_timesteps': 1000, 'beta_start': 0.0001, 'beta_end': 0.02, 'beta_schedule': 'linear', 'prediction_type': 'flow_prediction', 'variant': 'abnorsett_2m', 'use_analytic_solution': True, 'timestep_spacing': 'linspace', 'steps_offset': 0, 'use_flow_sigmas': True, 'shift': 3, 'base_shift': 0.5, 'max_shift': 1.15, 'base_image_seq_len': 256, 'max_image_seq_len': 4096} -- [x] check code (Complete) diff --git a/modules/res4lyf/__init__.py b/modules/res4lyf/__init__.py index 8d80acafd..576d8fb93 100644 --- a/modules/res4lyf/__init__.py +++ b/modules/res4lyf/__init__.py @@ -3,7 +3,7 @@ from .abnorsett_scheduler import ABNorsettScheduler from .bong_tangent_scheduler import BongTangentScheduler from .common_sigma_scheduler import CommonSigmaScheduler -from .deis_scheduler_alt import DEISMultistepScheduler +from .deis_scheduler_alt import RESDEISMultistepScheduler from .etdrk_scheduler import ETDRKScheduler from .gauss_legendre_scheduler import GaussLegendreScheduler from .langevin_dynamics_scheduler import LangevinDynamicsScheduler @@ -95,7 +95,7 @@ __all__ = [ # noqa: RUF022 "RESMultistepSDEScheduler", "RESSinglestepScheduler", "RESSinglestepSDEScheduler", - "DEISMultistepScheduler", + "RESDEISMultistepScheduler", "ETDRKScheduler", "LawsonScheduler", "ABNorsettScheduler", @@ -183,7 +183,7 @@ BASE = [ ("RES Multistep SDE", RESMultistepSDEScheduler), ("RES Singlestep", RESSinglestepScheduler), ("RES Singlestep SDE", RESSinglestepSDEScheduler), - ("DEIS Multistep", DEISMultistepScheduler), + ("DEIS Multistep", RESDEISMultistepScheduler), ("ETDRK", ETDRKScheduler), ("Lawson", LawsonScheduler), ("ABNorsett", ABNorsettScheduler), diff --git a/modules/res4lyf/abnorsett_scheduler.py b/modules/res4lyf/abnorsett_scheduler.py index 092e16471..e2ba0a686 100644 --- a/modules/res4lyf/abnorsett_scheduler.py +++ b/modules/res4lyf/abnorsett_scheduler.py @@ -123,7 +123,6 @@ class ABNorsettScheduler(SchedulerMixin, ConfigMixin): raise ValueError(f"timestep_spacing {self.config.timestep_spacing} is not supported.") sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas) if self.config.use_karras_sigmas: @@ -135,8 +134,10 @@ class ABNorsettScheduler(SchedulerMixin, ConfigMixin): elif self.config.use_flow_sigmas: s_min = getattr(self.config, "sigma_min", None) s_max = getattr(self.config, "sigma_max", None) - if s_min is None: s_min = 0.001 - if s_max is None: s_max = 1.0 + if s_min is None: + s_min = 0.001 + if s_max is None: + s_max = 1.0 sigmas = np.linspace(s_max, s_min, num_inference_steps) if self.config.shift != 1.0 or self.config.use_dynamic_shifting: @@ -279,11 +280,11 @@ class ABNorsettScheduler(SchedulerMixin, ConfigMixin): # AB2 Variable Step # x_{n+1} = x_n + dt * [ (1 + r/2) * v_n - (r/2) * v_{n-1} ] # where r = dt_cur / dt_prev - + v_nm1 = self.model_outputs[-2] sigma_prev = self.prev_sigmas[-2] dt_prev = sigma_curr - sigma_prev - + if abs(dt_prev) < 1e-8: # Fallback to Euler if division by zero risk x_next = sample + dt * v_n @@ -293,15 +294,15 @@ class ABNorsettScheduler(SchedulerMixin, ConfigMixin): c0 = 1 + 0.5 * r c1 = -0.5 * r x_next = sample + dt * (c0 * v_n + c1 * v_nm1) - + elif curr_order >= 3: - # For now, fallback to AB2 (variable) for higher orders to ensure stability - # given the complexity of variable-step AB3/4 formulas inline. + # For now, fallback to AB2 (variable) for higher orders to ensure stability + # given the complexity of variable-step AB3/4 formulas inline. # The user specifically requested abnorsett_2m. v_nm1 = self.model_outputs[-2] sigma_prev = self.prev_sigmas[-2] dt_prev = sigma_curr - sigma_prev - + if abs(dt_prev) < 1e-8: x_next = sample + dt * v_n else: diff --git a/modules/res4lyf/bong_tangent_scheduler.py b/modules/res4lyf/bong_tangent_scheduler.py index 9714cade2..a0c827218 100644 --- a/modules/res4lyf/bong_tangent_scheduler.py +++ b/modules/res4lyf/bong_tangent_scheduler.py @@ -99,6 +99,8 @@ class BongTangentScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample @@ -154,8 +156,8 @@ class BongTangentScheduler(SchedulerMixin, ConfigMixin): end_val = sigma_min mid_val = sigma_mid - tan_sigmas_1 = self._get_bong_tangent_sigmas(stage_1_len, s1, p1, start_val, mid_val) - tan_sigmas_2 = self._get_bong_tangent_sigmas(stage_2_len, s2, p2 - stage_1_len, mid_val, end_val) + tan_sigmas_1 = self._get_bong_tangent_sigmas(stage_1_len, s1, p1, start_val, mid_val, dtype=dtype) + tan_sigmas_2 = self._get_bong_tangent_sigmas(stage_2_len, s2, p2 - stage_1_len, mid_val, end_val, dtype=dtype) tan_sigmas_1 = tan_sigmas_1[:-1] sigmas_list = tan_sigmas_1 + tan_sigmas_2 @@ -208,7 +210,7 @@ class BongTangentScheduler(SchedulerMixin, ConfigMixin): from .scheduler_utils import add_noise_to_sample return add_noise_to_sample(original_samples, noise, self.sigmas, timesteps, self.timesteps) - def _get_bong_tangent_sigmas(self, steps: int, slope: float, pivot: int, start: float, end: float) -> List[float]: + def _get_bong_tangent_sigmas(self, steps: int, slope: float, pivot: int, start: float, end: float, dtype: torch.dtype = torch.float32) -> List[float]: x = torch.arange(steps, dtype=dtype) def bong_fn(val): diff --git a/modules/res4lyf/common_sigma_scheduler.py b/modules/res4lyf/common_sigma_scheduler.py index 61f47c015..202d289af 100644 --- a/modules/res4lyf/common_sigma_scheduler.py +++ b/modules/res4lyf/common_sigma_scheduler.py @@ -203,6 +203,8 @@ class CommonSigmaScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/deis_scheduler_alt.py b/modules/res4lyf/deis_scheduler_alt.py index 65c727c3a..70c63cecf 100644 --- a/modules/res4lyf/deis_scheduler_alt.py +++ b/modules/res4lyf/deis_scheduler_alt.py @@ -20,7 +20,7 @@ def get_def_integral_3(a, b, c, start, end, d): class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): """ - DEISMultistepScheduler: Diffusion Explicit Iterative Sampler with high-order multistep. + RESDEISMultistepScheduler: Diffusion Explicit Iterative Sampler with high-order multistep. Adapted from the RES4LYF repository. """ @@ -82,6 +82,7 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): self._step_index = None self._sigmas_cpu = None self.all_coeffs = [] + self.prev_sigmas = [] def set_timesteps( self, @@ -109,7 +110,7 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): timesteps = np.maximum(timesteps, 0) sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) + log_sigmas_all = np.log(np.maximum(sigmas, 1e-10)) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -244,7 +245,7 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): step_index = self._step_index sigma_t = self.sigmas[step_index] - + # RECONSTRUCT X0 (Matching PEC pattern) if self.config.prediction_type == "epsilon": denoised = sample - sigma_t * model_output @@ -262,13 +263,10 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): if self.config.clip_sample: denoised = denoised.clamp(-self.config.sample_max_value, self.config.sample_max_value) - # DEIS coefficients are precomputed in set_timesteps - coeffs = self.all_coeffs[step_index] - if self.config.prediction_type == "flow_prediction": # Variable Step Adams-Bashforth for Flow Matching self.model_outputs.append(model_output) - self.prev_sigmas.append(sigma_t) + self.prev_sigmas.append(sigma_t) # Note: deis uses hist_samples for x0? I'll use model_outputs for v. if len(self.model_outputs) > 4: self.model_outputs.pop(0) @@ -276,7 +274,7 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): dt = self.sigmas[step_index + 1] - sigma_t v_n = model_output - + curr_order = min(len(self.prev_sigmas), 3) if curr_order == 1: @@ -301,11 +299,11 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): x_next = sample + dt * (c0 * v_n + c1 * self.model_outputs[-2]) self._step_index += 1 - if not return_dict: return (x_next,) + if not return_dict: + return (x_next,) return SchedulerOutput(prev_sample=x_next) sigma_next = self.sigmas[step_index + 1] - alpha_next = 1 / (sigma_next**2 + 1) ** 0.5 if sigma_next > 0 else 1.0 if self.config.solver_order == 1: # 1st order step (Euler) in x-space @@ -316,11 +314,11 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): h = -torch.log(sigma_next / sigma_t) if sigma_t > 0 and sigma_next > 0 else torch.zeros_like(sigma_t) phi = Phi(h, [0], getattr(self.config, "use_analytic_solution", True)) phi_1 = phi(1) - + # History of denoised samples x0s = [denoised] + self.model_outputs[::-1] orders = min(len(x0s), self.config.solver_order) - + # Force Order 1 at the end of schedule if self.num_inference_steps is not None and step_index >= self.num_inference_steps - 3: res = phi_1 * denoised @@ -334,7 +332,7 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): h_prev = -np.log(self._sigmas_cpu[step_index] / (self._sigmas_cpu[step_index - 1] + 1e-9)) h_prev_t = torch.tensor(h_prev, device=sample.device, dtype=sample.dtype) r = h_prev_t / (h + 1e-9) - + # Hard Restart if r < 0.5 or r > 2.0: res = phi_1 * denoised @@ -355,7 +353,7 @@ class RESDEISMultistepScheduler(SchedulerMixin, ConfigMixin): h_p2 = -np.log(self._sigmas_cpu[step_index] / (self._sigmas_cpu[step_index - 2] + 1e-9)) r1 = torch.tensor(h_p1, device=sample.device, dtype=sample.dtype) / (h + 1e-9) r2 = torch.tensor(h_p2, device=sample.device, dtype=sample.dtype) / (h + 1e-9) - + # Hard Restart if r1 < 0.5 or r1 > 2.0 or r2 < 0.5 or r2 > 2.0: res = phi_1 * denoised diff --git a/modules/res4lyf/etdrk_scheduler.py b/modules/res4lyf/etdrk_scheduler.py index 94d8c2261..07b624ff6 100644 --- a/modules/res4lyf/etdrk_scheduler.py +++ b/modules/res4lyf/etdrk_scheduler.py @@ -174,6 +174,8 @@ class ETDRKScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/gauss_legendre_scheduler.py b/modules/res4lyf/gauss_legendre_scheduler.py index 9ba6c7472..38db308b8 100644 --- a/modules/res4lyf/gauss_legendre_scheduler.py +++ b/modules/res4lyf/gauss_legendre_scheduler.py @@ -165,7 +165,6 @@ class GaussLegendreScheduler(SchedulerMixin, ConfigMixin): raise ValueError(f"timestep_spacing must be one of 'linspace', 'leading', or 'trailing', got {self.config.timestep_spacing}") sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -323,7 +322,7 @@ class GaussLegendreScheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/langevin_dynamics_scheduler.py b/modules/res4lyf/langevin_dynamics_scheduler.py index fc2a83846..8e3c2eb48 100644 --- a/modules/res4lyf/langevin_dynamics_scheduler.py +++ b/modules/res4lyf/langevin_dynamics_scheduler.py @@ -190,6 +190,8 @@ class LangevinDynamicsScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/lawson_scheduler.py b/modules/res4lyf/lawson_scheduler.py index 7f9f80391..0af304eb2 100644 --- a/modules/res4lyf/lawson_scheduler.py +++ b/modules/res4lyf/lawson_scheduler.py @@ -172,6 +172,8 @@ class LawsonScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/linear_rk_scheduler.py b/modules/res4lyf/linear_rk_scheduler.py index 05ef5b2e3..8e2a9aac1 100644 --- a/modules/res4lyf/linear_rk_scheduler.py +++ b/modules/res4lyf/linear_rk_scheduler.py @@ -121,7 +121,6 @@ class LinearRKScheduler(SchedulerMixin, ConfigMixin): raise ValueError(f"timestep_spacing must be one of 'linspace', 'leading', or 'trailing', got {self.config.timestep_spacing}") sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -262,7 +261,7 @@ class LinearRKScheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/lobatto_scheduler.py b/modules/res4lyf/lobatto_scheduler.py index aa4dfe354..97d073e88 100644 --- a/modules/res4lyf/lobatto_scheduler.py +++ b/modules/res4lyf/lobatto_scheduler.py @@ -121,7 +121,6 @@ class LobattoScheduler(SchedulerMixin, ConfigMixin): raise ValueError(f"timestep_spacing must be one of 'linspace', 'leading', or 'trailing', got {self.config.timestep_spacing}") sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -262,7 +261,7 @@ class LobattoScheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/pec_scheduler.py b/modules/res4lyf/pec_scheduler.py index 8872ce207..f6df4f449 100644 --- a/modules/res4lyf/pec_scheduler.py +++ b/modules/res4lyf/pec_scheduler.py @@ -180,6 +180,8 @@ class PECScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] return sample / ((sigma**2 + 1) ** 0.5) diff --git a/modules/res4lyf/radau_iia_scheduler.py b/modules/res4lyf/radau_iia_scheduler.py index 7ea4dde2e..2cd5d85e3 100644 --- a/modules/res4lyf/radau_iia_scheduler.py +++ b/modules/res4lyf/radau_iia_scheduler.py @@ -155,7 +155,6 @@ class RadauIIAScheduler(SchedulerMixin, ConfigMixin): raise ValueError(f"timestep_spacing must be one of 'linspace', 'leading', or 'trailing', got {self.config.timestep_spacing}") sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -247,11 +246,7 @@ class RadauIIAScheduler(SchedulerMixin, ConfigMixin): sigma = self.sigmas[self._step_index] return sample / ((sigma**2 + 1) ** 0.5) - def index_for_timestep(self, timestep, schedule_timesteps=None): - from .scheduler_utils import index_for_timestep - if schedule_timesteps is None: - schedule_timesteps = self.timesteps - return index_for_timestep(timestep, schedule_timesteps) + def _init_step_index(self, timestep): if self._step_index is None: @@ -309,7 +304,7 @@ class RadauIIAScheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/res_multistep_scheduler.py b/modules/res4lyf/res_multistep_scheduler.py index f5313c603..e324408ee 100644 --- a/modules/res4lyf/res_multistep_scheduler.py +++ b/modules/res4lyf/res_multistep_scheduler.py @@ -250,7 +250,7 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): # Variable Step Adams-Bashforth for Flow Matching dt = sigma_next - sigma v_n = model_output - + if curr_order == 1: x_next = sample + dt * v_n elif curr_order == 2: @@ -258,7 +258,7 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): sigma_prev = self.prev_sigmas[-2] dt_prev = sigma - sigma_prev r = dt / dt_prev if abs(dt_prev) > 1e-8 else 0.0 - + # Stability check if dt_prev == 0 or r < -0.9 or r > 2.0: # Fallback x_next = sample + dt * v_n @@ -267,17 +267,6 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): c1 = -0.5 * r x_next = sample + dt * (c0 * v_n + c1 * self.model_outputs[-2]) elif curr_order >= 3: - # AB3 - sigma_prev1 = self.prev_sigmas[-2] - sigma_prev2 = self.prev_sigmas[-3] - dt_prev1 = sigma - sigma_prev1 - dt_prev2 = self.prev_sigmas[-2] - sigma_prev2 # This is not strictly correct for variable steps logic used in ABNorsett, assume simplified AB3 for now or stick to AB2 - # Actually, let's reuse ABNorsett logic - # x_{n+1} = x_n + dt * [ (1 + r1/2 + r2/2 + ... ) ] - Too complex to derive on the fly? - # Let's use AB2 for stability as requested "Variable Step Adams-Bashforth like ABNorsett" - # ABNorsett implemented AB2. I will downgrade order 3 to AB2 for safety or implement AB3 if confident. - # Let's stick to AB2 for Flow as it is robust enough. - # Re-implement AB2 logic sigma_prev = self.prev_sigmas[-2] dt_prev = sigma - sigma_prev @@ -291,7 +280,7 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): self.model_outputs.pop(0) self.x0_outputs.pop(0) self.prev_sigmas.pop(0) - + if not return_dict: return (x_next,) return SchedulerOutput(prev_sample=x_next) @@ -301,25 +290,16 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): phi_1 = phi(1) if variant.startswith("res"): - # REiS Multistep logic - c2, c3 = 0.5, 1.0 - # Force Order 1 at the end of schedule if self.num_inference_steps is not None and self._step_index >= self.num_inference_steps - 3: curr_order = 1 if curr_order == 2: h_prev = -torch.log(self.prev_sigmas[-1] / self.prev_sigmas[-2]) - c2 = (-h_prev / h).item() if h > 0 else 0.5 - rk_type = "res_2s" elif curr_order == 3: - h_prev1 = -torch.log(self.prev_sigmas[-1] / self.prev_sigmas[-2]) - h_prev2 = -torch.log(self.prev_sigmas[-1] / self.prev_sigmas[-3]) - c2 = (-h_prev1 / h).item() if h > 0 else 0.5 - c3 = (-h_prev2 / h).item() if h > 0 else 1.0 - rk_type = "res_3s" + pass else: - rk_type = "res_1s" + pass # Exponential Integrator Update in x-space if curr_order == 1: @@ -329,21 +309,21 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): # b2 = -phi_2 / r = -phi(2) / (h_prev/h) # Here we use: b2 = phi(2) / ((-h_prev / h) + 1e-9) # Since (-h_prev/h) is negative (-r), this gives correct negative sign for b2. - + # Stability check r_check = h_prev / (h + 1e-9) # This is effectively -r if using h_prev definition above? # Wait, h_prev above is -log(). Positive. # h is positive. # So h_prev/h is positive. defined as r in other files. # But here code uses -h_prev / h in denominator. - + # Stability check r_check = h_prev / (h + 1e-9) - + # Hard Restart if r_check < 0.5 or r_check > 2.0: res = phi_1 * x0 - else: + else: b2 = phi(2) / ((-h_prev / h) + 1e-9) b1 = phi_1 - b2 res = b1 * self.x0_outputs[-1] + b2 * self.x0_outputs[-2] @@ -353,7 +333,7 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): h_p2 = -torch.log(self.prev_sigmas[-1] / (self.prev_sigmas[-3] + 1e-9)) r1 = h_p1 / (h + 1e-9) r2 = h_p2 / (h + 1e-9) - + if r1 < 0.5 or r1 > 2.0 or r2 < 0.5 or r2 > 2.0: res = phi_1 * x0 else: @@ -429,13 +409,14 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): elif order == 2: h_prev = -torch.log(self.prev_sigmas[-1] / (self.prev_sigmas[-2] + 1e-9)) r = h_prev / (h + 1e-9) - + # Correct Adams-Bashforth-like coefficients for Exponential Integrators - + # Hard Restart for stability if r < 0.5 or r > 2.0: return [[phi_1]] + phi_2 = phi(2) b2 = -phi_2 / (r + 1e-9) b1 = phi_1 - b2 return [[b1, b2]] @@ -444,13 +425,13 @@ class RESMultistepScheduler(SchedulerMixin, ConfigMixin): h_prev2 = -torch.log(self.prev_sigmas[-1] / (self.prev_sigmas[-3] + 1e-9)) r1 = h_prev1 / (h + 1e-9) r2 = h_prev2 / (h + 1e-9) - + if r1 < 0.5 or r1 > 2.0 or r2 < 0.5 or r2 > 2.0: return [[phi_1]] - + phi_2 = phi(2) phi_3 = phi(3) - + # Generalized AB3 for Exponential Integrators (Varying steps) denom = r2 - r1 + 1e-9 b3 = (phi_3 + r1 * phi_2) / (r2 * denom) diff --git a/modules/res4lyf/res_multistep_sde_scheduler.py b/modules/res4lyf/res_multistep_sde_scheduler.py index 23057ce8e..8ed98688b 100644 --- a/modules/res4lyf/res_multistep_sde_scheduler.py +++ b/modules/res4lyf/res_multistep_sde_scheduler.py @@ -105,6 +105,8 @@ class RESMultistepSDEScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/res_singlestep_scheduler.py b/modules/res4lyf/res_singlestep_scheduler.py index 2692e97bc..29146029f 100644 --- a/modules/res4lyf/res_singlestep_scheduler.py +++ b/modules/res4lyf/res_singlestep_scheduler.py @@ -89,8 +89,6 @@ class RESSinglestepScheduler(SchedulerMixin, ConfigMixin): self._begin_index = begin_index def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: - if self._step_index is None: - self._init_step_index(timestep) if self._step_index is None: self._init_step_index(timestep) if self.config.prediction_type == "flow_prediction": @@ -124,12 +122,11 @@ class RESSinglestepScheduler(SchedulerMixin, ConfigMixin): raise ValueError(f"timestep_spacing {self.config.timestep_spacing} is not supported.") sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) + # Linear remapping logic if self.config.use_flow_sigmas: - # Logic handled below (linspace) or here? - # To match others: - pass + # Logic handled here + pass else: sigmas = np.interp(timesteps, np.arange(0, len(sigmas)), sigmas) @@ -152,10 +149,11 @@ class RESSinglestepScheduler(SchedulerMixin, ConfigMixin): self.config.base_image_seq_len, self.config.max_image_seq_len, ) + if not self.config.use_flow_sigmas: + sigmas = apply_shift(torch.from_numpy(sigmas), shift).numpy() + if self.config.use_flow_sigmas: sigmas = np.linspace(1.0, 1 / 1000, num_inference_steps) - else: - sigmas = apply_shift(torch.from_numpy(sigmas), shift).numpy() if self.config.use_flow_sigmas: timesteps = sigmas * self.config.num_train_timesteps @@ -211,12 +209,13 @@ class RESSinglestepScheduler(SchedulerMixin, ConfigMixin): x0 = sample - sigma * model_output else: x0 = model_output - + if self.config.prediction_type == "flow_prediction": dt = sigma_next - sigma x_next = sample + dt * model_output self._step_index += 1 - if not return_dict: return (x_next,) + if not return_dict: + return (x_next,) return SchedulerOutput(prev_sample=x_next) # Exponential Integrator Update diff --git a/modules/res4lyf/res_singlestep_sde_scheduler.py b/modules/res4lyf/res_singlestep_sde_scheduler.py index 93cea3d3e..ef7fea5b9 100644 --- a/modules/res4lyf/res_singlestep_sde_scheduler.py +++ b/modules/res4lyf/res_singlestep_sde_scheduler.py @@ -93,6 +93,8 @@ class RESSinglestepSDEScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/res_unified_scheduler.py b/modules/res4lyf/res_unified_scheduler.py index b5d16a6e5..5aa619db6 100644 --- a/modules/res4lyf/res_unified_scheduler.py +++ b/modules/res4lyf/res_unified_scheduler.py @@ -185,7 +185,7 @@ class RESUnifiedScheduler(SchedulerMixin, ConfigMixin): # phi_2 = phi(2) # Moved inside conditional blocks as needed history_len = len(self.x0_outputs) - + # Stability: Force Order 1 for final few steps to prevent degradation at low noise levels if self.num_inference_steps is not None and self._step_index >= self.num_inference_steps - 3: return [phi_1], h @@ -193,10 +193,10 @@ class RESUnifiedScheduler(SchedulerMixin, ConfigMixin): if self.config.rk_type in ["res_2m", "deis_2m"] and history_len >= 2: h_prev = -torch.log(self.prev_sigmas[-1] / (self.prev_sigmas[-2] + 1e-9)) r = h_prev / (h + 1e-9) - + h_prev = -torch.log(self.prev_sigmas[-1] / (self.prev_sigmas[-2] + 1e-9)) r = h_prev / (h + 1e-9) - + # Hard Restart: if step sizes vary too wildly, fallback to order 1 if r < 0.5 or r > 2.0: return [phi_1], h @@ -220,10 +220,10 @@ class RESUnifiedScheduler(SchedulerMixin, ConfigMixin): # Hard Restart check if r1 < 0.5 or r1 > 2.0 or r2 < 0.5 or r2 > 2.0: return [phi_1], h - + phi_2 = phi(2) phi_3 = phi(3) - + # Generalized AB3 for Exponential Integrators (Varying steps) denom = r2 - r1 + 1e-9 b3 = (phi_3 + r1 * phi_2) / (r2 * denom) @@ -275,9 +275,9 @@ class RESUnifiedScheduler(SchedulerMixin, ConfigMixin): # Variable Step Adams-Bashforth for Flow Matching dt = sigma_next - sigma v_n = model_output - + curr_order = min(len(self.prev_sigmas), 3) # Max order 3 here - + if curr_order == 1: x_next = sample + dt * v_n elif curr_order == 2: @@ -300,7 +300,8 @@ class RESUnifiedScheduler(SchedulerMixin, ConfigMixin): x_next = sample + dt * (c0 * v_n + c1 * self.model_outputs[-2]) self._step_index += 1 - if not return_dict: return (x_next,) + if not return_dict: + return (x_next,) return SchedulerOutput(prev_sample=x_next) # GET COEFFICIENTS diff --git a/modules/res4lyf/riemannian_flow_scheduler.py b/modules/res4lyf/riemannian_flow_scheduler.py index 6eaf71bdb..926c31c46 100644 --- a/modules/res4lyf/riemannian_flow_scheduler.py +++ b/modules/res4lyf/riemannian_flow_scheduler.py @@ -205,6 +205,8 @@ class RiemannianFlowScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/rungekutta_44s_scheduler.py b/modules/res4lyf/rungekutta_44s_scheduler.py index 1d5ef9a9c..be6efe9da 100644 --- a/modules/res4lyf/rungekutta_44s_scheduler.py +++ b/modules/res4lyf/rungekutta_44s_scheduler.py @@ -75,7 +75,6 @@ class RungeKutta44Scheduler(SchedulerMixin, ConfigMixin): # 1. Base sigmas timesteps = np.linspace(0, self.config.num_train_timesteps - 1, num_inference_steps, dtype=float)[::-1].copy() sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -108,7 +107,6 @@ class RungeKutta44Scheduler(SchedulerMixin, ConfigMixin): sigmas_expanded.append(0.0) # terminal sigma # 3. Map back to timesteps - log_sigmas_all = np.log(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) sigmas_interpolated = np.array(sigmas_expanded) # Linear remapping for Flow Matching timesteps_expanded = sigmas_interpolated * self.config.num_train_timesteps @@ -195,7 +193,7 @@ class RungeKutta44Scheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/rungekutta_57s_scheduler.py b/modules/res4lyf/rungekutta_57s_scheduler.py index 21bbeed54..d3f6b2297 100644 --- a/modules/res4lyf/rungekutta_57s_scheduler.py +++ b/modules/res4lyf/rungekutta_57s_scheduler.py @@ -94,7 +94,6 @@ class RungeKutta57Scheduler(SchedulerMixin, ConfigMixin): timesteps = np.maximum(timesteps, 0) sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -145,7 +144,6 @@ class RungeKutta57Scheduler(SchedulerMixin, ConfigMixin): sigmas_expanded.append(s_curr + c * (s_next - s_curr)) sigmas_expanded.append(0.0) - log_sigmas_all = np.log(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) sigmas_interpolated = np.array(sigmas_expanded) # Linear remapping for Flow Matching timesteps_expanded = sigmas_interpolated * self.config.num_train_timesteps @@ -198,6 +196,18 @@ class RungeKutta57Scheduler(SchedulerMixin, ConfigMixin): if self._step_index is None: self._init_step_index(timestep) + # Dormand-Prince 5(4) Coefficients + a = [ + [], + [1/5], + [3/40, 9/40], + [44/45, -56/15, 32/9], + [19372/6561, -25360/2187, 64448/6561, -212/729], + [9017/3168, -355/33, 46732/5247, 49/176, -5103/18656], + [35/384, 0, 500/1113, 125/192, -2187/6784, 11/84] + ] + b = [35/384, 0, 500/1113, 125/192, -2187/6784, 11/84, 0] + step_index = self._step_index stage_index = step_index % 7 @@ -229,7 +239,7 @@ class RungeKutta57Scheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/rungekutta_67s_scheduler.py b/modules/res4lyf/rungekutta_67s_scheduler.py index 41060bc89..b2c13ad47 100644 --- a/modules/res4lyf/rungekutta_67s_scheduler.py +++ b/modules/res4lyf/rungekutta_67s_scheduler.py @@ -94,7 +94,6 @@ class RungeKutta67Scheduler(SchedulerMixin, ConfigMixin): timesteps = np.maximum(timesteps, 0) sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -145,7 +144,6 @@ class RungeKutta67Scheduler(SchedulerMixin, ConfigMixin): sigmas_expanded.append(s_curr + c * (s_next - s_curr)) sigmas_expanded.append(0.0) - log_sigmas_all = np.log(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) sigmas_interpolated = np.array(sigmas_expanded) # Linear remapping for Flow Matching timesteps_expanded = sigmas_interpolated * self.config.num_train_timesteps @@ -229,7 +227,7 @@ class RungeKutta67Scheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/scheduler_utils.py b/modules/res4lyf/scheduler_utils.py index 99dab451a..608ac11ad 100644 --- a/modules/res4lyf/scheduler_utils.py +++ b/modules/res4lyf/scheduler_utils.py @@ -90,21 +90,19 @@ def get_dynamic_shift(mu, base_shift, max_shift, base_seq_len, max_seq_len): return m * mu + b def index_for_timestep(timestep, timesteps): - import numpy as _np - # Normalize inputs to numpy arrays for a robust, device-agnostic argmin if isinstance(timestep, torch.Tensor): timestep_np = timestep.detach().cpu().numpy() else: - timestep_np = _np.array(timestep) + timestep_np = np.array(timestep) if isinstance(timesteps, torch.Tensor): timesteps_np = timesteps.detach().cpu().numpy() else: - timesteps_np = _np.array(timesteps) + timesteps_np = np.array(timesteps) # Use numpy argmin on absolute difference for stability - idx = _np.abs(timesteps_np - timestep_np).argmin() + idx = np.abs(timesteps_np - timestep_np).argmin() return int(idx) def add_noise_to_sample( diff --git a/modules/res4lyf/simple_exponential_scheduler.py b/modules/res4lyf/simple_exponential_scheduler.py index 6b2c2f6a6..52e678ca9 100644 --- a/modules/res4lyf/simple_exponential_scheduler.py +++ b/modules/res4lyf/simple_exponential_scheduler.py @@ -155,6 +155,8 @@ class SimpleExponentialScheduler(SchedulerMixin, ConfigMixin): def scale_model_input(self, sample: torch.Tensor, timestep: Union[float, torch.Tensor]) -> torch.Tensor: if self._step_index is None: self._init_step_index(timestep) + if self.config.prediction_type == "flow_prediction": + return sample sigma = self.sigmas[self._step_index] sample = sample / ((sigma**2 + 1) ** 0.5) return sample diff --git a/modules/res4lyf/specialized_rk_scheduler.py b/modules/res4lyf/specialized_rk_scheduler.py index 5060d61e0..fa9b23a2e 100644 --- a/modules/res4lyf/specialized_rk_scheduler.py +++ b/modules/res4lyf/specialized_rk_scheduler.py @@ -125,7 +125,6 @@ class SpecializedRKScheduler(SchedulerMixin, ConfigMixin): raise ValueError(f"timestep_spacing must be one of 'linspace', 'leading', or 'trailing', got {self.config.timestep_spacing}") sigmas = np.array(((1 - self.alphas_cumprod) / self.alphas_cumprod) ** 0.5) - log_sigmas_all = np.log(sigmas) if self.config.interpolation_type == "linear": sigmas = np.interp(timesteps, np.arange(len(sigmas)), sigmas) elif self.config.interpolation_type == "log_linear": @@ -286,7 +285,7 @@ class SpecializedRKScheduler(SchedulerMixin, ConfigMixin): # derivative = (x - x0) / sigma derivative = (sample - denoised) / sigma_t if sigma_t > 1e-6 else torch.zeros_like(sample) - + if self.sample_at_start_of_step is None: if stage_index > 0: # Mid-step fallback for Img2Img/Inpainting diff --git a/modules/res4lyf/test.py b/modules/res4lyf/test.py new file mode 100644 index 000000000..24994c0df --- /dev/null +++ b/modules/res4lyf/test.py @@ -0,0 +1,228 @@ +import os +import sys +import time +import inspect +import numpy as np +import torch + +# Ensure we can import modules +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))) + +from modules.errors import log +from modules.res4lyf import ( + BASE, SIMPLE, VARIANTS, + RESUnifiedScheduler, RESMultistepScheduler, RESDEISMultistepScheduler, + ETDRKScheduler, LawsonScheduler, ABNorsettScheduler, PECScheduler, + RiemannianFlowScheduler, RESSinglestepScheduler, RESSinglestepSDEScheduler, + RESMultistepSDEScheduler, SimpleExponentialScheduler, LinearRKScheduler, + LobattoScheduler, GaussLegendreScheduler, RungeKutta44Scheduler, + RungeKutta57Scheduler, RungeKutta67Scheduler, SpecializedRKScheduler, + BongTangentScheduler, CommonSigmaScheduler, RadauIIAScheduler, + LangevinDynamicsScheduler +) + +def test_scheduler(name, scheduler_class, config): + try: + scheduler = scheduler_class(**config) + except Exception as e: + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} error="Init failed: {e}"') + return False + + num_steps = 20 + scheduler.set_timesteps(num_steps) + + sample = torch.randn((1, 4, 64, 64)) + has_changed = False + t0 = time.time() + messages = [] + + try: + for i, t in enumerate(scheduler.timesteps): + # Simulate model output (noise or x0 or v), Using random noise for stability check + model_output = torch.randn_like(sample) + + # Scaling Check + sigma = scheduler.sigmas[scheduler.step_index] if scheduler.step_index is not None else scheduler.sigmas[0] # Handle potential index mismatch if step_index is updated differently, usually step_index matches i for these tests + + # Re-introduce scaling calculation first + scaled_sample = scheduler.scale_model_input(sample, t) + + if config.get("prediction_type") == "flow_prediction": + expected_scale = 1.0 + else: + expected_scale = 1.0 / ((sigma**2 + 1) ** 0.5) + + # Simple check with loose tolerance due to float precision + expected_scaled_sample = sample * expected_scale + if not torch.allclose(scaled_sample, expected_scaled_sample, atol=1e-4): + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} expected={expected_scale} error="scaling mismatch"') + return False + + if torch.isnan(scaled_sample).any(): + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} error="NaN in scaled_sample"') + return False + + if torch.isinf(scaled_sample).any(): + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} error="Inf in scaled_sample"') + return False + + output = scheduler.step(model_output, t, sample) + + # Shape and Dtype check + if output.prev_sample.shape != sample.shape: + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} error="Shape mismatch: {output.prev_sample.shape} vs {sample.shape}"') + return False + if output.prev_sample.dtype != sample.dtype: + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} error="Dtype mismatch: {output.prev_sample.dtype} vs {sample.dtype}"') + return False + + # Update check: Did the sample change? + if not torch.equal(sample, output.prev_sample): + has_changed = True + + # Sample Evolution Check + step_diff = (sample - output.prev_sample).abs().mean().item() + if step_diff < 1e-6: + messages.append(f'warning="minimal sample change: {step_diff}"') + + sample = output.prev_sample + + if torch.isnan(sample).any(): + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} error="NaN in sample"') + return False + + if torch.isinf(sample).any(): + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} error="Inf in sample"') + return False + + # Divergence check + if sample.abs().max() > 1e10: + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} step={i} error="divergence detected"') + return False + + # External check for Sigma Monotonicity + if hasattr(scheduler, 'sigmas'): + sigmas = scheduler.sigmas.cpu().numpy() + if len(sigmas) > 1: + diffs = np.diff(sigmas) # Check if potentially monotonic decreasing (standard) OR increasing (some flow/inverse setups). We allow flat sections (diff=0) hence 1e-6 slack + is_monotonic_decreasing = np.all(diffs <= 1e-6) + is_monotonic_increasing = np.all(diffs >= -1e-6) + if not (is_monotonic_decreasing or is_monotonic_increasing): + messages.append('warning="sigmas are not monotonic"') + + except Exception as e: + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} exception: {e}') + import traceback + traceback.print_exc() + return False + + if not has_changed: + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} error="sample never changed"') + return False + + final_std = sample.std().item() + with open("std_log.txt", "a") as f: + f.write(f"STD_LOG: {name} config={config} std={final_std}\n") + + if final_std > 50.0 or final_std < 0.1: + log.error(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} std={final_std} error="variance drift"') + + t1 = time.time() + messages = list(set(messages)) + log.info(f'scheduler="{name}" cls={scheduler.__class__.__name__} config={config} time={t1-t0} messages={messages}') + return True + +def run_tests(): + prediction_types = ["epsilon", "v_prediction", "sample"] # flow_prediction is special, usually requires flow sigmas or specific setup, checking standard ones first + + # Test BASE schedulers with their specific parameters + log.warning('type="base"') + for name, cls in BASE: + configs = [] + + # prediction_types + for pt in prediction_types: + configs.append({"prediction_type": pt}) + + # Specific params for specific classes + if cls == RESUnifiedScheduler: + rk_types = ["res_2m", "res_3m", "res_2s", "res_3s", "res_5s", "res_6s", "deis_1s", "deis_2m", "deis_3m"] + for rk in rk_types: + for pt in prediction_types: + configs.append({"rk_type": rk, "prediction_type": pt}) + + elif cls == RESMultistepScheduler: + variants = ["res_2m", "res_3m", "deis_2m", "deis_3m"] + for v in variants: + for pt in prediction_types: + configs.append({"variant": v, "prediction_type": pt}) + + elif cls == RESDEISMultistepScheduler: + for order in range(1, 6): + for pt in prediction_types: + configs.append({"solver_order": order, "prediction_type": pt}) + + elif cls == ETDRKScheduler: + variants = ["etdrk2_2s", "etdrk3_a_3s", "etdrk3_b_3s", "etdrk4_4s", "etdrk4_4s_alt"] + for v in variants: + for pt in prediction_types: + configs.append({"variant": v, "prediction_type": pt}) + + elif cls == LawsonScheduler: + variants = ["lawson2a_2s", "lawson2b_2s", "lawson4_4s"] + for v in variants: + for pt in prediction_types: + configs.append({"variant": v, "prediction_type": pt}) + + elif cls == ABNorsettScheduler: + variants = ["abnorsett_2m", "abnorsett_3m", "abnorsett_4m"] + for v in variants: + for pt in prediction_types: + configs.append({"variant": v, "prediction_type": pt}) + + elif cls == PECScheduler: + variants = ["pec423_2h2s", "pec433_2h3s"] + for v in variants: + for pt in prediction_types: + configs.append({"variant": v, "prediction_type": pt}) + + elif cls == RiemannianFlowScheduler: + metrics = ["euclidean", "hyperbolic", "spherical", "lorentzian"] + for m in metrics: + configs.append({"metric_type": m, "prediction_type": "epsilon"}) # Flow usually uses v or raw, but epsilon check matches others + + if not configs: + for pt in prediction_types: + configs.append({"prediction_type": pt}) + + for conf in configs: + test_scheduler(name, cls, conf) + + log.warning('type="simple"') + for name, cls in SIMPLE: + for pt in prediction_types: + test_scheduler(name, cls, {"prediction_type": pt}) + + log.warning('type="variants"') + for name, cls in VARIANTS: + # these classes preset their variants/rk_types in __init__ so we just test prediction types + for pt in prediction_types: + test_scheduler(name, cls, {"prediction_type": pt}) + + # Extra robustness check: Flow Prediction Type + log.warning('type="flow"') + flow_schedulers = [ + RESUnifiedScheduler, RESMultistepScheduler, ABNorsettScheduler, + RESSinglestepScheduler, RESSinglestepSDEScheduler, RESDEISMultistepScheduler, + RESMultistepSDEScheduler, ETDRKScheduler, LawsonScheduler, PECScheduler, + SimpleExponentialScheduler, LinearRKScheduler, LobattoScheduler, + GaussLegendreScheduler, RungeKutta44Scheduler, RungeKutta57Scheduler, + RungeKutta67Scheduler, SpecializedRKScheduler, BongTangentScheduler, + CommonSigmaScheduler, RadauIIAScheduler, LangevinDynamicsScheduler, + RiemannianFlowScheduler + ] + for cls in flow_schedulers: + test_scheduler(cls.__name__, cls, {"prediction_type": "flow_prediction", "use_flow_sigmas": True}) + +if __name__ == "__main__": + run_tests() diff --git a/modules/res4lyf/variants.py b/modules/res4lyf/variants.py index c67c17355..fc7b5b1e9 100644 --- a/modules/res4lyf/variants.py +++ b/modules/res4lyf/variants.py @@ -1,6 +1,6 @@ from .abnorsett_scheduler import ABNorsettScheduler from .common_sigma_scheduler import CommonSigmaScheduler -from .deis_scheduler_alt import DEISMultistepScheduler +from .deis_scheduler_alt import RESDEISMultistepScheduler from .etdrk_scheduler import ETDRKScheduler from .gauss_legendre_scheduler import GaussLegendreScheduler from .lawson_scheduler import LawsonScheduler @@ -302,19 +302,19 @@ class SigmaSmoothScheduler(CommonSigmaScheduler): super().__init__(**kwargs) ## DEIS Multistep Variants -class DEIS1MultistepScheduler(DEISMultistepScheduler): +class DEIS1MultistepScheduler(RESDEISMultistepScheduler): def __init__(self, **kwargs): - kwargs["order"] = "1" + kwargs["solver_order"] = 1 super().__init__(**kwargs) -class DEIS2MultistepScheduler(DEISMultistepScheduler): +class DEIS2MultistepScheduler(RESDEISMultistepScheduler): def __init__(self, **kwargs): - kwargs["order"] = "2" + kwargs["solver_order"] = 2 super().__init__(**kwargs) -class DEIS3MultistepScheduler(DEISMultistepScheduler): +class DEIS3MultistepScheduler(RESDEISMultistepScheduler): def __init__(self, **kwargs): - kwargs["order"] = "3" + kwargs["solver_order"] = 3 super().__init__(**kwargs) ## Linear RK Variants diff --git a/modules/perflow/__init__.py b/modules/schedulers/perflow/__init__.py similarity index 100% rename from modules/perflow/__init__.py rename to modules/schedulers/perflow/__init__.py diff --git a/modules/perflow/pfode_solver.py b/modules/schedulers/perflow/pfode_solver.py similarity index 100% rename from modules/perflow/pfode_solver.py rename to modules/schedulers/perflow/pfode_solver.py diff --git a/modules/perflow/scheduler_perflow.py b/modules/schedulers/perflow/scheduler_perflow.py similarity index 100% rename from modules/perflow/scheduler_perflow.py rename to modules/schedulers/perflow/scheduler_perflow.py diff --git a/modules/perflow/utils_perflow.py b/modules/schedulers/perflow/utils_perflow.py similarity index 100% rename from modules/perflow/utils_perflow.py rename to modules/schedulers/perflow/utils_perflow.py diff --git a/modules/sd_samplers_diffusers.py b/modules/sd_samplers_diffusers.py index 8f6aabc69..02287cf9a 100644 --- a/modules/sd_samplers_diffusers.py +++ b/modules/sd_samplers_diffusers.py @@ -59,7 +59,7 @@ try: from modules.schedulers.scheduler_ufogen import UFOGenScheduler # pylint: disable=ungrouped-imports from modules.schedulers.scheduler_unipc_flowmatch import FlowUniPCMultistepScheduler # pylint: disable=ungrouped-imports from modules.schedulers.scheduler_flashflow import FlashFlowMatchEulerDiscreteScheduler # pylint: disable=ungrouped-imports - from modules.perflow import PeRFlowScheduler # pylint: disable=ungrouped-imports + from modules.schedulers.perflow import PeRFlowScheduler # pylint: disable=ungrouped-imports except Exception as e: shared.log.error(f'Sampler import: version={diffusers.__version__} error: {e}') if os.environ.get('SD_SAMPLER_DEBUG', None) is not None: diff --git a/scripts/ctrlx_ext.py b/scripts/ctrlx_ext.py index 8703a16c4..ed611f4ba 100644 --- a/scripts/ctrlx_ext.py +++ b/scripts/ctrlx_ext.py @@ -44,9 +44,9 @@ class Script(scripts_manager.Script): return None import yaml - from scripts.ctrlx import CtrlXStableDiffusionXLPipeline - from scripts.ctrlx.sdxl import get_control_config, register_control - from scripts.ctrlx.utils import get_self_recurrence_schedule + from scripts.ctrlx import CtrlXStableDiffusionXLPipeline # pylint: disable=no-name-in-module + from scripts.ctrlx.sdxl import get_control_config, register_control # pylint: disable=no-name-in-module + from scripts.ctrlx.utils import get_self_recurrence_schedule # pylint: disable=no-name-in-module orig_prompt_attention = shared.opts.prompt_attention shared.opts.data['prompt_attention'] = 'fixed' diff --git a/scripts/freescale_ext.py b/scripts/freescale_ext.py index f321ab581..4b7d916bb 100644 --- a/scripts/freescale_ext.py +++ b/scripts/freescale_ext.py @@ -58,7 +58,7 @@ class Script(scripts_manager.Script): shared.log.warning('FreeScale: missing input image') return None - from scripts.freescale import StableDiffusionXLFreeScale, StableDiffusionXLFreeScaleImg2Img + from scripts.freescale import StableDiffusionXLFreeScale, StableDiffusionXLFreeScaleImg2Img # pylint: disable=no-name-in-module self.orig_pipe = shared.sd_model self.orig_slice = shared.opts.diffusers_vae_slicing self.orig_tile = shared.opts.diffusers_vae_tiling diff --git a/scripts/infiniteyou_ext.py b/scripts/infiniteyou_ext.py index 677d56cca..8e3ab5a8a 100644 --- a/scripts/infiniteyou_ext.py +++ b/scripts/infiniteyou_ext.py @@ -19,7 +19,7 @@ def verify_insightface(): def load_infiniteyou(model: str): - from scripts.infiniteyou import InfUFluxPipeline + from scripts.infiniteyou import InfUFluxPipeline # pylint: disable=no-name-in-module shared.sd_model = InfUFluxPipeline( pipe=shared.sd_model, model_version=model, diff --git a/scripts/layerdiffuse/layerdiffuse_loader.py b/scripts/layerdiffuse/layerdiffuse_loader.py index 4c029f4d5..dc4b97ffe 100644 --- a/scripts/layerdiffuse/layerdiffuse_loader.py +++ b/scripts/layerdiffuse/layerdiffuse_loader.py @@ -1,5 +1,5 @@ from safetensors.torch import load_file -from scripts.layerdiffuse.layerdiffuse_model import LoraLoader, AttentionSharingProcessor +from scripts.layerdiffuse.layerdiffuse_model import LoraLoader, AttentionSharingProcessor # pylint: disable=no-name-in-module def merge_delta_weights_into_unet(pipe, delta_weights): diff --git a/scripts/lbm_ext.py b/scripts/lbm_ext.py index 3c268fb26..d08a72b25 100644 --- a/scripts/lbm_ext.py +++ b/scripts/lbm_ext.py @@ -65,7 +65,7 @@ class Script(scripts_manager.Script): if repo_id is not None: import huggingface_hub as hf repo_file = hf.snapshot_download(repo_id, cache_dir=shared.opts.hfcache_dir) - from scripts.lbm import get_model + from scripts.lbm import get_model # pylint: disable=no-name-in-module model = get_model( repo_file, save_dir=None, @@ -85,7 +85,7 @@ class Script(scripts_manager.Script): install('lpips') from torchvision.transforms import ToPILImage, ToTensor - from scripts.lbm import get_model, extract_object, resize_and_center_crop + from scripts.lbm import get_model, extract_object, resize_and_center_crop # pylint: disable=no-name-in-module ori_h_bg, ori_w_bg = fg_image.size ar_bg = ori_h_bg / ori_w_bg diff --git a/scripts/pixelsmith_ext.py b/scripts/pixelsmith_ext.py index 565946d11..da9686e7b 100644 --- a/scripts/pixelsmith_ext.py +++ b/scripts/pixelsmith_ext.py @@ -49,7 +49,7 @@ class Script(scripts_manager.Script): supported_model_list = ['sdxl'] if shared.sd_model_type not in supported_model_list: shared.log.warning(f'PixelSmith: class={shared.sd_model.__class__.__name__} model={shared.sd_model_type} required={supported_model_list}') - from scripts.pixelsmith import PixelSmithXLPipeline, PixelSmithVAE + from scripts.pixelsmith import PixelSmithXLPipeline, PixelSmithVAE # pylint: disable=no-name-in-module self.orig_pipe = shared.sd_model self.orig_vae = shared.sd_model.vae if self.vae is None: diff --git a/scripts/pulid/pulid_sdxl.py b/scripts/pulid/pulid_sdxl.py index e964e58fe..821622a2a 100644 --- a/scripts/pulid/pulid_sdxl.py +++ b/scripts/pulid/pulid_sdxl.py @@ -123,7 +123,7 @@ class StableDiffusionXLPuLIDPipeline: if sampler is not None: self.sampler = sampler else: - from scripts.pulid import sampling + from scripts.pulid import sampling # pylint: disable=no-name-in-module self.sampler = sampling.sample_dpmpp_sde @property diff --git a/scripts/xadapter_ext.py b/scripts/xadapter_ext.py index 4afb46575..0f244babd 100644 --- a/scripts/xadapter_ext.py +++ b/scripts/xadapter_ext.py @@ -34,11 +34,11 @@ class Script(scripts_manager.Script): return model, sampler, width, height, start, scale, lora def run(self, p: processing.StableDiffusionProcessing, model, sampler, width, height, start, scale, lora): # pylint: disable=arguments-differ, unused-argument - from scripts.xadapter.xadapter_hijacks import PositionNet + from scripts.xadapter.xadapter_hijacks import PositionNet # pylint: disable=no-name-in-module diffusers.models.embeddings.PositionNet = PositionNet # patch diffusers==0.26 from diffusers==0.20 - from scripts.xadapter.adapter import Adapter_XL - from scripts.xadapter.pipeline_sd_xl_adapter import StableDiffusionXLAdapterPipeline - from scripts.xadapter.unet_adapter import UNet2DConditionModel as UNet2DConditionModelAdapter + from scripts.xadapter.adapter import Adapter_XL # pylint: disable=no-name-in-module + from scripts.xadapter.pipeline_sd_xl_adapter import StableDiffusionXLAdapterPipeline # pylint: disable=no-name-in-module + from scripts.xadapter.unet_adapter import UNet2DConditionModel as UNet2DConditionModelAdapter # pylint: disable=no-name-in-module global adapter # pylint: disable=global-statement if model == 'None': diff --git a/wiki b/wiki index a678731c5..da7620df1 160000 --- a/wiki +++ b/wiki @@ -1 +1 @@ -Subproject commit a678731c5da5d77f7c21edf7f1d62d8307d0d7fe +Subproject commit da7620df144de8d2af259eff2b7a4522783f38cc