diff --git a/denoiser.hpp b/denoiser.hpp index 98aef702d..7e99b84a8 100644 --- a/denoiser.hpp +++ b/denoiser.hpp @@ -1,6 +1,8 @@ #ifndef __DENOISER_HPP__ #define __DENOISER_HPP__ +#include + #include "ggml_extend.hpp" #include "gits_noise.inl" @@ -351,6 +353,95 @@ struct SmoothStepScheduler : SigmaScheduler { } }; +struct BongTangentScheduler : SigmaScheduler { + static constexpr float kPi = 3.14159265358979323846f; + + static std::vector get_bong_tangent_sigmas(int steps, float slope, float pivot, float start, float end) { + std::vector sigmas; + if (steps <= 0) { + return sigmas; + } + + float smax = ((2.0f / kPi) * atanf(-slope * (0.0f - pivot)) + 1.0f) * 0.5f; + float smin = ((2.0f / kPi) * atanf(-slope * ((float)(steps - 1) - pivot)) + 1.0f) * 0.5f; + float srange = smax - smin; + float sscale = start - end; + + sigmas.reserve(steps); + + if (fabsf(srange) < 1e-8f) { + if (steps == 1) { + sigmas.push_back(start); + return sigmas; + } + for (int i = 0; i < steps; ++i) { + float t = (float)i / (float)(steps - 1); + sigmas.push_back(start + (end - start) * t); + } + return sigmas; + } + + float inv_srange = 1.0f / srange; + for (int x = 0; x < steps; ++x) { + float v = ((2.0f / kPi) * atanf(-slope * ((float)x - pivot)) + 1.0f) * 0.5f; + float sigma = ((v - smin) * inv_srange) * sscale + end; + sigmas.push_back(sigma); + } + + return sigmas; + } + + std::vector get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t /*t_to_sigma*/) override { + std::vector result; + if (n == 0) { + return result; + } + + float start = sigma_max; + float end = sigma_min; + float middle = sigma_min + (sigma_max - sigma_min) * 0.5f; + + float pivot_1 = 0.6f; + float pivot_2 = 0.6f; + float slope_1 = 0.2f; + float slope_2 = 0.2f; + + int steps = static_cast(n) + 2; + int midpoint = static_cast(((float)steps * pivot_1 + (float)steps * pivot_2) * 0.5f); + int pivot_1_i = static_cast((float)steps * pivot_1); + int pivot_2_i = static_cast((float)steps * pivot_2); + + float slope_scale = (float)steps / 40.0f; + slope_1 = slope_1 / slope_scale; + slope_2 = slope_2 / slope_scale; + + int stage_2_len = steps - midpoint; + int stage_1_len = steps - stage_2_len; + + std::vector sigmas_1 = get_bong_tangent_sigmas(stage_1_len, slope_1, (float)pivot_1_i, start, middle); + std::vector sigmas_2 = get_bong_tangent_sigmas(stage_2_len, slope_2, (float)(pivot_2_i - stage_1_len), middle, end); + + if (!sigmas_1.empty()) { + sigmas_1.pop_back(); + } + + result.reserve(n + 1); + result.insert(result.end(), sigmas_1.begin(), sigmas_1.end()); + result.insert(result.end(), sigmas_2.begin(), sigmas_2.end()); + + if (result.size() < n + 1) { + while (result.size() < n + 1) { + result.push_back(end); + } + } else if (result.size() > n + 1) { + result.resize(n + 1); + } + + result[n] = 0.0f; + return result; + } +}; + struct KLOptimalScheduler : SigmaScheduler { std::vector get_sigmas(uint32_t n, float sigma_min, float sigma_max, t_to_sigma_t t_to_sigma) override { std::vector sigmas; @@ -431,6 +522,10 @@ struct Denoiser { LOG_INFO("get_sigmas with SmoothStep scheduler"); scheduler = std::make_shared(); break; + case BONG_TANGENT_SCHEDULER: + LOG_INFO("get_sigmas with bong_tangent scheduler"); + scheduler = std::make_shared(); + break; case KL_OPTIMAL_SCHEDULER: LOG_INFO("get_sigmas with KL Optimal scheduler"); scheduler = std::make_shared(); @@ -1634,6 +1729,216 @@ static bool sample_k_diffusion(sample_method_t method, } } } break; + case RES_MULTISTEP_SAMPLE_METHOD: // Res Multistep sampler + { + struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x); + struct ggml_tensor* old_denoised = ggml_dup_tensor(work_ctx, x); + + bool have_old_sigma = false; + float old_sigma_down = 0.0f; + + auto t_fn = [](float sigma) -> float { return -logf(sigma); }; + auto sigma_fn = [](float t) -> float { return expf(-t); }; + auto phi1_fn = [](float t) -> float { + if (fabsf(t) < 1e-6f) { + return 1.0f + t * 0.5f + (t * t) / 6.0f; + } + return (expf(t) - 1.0f) / t; + }; + auto phi2_fn = [&](float t) -> float { + if (fabsf(t) < 1e-6f) { + return 0.5f + t / 6.0f + (t * t) / 24.0f; + } + float phi1_val = phi1_fn(t); + return (phi1_val - 1.0f) / t; + }; + + for (int i = 0; i < steps; i++) { + ggml_tensor* denoised = model(x, sigmas[i], i + 1); + if (denoised == nullptr) { + return false; + } + + float sigma_from = sigmas[i]; + float sigma_to = sigmas[i + 1]; + float sigma_up = 0.0f; + float sigma_down = sigma_to; + + if (eta > 0.0f) { + float sigma_from_sq = sigma_from * sigma_from; + float sigma_to_sq = sigma_to * sigma_to; + if (sigma_from_sq > 0.0f) { + float term = sigma_to_sq * (sigma_from_sq - sigma_to_sq) / sigma_from_sq; + if (term > 0.0f) { + sigma_up = eta * std::sqrt(term); + } + } + sigma_up = std::min(sigma_up, sigma_to); + float sigma_down_sq = sigma_to_sq - sigma_up * sigma_up; + sigma_down = sigma_down_sq > 0.0f ? std::sqrt(sigma_down_sq) : 0.0f; + } + + if (sigma_down == 0.0f || !have_old_sigma) { + float dt = sigma_down - sigma_from; + float* vec_x = (float*)x->data; + float* vec_denoised = (float*)denoised->data; + + for (int j = 0; j < ggml_nelements(x); j++) { + float d = (vec_x[j] - vec_denoised[j]) / sigma_from; + vec_x[j] = vec_x[j] + d * dt; + } + } else { + float t = t_fn(sigma_from); + float t_old = t_fn(old_sigma_down); + float t_next = t_fn(sigma_down); + float t_prev = t_fn(sigmas[i - 1]); + float h = t_next - t; + float c2 = (t_prev - t_old) / h; + + float phi1_val = phi1_fn(-h); + float phi2_val = phi2_fn(-h); + float b1 = phi1_val - phi2_val / c2; + float b2 = phi2_val / c2; + + if (!std::isfinite(b1)) { + b1 = 0.0f; + } + if (!std::isfinite(b2)) { + b2 = 0.0f; + } + + float sigma_h = sigma_fn(h); + float* vec_x = (float*)x->data; + float* vec_denoised = (float*)denoised->data; + float* vec_old_denoised = (float*)old_denoised->data; + + for (int j = 0; j < ggml_nelements(x); j++) { + vec_x[j] = sigma_h * vec_x[j] + h * (b1 * vec_denoised[j] + b2 * vec_old_denoised[j]); + } + } + + if (sigmas[i + 1] > 0 && sigma_up > 0.0f) { + ggml_ext_im_set_randn_f32(noise, rng); + float* vec_x = (float*)x->data; + float* vec_noise = (float*)noise->data; + + for (int j = 0; j < ggml_nelements(x); j++) { + vec_x[j] = vec_x[j] + vec_noise[j] * sigma_up; + } + } + + float* vec_old_denoised = (float*)old_denoised->data; + float* vec_denoised = (float*)denoised->data; + for (int j = 0; j < ggml_nelements(x); j++) { + vec_old_denoised[j] = vec_denoised[j]; + } + + old_sigma_down = sigma_down; + have_old_sigma = true; + } + } break; + case RES_2S_SAMPLE_METHOD: // Res 2s sampler + { + struct ggml_tensor* noise = ggml_dup_tensor(work_ctx, x); + struct ggml_tensor* x0 = ggml_dup_tensor(work_ctx, x); + struct ggml_tensor* x2 = ggml_dup_tensor(work_ctx, x); + + const float c2 = 0.5f; + auto t_fn = [](float sigma) -> float { return -logf(sigma); }; + auto phi1_fn = [](float t) -> float { + if (fabsf(t) < 1e-6f) { + return 1.0f + t * 0.5f + (t * t) / 6.0f; + } + return (expf(t) - 1.0f) / t; + }; + auto phi2_fn = [&](float t) -> float { + if (fabsf(t) < 1e-6f) { + return 0.5f + t / 6.0f + (t * t) / 24.0f; + } + float phi1_val = phi1_fn(t); + return (phi1_val - 1.0f) / t; + }; + + for (int i = 0; i < steps; i++) { + float sigma_from = sigmas[i]; + float sigma_to = sigmas[i + 1]; + + ggml_tensor* denoised = model(x, sigma_from, -(i + 1)); + if (denoised == nullptr) { + return false; + } + + float sigma_up = 0.0f; + float sigma_down = sigma_to; + if (eta > 0.0f) { + float sigma_from_sq = sigma_from * sigma_from; + float sigma_to_sq = sigma_to * sigma_to; + if (sigma_from_sq > 0.0f) { + float term = sigma_to_sq * (sigma_from_sq - sigma_to_sq) / sigma_from_sq; + if (term > 0.0f) { + sigma_up = eta * std::sqrt(term); + } + } + sigma_up = std::min(sigma_up, sigma_to); + float sigma_down_sq = sigma_to_sq - sigma_up * sigma_up; + sigma_down = sigma_down_sq > 0.0f ? std::sqrt(sigma_down_sq) : 0.0f; + } + + float* vec_x = (float*)x->data; + float* vec_x0 = (float*)x0->data; + for (int j = 0; j < ggml_nelements(x); j++) { + vec_x0[j] = vec_x[j]; + } + + if (sigma_down == 0.0f || sigma_from == 0.0f) { + float* vec_denoised = (float*)denoised->data; + for (int j = 0; j < ggml_nelements(x); j++) { + vec_x[j] = vec_denoised[j]; + } + } else { + float t = t_fn(sigma_from); + float t_next = t_fn(sigma_down); + float h = t_next - t; + + float a21 = c2 * phi1_fn(-h * c2); + float phi1_val = phi1_fn(-h); + float phi2_val = phi2_fn(-h); + float b2 = phi2_val / c2; + float b1 = phi1_val - b2; + + float sigma_c2 = expf(-(t + h * c2)); + + float* vec_denoised = (float*)denoised->data; + float* vec_x2 = (float*)x2->data; + for (int j = 0; j < ggml_nelements(x); j++) { + float eps1 = vec_denoised[j] - vec_x0[j]; + vec_x2[j] = vec_x0[j] + h * a21 * eps1; + } + + ggml_tensor* denoised2 = model(x2, sigma_c2, i + 1); + if (denoised2 == nullptr) { + return false; + } + float* vec_denoised2 = (float*)denoised2->data; + + for (int j = 0; j < ggml_nelements(x); j++) { + float eps1 = vec_denoised[j] - vec_x0[j]; + float eps2 = vec_denoised2[j] - vec_x0[j]; + vec_x[j] = vec_x0[j] + h * (b1 * eps1 + b2 * eps2); + } + } + + if (sigmas[i + 1] > 0 && sigma_up > 0.0f) { + ggml_ext_im_set_randn_f32(noise, rng); + float* vec_x = (float*)x->data; + float* vec_noise = (float*)noise->data; + + for (int j = 0; j < ggml_nelements(x); j++) { + vec_x[j] = vec_x[j] + vec_noise[j] * sigma_up; + } + } + } + } break; default: LOG_ERROR("Attempting to sample with nonexisting sample method %i", method); diff --git a/examples/cli/README.md b/examples/cli/README.md index 84dd5c716..d6c36b8b0 100644 --- a/examples/cli/README.md +++ b/examples/cli/README.md @@ -107,14 +107,14 @@ Generation Options: medium --skip-layer-start SLG enabling point (default: 0.01) --skip-layer-end SLG disabling point (default: 0.2) - --eta eta in DDIM, only for DDIM and TCD (default: 0) + --eta eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0) --high-noise-cfg-scale (high noise) unconditional guidance scale: (default: 7.0) --high-noise-img-cfg-scale (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale) --high-noise-guidance (high noise) distilled guidance scale for models with guidance input (default: 3.5) --high-noise-slg-scale (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0) --high-noise-skip-layer-start (high noise) SLG enabling point (default: 0.01) --high-noise-skip-layer-end (high noise) SLG disabling point (default: 0.2) - --high-noise-eta (high noise) eta in DDIM, only for DDIM and TCD (default: 0) + --high-noise-eta (high noise) eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0) --strength strength for noising/unnoising (default: 0.75) --pm-style-strength --control-strength strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image @@ -123,12 +123,12 @@ Generation Options: --increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1). --disable-auto-resize-ref-image disable auto resize of ref images -s, --seed RNG seed (default: 42, use random seed for < 0) - --sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, - tcd] (default: euler for Flux/SD3/Wan, euler_a otherwise) - --high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, - ddim_trailing, tcd] default: euler for Flux/SD3/Wan, euler_a otherwise + --sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, + res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a otherwise) + --high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, + tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan, euler_a otherwise --scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple, - kl_optimal, lcm], default: discrete + kl_optimal, lcm, bong_tangent], default: discrete --sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0"). --skip-layers layers to skip for SLG steps (default: [7,8,9]) --high-noise-skip-layers (high noise) layers to skip for SLG steps (default: [7,8,9]) diff --git a/examples/common/common.hpp b/examples/common/common.hpp index ba1b0d8d9..11d30a3b5 100644 --- a/examples/common/common.hpp +++ b/examples/common/common.hpp @@ -1478,17 +1478,17 @@ struct SDGenerationParams { on_seed_arg}, {"", "--sampling-method", - "sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd] " + "sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s] " "(default: euler for Flux/SD3/Wan, euler_a otherwise)", on_sample_method_arg}, {"", "--high-noise-sampling-method", - "(high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd]" + "(high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s]" " default: euler for Flux/SD3/Wan, euler_a otherwise", on_high_noise_sample_method_arg}, {"", "--scheduler", - "denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple, kl_optimal, lcm], default: discrete", + "denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple, kl_optimal, lcm, bong_tangent], default: discrete", on_scheduler_arg}, {"", "--sigmas", diff --git a/examples/server/README.md b/examples/server/README.md index 7e6681570..354075cc4 100644 --- a/examples/server/README.md +++ b/examples/server/README.md @@ -99,14 +99,14 @@ Default Generation Options: medium --skip-layer-start SLG enabling point (default: 0.01) --skip-layer-end SLG disabling point (default: 0.2) - --eta eta in DDIM, only for DDIM and TCD (default: 0) + --eta eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0) --high-noise-cfg-scale (high noise) unconditional guidance scale: (default: 7.0) --high-noise-img-cfg-scale (high noise) image guidance scale for inpaint or instruct-pix2pix models (default: same as --cfg-scale) --high-noise-guidance (high noise) distilled guidance scale for models with guidance input (default: 3.5) --high-noise-slg-scale (high noise) skip layer guidance (SLG) scale, only for DiT models: (default: 0) --high-noise-skip-layer-start (high noise) SLG enabling point (default: 0.01) --high-noise-skip-layer-end (high noise) SLG disabling point (default: 0.2) - --high-noise-eta (high noise) eta in DDIM, only for DDIM and TCD (default: 0) + --high-noise-eta (high noise) eta in DDIM, only for DDIM/TCD/res_multistep/res_2s (default: 0) --strength strength for noising/unnoising (default: 0.75) --pm-style-strength --control-strength strength to apply Control Net (default: 0.9). 1.0 corresponds to full destruction of information in init image @@ -115,12 +115,12 @@ Default Generation Options: --increase-ref-index automatically increase the indices of references images based on the order they are listed (starting with 1). --disable-auto-resize-ref-image disable auto resize of ref images -s, --seed RNG seed (default: 42, use random seed for < 0) - --sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, - tcd] (default: euler for Flux/SD3/Wan, euler_a otherwise) - --high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, - ddim_trailing, tcd] default: euler for Flux/SD3/Wan, euler_a otherwise + --sampling-method sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, tcd, + res_multistep, res_2s] (default: euler for Flux/SD3/Wan, euler_a otherwise) + --high-noise-sampling-method (high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, ipndm, ipndm_v, lcm, ddim_trailing, + tcd, res_multistep, res_2s] default: euler for Flux/SD3/Wan, euler_a otherwise --scheduler denoiser sigma scheduler, one of [discrete, karras, exponential, ays, gits, smoothstep, sgm_uniform, simple, - kl_optimal, lcm], default: discrete + kl_optimal, lcm, bong_tangent], default: discrete --sigmas custom sigma values for the sampler, comma-separated (e.g., "14.61,7.8,3.5,0.0"). --skip-layers layers to skip for SLG steps (default: [7,8,9]) --high-noise-skip-layers (high noise) layers to skip for SLG steps (default: [7,8,9]) diff --git a/examples/server/main.cpp b/examples/server/main.cpp index 76199ac69..def499755 100644 --- a/examples/server/main.cpp +++ b/examples/server/main.cpp @@ -785,7 +785,11 @@ int main(int argc, const char** argv) { {"lcm", LCM_SAMPLE_METHOD}, {"ddim", DDIM_TRAILING_SAMPLE_METHOD}, {"dpm++ 2m", DPMPP2M_SAMPLE_METHOD}, - {"k_dpmpp_2m", DPMPP2M_SAMPLE_METHOD}}; + {"k_dpmpp_2m", DPMPP2M_SAMPLE_METHOD}, + {"res multistep", RES_MULTISTEP_SAMPLE_METHOD}, + {"k_res_multistep", RES_MULTISTEP_SAMPLE_METHOD}, + {"res 2s", RES_2S_SAMPLE_METHOD}, + {"k_res_2s", RES_2S_SAMPLE_METHOD}}; auto it = hardcoded.find(name); if (it != hardcoded.end()) return it->second; return SAMPLE_METHOD_COUNT; diff --git a/stable-diffusion.cpp b/stable-diffusion.cpp index b181f994b..44412edc8 100644 --- a/stable-diffusion.cpp +++ b/stable-diffusion.cpp @@ -67,6 +67,8 @@ const char* sampling_methods_str[] = { "LCM", "DDIM \"trailing\"", "TCD", + "Res Multistep", + "Res 2s", }; /*================================================== Helper Functions ================================================*/ @@ -2743,6 +2745,8 @@ const char* sample_method_to_str[] = { "lcm", "ddim_trailing", "tcd", + "res_multistep", + "res_2s", }; const char* sd_sample_method_name(enum sample_method_t sample_method) { @@ -2772,6 +2776,7 @@ const char* scheduler_to_str[] = { "smoothstep", "kl_optimal", "lcm", + "bong_tangent", }; const char* sd_scheduler_name(enum scheduler_t scheduler) { diff --git a/stable-diffusion.h b/stable-diffusion.h index 8f040d2bd..85768f405 100644 --- a/stable-diffusion.h +++ b/stable-diffusion.h @@ -48,6 +48,8 @@ enum sample_method_t { LCM_SAMPLE_METHOD, DDIM_TRAILING_SAMPLE_METHOD, TCD_SAMPLE_METHOD, + RES_MULTISTEP_SAMPLE_METHOD, + RES_2S_SAMPLE_METHOD, SAMPLE_METHOD_COUNT }; @@ -62,6 +64,7 @@ enum scheduler_t { SMOOTHSTEP_SCHEDULER, KL_OPTIMAL_SCHEDULER, LCM_SCHEDULER, + BONG_TANGENT_SCHEDULER, SCHEDULER_COUNT };