feat: add warp_endpoints (global residual correction, shape-preserving)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
dog
2026-07-09 17:44:07 +08:00
parent d1f70e5753
commit 556f7f861d
2 changed files with 75 additions and 0 deletions

View File

@@ -206,3 +206,45 @@ def test_damp_start_short_input_safe() -> None:
lat = np.array([0.0, 0.5, 0.3])
out = damp_start(lat, n=4) # n capped to len//4 = 0 → no-op
assert np.array_equal(out, lat)
from ai_mouse._postprocess import warp_endpoints
def test_warp_endpoints_exact_pin() -> None:
f = np.linspace(0.05, 1.10, 32)
l = np.linspace(0.03, -0.07, 32)
fo, lo = warp_endpoints(f, l)
assert fo[0] == 0.0 and lo[0] == 0.0
assert fo[-1] == 1.0 and lo[-1] == 0.0
def test_warp_endpoints_identity_when_already_pinned() -> None:
f = np.linspace(0.0, 1.0, 32)
l = np.sin(np.linspace(0, np.pi, 32)) * 0.1
l[0] = l[-1] = 0.0
fo, lo = warp_endpoints(f.copy(), l.copy())
assert np.allclose(fo, f, atol=1e-12)
assert np.allclose(lo, l, atol=1e-12)
def test_warp_endpoints_preserves_smoothness() -> None:
# Correcting a smooth curve must not introduce sharp local bends:
# the warp adds a smoothstep-weighted offset, so the second
# difference (discrete curvature proxy) stays small.
f = np.linspace(0.02, 1.08, 32)
l = np.full(32, 0.05)
fo, lo = warp_endpoints(f, l)
assert np.abs(np.diff(lo, 2)).max() < 0.01
assert np.abs(np.diff(fo, 2)).max() < 0.01
def test_warp_endpoints_correction_local_to_each_end() -> None:
# A start-only residual should barely move the last quarter.
# (smoothstep weight at i=24/31 is ~0.13, so 0.08 residual leaves
# ~0.010 there — threshold 0.02 gives margin without losing meaning)
f = np.linspace(0.0, 1.0, 32) + 0.0
l = np.zeros(32)
f[0] = 0.08 # start residual only
fo, _ = warp_endpoints(f, l)
assert np.abs(fo[24:] - f[24:]).max() < 0.02