Initial release
This commit is contained in:
@@ -0,0 +1,280 @@
|
||||
-- ============================================================================
|
||||
-- SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-- Copyright (C) 2026 Alexander Allan (MDMAchine) -- A&E Concepts
|
||||
-- ============================================================================
|
||||
|
||||
-- MD OmniRelational Solver v3.0 -- Barbour Best Matching + Sigma Decay + Look-Back
|
||||
-- MDMAchine | A&E Concepts 2026
|
||||
--
|
||||
-- WHAT IS NEW IN V3:
|
||||
--
|
||||
-- V1/V2 applied the same relational weight (rw) at every step regardless
|
||||
-- of where you are in the denoising schedule. This is suboptimal:
|
||||
--
|
||||
-- High sigma (early steps): structure formation phase. The latent is still
|
||||
-- mostly noise. Shape-preserving relational geometry matters most here --
|
||||
-- normalizing direction prevents any single component dominating.
|
||||
--
|
||||
-- Low sigma (late steps): detail refinement phase. The latent is close to
|
||||
-- x0. Raw velocity is more accurate. Relational re-injection here over-
|
||||
-- smooths fine detail.
|
||||
--
|
||||
-- SIGMA-ADAPTIVE RELATIONAL WEIGHT:
|
||||
-- rw_eff = rw * (t_curr / sigma_max) ^ sigma_power
|
||||
-- At t=sigma_max (first step): rw_eff = rw (full effect)
|
||||
-- At t=0 (final step): rw_eff = 0 (pure Euler)
|
||||
-- sigma_power controls the decay curve. 1.0 = linear, 2.0 = quadratic.
|
||||
--
|
||||
-- LOOK-BACK SNR SMOOTHER (arXiv:2602.09449):
|
||||
-- lambda_eff = lb_lambda * (t_curr / sigma_max) ^ lb_snr_power
|
||||
-- x_smooth = (1 - lambda_eff) * x_next + lambda_eff * x_prev
|
||||
-- Heavy at high sigma (blends structure), fades at low sigma (preserves
|
||||
-- detail). Same mechanism as STORM and PingPong. Off by default.
|
||||
--
|
||||
-- GENERATION STATE RESET:
|
||||
-- Module-level state (sigma_max, x_prev for look-back) resets on
|
||||
-- step_idx_==0 so same-size consecutive generations don't bleed.
|
||||
--
|
||||
-- DRIFT GUARD kept from V1 -- simple, clean, no hoisted buffer.
|
||||
|
||||
solver = {
|
||||
name = "md_omni_relational_V3",
|
||||
display = "MD OmniRelational V3 (Sigma Adaptive)",
|
||||
description = "Barbour Best Matching with sigma-adaptive relational weight and look-back smoother. rw fades toward pure Euler at low sigma. Proper state reset between generations.",
|
||||
nfe = 1,
|
||||
order = 1,
|
||||
needs_model = false,
|
||||
stateful = true,
|
||||
stochastic = true,
|
||||
params = {
|
||||
{
|
||||
key = "relational_weight",
|
||||
type = "slider",
|
||||
label = "Relational Weight",
|
||||
default = 0.5,
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
step = 0.05,
|
||||
hint = "Base relational weight at high sigma. Fades toward 0 at low sigma. 0=pure Euler always. 0.5=balanced at structure phase.",
|
||||
},
|
||||
{
|
||||
key = "sigma_power",
|
||||
type = "slider",
|
||||
label = "Sigma Decay Power",
|
||||
default = 1.0,
|
||||
min = 0.25,
|
||||
max = 4.0,
|
||||
step = 0.25,
|
||||
hint = "Controls how fast rw fades with sigma. 1.0=linear decay. 2.0=quadratic (faster fade). 0.5=slow fade. Higher = relational effect concentrated earlier.",
|
||||
},
|
||||
{
|
||||
key = "look_back_lambda",
|
||||
type = "slider",
|
||||
label = "Look-Back Lambda",
|
||||
default = 0.0,
|
||||
min = 0.0,
|
||||
max = 0.5,
|
||||
step = 0.01,
|
||||
hint = "Look-back coherence smoother. 0=off. Blends current step with previous, fading out at low sigma. Suppresses trajectory shear. Start at 0.05-0.15.",
|
||||
},
|
||||
{
|
||||
key = "look_back_snr_power",
|
||||
type = "slider",
|
||||
label = "Look-Back SNR Power",
|
||||
default = 1.5,
|
||||
min = 0.5,
|
||||
max = 3.0,
|
||||
step = 0.1,
|
||||
hint = "Controls how fast look-back fades with sigma. Higher = smoothing concentrated on early structure steps only.",
|
||||
},
|
||||
{
|
||||
key = "drift_guard",
|
||||
type = "toggle",
|
||||
label = "Drift Guard (AOS)",
|
||||
default = false,
|
||||
hint = "Project shape_vec onto orthogonal complement of x when cos_sim exceeds threshold. Prevents update reinforcing existing latent structure.",
|
||||
},
|
||||
{
|
||||
key = "drift_threshold",
|
||||
type = "slider",
|
||||
label = "Drift Threshold",
|
||||
default = 0.85,
|
||||
min = 0.1,
|
||||
max = 1.0,
|
||||
step = 0.05,
|
||||
hint = "Cosine similarity ceiling before Gram-Schmidt projection fires. 0.85=standard. Only active when Drift Guard is on.",
|
||||
},
|
||||
{
|
||||
key = "eta",
|
||||
type = "slider",
|
||||
label = "Eta (SDE Noise)",
|
||||
default = 0.0,
|
||||
min = 0.0,
|
||||
max = 1.0,
|
||||
step = 0.05,
|
||||
hint = "Ancestral noise injection. 0=deterministic ODE. Scales with t_prev.",
|
||||
},
|
||||
{
|
||||
key = "seed",
|
||||
type = "slider",
|
||||
label = "Seed",
|
||||
default = 42,
|
||||
min = 0,
|
||||
max = 999999,
|
||||
step = 1,
|
||||
hint = "RNG seed for SDE noise.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local EPSILON = 1e-8
|
||||
|
||||
-- Module state -- reset on step_idx_==0
|
||||
local _sigma_max = nil
|
||||
local _x_prev_lb = nil -- look-back previous x (before update)
|
||||
|
||||
local function clamp(v, lo, hi)
|
||||
if v < lo then return lo end
|
||||
if v > hi then return hi end
|
||||
return v
|
||||
end
|
||||
|
||||
local function make_rng(seed)
|
||||
local state = math.floor(seed) % 2147483647
|
||||
if state <= 0 then state = state + 2147483646 end
|
||||
return function()
|
||||
state = (state * 1664525 + 1013904223) % 2147483648
|
||||
return state / 2147483648.0
|
||||
end
|
||||
end
|
||||
|
||||
local function normal(u1, u2)
|
||||
return math.sqrt(-2.0 * math.log(math.max(u1, EPSILON))) * math.cos(2.0 * math.pi * u2)
|
||||
end
|
||||
|
||||
local function l2_norm(arr, n)
|
||||
local s = 0.0
|
||||
for i = 0, n - 1 do s = s + arr[i] * arr[i] end
|
||||
return math.sqrt(s + EPSILON)
|
||||
end
|
||||
|
||||
-- Shape decomposition: returns unit direction table + L2 norm (mean_scale)
|
||||
local function decompose_shape(vt, n)
|
||||
local norm = l2_norm(vt, n)
|
||||
local inv = norm > EPSILON and (1.0 / norm) or 0.0
|
||||
local shape = {}
|
||||
for i = 0, n - 1 do shape[i] = vt[i] * inv end
|
||||
return shape, norm
|
||||
end
|
||||
|
||||
-- Gram-Schmidt drift guard: projects shape onto orthogonal complement of xt
|
||||
local function apply_drift_guard(shape, xt, n, threshold)
|
||||
local norm_x = l2_norm(xt, n)
|
||||
if norm_x < EPSILON then return shape end
|
||||
|
||||
local inv_x = 1.0 / norm_x
|
||||
local dot_sx = 0.0
|
||||
for i = 0, n - 1 do dot_sx = dot_sx + shape[i] * (xt[i] * inv_x) end
|
||||
|
||||
if math.abs(dot_sx) <= threshold then return shape end
|
||||
|
||||
local proj = {}
|
||||
for i = 0, n - 1 do
|
||||
proj[i] = shape[i] - dot_sx * (xt[i] * inv_x)
|
||||
end
|
||||
|
||||
local proj_norm = l2_norm(proj, n)
|
||||
if proj_norm < EPSILON then return shape end
|
||||
|
||||
local inv_proj = 1.0 / proj_norm
|
||||
for i = 0, n - 1 do proj[i] = proj[i] * inv_proj end
|
||||
return proj
|
||||
end
|
||||
|
||||
-- Look-back SNR smoother: blend x_curr toward x_prev, lambda fades with sigma
|
||||
local function look_back_smooth(x_curr, x_prev, t_curr, sigma_max, lb_lambda, snr_power, n)
|
||||
if x_prev == nil or lb_lambda < EPSILON then return x_curr, 0.0 end
|
||||
local ratio = math.min(t_curr / math.max(sigma_max, EPSILON), 1.0)
|
||||
local lam = lb_lambda * (ratio ^ snr_power)
|
||||
local out = {}
|
||||
for i = 0, n - 1 do
|
||||
out[i] = (1.0 - lam) * x_curr[i] + lam * x_prev[i]
|
||||
end
|
||||
return out, lam
|
||||
end
|
||||
|
||||
-- ── step() function ───────────────────────────────────────────────────────────
|
||||
|
||||
function step(xt, vt, t_curr, t_prev, n)
|
||||
local rw = clamp((params and params.relational_weight) or 0.5, 0.0, 1.0)
|
||||
local sig_power = clamp((params and params.sigma_power) or 1.0, 0.25, 4.0)
|
||||
local lb_lambda = clamp((params and params.look_back_lambda) or 0.0, 0.0, 0.5)
|
||||
local lb_snr_pow = clamp((params and params.look_back_snr_power) or 1.5, 0.5, 3.0)
|
||||
local drift_on = (params and params.drift_guard) or false
|
||||
local drift_thr = clamp((params and params.drift_threshold) or 0.85, 0.1, 1.0)
|
||||
local eta = clamp((params and params.eta) or 0.0, 0.0, 1.0)
|
||||
local seed = math.floor((params and params.seed) or 42)
|
||||
|
||||
local step_idx_ = step_index or 0
|
||||
|
||||
-- Reset state at start of each generation
|
||||
if step_idx_ == 0 then
|
||||
_sigma_max = t_curr
|
||||
_x_prev_lb = nil
|
||||
end
|
||||
if _sigma_max == nil then _sigma_max = t_curr end
|
||||
|
||||
-- Snapshot xt before update for look-back (copy to plain table)
|
||||
local x_curr_snapshot = nil
|
||||
if lb_lambda > EPSILON then
|
||||
x_curr_snapshot = {}
|
||||
for i = 0, n - 1 do x_curr_snapshot[i] = xt[i] end
|
||||
end
|
||||
|
||||
-- ── 1. Shape decomposition ────────────────────────────────────────────────
|
||||
local shape_vec, mean_scale = decompose_shape(vt, n)
|
||||
|
||||
-- ── 2. Optional drift guard ───────────────────────────────────────────────
|
||||
if drift_on and drift_thr < 1.0 then
|
||||
-- xt is a plain table in step() -- pass directly
|
||||
local xt_tbl = {}
|
||||
for i = 0, n - 1 do xt_tbl[i] = xt[i] end
|
||||
shape_vec = apply_drift_guard(shape_vec, xt_tbl, n, drift_thr)
|
||||
end
|
||||
|
||||
-- ── 3. Sigma-adaptive relational weight ───────────────────────────────────
|
||||
-- rw_eff = rw * (t_curr / sigma_max) ^ sigma_power
|
||||
-- At high sigma: rw_eff = rw (full relational). At low sigma: fades to 0.
|
||||
local sigma_ratio = math.min(t_curr / math.max(_sigma_max, EPSILON), 1.0)
|
||||
local rw_eff = rw * (sigma_ratio ^ sig_power)
|
||||
|
||||
-- ── 4. Blend + Euler update ───────────────────────────────────────────────
|
||||
local dt = t_prev - t_curr -- negative in flow-matching
|
||||
local x_next = {}
|
||||
for i = 0, n - 1 do
|
||||
local rel_i = shape_vec[i] * mean_scale
|
||||
local eff_vt_i = rw_eff * rel_i + (1.0 - rw_eff) * vt[i]
|
||||
x_next[i] = xt[i] + dt * eff_vt_i
|
||||
end
|
||||
|
||||
-- ── 5. Look-back smoother ─────────────────────────────────────────────────
|
||||
if lb_lambda > EPSILON then
|
||||
local lam
|
||||
x_next, lam = look_back_smooth(x_next, _x_prev_lb, t_curr, _sigma_max, lb_lambda, lb_snr_pow, n)
|
||||
_x_prev_lb = x_curr_snapshot
|
||||
end
|
||||
|
||||
-- ── 6. Write x_next back to xt ───────────────────────────────────────────
|
||||
for i = 0, n - 1 do xt[i] = x_next[i] end
|
||||
|
||||
-- ── 7. Optional SDE noise ─────────────────────────────────────────────────
|
||||
if eta > 0.0 and t_prev > EPSILON then
|
||||
local rng = make_rng(seed + step_idx_ * 7919)
|
||||
local scale = t_prev * eta
|
||||
for i = 0, n - 1 do
|
||||
local u1 = math.max(rng(), EPSILON)
|
||||
local u2 = rng()
|
||||
xt[i] = xt[i] + normal(u1, u2) * scale
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user