Initial release
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
-- apg.lua: Adaptive Perpendicular Guidance (default)
|
||||
-- Routes through the native C++ apg_forward() for momentum smoothing,
|
||||
-- per-channel norm thresholding, and perpendicular projection.
|
||||
-- In practice, the engine takes the native C++ path for APG directly,
|
||||
-- but this Lua implementation exists as a correct fallback.
|
||||
|
||||
guidance = {
|
||||
name = "apg",
|
||||
display = "APG",
|
||||
description = "Adaptive perpendicular guidance (default)",
|
||||
params = {
|
||||
{ key = "momentum", type = "slider", label = "Momentum",
|
||||
default = 0.75, min = -1, max = 1, step = 0.01,
|
||||
hint = "APG momentum coefficient (negative = adaptive)" },
|
||||
{ key = "norm_threshold", type = "slider", label = "Norm Threshold",
|
||||
default = 2.5, min = 0, max = 10, step = 0.1,
|
||||
hint = "APG norm clipping threshold" },
|
||||
},
|
||||
}
|
||||
|
||||
function guide(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
-- Route through native APG C++ implementation
|
||||
apg(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
end
|
||||
@@ -0,0 +1,64 @@
|
||||
-- cfg_mp.lua: CFG-MP — Manifold Projection Guidance
|
||||
-- Paper: "Improving CFG of Flow Matching via Manifold Projection"
|
||||
-- Su et al., 2025 (arXiv:2601.21892)
|
||||
--
|
||||
-- After each solver step, projects the latent back onto a manifold where the
|
||||
-- prediction gap (cond - uncond) is minimised. Uses iterative fixed-point
|
||||
-- iteration of the operator G(x, t):
|
||||
--
|
||||
-- z = x - a * v_uncond(t, x) -- push away from unconditioned manifold
|
||||
-- x = z + a * v_cond(t, z) -- pull toward conditioned manifold
|
||||
--
|
||||
-- where a = |dt| / 2 (validated in paper Appendix C.2.1).
|
||||
--
|
||||
-- The guide() function applies standard linear CFG for the base velocity.
|
||||
-- The post_step() function performs K iterations of manifold projection using
|
||||
-- real model evaluations at the post-solver latent position.
|
||||
--
|
||||
-- Performance note: each iteration = 2 extra NFEs (one cond, one uncond).
|
||||
-- K=2 adds ~3x total compute; K=1 adds ~2x.
|
||||
|
||||
guidance = {
|
||||
name = "cfg_mp",
|
||||
display = "CFG-MP",
|
||||
description = "Manifold projection guidance (Su et al. 2025)",
|
||||
params = {
|
||||
{ key = "iterations", type = "slider", label = "Projection Iterations (K)",
|
||||
default = 1, min = 1, max = 5, step = 1,
|
||||
hint = "Fixed-point iterations per step. Paper recommends 2." },
|
||||
},
|
||||
}
|
||||
|
||||
-- Standard linear CFG for the base velocity step
|
||||
function guide(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
-- Route through native APG for momentum/projection consistency
|
||||
apg(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
end
|
||||
|
||||
-- Post-step manifold projection: called AFTER the solver updates xt
|
||||
-- Args:
|
||||
-- xt : mutable FloatArray — current latent state (modified in-place)
|
||||
-- t : float — timestep (t_next, the timestep we just stepped TO)
|
||||
-- n : int — total elements in xt
|
||||
-- eval_cond : function(xt_arr, t) — evaluates model with conditioning → vt_cond
|
||||
-- eval_uncond : function(xt_arr, t) — evaluates model without conditioning → vt_uncond
|
||||
-- vt_cond : mutable FloatArray — output buffer for conditional velocity
|
||||
-- vt_uncond : mutable FloatArray — output buffer for unconditional velocity
|
||||
function post_step(xt, t, n, eval_cond, eval_uncond, vt_cond, vt_uncond)
|
||||
local K = (params and params.iterations) or 2
|
||||
local a = math.abs(dt or 0.03) / 2.0 -- dt is a global from the C++ bridge
|
||||
|
||||
for k = 1, K do
|
||||
-- Step 1: z = xt - a * v_uncond(t, xt)
|
||||
eval_uncond(xt, t)
|
||||
for i = 0, n - 1 do
|
||||
xt[i] = xt[i] - a * vt_uncond[i]
|
||||
end
|
||||
|
||||
-- Step 2: xt = z + a * v_cond(t, z)
|
||||
eval_cond(xt, t)
|
||||
for i = 0, n - 1 do
|
||||
xt[i] = xt[i] + a * vt_cond[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,21 @@
|
||||
-- cfg_pp.lua: CFG++ — Step-scaled guidance for large steps
|
||||
-- Reduces effective scale by the step-size-to-sigma ratio.
|
||||
-- Routes through native APG (momentum + projection + norm thresholding).
|
||||
|
||||
guidance = {
|
||||
name = "cfg_pp",
|
||||
display = "CFG++",
|
||||
description = "Step-scaled guidance for few-step models",
|
||||
}
|
||||
|
||||
function guide(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
-- Effective scale adjusted by step ratio (t_curr and dt are globals)
|
||||
local effective_scale = guidance_scale
|
||||
if t_curr and t_curr > 1e-6 and dt then
|
||||
local step_scale = math.abs(dt) / t_curr
|
||||
effective_scale = 1.0 + (guidance_scale - 1.0) * step_scale
|
||||
end
|
||||
|
||||
-- Route through native APG with the adjusted scale
|
||||
apg(pred_cond, pred_uncond, effective_scale, result, Oc, T, norm_threshold)
|
||||
end
|
||||
@@ -0,0 +1,39 @@
|
||||
-- cfg_zero_star.lua: CFG-Zero⋆ — Zero-Init Guidance
|
||||
-- Paper: "CFG-Zero⋆: Improved Classifier-Free Guidance for Flow Matching Models"
|
||||
-- Fan et al., 2025 (arXiv:2503.18886)
|
||||
--
|
||||
-- The paper proposes two improvements: optimised scale (s⋆) and zero-init.
|
||||
-- Since our engine uses APG (perpendicular projection + momentum), which
|
||||
-- already corrects for the underfitting that s⋆ addresses, only zero-init
|
||||
-- is applied here. Combining both would double-correct.
|
||||
--
|
||||
-- Zero-init: zeroes out velocity for the first N ODE steps, since early-step
|
||||
-- CFG predictions in flow matching are often worse than doing nothing.
|
||||
-- All subsequent steps use the standard APG pipeline.
|
||||
|
||||
guidance = {
|
||||
name = "cfg_zero_star",
|
||||
display = "CFG-Zero⋆",
|
||||
description = "Zero-init + APG guidance (Fan et al. 2025)",
|
||||
params = {
|
||||
{ key = "zero_init_steps", type = "slider", label = "Zero-Init Steps",
|
||||
default = 1, min = 0, max = 5, step = 1,
|
||||
hint = "Number of initial ODE steps to zero out (paper recommends 1)" },
|
||||
},
|
||||
}
|
||||
|
||||
function guide(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
local n = Oc * T
|
||||
local zero_init_steps = (params and params.zero_init_steps) or 1
|
||||
|
||||
-- Zero-init: zero out velocity for the first N steps
|
||||
if (step_idx or 0) < zero_init_steps then
|
||||
for i = 0, n - 1 do
|
||||
result[i] = 0.0
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Standard APG for all other steps
|
||||
apg(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
end
|
||||
@@ -0,0 +1,20 @@
|
||||
-- dynamic_cfg.lua: Cosine-decaying guidance schedule
|
||||
-- Full guidance early (structure), reduced guidance late (fine detail).
|
||||
-- Routes through native APG (momentum + projection + norm thresholding).
|
||||
|
||||
guidance = {
|
||||
name = "dynamic_cfg",
|
||||
display = "Dynamic CFG",
|
||||
description = "Cosine-decaying guidance schedule with APG projection",
|
||||
}
|
||||
|
||||
function guide(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
local power = 0.5
|
||||
local progress = (step_idx or 0) / math.max((total_steps or 1) - 1, 1)
|
||||
local cos_val = math.max(math.cos(math.pi / 2 * progress), 0)
|
||||
local decay = cos_val ^ power
|
||||
local effective_scale = 1.0 + (guidance_scale - 1.0) * decay
|
||||
|
||||
-- Route through native APG with the decayed scale
|
||||
apg(pred_cond, pred_uncond, effective_scale, result, Oc, T, norm_threshold)
|
||||
end
|
||||
@@ -0,0 +1,41 @@
|
||||
-- rescaled_cfg.lua: Std-matched post-processing guidance
|
||||
-- Runs APG at full scale, then rescales output to match conditional std.
|
||||
-- Routes through native APG (momentum + projection + norm thresholding),
|
||||
-- then applies std-matching post-processing.
|
||||
|
||||
guidance = {
|
||||
name = "rescaled_cfg",
|
||||
display = "Rescaled CFG",
|
||||
description = "Std-matched to prevent saturation",
|
||||
}
|
||||
|
||||
function guide(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
local n = Oc * T
|
||||
local phi = (guidance_scale > 4.0) and 0.95 or 0.7
|
||||
|
||||
-- Run APG at full guidance scale first
|
||||
apg(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
|
||||
-- Compute std of conditional prediction and guided output
|
||||
local sum_c, sum2_c = 0, 0
|
||||
local sum_g, sum2_g = 0, 0
|
||||
for i = 0, n - 1 do
|
||||
local c = pred_cond[i]
|
||||
local g = result[i]
|
||||
sum_c = sum_c + c; sum2_c = sum2_c + c * c
|
||||
sum_g = sum_g + g; sum2_g = sum2_g + g * g
|
||||
end
|
||||
local mean_c = sum_c / n
|
||||
local mean_g = sum_g / n
|
||||
local var_c = sum2_c / n - mean_c * mean_c
|
||||
local var_g = sum2_g / n - mean_g * mean_g
|
||||
local std_c = (var_c > 0) and math.sqrt(var_c) or 1e-5
|
||||
local std_g = (var_g > 0) and math.sqrt(var_g) or 1e-5
|
||||
|
||||
-- Rescale to match conditional std, blend with raw APG output
|
||||
local factor = std_c / (std_g + 1e-5)
|
||||
for i = 0, n - 1 do
|
||||
local rescaled = result[i] * factor
|
||||
result[i] = phi * rescaled + (1 - phi) * result[i]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,84 @@
|
||||
-- smc_cfg.lua: SMC-CFG — Sliding Mode Control Guidance
|
||||
-- Paper: "CFG-Ctrl: Control-Based Classifier-Free Diffusion Guidance"
|
||||
-- Han et al., 2025 (arXiv:2603.03281)
|
||||
--
|
||||
-- Reinterprets CFG as a feedback control system and applies Sliding Mode
|
||||
-- Control (SMC) to stabilise guidance, especially at high scales.
|
||||
--
|
||||
-- Key idea: define a sliding surface s(t) = ė(t) + λ·e(t) over the
|
||||
-- semantic error e = v_cond - v_uncond, then apply a switching control
|
||||
-- term Δe = -k·sign(s) that enforces convergence to a stable manifold.
|
||||
--
|
||||
-- Implementation: routes through native APG for stability (momentum,
|
||||
-- projection, norm thresholding), then applies the SMC correction as
|
||||
-- a delta on top: result = APG(cond, uncond, w) + w · Δe
|
||||
--
|
||||
-- Stateful: stores previous error vector across steps.
|
||||
|
||||
guidance = {
|
||||
name = "smc_cfg",
|
||||
display = "SMC-CFG",
|
||||
description = "Sliding mode control guidance (Han et al. 2025)",
|
||||
params = {
|
||||
{ key = "lambda", type = "slider", label = "λ (Surface Slope)",
|
||||
default = 0.5, min = 0.01, max = 2.0, step = 0.01,
|
||||
hint = "Controls the sliding surface shape. Higher = faster convergence" },
|
||||
{ key = "k", type = "slider", label = "k (Switching Gain)",
|
||||
default = 0.1, min = 0.01, max = 1.0, step = 0.01,
|
||||
hint = "Force toward the sliding surface. Too high = vibrations" },
|
||||
},
|
||||
}
|
||||
|
||||
-- Stateful: previous error buffer
|
||||
local prev_error = nil
|
||||
local prev_n = 0
|
||||
|
||||
local function sign(x)
|
||||
if x > 0 then return 1.0
|
||||
elseif x < 0 then return -1.0
|
||||
else return 0.0
|
||||
end
|
||||
end
|
||||
|
||||
function guide(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
local n = Oc * T
|
||||
local lam = (params and params.lambda) or 0.5
|
||||
local k = (params and params.k) or 0.1
|
||||
|
||||
-- Reset state on first step of a new generation
|
||||
if (step_idx or 0) == 0 then prev_error = nil; prev_n = 0 end
|
||||
|
||||
-- Base guidance through APG (handles momentum, projection, norm thresholding)
|
||||
apg(pred_cond, pred_uncond, guidance_scale, result, Oc, T, norm_threshold)
|
||||
|
||||
-- Compute semantic error e(t) = cond - uncond
|
||||
local error_now = {}
|
||||
for i = 0, n - 1 do
|
||||
error_now[i] = pred_cond[i] - pred_uncond[i]
|
||||
end
|
||||
|
||||
-- First step or size change: no previous error, just use APG as-is
|
||||
if prev_error == nil or prev_n ~= n then
|
||||
prev_error = error_now
|
||||
prev_n = n
|
||||
return
|
||||
end
|
||||
|
||||
-- Compute ė ≈ (e_now - e_prev) / dt
|
||||
local dt_abs = math.abs(dt or 1.0)
|
||||
if dt_abs < 1e-8 then dt_abs = 1e-8 end
|
||||
local inv_dt = 1.0 / dt_abs
|
||||
|
||||
-- Apply SMC correction: Δe = -k · sign(ė + λ·e)
|
||||
-- Add w · Δe as delta on top of APG result
|
||||
for i = 0, n - 1 do
|
||||
local e_dot = (error_now[i] - prev_error[i]) * inv_dt
|
||||
local s = e_dot + lam * error_now[i]
|
||||
local delta_e = -k * sign(s)
|
||||
result[i] = result[i] + guidance_scale * delta_e
|
||||
end
|
||||
|
||||
-- Store for next step
|
||||
prev_error = error_now
|
||||
prev_n = n
|
||||
end
|
||||
Reference in New Issue
Block a user