Files
wehub-resource-sync cddb07a176
docs / deploy (push) Has been cancelled
docs / changes (push) Has been cancelled
docs / check-and-build (push) Has been cancelled
build container image / cpu (push) Has been cancelled
build container image / cuda (push) Has been cancelled
build container image / rocm (push) Has been cancelled
frontend checks / frontend-checks (push) Has been cancelled
frontend tests / frontend-tests (push) Has been cancelled
lfs checks / lfs-check (push) Has been cancelled
python checks / python-checks (push) Has been cancelled
python tests / py3.12: macos-default (push) Has been cancelled
python tests / py3.11: windows-cpu (push) Has been cancelled
python tests / py3.12: windows-cpu (push) Has been cancelled
python tests / py3.11: linux-cpu (push) Has been cancelled
typegen checks / typegen-checks (push) Has been cancelled
uv lock checks / uv-lock-checks (push) Has been cancelled
openapi checks / openapi-checks (push) Has been cancelled
python tests / py3.11: macos-default (push) Has been cancelled
python tests / py3.12: linux-cpu (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:06 +08:00

145 lines
5.7 KiB
Python

import pytest
from invokeai.app.invocations.anima_denoise import (
ANIMA_SHIFT,
AnimaDenoiseInvocation,
inverse_loglinear_timestep_shift,
loglinear_timestep_shift,
)
class TestLoglinearTimestepShift:
"""Test the log-linear timestep shift function used for Anima's noise schedule."""
def test_shift_1_is_identity(self):
"""With alpha=1.0, shift should be identity."""
for t in [0.0, 0.25, 0.5, 0.75, 1.0]:
assert loglinear_timestep_shift(1.0, t) == t
def test_shift_at_zero(self):
"""At t=0, shifted sigma should be 0 regardless of alpha."""
assert loglinear_timestep_shift(3.0, 0.0) == 0.0
def test_shift_at_one(self):
"""At t=1, shifted sigma should be 1 regardless of alpha."""
assert loglinear_timestep_shift(3.0, 1.0) == pytest.approx(1.0)
def test_shift_3_increases_sigma(self):
"""With alpha=3.0, sigma should be larger than t (spends more time at high noise)."""
for t in [0.1, 0.25, 0.5, 0.75, 0.9]:
sigma = loglinear_timestep_shift(3.0, t)
assert sigma > t, f"At t={t}, sigma={sigma} should be > t"
def test_shift_monotonic(self):
"""Shifted sigmas should be monotonically increasing with t."""
prev = 0.0
for i in range(1, 101):
t = i / 100.0
sigma = loglinear_timestep_shift(3.0, t)
assert sigma > prev, f"Not monotonic at t={t}"
prev = sigma
def test_known_value(self):
"""Test a known value: at t=0.5, alpha=3.0, sigma = 3*0.5 / (1 + 2*0.5) = 0.75."""
assert loglinear_timestep_shift(3.0, 0.5) == pytest.approx(0.75)
class TestInverseLoglinearTimestepShift:
"""Test the inverse log-linear timestep shift (used for inpainting mask correction)."""
def test_inverse_shift_1_is_identity(self):
"""With alpha=1.0, inverse should be identity."""
for sigma in [0.0, 0.25, 0.5, 0.75, 1.0]:
assert inverse_loglinear_timestep_shift(1.0, sigma) == sigma
def test_roundtrip(self):
"""shift(inverse(sigma)) should recover sigma, and inverse(shift(t)) should recover t."""
for t in [0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.0]:
sigma = loglinear_timestep_shift(3.0, t)
recovered_t = inverse_loglinear_timestep_shift(3.0, sigma)
assert recovered_t == pytest.approx(t, abs=1e-7), (
f"Roundtrip failed: t={t} -> sigma={sigma} -> recovered_t={recovered_t}"
)
def test_known_value(self):
"""At sigma=0.75, alpha=3.0, t should be 0.5 (inverse of the known shift value)."""
assert inverse_loglinear_timestep_shift(3.0, 0.75) == pytest.approx(0.5)
class TestGetSigmas:
"""Test the sigma schedule generation."""
def test_schedule_length(self):
"""Schedule should have num_steps + 1 entries."""
inv = AnimaDenoiseInvocation(
positive_conditioning=None, # type: ignore
transformer=None, # type: ignore
)
sigmas = inv._get_sigmas(30)
assert len(sigmas) == 31
def test_schedule_endpoints(self):
"""Schedule should start near 1.0 and end at 0.0."""
inv = AnimaDenoiseInvocation(
positive_conditioning=None, # type: ignore
transformer=None, # type: ignore
)
sigmas = inv._get_sigmas(30)
assert sigmas[0] == pytest.approx(loglinear_timestep_shift(ANIMA_SHIFT, 1.0))
assert sigmas[-1] == pytest.approx(0.0)
def test_schedule_monotonically_decreasing(self):
"""Sigmas should decrease from noise to clean."""
inv = AnimaDenoiseInvocation(
positive_conditioning=None, # type: ignore
transformer=None, # type: ignore
)
sigmas = inv._get_sigmas(30)
for i in range(len(sigmas) - 1):
assert sigmas[i] > sigmas[i + 1], f"Not decreasing at index {i}: {sigmas[i]} <= {sigmas[i + 1]}"
def test_schedule_uses_shift(self):
"""With shift=3.0, middle sigmas should be larger than the linear midpoint."""
inv = AnimaDenoiseInvocation(
positive_conditioning=None, # type: ignore
transformer=None, # type: ignore
)
sigmas = inv._get_sigmas(10)
# At step 5/10, linear t = 0.5, shifted sigma should be 0.75
assert sigmas[5] == pytest.approx(loglinear_timestep_shift(3.0, 0.5))
class TestGetSigmasEdgeCases:
"""Test edge cases for sigma schedule generation."""
def test_single_step_produces_valid_schedule(self):
"""_get_sigmas(num_steps=1) should produce a valid 2-element schedule."""
inv = AnimaDenoiseInvocation(
positive_conditioning=None, # type: ignore
transformer=None, # type: ignore
)
sigmas = inv._get_sigmas(1)
assert len(sigmas) == 2
assert sigmas[0] > sigmas[1]
assert sigmas[0] == pytest.approx(loglinear_timestep_shift(ANIMA_SHIFT, 1.0))
assert sigmas[-1] == pytest.approx(0.0)
class TestInverseLoglinearEdgeCases:
"""Test edge cases for inverse_loglinear_timestep_shift."""
def test_alpha_zero_does_not_divide_by_zero(self):
"""inverse_loglinear_timestep_shift with alpha=0 should not raise ZeroDivisionError.
With alpha=0: denominator = 0 - (0-1)*sigma = sigma.
At sigma=0, denominator=0 which hits the epsilon guard and returns 1.0.
At sigma>0, denominator=sigma, result = sigma/sigma = 1.0.
"""
# Should not raise
result = inverse_loglinear_timestep_shift(0.0, 0.5)
assert isinstance(result, float)
# At sigma=0, denominator would be 0 — should hit the epsilon guard
result_zero = inverse_loglinear_timestep_shift(0.0, 0.0)
assert isinstance(result_zero, float)