Initial release
This commit is contained in:
@@ -0,0 +1,439 @@
|
||||
-- ============================================================================
|
||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-- Copyright (C) 2026 Alexander Allan (MDMAchine) -- A&E Concepts
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details: https://www.gnu.org/licenses/
|
||||
-- ============================================================================
|
||||
|
||||
-- MD Causal Scheduler v2.1 — LINA Time Warping + Multi-Mode Base Curves
|
||||
-- MDMAchine | A&E Concepts © 2026
|
||||
--
|
||||
-- Port of md_causal_scheduler_core.py to HOT-Step-CPP Lua.
|
||||
--
|
||||
-- 14 BASE SCHEDULE MODES:
|
||||
-- karras — power-law rho spacing (rho=7 default, Karras et al.)
|
||||
-- simple — smoothstep (cubic hermite: t*t*(3-2t))
|
||||
-- linear — uniform spacing
|
||||
-- exponential — exp decay: sigma_max * (sigma_min/sigma_max)^t
|
||||
-- polynomial — power curve: linspace of sigma^(1/power)
|
||||
-- beta — beta distribution curve (alpha, beta params)
|
||||
-- ays — AYS adaptive schedule (sigmoid + concentration blend)
|
||||
-- bong — tangent-based 2-phase schedule (pivot point)
|
||||
-- linear_quadratic — linear phase then quadratic phase
|
||||
-- ddim_uniform — DDIM-style uniform timestep mapping
|
||||
-- sgm_uniform — SGM uniform (linear 999→0 mapped to sigma range)
|
||||
-- blended — karras + linear blend by blend_factor
|
||||
-- variance_preserving — log-space interpolation
|
||||
-- kl_optimal — arctan-based KL-optimal spacing
|
||||
--
|
||||
-- LINA WARP:
|
||||
-- Post-processes any base schedule by warping the time axis:
|
||||
-- t_warped = t^shift (power warp on the CDF index)
|
||||
-- shift < 1: front-loads steps (more at high sigma)
|
||||
-- shift > 1: back-loads steps (more at low sigma)
|
||||
-- shift = 1: no warp (identity)
|
||||
-- ============================================================================
|
||||
|
||||
scheduler = {
|
||||
name = "md_causal",
|
||||
display = "MD Causal (LINA + 14 Modes)",
|
||||
description = "14 base schedule modes with LINA time-axis warp. Karras, smoothstep, beta, AYS, bong, DDIM, SGM, blended, variance-preserving, KL-optimal and more. Port of md_causal_scheduler_core v2.1.",
|
||||
params = {
|
||||
{
|
||||
key = "mode",
|
||||
type = "select",
|
||||
label = "Schedule Mode",
|
||||
default = "polynomial",
|
||||
options = {
|
||||
{ value = "karras", label = "Karras (rho)" },
|
||||
{ value = "simple", label = "Simple (Smoothstep)" },
|
||||
{ value = "linear", label = "Linear" },
|
||||
{ value = "exponential", label = "Exponential" },
|
||||
{ value = "polynomial", label = "Polynomial" },
|
||||
{ value = "beta", label = "Beta" },
|
||||
{ value = "ays", label = "AYS" },
|
||||
{ value = "bong", label = "Bong (Tangent)" },
|
||||
{ value = "linear_quadratic", label = "Linear-Quadratic" },
|
||||
{ value = "ddim_uniform", label = "DDIM Uniform" },
|
||||
{ value = "sgm_uniform", label = "SGM Uniform" },
|
||||
{ value = "blended", label = "Blended (Karras+Lin)" },
|
||||
{ value = "variance_preserving", label = "Variance Preserving" },
|
||||
{ value = "kl_optimal", label = "KL Optimal" },
|
||||
},
|
||||
hint = "Base schedule curve before LINA warp is applied.",
|
||||
},
|
||||
{
|
||||
key = "lina_shift",
|
||||
type = "slider",
|
||||
label = "LINA Shift",
|
||||
default = 1.2,
|
||||
min = 0.1,
|
||||
max = 3.0,
|
||||
step = 0.05,
|
||||
hint = "Time-axis warp. 1.0=none. <1=front-load (more high-sigma steps). >1=back-load (more low-sigma steps).",
|
||||
},
|
||||
{
|
||||
key = "rho",
|
||||
type = "slider",
|
||||
label = "Rho (Karras)",
|
||||
default = 7.0,
|
||||
min = 1.0,
|
||||
max = 15.0,
|
||||
step = 0.5,
|
||||
hint = "Karras rho parameter. 7=default. Higher=more steps at low sigma.",
|
||||
visible_when = { key = "mode", equals = "karras" },
|
||||
},
|
||||
{
|
||||
key = "power",
|
||||
type = "slider",
|
||||
label = "Power (Polynomial)",
|
||||
default = 2.0,
|
||||
min = 0.5,
|
||||
max = 5.0,
|
||||
step = 0.1,
|
||||
hint = "Polynomial exponent. 2=quadratic, 1=linear.",
|
||||
visible_when = { key = "mode", equals = "polynomial" },
|
||||
},
|
||||
{
|
||||
key = "beta_alpha",
|
||||
type = "slider",
|
||||
label = "Beta Alpha",
|
||||
default = 0.6,
|
||||
min = 0.1,
|
||||
max = 3.0,
|
||||
step = 0.1,
|
||||
hint = "Beta distribution alpha parameter.",
|
||||
visible_when = { key = "mode", equals = "beta" },
|
||||
},
|
||||
{
|
||||
key = "beta_beta",
|
||||
type = "slider",
|
||||
label = "Beta Beta",
|
||||
default = 0.6,
|
||||
min = 0.1,
|
||||
max = 3.0,
|
||||
step = 0.1,
|
||||
hint = "Beta distribution beta parameter.",
|
||||
visible_when = { key = "mode", equals = "beta" },
|
||||
},
|
||||
{
|
||||
key = "blend_factor",
|
||||
type = "slider",
|
||||
label = "Blend Factor",
|
||||
default = 0.5,
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
step = 0.05,
|
||||
hint = "Blend between Karras (0) and Linear (1).",
|
||||
visible_when = { key = "mode", equals = "blended" },
|
||||
},
|
||||
{
|
||||
key = "bong_pivot",
|
||||
type = "slider",
|
||||
label = "Bong Pivot",
|
||||
default = 0.5,
|
||||
min = 0.1,
|
||||
max = 0.9,
|
||||
step = 0.05,
|
||||
hint = "Bong: fraction of steps in compression phase.",
|
||||
visible_when = { key = "mode", equals = "bong" },
|
||||
},
|
||||
{
|
||||
key = "bong_slope_comp",
|
||||
type = "slider",
|
||||
label = "Bong Slope Comp",
|
||||
default = 1.2,
|
||||
min = 0.1,
|
||||
max = 3.0,
|
||||
step = 0.1,
|
||||
hint = "Bong: tangent slope in compression phase.",
|
||||
visible_when = { key = "mode", equals = "bong" },
|
||||
},
|
||||
{
|
||||
key = "bong_slope_detail",
|
||||
type = "slider",
|
||||
label = "Bong Slope Detail",
|
||||
default = 0.8,
|
||||
min = 0.1,
|
||||
max = 3.0,
|
||||
step = 0.1,
|
||||
hint = "Bong: tangent slope in detail phase.",
|
||||
visible_when = { key = "mode", equals = "bong" },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local EPSILON = 1e-6
|
||||
local MONOTONIC_DECAY = 0.99
|
||||
|
||||
local function clamp(v, lo, hi)
|
||||
if v < lo then return lo end
|
||||
if v > hi then return hi end
|
||||
return v
|
||||
end
|
||||
|
||||
-- ── Base schedule generators ─────────────────────────────────────────────────
|
||||
|
||||
local function karras(n, s_min, s_max, rho)
|
||||
-- sigma[i] = (s_max^(1/rho) + i/(n-1) * (s_min^(1/rho) - s_max^(1/rho)))^rho
|
||||
local inv_rho = 1.0 / rho
|
||||
local max_inv = s_max ^ inv_rho
|
||||
local min_inv = s_min ^ inv_rho
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
s[i] = (max_inv + t * (min_inv - max_inv)) ^ rho
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function simple(n, s_min, s_max)
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
local smooth = t * t * (3.0 - 2.0 * t)
|
||||
s[i] = s_max - (s_max - s_min) * smooth
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function linear(n, s_min, s_max)
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
s[i] = s_max - (s_max - s_min) * (i / n)
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function exponential(n, s_min, s_max)
|
||||
local s = {}
|
||||
local safe_max = math.max(s_max, 1e-9)
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
s[i] = safe_max * (s_min / safe_max) ^ t
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function polynomial(n, s_min, s_max, power)
|
||||
local s = {}
|
||||
local inv_p = 1.0 / math.max(power, 0.1)
|
||||
local lo = s_min ^ inv_p
|
||||
local hi = s_max ^ inv_p
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
s[i] = (hi + t * (lo - hi)) ^ power
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function beta_sched(n, s_min, s_max, alpha, beta_)
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
local alpha_ = math.max(alpha, 0.1)
|
||||
local beta__ = math.max(beta_, 0.1)
|
||||
local beta_curve = clamp(1.0 - (1.0 - t ^ alpha_) ^ beta__, 0.0, 1.0)
|
||||
s[i] = s_max * (1.0 - beta_curve) + s_min * beta_curve
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function ays_sched(n, s_min, s_max)
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
-- sigmoid centered at 0.5, steepness 10
|
||||
local sig = 1.0 / (1.0 + math.exp(-10.0 * (t - 0.5)))
|
||||
-- AYS blend: sigmoid 0.7 + concentration (exp decay) 0.3
|
||||
local conc = math.exp(-2.0 * t)
|
||||
local ays = sig * 0.7 + conc * 0.3
|
||||
-- normalize and invert: high sigma at start
|
||||
s[i] = s_min + (s_max - s_min) * (1.0 - ays)
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function bong_sched(n, s_min, s_max, pivot, slope_comp, slope_det)
|
||||
local comp_steps = math.max(1, math.floor(n * pivot))
|
||||
local det_steps = math.max(1, n - comp_steps)
|
||||
|
||||
local sigmas = {}
|
||||
local pi_half = math.pi / 2.0 - 0.1
|
||||
|
||||
-- Compression phase
|
||||
for i = 0, comp_steps - 1 do
|
||||
local t = i / math.max(comp_steps - 1, 1)
|
||||
local angle = t * pi_half * slope_comp
|
||||
local warped = math.tan(angle) / math.tan(pi_half * slope_comp)
|
||||
sigmas[i] = s_max * (1.0 - warped * pivot)
|
||||
end
|
||||
|
||||
-- Detail phase
|
||||
for i = 0, det_steps - 1 do
|
||||
local t = i / math.max(det_steps - 1, 1)
|
||||
local angle = t * pi_half * slope_det
|
||||
local warped = math.tan(angle) / math.tan(pi_half * slope_det)
|
||||
local start = s_max * (1.0 - pivot)
|
||||
sigmas[comp_steps + i] = start * (1.0 - warped) + s_min * warped
|
||||
end
|
||||
|
||||
sigmas[n] = s_min
|
||||
|
||||
-- Enforce monotonic
|
||||
for i = 0, n - 1 do
|
||||
if sigmas[i] ~= nil and sigmas[i + 1] ~= nil then
|
||||
if sigmas[i] <= sigmas[i + 1] then
|
||||
sigmas[i + 1] = math.max(sigmas[i] * MONOTONIC_DECAY, sigmas[i] - EPSILON)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sigmas[0] = s_max; sigmas[n] = s_min
|
||||
return sigmas
|
||||
end
|
||||
|
||||
local function ddim_uniform(n, s_min, s_max)
|
||||
local max_ts = 1000
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
local ts = max_ts - i * (max_ts / n)
|
||||
s[i] = s_min + (s_max - s_min) * ((ts / max_ts) ^ 0.5)
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function sgm_uniform(n, s_min, s_max)
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
s[i] = s_min + (s_max - s_min) * (1.0 - t)
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function blended(n, s_min, s_max, rho, blend)
|
||||
local k = karras(n, s_min, s_max, rho)
|
||||
local l = linear(n, s_min, s_max)
|
||||
local s = {}
|
||||
for i = 0, n do
|
||||
s[i] = (1.0 - blend) * k[i] + blend * l[i]
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function variance_preserving(n, s_min, s_max)
|
||||
local s = {}
|
||||
local log_min = math.log(math.max(s_min, 1e-9))
|
||||
local log_max = math.log(math.max(s_max, 1e-9))
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
s[i] = math.exp((1.0 - t) * log_max + t * log_min)
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
local function kl_optimal(n, s_min, s_max)
|
||||
local s = {}
|
||||
local atan_min = math.atan(s_min)
|
||||
local atan_max = math.atan(s_max)
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
s[i] = math.tan((1.0 - t) * atan_max + t * atan_min)
|
||||
end
|
||||
s[0] = s_max; s[n] = s_min
|
||||
return s
|
||||
end
|
||||
|
||||
-- ── LINA warp ─────────────────────────────────────────────────────────────────
|
||||
|
||||
local function apply_lina_warp(sigmas, n, shift)
|
||||
if shift == 1.0 then return sigmas end
|
||||
local warped = {}
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
-- Warp: t_warped = t^shift → index into sigma array
|
||||
local t_w = t ^ shift
|
||||
local raw_idx = t_w * n
|
||||
local idx_lo = clamp(math.floor(raw_idx), 0, n)
|
||||
local idx_hi = clamp(idx_lo + 1, 0, n)
|
||||
local frac = raw_idx - idx_lo
|
||||
local s_lo = sigmas[idx_lo] or sigmas[n]
|
||||
local s_hi = sigmas[idx_hi] or sigmas[n]
|
||||
warped[i] = s_lo * (1.0 - frac) + s_hi * frac
|
||||
end
|
||||
warped[0] = sigmas[0]
|
||||
warped[n] = sigmas[n]
|
||||
return warped
|
||||
end
|
||||
|
||||
-- ── Required schedule() function ─────────────────────────────────────────────
|
||||
|
||||
function schedule(output, num_steps, shift)
|
||||
local mode = (params and params.mode) or "karras"
|
||||
local lina_shift = (params and params.lina_shift) or 1.0
|
||||
local rho = (params and params.rho) or 7.0
|
||||
local power = (params and params.power) or 2.0
|
||||
local ba = (params and params.beta_alpha) or 0.6
|
||||
local bb = (params and params.beta_beta) or 0.6
|
||||
local blend = (params and params.blend_factor) or 0.5
|
||||
local b_pivot = (params and params.bong_pivot) or 0.5
|
||||
local b_comp = (params and params.bong_slope_comp) or 1.2
|
||||
local b_det = (params and params.bong_slope_detail) or 0.8
|
||||
|
||||
local s_max = 1.0
|
||||
local s_min = 0.0
|
||||
|
||||
local sigmas
|
||||
if mode == "karras" then sigmas = karras(num_steps, s_min, s_max, rho)
|
||||
elseif mode == "simple" then sigmas = simple(num_steps, s_min, s_max)
|
||||
elseif mode == "linear" then sigmas = linear(num_steps, s_min, s_max)
|
||||
elseif mode == "exponential" then sigmas = exponential(num_steps, s_min, s_max)
|
||||
elseif mode == "polynomial" then sigmas = polynomial(num_steps, s_min, s_max, power)
|
||||
elseif mode == "beta" then sigmas = beta_sched(num_steps, s_min, s_max, ba, bb)
|
||||
elseif mode == "ays" then sigmas = ays_sched(num_steps, s_min, s_max)
|
||||
elseif mode == "bong" then sigmas = bong_sched(num_steps, s_min, s_max, b_pivot, b_comp, b_det)
|
||||
elseif mode == "ddim_uniform" then sigmas = ddim_uniform(num_steps, s_min, s_max)
|
||||
elseif mode == "sgm_uniform" then sigmas = sgm_uniform(num_steps, s_min, s_max)
|
||||
elseif mode == "blended" then sigmas = blended(num_steps, s_min, s_max, rho, blend)
|
||||
elseif mode == "variance_preserving" then sigmas = variance_preserving(num_steps, s_min, s_max)
|
||||
elseif mode == "kl_optimal" then sigmas = kl_optimal(num_steps, s_min, s_max)
|
||||
else sigmas = karras(num_steps, s_min, s_max, rho)
|
||||
end
|
||||
|
||||
-- LINA warp
|
||||
if lina_shift ~= 1.0 then
|
||||
sigmas = apply_lina_warp(sigmas, num_steps, lina_shift)
|
||||
end
|
||||
|
||||
-- Native shift warp
|
||||
if shift ~= 1.0 then
|
||||
for i = 0, num_steps do
|
||||
local t = sigmas[i]
|
||||
sigmas[i] = shift * t / (1.0 + (shift - 1.0) * t)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 0, num_steps - 1 do
|
||||
output[i] = sigmas[i] or 1.0 - i / num_steps
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,120 @@
|
||||
-- ============================================================================
|
||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-- Copyright (C) 2026 Alexander Allan (MDMAchine) -- A&E Concepts
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details: https://www.gnu.org/licenses/
|
||||
-- ============================================================================
|
||||
|
||||
-- MD HAP Scheduler v1.0 — Hamiltonian Action-Principle
|
||||
-- MDMAchine | A&E Concepts © 2026
|
||||
--
|
||||
-- Port of hap_scheduler_core.py calculate_hap_sigmas() to HOT-Step-CPP Lua.
|
||||
--
|
||||
-- WHAT THIS DOES:
|
||||
-- Simulates a particle falling through a gravitational potential well with
|
||||
-- atmospheric drag. Maps the particle's velocity to sigma step sizes.
|
||||
--
|
||||
-- velocity(t) = (1 + kinetic_energy * t) * exp(-damping_friction * t)
|
||||
--
|
||||
-- - kinetic_energy: initial boost — stretches steps in the middle of the run
|
||||
-- (particle accelerates as it falls into the well)
|
||||
-- - damping_friction: atmospheric drag — compresses steps at the end
|
||||
-- (particle slows as drag increases with velocity)
|
||||
--
|
||||
-- distance = cumsum(velocity) → normalize → map to sigma space
|
||||
--
|
||||
-- HIGH kinetic_energy: more steps in the mid-sigma zone (structure formation)
|
||||
-- HIGH damping_friction: more steps compressed toward the end (detail refinement)
|
||||
--
|
||||
-- This is the HAP component of the HT scheduler (used standalone here).
|
||||
-- ============================================================================
|
||||
|
||||
scheduler = {
|
||||
name = "md_hap",
|
||||
display = "MD HAP (Hamiltonian Potential Well)",
|
||||
description = "Particle-in-potential-well sigma schedule. Kinetic energy stretches mid steps, damping friction compresses end steps. Port of hap_scheduler_core v1.0.",
|
||||
params = {
|
||||
{
|
||||
key = "kinetic_energy",
|
||||
type = "slider",
|
||||
label = "Kinetic Energy",
|
||||
default = 1.0,
|
||||
min = 0.0,
|
||||
max = 5.0,
|
||||
step = 0.1,
|
||||
hint = "Initial velocity boost. Stretches steps in the middle of the trajectory (structure formation zone).",
|
||||
},
|
||||
{
|
||||
key = "damping_friction",
|
||||
type = "slider",
|
||||
label = "Damping Friction",
|
||||
default = 0.5,
|
||||
min = 0.0,
|
||||
max = 8.0,
|
||||
step = 0.1,
|
||||
hint = "Atmospheric drag. Compresses steps toward the end (detail refinement zone). Higher=more end compression.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local EPSILON = 1e-6
|
||||
|
||||
local function clamp(v, lo, hi)
|
||||
if v < lo then return lo end
|
||||
if v > hi then return hi end
|
||||
return v
|
||||
end
|
||||
|
||||
function schedule(output, num_steps, shift)
|
||||
local ke = (params and params.kinetic_energy) or 1.5
|
||||
local df = (params and params.damping_friction) or 3.0
|
||||
|
||||
-- Compute velocity at each normalized time point
|
||||
local velocity = {}
|
||||
for i = 0, num_steps - 1 do
|
||||
local t = i / math.max(num_steps - 1, 1)
|
||||
local v = (1.0 + ke * t) * math.exp(-df * t)
|
||||
velocity[i] = math.max(v, EPSILON) -- never negative
|
||||
end
|
||||
|
||||
-- Integrate: cumulative distance
|
||||
local distance = {}
|
||||
distance[0] = 0.0
|
||||
local running = 0.0
|
||||
for i = 0, num_steps - 1 do
|
||||
running = running + velocity[i]
|
||||
distance[i + 1] = running
|
||||
end
|
||||
|
||||
-- Normalize and map to sigma [1.0 → 0.0]
|
||||
local total = distance[num_steps]
|
||||
if total < EPSILON then total = EPSILON end
|
||||
|
||||
local sigmas = {}
|
||||
for i = 0, num_steps do
|
||||
sigmas[i] = 1.0 - (distance[i] / total)
|
||||
end
|
||||
|
||||
sigmas[0] = 1.0
|
||||
sigmas[num_steps] = 0.0
|
||||
|
||||
-- Shift warp
|
||||
if shift ~= 1.0 then
|
||||
for i = 0, num_steps do
|
||||
local t = sigmas[i]
|
||||
sigmas[i] = shift * t / (1.0 + (shift - 1.0) * t)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 0, num_steps - 1 do
|
||||
output[i] = sigmas[i]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,392 @@
|
||||
-- ============================================================================
|
||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-- Copyright (C) 2026 Alexander Allan (MDMAchine) -- A&E Concepts
|
||||
--
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
--
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details: https://www.gnu.org/licenses/
|
||||
-- ============================================================================
|
||||
|
||||
-- MD HT Scheduler v5.0 — HAP + TPT Thermodynamic Timestep Schedule
|
||||
-- MDMAchine | A&E Concepts © 2026
|
||||
--
|
||||
-- Plugin version: V3 (HOT-Step UI / filename — what users see)
|
||||
-- Internal version: v5.0 (math/changelog — what developers track)
|
||||
-- These are different: plugin version bumps on breaking changes or major
|
||||
-- feature drops. Internal version bumps on any code change.
|
||||
--
|
||||
-- Sub-index CDF interpolation, parameter caching, post-CDF smoothing,
|
||||
-- density floor, LINA warp, SNR-space mode, poly slope, uniformity blend,
|
||||
-- verbose step-size diagnostics.
|
||||
--
|
||||
-- WHAT THIS DOES:
|
||||
-- Standard schedulers space timesteps linearly or with a simple power curve.
|
||||
-- HT uses two coupled density functions to place steps where they matter:
|
||||
--
|
||||
-- HAP (Hamiltonian Action-Principle):
|
||||
-- density_hap = 1 / ((1 + KE * t) * exp(-DF * t))
|
||||
-- High KE = front-loaded steps (aggressive early denoising)
|
||||
-- High DF = fast exponential damping toward formation
|
||||
--
|
||||
-- TPT (Thermodynamic Phase Transition):
|
||||
-- density_tpt = 1 / (|sigma - Tc| + well_width)
|
||||
-- Creates a "gravity well" that clusters steps near the critical temp
|
||||
-- where latent structure crystallizes.
|
||||
--
|
||||
-- Combined: density = density_hap + phase_intensity * density_tpt + floor
|
||||
-- Result: non-uniform sigma sequence. Works at any step count (12-150+).
|
||||
--
|
||||
-- POST-PROCESSING CHAIN (all optional, all default off):
|
||||
-- 1. Shift warp (native HOT-Step sigma warp)
|
||||
-- 2. LINA warp (time-axis resampling from MD Causal)
|
||||
-- 3. Poly slope (power curve on sigma values)
|
||||
-- 4. Uniformity blend (blend with linear uniform schedule)
|
||||
-- 5. Schedule smoothing (moving average on final sigmas)
|
||||
--
|
||||
-- CHANGELOG:
|
||||
-- v5.0: Density floor, LINA warp, SNR-space mode, poly slope, uniformity
|
||||
-- blend, post-CDF smoothing, verbose diagnostics. Additive HAP+TPT
|
||||
-- blending. Exposed well width. Restored descriptive header. Complete
|
||||
-- rework from v4.0 baseline.
|
||||
-- ============================================================================
|
||||
|
||||
scheduler = {
|
||||
name = "md_ht_scheduler V3",
|
||||
display = "MD HT Scheduler (HAP+TPT) V3",
|
||||
description = "Thermodynamic timestep schedule: HAP + TPT additive density, density floor, LINA warp, SNR-space, poly slope, uniformity blend, smoothing. 12 to 150+ steps.",
|
||||
params = {
|
||||
-- ── HAP ─────────────────────────────────────────────────────────────
|
||||
{ key = "kinetic_energy", type = "slider", label = "Kinetic Energy",
|
||||
default = 0.3, min = 0.0, max = 3.0, step = 0.05,
|
||||
hint = "HAP leading-edge sharpness. 0=uniform, 0.3=standard, 2+=aggressive front-loading." },
|
||||
{ key = "damping_friction", type = "slider", label = "Damping Friction",
|
||||
default = 2.2, min = 0.0, max = 6.0, step = 0.1,
|
||||
hint = "HAP tail compression. Higher = steps cluster toward the front." },
|
||||
-- ── TPT ─────────────────────────────────────────────────────────────
|
||||
{ key = "critical_temp", type = "slider", label = "Critical Temp",
|
||||
default = 0.6, min = 0.05, max = 0.95, step = 0.05,
|
||||
hint = "TPT phase transition center (sigma fraction). Steps cluster here." },
|
||||
{ key = "phase_intensity", type = "slider", label = "Phase Intensity",
|
||||
default = 1.0, min = 0.0, max = 3.0, step = 0.1,
|
||||
hint = "TPT clustering strength. 0=off (pure HAP). 1=moderate. 2+=strong." },
|
||||
{ key = "well_width", type = "slider", label = "Well Width",
|
||||
default = 0.25, min = 0.05, max = 0.5, step = 0.05,
|
||||
hint = "TPT softening radius. 0.1=tight. 0.25=balanced. 0.4+=broad." },
|
||||
-- ── Density Floor ───────────────────────────────────────────────────
|
||||
{ key = "density_floor", type = "slider", label = "Density Floor",
|
||||
default = 0.1, min = 0.0, max = 1.0, step = 0.05,
|
||||
hint = "Minimum density everywhere. Prevents sparse gaps. 0=off. 0.1=gentle. 0.3+=uniform-leaning." },
|
||||
-- ── SNR Space ───────────────────────────────────────────────────────
|
||||
{ key = "snr_space", type = "toggle", label = "SNR Space",
|
||||
default = false,
|
||||
hint = "Compute density on an SNR-uniform grid instead of sigma-uniform. Steps track perceptual importance. Better for audio at high step counts." },
|
||||
-- ── Post-Processing ─────────────────────────────────────────────────
|
||||
{ key = "lina_shift", type = "slider", label = "LINA Warp",
|
||||
default = 1.0, min = 0.5, max = 2.0, step = 0.05,
|
||||
hint = "Time-axis resampling (from MD Causal). 1.0=off. <1=front-load (more high-sigma steps). >1=back-load (more low-sigma steps). Different from shift warp — this resamples WHERE on the curve, not the sigma VALUES." },
|
||||
{ key = "poly_slope", type = "slider", label = "Poly Slope",
|
||||
default = 1.0, min = 0.5, max = 2.0, step = 0.05,
|
||||
hint = "Power curve on sigma values. 1.0=off. >1=compress toward zero (more detail steps, good for long runs). <1=compress toward one (more structure steps, good for 12-step turbo)." },
|
||||
{ key = "uniform_blend", type = "slider", label = "Uniformity Blend",
|
||||
default = 0.0, min = 0.0, max = 1.0, step = 0.05,
|
||||
hint = "Blend with linear uniform schedule. 0=pure HT. 0.3=gentle uniformity. 1.0=pure uniform. Tames HT clustering for stabilization solvers (Trajectory Anchor)." },
|
||||
{ key = "smooth_window", type = "slider", label = "Schedule Smoothing",
|
||||
default = 0, min = 0, max = 7, step = 1,
|
||||
hint = "Post-CDF moving average on final sigmas. 0=off. 3=mild. 5+=heavy. Smooths step-size transitions." },
|
||||
-- ── Engine ──────────────────────────────────────────────────────────
|
||||
{ key = "dense_steps", type = "slider", label = "CDF Resolution",
|
||||
default = 1000, min = 200, max = 5000, step = 100,
|
||||
hint = "Resolution of the integration grid." },
|
||||
{ key = "shift", type = "slider", label = "Shift Warp",
|
||||
default = 1.0, min = 0.5, max = 8.0, step = 0.1,
|
||||
hint = "Native HOT-Step sigma warp. Applied first in the post-processing chain." },
|
||||
{ key = "verbose", type = "toggle", label = "Verbose",
|
||||
default = false,
|
||||
hint = "Print per-step sigma values, step sizes, and gap ratio to console." },
|
||||
},
|
||||
}
|
||||
|
||||
-- ── Hoisted Buffers & Cache ──────────────────────────────────────────────────
|
||||
|
||||
local EPSILON = 1e-6
|
||||
|
||||
local _cache = { ke = -1, df = -1, tc = -1, pi = -1, ww = -1, fl = -1, snr = -1, dense_n = -1 }
|
||||
local _dense = {}
|
||||
local _cdf = {}
|
||||
local _sigmas = {}
|
||||
local _smooth_buf = {}
|
||||
local _last_num_steps = -1
|
||||
|
||||
local function clamp(v, lo, hi)
|
||||
if v < lo then return lo end
|
||||
if v > hi then return hi end
|
||||
return v
|
||||
end
|
||||
|
||||
-- ── SNR-Space Grid ──────────────────────────────────────────────────────────
|
||||
-- Build dense grid uniform in SNR space: snr = log(sigma / (1 - sigma)).
|
||||
-- Maps back to sigma via sigmoid: sigma = 1 / (1 + exp(-snr)).
|
||||
-- Endpoints clamped to avoid inf at sigma=0 and sigma=1.
|
||||
|
||||
local function build_snr_grid(dense, dense_n, sigma_max, sigma_min)
|
||||
local s_hi = clamp(sigma_max, 0.001, 0.999)
|
||||
local s_lo = clamp(sigma_min + 0.001, 0.001, 0.999)
|
||||
local snr_hi = math.log(s_hi / (1.0 - s_hi))
|
||||
local snr_lo = math.log(s_lo / (1.0 - s_lo))
|
||||
|
||||
for i = 0, dense_n - 1 do
|
||||
local snr = snr_hi + (snr_lo - snr_hi) * i / (dense_n - 1)
|
||||
dense[i] = 1.0 / (1.0 + math.exp(-snr))
|
||||
end
|
||||
dense[0] = sigma_max
|
||||
dense[dense_n - 1] = sigma_min
|
||||
end
|
||||
|
||||
-- ── LINA Warp (from MD Causal) ──────────────────────────────────────────────
|
||||
-- Time-axis resampling: t_warped = t^shift, then interpolate into the sigma
|
||||
-- array at the warped position. Shift < 1 front-loads, shift > 1 back-loads.
|
||||
-- Different from native shift warp which transforms sigma values directly.
|
||||
|
||||
local function apply_lina_warp(sigmas, n, shift)
|
||||
if shift == 1.0 then return end
|
||||
-- Read into scratch buffer first
|
||||
for i = 0, n do _smooth_buf[i] = sigmas[i] end
|
||||
|
||||
for i = 0, n do
|
||||
local t = i / n
|
||||
local t_w = t ^ shift
|
||||
local raw_idx = t_w * n
|
||||
local idx_lo = clamp(math.floor(raw_idx), 0, n)
|
||||
local idx_hi = clamp(idx_lo + 1, 0, n)
|
||||
local frac = raw_idx - idx_lo
|
||||
local s_lo = _smooth_buf[idx_lo]
|
||||
local s_hi = _smooth_buf[idx_hi] or _smooth_buf[n]
|
||||
sigmas[i] = s_lo * (1.0 - frac) + s_hi * frac
|
||||
end
|
||||
sigmas[0] = _smooth_buf[0]
|
||||
sigmas[n] = _smooth_buf[n]
|
||||
end
|
||||
|
||||
-- ── Post-CDF Schedule Smoothing ─────────────────────────────────────────────
|
||||
|
||||
local function smooth_schedule(sigmas, n, window)
|
||||
if window < 2 or n < window then return end
|
||||
local half = math.floor(window / 2)
|
||||
|
||||
for i = 1, n - 1 do
|
||||
local sum = 0.0
|
||||
local count = 0
|
||||
for j = math.max(0, i - half), math.min(n, i + half) do
|
||||
sum = sum + sigmas[j]
|
||||
count = count + 1
|
||||
end
|
||||
_smooth_buf[i] = sum / count
|
||||
end
|
||||
|
||||
for i = 1, n - 1 do
|
||||
sigmas[i] = _smooth_buf[i]
|
||||
end
|
||||
|
||||
-- Enforce monotonically decreasing
|
||||
for i = 1, n do
|
||||
if sigmas[i] >= sigmas[i - 1] then
|
||||
sigmas[i] = sigmas[i - 1] - EPSILON
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ── Core Schedule Builder ────────────────────────────────────────────────────
|
||||
|
||||
local function build_ht_schedule(num_steps, ke, df, tc_frac, pi, ww, fl, snr_mode, dense_n)
|
||||
local sigma_max = 1.0
|
||||
local sigma_min = 0.0
|
||||
local snr_flag = snr_mode and 1 or 0
|
||||
|
||||
-- Only rebuild CDF if params changed
|
||||
if ke ~= _cache.ke or df ~= _cache.df or tc_frac ~= _cache.tc
|
||||
or pi ~= _cache.pi or ww ~= _cache.ww or fl ~= _cache.fl
|
||||
or snr_flag ~= _cache.snr or dense_n ~= _cache.dense_n then
|
||||
|
||||
-- Build dense grid (sigma-uniform or SNR-uniform)
|
||||
if snr_mode then
|
||||
build_snr_grid(_dense, dense_n, sigma_max, sigma_min)
|
||||
else
|
||||
for i = 0, dense_n - 1 do
|
||||
_dense[i] = sigma_max - (sigma_max - sigma_min) * i / (dense_n - 1)
|
||||
end
|
||||
end
|
||||
|
||||
local critical_temp = sigma_min + tc_frac * (sigma_max - sigma_min)
|
||||
local running = 0.0
|
||||
|
||||
for i = 0, dense_n - 1 do
|
||||
local s = _dense[i]
|
||||
local t = (sigma_max - s) / (sigma_max - sigma_min + EPSILON)
|
||||
|
||||
-- HAP density
|
||||
local v_hap = (1.0 + ke * t) * math.exp(-df * t)
|
||||
local d_hap = 1.0 / (v_hap + EPSILON)
|
||||
|
||||
-- TPT density
|
||||
local dist_tc = math.abs(s - critical_temp)
|
||||
local d_tpt = 1.0 / (dist_tc + ww)
|
||||
|
||||
-- Additive blending + density floor
|
||||
running = running + d_hap + pi * d_tpt + fl
|
||||
_cdf[i] = running
|
||||
end
|
||||
|
||||
-- Normalize CDF to [0, 1]
|
||||
local cdf0 = _cdf[0]
|
||||
local cdf_n1 = _cdf[dense_n - 1]
|
||||
local range = cdf_n1 - cdf0 + EPSILON
|
||||
|
||||
for i = 0, dense_n - 1 do
|
||||
_cdf[i] = (_cdf[i] - cdf0) / range
|
||||
end
|
||||
|
||||
_cache.ke = ke
|
||||
_cache.df = df
|
||||
_cache.tc = tc_frac
|
||||
_cache.pi = pi
|
||||
_cache.ww = ww
|
||||
_cache.fl = fl
|
||||
_cache.snr = snr_flag
|
||||
_cache.dense_n = dense_n
|
||||
end
|
||||
|
||||
-- Pre-allocate
|
||||
if num_steps > _last_num_steps then
|
||||
for i = 0, num_steps do _sigmas[i] = 0.0 end
|
||||
for i = 0, num_steps do _smooth_buf[i] = 0.0 end
|
||||
_last_num_steps = num_steps
|
||||
end
|
||||
|
||||
-- Binary search
|
||||
local function searchsorted(target)
|
||||
local lo, hi = 0, dense_n - 1
|
||||
while lo < hi do
|
||||
local mid = math.floor((lo + hi) / 2)
|
||||
if _cdf[mid] < target then lo = mid + 1 else hi = mid end
|
||||
end
|
||||
return clamp(lo, 0, dense_n - 1)
|
||||
end
|
||||
|
||||
-- Sub-index interpolation
|
||||
for i = 0, num_steps do
|
||||
local tgt = i / num_steps
|
||||
local idx = searchsorted(tgt)
|
||||
|
||||
if idx == 0 then
|
||||
_sigmas[i] = _dense[0]
|
||||
else
|
||||
local c0 = _cdf[idx - 1]
|
||||
local c1 = _cdf[idx]
|
||||
local t_interp = (c1 > c0) and ((tgt - c0) / (c1 - c0)) or 0.0
|
||||
|
||||
local d0 = _dense[idx - 1]
|
||||
local d1 = _dense[idx]
|
||||
_sigmas[i] = d0 + t_interp * (d1 - d0)
|
||||
end
|
||||
end
|
||||
|
||||
-- Force exact endpoints
|
||||
_sigmas[0] = sigma_max
|
||||
_sigmas[num_steps] = sigma_min
|
||||
|
||||
return _sigmas
|
||||
end
|
||||
|
||||
-- ── Required schedule() function ─────────────────────────────────────────────
|
||||
|
||||
function schedule(output, num_steps, shift_val)
|
||||
local ke = (params and params.kinetic_energy) or 0.3
|
||||
local df = (params and params.damping_friction) or 2.2
|
||||
local tc_frac = (params and params.critical_temp) or 0.6
|
||||
local pi_ = (params and params.phase_intensity) or 1.0
|
||||
local ww = (params and params.well_width) or 0.25
|
||||
local fl = (params and params.density_floor) or 0.1
|
||||
local snr_mode = (params and params.snr_space) or false
|
||||
local lina = (params and params.lina_shift) or 1.0
|
||||
local poly = (params and params.poly_slope) or 1.0
|
||||
local u_blend = (params and params.uniform_blend) or 0.0
|
||||
local sm_win = math.floor((params and params.smooth_window) or 0)
|
||||
local dense_n = math.floor((params and params.dense_steps) or 1000)
|
||||
local sh = (params and params.shift) or shift_val
|
||||
local verbose = (params and params.verbose) or false
|
||||
|
||||
local sigmas = build_ht_schedule(num_steps, ke, df, tc_frac, pi_, ww, fl, snr_mode, dense_n)
|
||||
|
||||
-- ── POST-PROCESSING CHAIN ───────────────────────────────────────────
|
||||
-- Order: shift → LINA → poly → uniformity → smooth
|
||||
-- Each is independent and default-off. Compose cleanly at any step count.
|
||||
|
||||
-- 1. Native shift warp (sigma-value transform)
|
||||
if sh ~= 1.0 then
|
||||
for i = 0, num_steps do
|
||||
local t = sigmas[i]
|
||||
sigmas[i] = sh * t / (1.0 + (sh - 1.0) * t)
|
||||
end
|
||||
end
|
||||
|
||||
-- 2. LINA warp (time-axis resampling)
|
||||
if lina ~= 1.0 then
|
||||
apply_lina_warp(sigmas, num_steps, lina)
|
||||
end
|
||||
|
||||
-- 3. Poly slope (power curve on sigma values)
|
||||
-- >1 = compress toward zero (more detail steps)
|
||||
-- <1 = compress toward one (more structure steps, good for 12-step turbo)
|
||||
if poly ~= 1.0 then
|
||||
for i = 1, num_steps - 1 do
|
||||
sigmas[i] = sigmas[i] ^ poly
|
||||
end
|
||||
-- Endpoints stay exact
|
||||
end
|
||||
|
||||
-- 4. Uniformity blend (blend with linear schedule)
|
||||
-- Tames HT clustering for stabilization solvers
|
||||
if u_blend > 0.0 then
|
||||
local inv = 1.0 - u_blend
|
||||
for i = 0, num_steps do
|
||||
local uniform_sigma = 1.0 - (i / num_steps)
|
||||
sigmas[i] = inv * sigmas[i] + u_blend * uniform_sigma
|
||||
end
|
||||
end
|
||||
|
||||
-- 5. Schedule smoothing (moving average)
|
||||
if sm_win >= 2 then
|
||||
smooth_schedule(sigmas, num_steps, sm_win)
|
||||
sigmas[0] = 1.0
|
||||
sigmas[num_steps] = 0.0
|
||||
end
|
||||
|
||||
-- ── WRITE OUTPUT (no trailing zero — engine contract) ───────────────
|
||||
for i = 0, num_steps - 1 do
|
||||
output[i] = sigmas[i]
|
||||
end
|
||||
|
||||
-- ── VERBOSE ─────────────────────────────────────────────────────────
|
||||
if verbose then
|
||||
print(string.format(
|
||||
"[HT V5] %d steps | ke=%.2f df=%.1f tc=%.2f pi=%.1f ww=%.2f fl=%.2f | snr=%s lina=%.2f poly=%.2f ub=%.2f sm=%d",
|
||||
num_steps, ke, df, tc_frac, pi_, ww, fl,
|
||||
snr_mode and "on" or "off", lina, poly, u_blend, sm_win))
|
||||
local min_gap, max_gap = 1.0, 0.0
|
||||
for i = 0, num_steps - 1 do
|
||||
local sigma_next = (i < num_steps - 1) and sigmas[i + 1] or 0.0
|
||||
local gap = sigmas[i] - sigma_next
|
||||
if gap < min_gap then min_gap = gap end
|
||||
if gap > max_gap then max_gap = gap end
|
||||
print(string.format(" step %02d: sigma=%.5f gap=%.5f", i, sigmas[i], gap))
|
||||
end
|
||||
print(string.format("[HT V5] Gap range: min=%.5f max=%.5f ratio=%.1f:1",
|
||||
min_gap, max_gap, max_gap / (min_gap + EPSILON)))
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user