Update Supreme Sampler to v1.2

Add new step methods
Add noise modulation parameter (default of 2.0 on Intensity mode)
Change how enhancements are applied based on step and substep order.
Allow for user-chosen substep method
Add dynamic step methods for Runge-Kutta. 'Dynamic' will choose from a select amount of existing methods.*
* Error values were derived from heuristically-driven testing, may change in the future.
This commit is contained in:
Clybius
2024-04-03 12:07:32 -05:00
parent b8921af0b9
commit ebbbbc4d25
2 changed files with 279 additions and 30 deletions
+270 -27
View File
@@ -795,7 +795,7 @@ def sample_dpmpp_3m_sde_dynamic_eta(model, x, sigmas, extra_args=None, callback=
return sampler_dpmpp_3m_sde_dynamic_eta(model, x, sigmas, extra_args=extra_args, callback=callback, disable=disable, eta_max=eta_max, eta_min=eta_min, s_noise=s_noise, noise_sampler=noise_sampler or get_noise_sampler(x, sigmas, noise_sampler_type, noise_sampler, extra_args))
@torch.no_grad()
def sampler_supreme(model, x, sigmas, extra_args=None, callback=None, disable=None, s_noise=1., noise_sampler=None, eta=1.0, step_method="euler", centralization=0.02, normalization=0.01, edge_enhancement=0.05, perphist=0, substeps=2):
def sampler_supreme(model, x, sigmas, extra_args=None, callback=None, disable=None, s_noise=1., noise_sampler=None, eta=1.0, step_method="euler", substep_method="euler", centralization=0.05, normalization=0.05, edge_enhancement=0.25, perphist=0.5, substeps=2, noise_modulation="intensity", modulation_strength=2.0):
"""
Supreme Sampler, Euler steps. Based on no paper, purely interesting thoughts.
@@ -814,26 +814,31 @@ def sampler_supreme(model, x, sigmas, extra_args=None, callback=None, disable=No
edge_enhancement: Multiplies the edges by the mean using a laplacian kernel
perphist: Adds previous denoised variable to the current denoised using perpendicular vector projection
substeps: Amount of times to iterate over each step and average the results
noise_modulation: Method of changing the noise based on situations within the sampler
modulation_strength: Strength of the modulation using a weighted sum between the modulation and noise sampler's noise.
"""
extra_args = {} if extra_args is None else extra_args
noise_sampler = default_noise_sampler(x) if noise_sampler is None else noise_sampler
s_in = x.new_ones([x.shape[0]])
orig_cond_scale = extra_args["cond_scale"] if "cond_scale" in extra_args else None
modified_cond_scale = extra_args["cond_scale"] if "cond_scale" in extra_args else None
# Centralization
def centralize(denoised_sample, centralization):
def centralize(denoised_sample, centralization, iteration):
for b in range(len(denoised_sample)):
for c in range(len(denoised_sample[b])):
channel = denoised_sample[b][c]
denoised_sample[b][c] -= channel.mean() * centralization
denoised_sample[b][c] -= channel.mean() * centralization * (sigmas[iteration] ** 0.5)
return denoised_sample
# Normalization
def normalize(denoised_sample, normalization):
def normalize(denoised_sample, normalization, iteration):
for b in range(len(denoised_sample)):
for c in range(len(denoised_sample[b])):
channel = denoised_sample[b][c]
denoised_sample[b][c] += ((denoised_sample[b][c] / channel.std()) - denoised_sample[b][c]) * normalization
denoised_sample[b][c] += ((denoised_sample[b][c] / channel.std()) - denoised_sample[b][c]) * normalization * (sigmas[iteration] ** 0.5)
return denoised_sample
# Perp-hist
@@ -852,30 +857,189 @@ def sampler_supreme(model, x, sigmas, extra_args=None, callback=None, disable=No
eta = eta_min + 0.5 * (eta_max - eta_min) * (1 + math.cos(math.pi * progress))
return eta
def apply_enhancements(x, i, denoised, old_denoised):
# Calculate steps per sigma for strength adjustment, call me YandereDev since there is for sure a more efficient way to do this. Like maybe a dict with order and stuff.
steps_per_sigma = 0
match step_method:
case "euler":
order = 1 # Where order is the amount of model calls per sigma
steps_per_sigma += order # Multiply 1 by the amount of substeps
case "dpm_1s": # DPM Family
order = 1
steps_per_sigma += order
case "dpm_2s":
order = 2
steps_per_sigma += order
case "dpm_3s":
order = 3
steps_per_sigma += order
case "rk4": # Fourth-order Runge-Kutta method
order = 4
steps_per_sigma += order
case "reversible_heun":
order = 2
steps_per_sigma += order
case "rkf45":
order = 6
steps_per_sigma += order
case "trapezoidal":
order = 2
steps_per_sigma += order
case "bogacki_shampine":
order = 3
steps_per_sigma += order
case "dynamic":
order = 2 # While the step method is dynamic, I've found that it will average around 2 steps per sigma moreso than 1 step.
steps_per_sigma += order
case "adaptive_rk":
order = 2 # While the step method is dynamic, I've found that it will average around 2 steps per sigma moreso than 1 step.
steps_per_sigma += order
case _:
order = 2
steps_per_sigma += order
match substep_method:
case "euler":
order = 1 # Where order is the amount of model calls per sigma
steps_per_sigma += order * (substeps - 1) # Multiply 1 by the amount of substeps
case "dpm_1s": # DPM Family
order = 1
steps_per_sigma += order * (substeps - 1)
case "dpm_2s":
order = 2
steps_per_sigma += order * (substeps - 1)
case "dpm_3s":
order = 3
steps_per_sigma += order * (substeps - 1)
case "rk4": # Fourth-order Runge-Kutta method
order = 4
steps_per_sigma += order * (substeps - 1)
case "reversible_heun":
order = 2
steps_per_sigma += order * (substeps - 1)
case "rkf45":
order = 6
steps_per_sigma += order * (substeps - 1)
case "trapezoidal":
order = 2
steps_per_sigma += order * (substeps - 1)
case "bogacki_shampine":
order = 3
steps_per_sigma += order * (substeps - 1)
case "dynamic":
order = 2 # While the step method is dynamic, I've found that it will average around 2 steps per sigma moreso than 1 step.
steps_per_sigma += order * (substeps - 1)
case "adaptive_rk":
order = 2 # While the step method is dynamic, I've found that it will average around 2 steps per sigma moreso than 1 step.
steps_per_sigma += order * (substeps - 1)
case _:
order = 2
steps_per_sigma += order * (substeps - 1)
def apply_enhancements(x, i, model, sigma_s_in, old_denoised, modified_cond_scale):
args = extra_args
args["cond_scale"] = modified_cond_scale
denoised = model(x, sigma_s_in, **args)
if edge_enhancement != 0:
blur = (kornia.filters.joint_bilateral_blur(x, denoised, (3, 3), 0.1, (1.5, 1.5)) - x) # Blurs non-edges
denoised += (kornia.filters.unsharp_mask(denoised, (3, 3), (1.5, 1.5)) - denoised) * (sigmas[i] - sigmas[i + 1]) * edge_enhancement # Sharpens everything
denoised += blur * (sigmas[i] - sigmas[i + 1]) * edge_enhancement # Apply blur to non-edges, thus leaving edges sharpened
denoised += (kornia.filters.unsharp_mask(denoised, (3, 3), (1.5, 1.5)) - denoised) * (sigmas[i] - sigmas[i + 1]) * edge_enhancement / steps_per_sigma # Sharpens everything
denoised += blur * (sigmas[i] - sigmas[i + 1]) * edge_enhancement / steps_per_sigma # Apply blur to non-edges, thus leaving edges sharpened
if centralization != 0:
denoised = centralize(denoised, centralization)
denoised = centralize(denoised, centralization / steps_per_sigma, i)
if normalization != 0:
denoised = normalize(denoised, normalization)
denoised = normalize(denoised, normalization / steps_per_sigma, i)
if old_denoised != None and perphist != 0:
denoised = perpadd(denoised, old_denoised, x, perphist)
denoised = perpadd(denoised, old_denoised, x, perphist / steps_per_sigma)
return denoised
# Dynamic sampling
dynamic_order_samplers = {
1: "euler",
2: "trapezoidal",
3: "bogacki_shampine",
4: "rk4",
6: "rkf45",
}
# Adaptive RK order sampling
adaptive_rk_weights = {
1: [1],
2: [0.5, 0.5],
3: [1/6, 2/3, 1/6],
4: [1/8, 3/8, 3/8, 1/8],
}
def dynamic_step_method(step_method, model, prev_x, denoised, prev_denoised, iteration, substep_iter, modified_cond_scale):
"""
Step method function, applies cond-error modification, and dynamic step selection if chosen.
"""
sampler = step_method
order = 1
if iteration == 0 or prev_denoised == None: # Warmup with a RKF45 step, else use substep method for substeps
if substep_iter > 0:
return substep_method, 1, modified_cond_scale
order = 6
return dynamic_order_samplers[order], order, modified_cond_scale
d = to_d(prev_x, sigmas[iteration - 1], prev_denoised)
x_pred = prev_x + d * (sigmas[iteration] - sigmas[iteration - 1])
d_pred = to_d(x_pred, sigmas[iteration], denoised)
error = torch.linalg.norm(d_pred - d) / torch.linalg.norm(d)
modified_cond_scale = orig_cond_scale * (1 / (1 + error))
if substep_iter > 0:
return substep_method, 1, modified_cond_scale
if step_method != "dynamic" and step_method != "adaptive_rk": # If we're not a dynamic sampler, return the step unmodified step method
return step_method, order, modified_cond_scale
if (error < 1e-2):
order = 6
elif (error < 3.75e-2):
order = 4
elif (error < 7.5e-2):
order = 3
elif (error < 1.5e-1):
order = 2
else:
order = 1
if step_method == "adaptive_rk":
return step_method, min(order, 4), modified_cond_scale
return dynamic_order_samplers[order], order, modified_cond_scale
renoise_weights = torch.ones(substeps, device=x.device) / substeps
def intensity_based_multiplicative_noise_fn(x, noise, s_noise, sigma_up, intensity):
"""
Scales noise based on the intensities of the input tensor.
"""
std = torch.std(x - x.mean(), dim=1, keepdim=True) # Average across channels to get intensity
scaling = (1 / (std * intensity + 1.0)) # Scale std by intensity, as not doing this leads to more noise being left over, leading to crusty/preceivably extremely oversharpened images
additive_noise = noise * s_noise * sigma_up
scaled_noise = noise * s_noise * sigma_up * scaling + additive_noise
noise_norm = torch.norm(additive_noise)
scaled_noise_norm = torch.norm(scaled_noise)
scaled_noise *= noise_norm / scaled_noise_norm # Scale to normal noise strength
scaled_noise = scaled_noise * intensity + additive_noise * (1 - intensity)
return scaled_noise
orig_model = model
old_denoised = None
prev_denoised = None
prev_x = x
for i in trange(len(sigmas) - 1, disable=disable):
def model(x, sigma_s_in, **extra_args):
return apply_enhancements(x, i, orig_model(x, sigma_s_in, **extra_args), old_denoised)
def model(x, sigma_s_in, **extra_args): # Model wrapper to apply enhancements at every call
nonlocal old_denoised
denoised = apply_enhancements(x, i, orig_model, sigma_s_in, old_denoised, modified_cond_scale)
old_denoised = denoised
if callback is not None:
callback({'x': z_k, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
return denoised
# DynETA
eta = eta_schedule_cosine_annealing(i, len(sigmas))
@@ -890,32 +1054,32 @@ def sampler_supreme(model, x, sigmas, extra_args=None, callback=None, disable=No
denoised = model(z_k, sigmas[i] * s_in, **extra_args)
if callback is not None:
callback({'x': z_k, 'i': i, 'sigma': sigmas[i], 'sigma_hat': sigmas[i], 'denoised': denoised})
eps = (z_k - denoised) / sigmas[i]
eps_cache = {'eps': eps}
step_method_dyn, order, modified_cond_scale = dynamic_step_method(step_method, model, prev_x, denoised, prev_denoised, i, k, modified_cond_scale) #step_method, model, prev_x, denoised, prev_denoised, i, k
match step_method if sigmas[i + 1] != 0 else "euler":
case "euler":
match step_method_dyn if sigmas[i + 1] != 0 else "euler":
case "euler": # 1 model call
d = to_d(z_k, sigmas[i], denoised)
dt = sigma_down - sigmas[i]
z_k = z_k + d * dt
case "dpm_1s": # DPM Family
case "dpm_1s": # DPM Family, 1 model call
if callback is not None:
dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info})
z_k, eps_cache = dpm_solver.dpm_solver_1_step(z_k, dpm_solver.t(sigmas[i]), dpm_solver.t(sigma_down), eps_cache=eps_cache)
case "dpm_2s":
case "dpm_2s": # 2 model calls
if callback is not None:
dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info})
z_k, eps_cache = dpm_solver.dpm_solver_2_step(z_k, dpm_solver.t(sigmas[i]), dpm_solver.t(sigma_down), eps_cache=eps_cache)
case "dpm_3s":
case "dpm_3s": # 3 model calls
if callback is not None:
dpm_solver.info_callback = lambda info: callback({'sigma': dpm_solver.sigma(info['t']), 'sigma_hat': dpm_solver.sigma(info['t_up']), **info})
z_k, eps_cache = dpm_solver.dpm_solver_3_step(z_k, dpm_solver.t(sigmas[i]), dpm_solver.t(sigma_down), eps_cache=eps_cache)
case "rk4": # Fourth-order Runge-Kutta method
case "rk4": # Fourth-order Runge-Kutta method, 4 model calls
# Calculate the derivative using the model
d = to_d(z_k, sigmas[i], denoised)
dt = sigma_down - sigmas[i]
@@ -928,7 +1092,70 @@ def sampler_supreme(model, x, sigmas, extra_args=None, callback=None, disable=No
# Update the sample
z_k = z_k + (k1 + 2 * k2 + 2 * k3 + k4) / 6
case "trapezoidal":
case "reversible_heun": # 2 model calls
sigma_i, sigma_i_plus_1 = sigmas[i], sigma_down
dt = sigma_i_plus_1 - sigma_i
# Calculate the derivative using the model
d_i = to_d(z_k, sigma_i, denoised)
# Predict the sample at the next sigma using Euler step
x_pred = z_k + d_i * dt
# Denoised sample at the next sigma
denoised_i_plus_1 = model(x_pred, sigma_i_plus_1 * s_in, **extra_args)
# Calculate the derivative at the next sigma
d_i_plus_1 = to_d(x_pred, sigma_i_plus_1, denoised_i_plus_1)
# Update the sample using the Reversible Heun formula
z_k = z_k + dt * (d_i + d_i_plus_1) / 2 - dt**2 * (d_i_plus_1 - d_i) / 4
case "rkf45": # 6 model calls (expensive)
sigma_i, sigma_i_plus_1 = sigmas[i], sigma_down
dt = sigma_i_plus_1 - sigma_i
# Calculate the derivative using the model
d_i = to_d(z_k, sigmas[i], denoised)
# RKF45 steps
k1 = d_i * dt
k2 = to_d(z_k + k1 / 4, sigmas[i] + dt / 4, model(z_k + k1 / 4, (sigmas[i] + dt / 4) * s_in, **extra_args)) * dt
k3 = to_d(z_k + 3 * k1 / 32 + 9 * k2 / 32, sigmas[i] + 3 * dt / 8, model(z_k + 3 * k1 / 32 + 9 * k2 / 32, (sigmas[i] + 3 * dt / 8) * s_in, **extra_args)) * dt
k4 = to_d(z_k + 1932 * k1 / 2197 - 7200 * k2 / 2197 + 7296 * k3 / 2197, sigmas[i] + 12 * dt / 13, model(z_k + 1932 * k1 / 2197 - 7200 * k2 / 2197 + 7296 * k3 / 2197, (sigmas[i] + 12 * dt / 13) * s_in, **extra_args)) * dt
k5 = to_d(z_k + 439 * k1 / 216 - 8 * k2 + 3680 * k3 / 513 - 845 * k4 / 4104, sigmas[i] + dt, model(z_k + 439 * k1 / 216 - 8 * k2 + 3680 * k3 / 513 - 845 * k4 / 4104, (sigmas[i] + dt) * s_in, **extra_args)) * dt
k6 = to_d(z_k - 8 * k1 / 27 + 2 * k2 - 3544 * k3 / 2565 + 1859 * k4 / 4104 - 11 * k5 / 40, sigmas[i] + dt / 2, model(z_k - 8 * k1 / 27 + 2 * k2 - 3544 * k3 / 2565 + 1859 * k4 / 4104 - 11 * k5 / 40, (sigmas[i] + dt / 2) * s_in, **extra_args)) * dt
# Update the sample
z_k = z_k + 25 * k1 / 216 + 1408 * k3 / 2565 + 2197 * k4 / 4104 - k5 / 5
case "adaptive_rk":
sigma_i, sigma_i_plus_1 = sigmas[i], sigma_down
dt = sigma_i_plus_1 - sigma_i
# Calculate the derivative using the model
d_i = to_d(z_k, sigma_i, denoised)
# Adaptive order Runge-Kutta steps
k_values = [d_i * dt] # Initialize with k1
for j in range(1, order):
# Calculate intermediate k values based on the current order
k_sum = sum(adaptive_rk_weights[order][l] * k_values[l] for l in range(j))
k_values.append(to_d(z_k + k_sum, sigma_i + dt * sum(adaptive_rk_weights[order][:j]), model(z_k + k_sum, (sigma_i + dt * sum(adaptive_rk_weights[order][:j])) * s_in, **extra_args)) * dt)
# Update the sample using the weighted sum of k values
z_k = z_k + sum(adaptive_rk_weights[order][j] * k_values[j] for j in range(order))
case "bogacki_shampine":
sigma_i, sigma_i_plus_1 = sigmas[i], sigma_down
dt = sigma_i_plus_1 - sigma_i
# Calculate the derivative using the model
d_i = to_d(z_k, sigma_i, denoised)
# Bogacki-Shampine steps
k1 = d_i * dt
k2 = to_d(z_k + k1 / 2, sigma_i + dt / 2, model(z_k + k1 / 2, (sigma_i + dt / 2) * s_in, **extra_args)) * dt
k3 = to_d(z_k + 3 * k1 / 4 + k2 / 4, sigma_i + 3 * dt / 4, model(z_k + 3 * k1 / 4 + k2 / 4, (sigma_i + 3 * dt / 4) * s_in, **extra_args)) * dt
# Update the sample
z_k = z_k + 2 * k1 / 9 + k2 / 3 + 4 * k3 / 9
case "trapezoidal": # 2 model calls
if sigmas[i + 1] > 0:
dt = sigmas[i + 1] - sigmas[i]
@@ -952,17 +1179,33 @@ def sampler_supreme(model, x, sigmas, extra_args=None, callback=None, disable=No
z_avg += renoise_weights[k] * z_k
if sigmas[i + 1] > 0: # Random noise for variance on ancestral samplers
z_k = z_k + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
noise_mod = noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
match noise_modulation:
case "none":
noise_mod = noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
case "intensity":
noise = noise_sampler(sigmas[i], sigmas[i + 1])
noise_mod = intensity_based_multiplicative_noise_fn(z_k, noise, s_noise, sigma_up, modulation_strength)# * modulation_strength + noise * s_noise * sigma_up * (1.0 - modulation_strength)
z_k = z_k + noise_mod
x = z_avg
if sigmas[i + 1] > 0:
x = x + noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
old_denoised = denoised
noise_mod = noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
match noise_modulation:
case "none":
noise_mod = noise_sampler(sigmas[i], sigmas[i + 1]) * s_noise * sigma_up
case "intensity":
noise = noise_sampler(sigmas[i], sigmas[i + 1])
noise_mod = intensity_based_multiplicative_noise_fn(x, noise, s_noise, sigma_up, modulation_strength)# * modulation_strength + noise * s_noise * sigma_up * (1.0 - modulation_strength)
x = x + noise_mod
prev_x = x
prev_denoised = denoised
return x
def sample_supreme(model, x, sigmas, extra_args=None, callback=None, disable=None, s_noise=1., noise_sampler_type="gaussian", noise_sampler=None, eta=1.0, step_method="euler", centralization=0.02, normalization=0.01, edge_enhancement=0.05, perphist=0, substeps=2):
return sampler_supreme(model, x, sigmas, extra_args=extra_args, callback=callback, disable=disable, s_noise=s_noise, noise_sampler=noise_sampler or get_noise_sampler(x, sigmas, noise_sampler_type, noise_sampler, extra_args), eta=eta, step_method=step_method, centralization=centralization, normalization=normalization, edge_enhancement=edge_enhancement, perphist=perphist, substeps=substeps)
def sample_supreme(model, x, sigmas, extra_args=None, callback=None, disable=None, s_noise=1., noise_sampler_type="gaussian", noise_sampler=None, eta=1.0, step_method="euler", substep_method="euler", centralization=0.05, normalization=0.05, edge_enhancement=0.25, perphist=0.5, substeps=2, noise_modulation="none", modulation_strength=2.0):
return sampler_supreme(model, x, sigmas, extra_args=extra_args, callback=callback, disable=disable, s_noise=s_noise, noise_sampler=noise_sampler or get_noise_sampler(x, sigmas, noise_sampler_type, noise_sampler, extra_args), eta=eta, step_method=step_method, substep_method=substep_method, centralization=centralization, normalization=normalization, edge_enhancement=edge_enhancement, perphist=perphist, substeps=substeps, noise_modulation=noise_modulation, modulation_strength=modulation_strength)
# Add your personal samplers below here, just for formatting purposes ;3
+9 -3
View File
@@ -144,9 +144,13 @@ class SamplerDPMPP_3M_SDE_DYN_ETA:
class SamplerSUPREME:
@classmethod
def INPUT_TYPES(s):
SUBSTEP_METHODS=["euler", "dpm_1s", "dpm_2s", "dpm_3s", "bogacki_shampine", "rk4", "rkf45", "reversible_heun", "trapezoidal"]
STEP_METHODS=SUBSTEP_METHODS+["dynamic", "adaptive_rk"]
NOISE_MODULATION_TYPES=["none", "intensity"]
return {"required":
{"noise_sampler_type": (get_noise_sampler_names(),),
"step_method": (["euler", "dpm_1s", "dpm_2s", "dpm_3s", "rk4", "trapezoidal"], ),
"step_method": (STEP_METHODS, ),
"substep_method": (SUBSTEP_METHODS, ),
"eta": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 100.0, "step":0.01}),
"centralization": ("FLOAT", {"default": 0.02, "min": -1.0, "max": 1.0, "step":0.01}),
"normalization": ("FLOAT", {"default": 0.01, "min": -1.0, "max": 1.0, "step":0.01}),
@@ -154,6 +158,8 @@ class SamplerSUPREME:
"perphist": ("FLOAT", {"default": 0, "min": -5.0, "max": 5.0, "step":0.01}),
"substeps": ("INT", {"default": 2, "min": 1, "max": 100, "step":1}),
"s_noise": ("FLOAT", {"default": 1, "min": 0.0, "max": 100.0, "step":0.01}),
"noise_modulation": (NOISE_MODULATION_TYPES, ),
"modulation_strength": ("FLOAT", {"default": 2.0, "min": 0.0, "max": 100.0, "step":0.01}),
}
}
RETURN_TYPES = ("SAMPLER",)
@@ -161,8 +167,8 @@ class SamplerSUPREME:
FUNCTION = "get_sampler"
def get_sampler(self, noise_sampler_type, step_method, eta, centralization, normalization, edge_enhancement, perphist, substeps, s_noise):
sampler = comfy.samplers.ksampler("supreme", {"noise_sampler_type": noise_sampler_type, "step_method": step_method, "eta": eta, "centralization": centralization, "normalization": normalization, "edge_enhancement": edge_enhancement, "perphist": perphist, "substeps": substeps, "s_noise": s_noise})
def get_sampler(self, noise_sampler_type, step_method, substep_method, eta, centralization, normalization, edge_enhancement, perphist, substeps, noise_modulation, modulation_strength, s_noise):
sampler = comfy.samplers.ksampler("supreme", {"noise_sampler_type": noise_sampler_type, "step_method": step_method, "eta": eta, "centralization": centralization, "normalization": normalization, "edge_enhancement": edge_enhancement, "perphist": perphist, "substeps": substeps, "substep_method": substep_method, "noise_modulation": noise_modulation, "modulation_strength": modulation_strength, "s_noise": s_noise})
return (sampler, )
### Schedulers