Upload adept_scheduler_pack.py
Browse files
adept-sampler-v6/scripts/adept_scheduler_pack.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Adept Scheduler Pack for Automatic1111 WebUI
|
| 3 |
+
Registers Adept Sampler's scheduler algorithms into A1111's native
|
| 4 |
+
"Schedule type" dropdown (the one next to "Sampling method"), so they
|
| 5 |
+
become usable with ANY sampler -- not only when the Adept Sampler script
|
| 6 |
+
itself is active via its own "Scheduler Type" dropdown.
|
| 7 |
+
|
| 8 |
+
Companion file to adept_sampler_v5.py: it must be installed in the same
|
| 9 |
+
scripts/ folder. This file reads Adept's scheduler functions directly off
|
| 10 |
+
the loaded adept_sampler_v5 module (no duplicated formulas), so any future
|
| 11 |
+
tweak to those functions is picked up automatically -- nothing here needs
|
| 12 |
+
updating when Adept's own schedulers change.
|
| 13 |
+
|
| 14 |
+
If adept_sampler_v5.py isn't found (not installed, or failed to load for
|
| 15 |
+
some other reason), this file logs a message and does nothing further; it
|
| 16 |
+
never raises, so a missing companion file can't break WebUI startup.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import os
|
| 20 |
+
import torch
|
| 21 |
+
from modules import scripts, script_callbacks
|
| 22 |
+
import modules.sd_schedulers as sd_schedulers
|
| 23 |
+
|
| 24 |
+
_ADEPT_SCRIPT_FILENAME = "adept_sampler_v5.py"
|
| 25 |
+
|
| 26 |
+
# (label shown in the Schedule-type dropdown, internal name, function name on
|
| 27 |
+
# the adept_sampler_v5 module). Mirrors apply_custom_scheduler's own
|
| 28 |
+
# scheduler_map in adept_sampler_v5.py exactly -- kept as literal strings
|
| 29 |
+
# (not imported) so this file has no hard import-time dependency on that
|
| 30 |
+
# module actually existing.
|
| 31 |
+
_ADEPT_SCHEDULER_SPECS = [
|
| 32 |
+
("Adept: AOS-V", "adept_aos_v", "create_aos_v_sigmas"),
|
| 33 |
+
("Adept: AOS-Epsilon", "adept_aos_epsilon", "create_aos_e_sigmas"),
|
| 34 |
+
("Adept: AkashicAOS", "adept_akashic_aos", "create_aos_akashic_sigmas"),
|
| 35 |
+
("Adept: Entropic", "adept_entropic", "create_entropic_sigmas"),
|
| 36 |
+
("Adept: SNR-Optimized", "adept_snr_optimized", "create_snr_optimized_sigmas"),
|
| 37 |
+
("Adept: Constant-Rate", "adept_constant_rate", "create_constant_rate_sigmas"),
|
| 38 |
+
("Adept: Adaptive-Optimized", "adept_adaptive_optimized", "create_adaptive_optimized_sigmas"),
|
| 39 |
+
("Adept: Cosine-Annealed", "adept_cosine_annealed", "create_cosine_sigmas"),
|
| 40 |
+
("Adept: LogSNR-Uniform", "adept_logsnr_uniform", "create_logsnr_uniform_sigmas"),
|
| 41 |
+
("Adept: Tanh Mid-Boost", "adept_tanh_midboost", "create_tanh_midboost_sigmas"),
|
| 42 |
+
("Adept: Exponential Tail", "adept_exponential_tail", "create_exponential_tail_sigmas"),
|
| 43 |
+
("Adept: Jittered-Karras", "adept_jittered_karras", "create_jittered_karras_sigmas"),
|
| 44 |
+
("Adept: Stochastic", "adept_stochastic", "create_stochastic_sigmas"),
|
| 45 |
+
("Adept: JYS (Dynamic)", "adept_jys", "create_jys_sigmas"),
|
| 46 |
+
("Adept: Hybrid JYS-Karras", "adept_hybrid_jys_karras", "create_hybrid_jys_karras_sigmas"),
|
| 47 |
+
("Adept: AYS-SDXL", "adept_ays_sdxl", "create_ays_sdxl_sigmas"),
|
| 48 |
+
("Adept: AkashicAOS Alt", "adept_akashic_aos_alt", "create_aos_akashic_alt_sigmas"),
|
| 49 |
+
("Adept: AkashicEQFlow", "adept_akashic_eqflow", "create_akashic_eqflow_sigmas"),
|
| 50 |
+
]
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def _find_adept_module():
|
| 54 |
+
"""Locate the already-loaded adept_sampler_v5 module via A1111's own
|
| 55 |
+
script registry (scripts.scripts_data), the same technique Adept itself
|
| 56 |
+
uses to find xyz_grid.py. Deferred to on_before_ui time so load order
|
| 57 |
+
between the two files never matters -- every script's top-level code
|
| 58 |
+
has already run by the time on_before_ui callbacks fire."""
|
| 59 |
+
for data in scripts.scripts_data:
|
| 60 |
+
if os.path.basename(data.path) == _ADEPT_SCRIPT_FILENAME:
|
| 61 |
+
return data.module
|
| 62 |
+
return None
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def _make_adapter(adept_fn, label):
|
| 66 |
+
"""
|
| 67 |
+
Wrap one of Adept's create_*_sigmas(sigma_max, sigma_min, num_steps,
|
| 68 |
+
device, ...) functions to match A1111's native scheduler signature:
|
| 69 |
+
function(n, sigma_min, sigma_max, device, **extra). A1111 calls this
|
| 70 |
+
with keyword arguments (n=steps, sigma_min=..., sigma_max=...,
|
| 71 |
+
device=...), never positionally, so only the parameter *names* need to
|
| 72 |
+
line up -- the underlying function's extra optional kwargs (Entropic's
|
| 73 |
+
`power`, Stochastic's `noise_type`, etc.) keep their own defaults since
|
| 74 |
+
A1111 has no UI for them and won't pass them.
|
| 75 |
+
"""
|
| 76 |
+
def adapter(n, sigma_min, sigma_max, device='cpu', **_ignored):
|
| 77 |
+
try:
|
| 78 |
+
# A1111's native scheduler API passes sigma_min/sigma_max as
|
| 79 |
+
# plain Python floats (via .item()); Adept's own internal call
|
| 80 |
+
# path (apply_custom_scheduler) always passes tensor elements
|
| 81 |
+
# instead. A few of Adept's scheduler functions call torch.log()
|
| 82 |
+
# directly on these arguments, which requires a tensor -- so we
|
| 83 |
+
# convert here, at the compatibility boundary, rather than
|
| 84 |
+
# touching the already-shipped, tested adept_sampler_v5.py.
|
| 85 |
+
sigma_min_t = torch.as_tensor(sigma_min, dtype=torch.float32, device=device)
|
| 86 |
+
sigma_max_t = torch.as_tensor(sigma_max, dtype=torch.float32, device=device)
|
| 87 |
+
result = adept_fn(sigma_max_t, sigma_min_t, n, device=device)
|
| 88 |
+
if result is None or len(result) != n + 1:
|
| 89 |
+
raise ValueError(f"unexpected shape from {label}")
|
| 90 |
+
if torch.isnan(result).any() or torch.isinf(result).any():
|
| 91 |
+
raise ValueError(f"NaN/Inf from {label}")
|
| 92 |
+
return result
|
| 93 |
+
except Exception as e:
|
| 94 |
+
print(f"⚠️ Adept Scheduler Pack: '{label}' failed ({e}), falling back to Karras")
|
| 95 |
+
import k_diffusion.sampling as k_diff
|
| 96 |
+
return k_diff.get_sigmas_karras(n, sigma_min, sigma_max, device=device)
|
| 97 |
+
return adapter
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def register_adept_schedulers():
|
| 101 |
+
adept_mod = _find_adept_module()
|
| 102 |
+
if adept_mod is None:
|
| 103 |
+
print("⚠️ Adept Scheduler Pack: adept_sampler_v5.py not found -- "
|
| 104 |
+
"install it alongside this file to enable Adept schedulers "
|
| 105 |
+
"in the native Schedule type dropdown.")
|
| 106 |
+
return
|
| 107 |
+
|
| 108 |
+
# Dedup guard: safe to call more than once (e.g. UI reload).
|
| 109 |
+
if any(getattr(s, "name", "").startswith("adept_") for s in sd_schedulers.schedulers):
|
| 110 |
+
return
|
| 111 |
+
|
| 112 |
+
registered = 0
|
| 113 |
+
for label, name, fn_name in _ADEPT_SCHEDULER_SPECS:
|
| 114 |
+
adept_fn = getattr(adept_mod, fn_name, None)
|
| 115 |
+
if adept_fn is None:
|
| 116 |
+
print(f"⚠️ Adept Scheduler Pack: {fn_name} not found on adept_sampler_v5 "
|
| 117 |
+
f"(version mismatch?), skipping '{label}'")
|
| 118 |
+
continue
|
| 119 |
+
|
| 120 |
+
sched = sd_schedulers.Scheduler(
|
| 121 |
+
name=name,
|
| 122 |
+
label=label,
|
| 123 |
+
function=_make_adapter(adept_fn, label),
|
| 124 |
+
default_rho=-1,
|
| 125 |
+
need_inner_model=False,
|
| 126 |
+
)
|
| 127 |
+
sd_schedulers.schedulers.append(sched)
|
| 128 |
+
# schedulers_map is built once (as a dict comprehension) right after
|
| 129 |
+
# the schedulers list at sd_schedulers.py's own import time -- it
|
| 130 |
+
# does NOT update itself when the list is appended to afterwards.
|
| 131 |
+
# Both the name and label must be added, matching how the map is
|
| 132 |
+
# originally built, or lookups by either key at generation time
|
| 133 |
+
# would fail to find these newly-registered entries.
|
| 134 |
+
sd_schedulers.schedulers_map[sched.name] = sched
|
| 135 |
+
sd_schedulers.schedulers_map[sched.label] = sched
|
| 136 |
+
registered += 1
|
| 137 |
+
|
| 138 |
+
print(f"✅ Adept Scheduler Pack: registered {registered}/{len(_ADEPT_SCHEDULER_SPECS)} "
|
| 139 |
+
f"schedulers into the native Schedule type dropdown")
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def on_before_ui():
|
| 143 |
+
try:
|
| 144 |
+
register_adept_schedulers()
|
| 145 |
+
except Exception:
|
| 146 |
+
import traceback
|
| 147 |
+
print(f"⚠️ Adept Scheduler Pack: registration failed:\n{traceback.format_exc()}")
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
script_callbacks.on_before_ui(on_before_ui)
|