File size: 12,702 Bytes
e488ce7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | #!/usr/bin/env python3
"""Unit test for H3 Temporal Insert, the node that does the time-smear retime
in LATENT space: expand the video latent onto the dilated token grid,
interpolate the inserted token-times, freeze the originals with a mask.
python tests/test_temporal_insert.py
Synthetic, no GPU, no models. comfy.nested_tensor is stubbed.
What it checks:
1. THE T2a MAP, row for row against the probe's worked example
(ComfyUI-ModelCatalog benchmarks/scripts/tinterp/token_map.csv, the
124f clip with hold 2 on world frames 85..118): 124 -> 158 frames,
37 -> 47 tokens, and the exact interpolation weights - smear token 26
is w=0.400 between base tokens 25 and 26. The map is PORTED from that
harness, so this test is what stops it drifting.
2. THE EXACTNESS INVARIANT. Every token-time whose target lands on a base
token centre (all the copied originals, and inserted singletons aligned
to base 17-multiples) is a BIT-COPY of that base token: maxabs 0.0, not
"close". T2a measured corr 1.0 there and the node must not do worse.
3. LEGAL-SNAP PARITY with H3TimeSmear on three hold maps, including one
that needs a tail pad: same dilated length, same snapped hold map, so
the pixel and latent routes can never disagree about the grid.
4. MASK CORRECTNESS: inserted token-times 1, originals 0, nothing partial,
full spatial extent, and the mask rides on the samples output (that is
how it reaches the sampler) as well as on its own output.
5. NOISE MODE fills only the inserted tokens, leaves the copies verbatim,
and is reproducible from noise_seed.
6. NESTED AV: the audio component comes through bit-exact and by
reference; a plain video latent gets a zero audio track sized for the
DILATED length.
7. THE REPORT names the lengths, the token counts, the split, the
17-multiple alignment rule and the audio caveat.
Exit code 0 = pass.
"""
import importlib.util
import os
import sys
import types
import torch
HERE = os.path.dirname(os.path.abspath(__file__))
# The node builds real comfy NestedTensors; stub the module so the test needs
# nothing but torch. Same duck type comfy's has (unbind() / .is_nested).
if "comfy.nested_tensor" not in sys.modules:
class _StubNested:
def __init__(self, tensors):
self.tensors = list(tensors)
self.is_nested = True
def unbind(self):
return self.tensors
_pkg = sys.modules.setdefault("comfy", types.ModuleType("comfy"))
_mod = types.ModuleType("comfy.nested_tensor")
_mod.NestedTensor = _StubNested
_pkg.nested_tensor = _mod
sys.modules["comfy.nested_tensor"] = _mod
spec = importlib.util.spec_from_file_location(
"mainodes_motion", os.path.join(os.path.dirname(HERE), "motion.py"))
motion = importlib.util.module_from_spec(spec)
spec.loader.exec_module(motion)
FAILS = []
# The T2a probe's retime: 124 world frames, hold 2 on frames 85..118 (34
# frames, starting on a 17-multiple). 158 = 17*9+5 is legal, no tail pad.
# NOTE: this map has the end-jump shape (a rate-1 tail behind a rate-2
# span), so every call below passes expand_to_end=False: the T2a probe's
# worked example is what this suite pins, and the toggle would rewrite it.
# The toggle itself is pinned by tests/test_expand_to_end.py.
T2A_HOLDS = [1] * 85 + [2] * 34 + [1] * 5
T2A_MAP = json_map = '{"holds": %s, "world_len": 124}' % T2A_HOLDS
# (token, target_base_time, lo, hi, w_hi) straight out of token_map.csv.
T2A_ROWS = [
(1, 2.5, 0, 1, 1.0), # identity region: lands on base token 1's centre
(25, 85.0, 24, 25, 1.0), # the hold's first frame, still an exact anchor
(26, 86.0, 25, 26, 0.400), # THE worked example
(27, 88.0, 26, 27, 0.125),
(28, 90.0, 26, 27, 0.625),
]
# tokens whose target lands exactly on a base token centre -> bit-copies
T2A_EXACT = list(range(26)) + [35, 45, 46]
def check(name, ok, detail=""):
print((" PASS " if ok else " FAIL ") + name + (" " + detail if detail else ""))
if not ok:
FAILS.append(name)
def base_latent(t=37, h=6, w=10, seed=7):
torch.manual_seed(seed)
return {"samples": torch.randn(1, 24, t, h, w)}
def main():
node = motion.H3TemporalInsert()
# ---- 1. the T2a map
holds, dil, t_base, t_dil, plan = motion.temporal_insert_map(T2A_HOLDS)
check("T2a retime: 124f -> 158f dilated", dil == 158, str(dil))
check("T2a token grid: 37 -> 47 tokens",
(t_base, t_dil) == (37, 47), f"{t_base} -> {t_dil}")
check("no tail pad needed (158 = 17*9+5), hold map unchanged",
holds == T2A_HOLDS and sum(holds) == 158, f"sum={sum(holds)}")
for tok, t, lo, hi, w in T2A_ROWS:
pt, plo, phi, pw, pex = plan[tok]
if pex >= 0: # exact hits are recorded as a copy
plo, phi, pw = pex - 1, pex, 1.0
ok = (pt == t and (plo, phi) == (lo, hi) and abs(pw - w) < 1e-12)
check(f"token {tok}: base time {t}, lerp {lo}/{hi} w={w}", ok,
f"got t={pt} {plo}/{phi} w={pw!r}")
check("smear token 26's weight is EXACTLY 0.400 (the csv's row)",
plan[26][3] == 0.4, repr(plan[26][3]))
check("the exact-hit set matches the probe's",
[n for n, p in enumerate(plan) if p[4] >= 0] == T2A_EXACT,
str([n for n, p in enumerate(plan) if p[4] >= 0]))
check("18 of the 47 token-times are inserted, 29 copied",
len([p for p in plan if p[4] < 0]) == 18 and len(T2A_EXACT) == 29,
f"{len([p for p in plan if p[4] < 0])} inserted")
# ---- 2 + 4. run the node: bit-copies and mask
lat = base_latent()
v0 = lat["samples"]
out, mask_out, used, rep = node.insert(samples=lat, hold_map=T2A_MAP,
init_mode="lerp",
expand_to_end=False)
ov, oa = out["samples"].unbind()
mv, ma = out["noise_mask"].unbind()
check("the video latent is expanded along t only",
tuple(ov.shape) == (1, 24, 47, 6, 10), str(tuple(ov.shape)))
worst = max(float((ov[:, :, n] - v0[:, :, plan[n][4]]).abs().max())
for n in T2A_EXACT)
check("EXACTNESS: every exact-hit token is a BIT-COPY of its base token",
worst == 0.0, f"maxabs over {len(T2A_EXACT)} tokens = {worst!r}")
ins = [n for n, p in enumerate(plan) if p[4] < 0]
n26 = (0.6 * v0[:, :, 25] + 0.4 * v0[:, :, 26])
check("an inserted token is the plain lerp of its bracketing base tokens",
float((ov[:, :, 26] - n26).abs().max()) < 1e-6,
f"maxabs {float((ov[:, :, 26] - n26).abs().max()):.3e}")
check("mask shape is (1,1,t_dil,h,w)", tuple(mv.shape) == (1, 1, 47, 6, 10),
str(tuple(mv.shape)))
check("inserted token-times are marked 1 (regenerate)",
all(float(mv[:, :, n].min()) == 1.0 for n in ins), f"{len(ins)} tokens")
check("original token-times are marked 0 (frozen)",
all(float(mv[:, :, n].max()) == 0.0 for n in T2A_EXACT))
check("the mask is hard everywhere (no partial cells)",
bool(((mv == 0) | (mv == 1)).all()))
check("the audio half of the mask is all ones",
bool((ma == 1).all()) and ma.shape[1:] == oa.shape[1:],
str(tuple(ma.shape)))
check("the mask rides on the samples output (this is how it reaches "
"the sampler)", out["noise_mask"] is mask_out["samples"])
check("the input latent is not mutated",
tuple(lat["samples"].shape) == (1, 24, 37, 6, 10) and
torch.equal(lat["samples"], v0))
# ---- 3. legal-snap parity with H3TimeSmear
smear = motion.H3TimeSmear()
import json as _json
cases = [("T2a map (no pad)", T2A_HOLDS),
("uniform x2 on 39f (pad)", [2] * 39),
("adaptive burst on 90f (pad)",
[1] * 20 + [3] * 12 + [2] * 8 + [1] * 50)]
for name, hm in cases:
n = len(hm)
img = torch.zeros(n, 4, 4, 3)
_, used_smear, length_smear, _ = smear.smear(
images=img, dilation=1, expand_to_end=False,
hold_map=_json.dumps({"holds": hm, "world_len": n}))
h2, dil2, tb2, td2, _p = motion.temporal_insert_map(hm)
s = _json.loads(used_smear)
check(f"snap parity, {name}",
s["holds"] == h2 and int(length_smear) == dil2 and
s["world_len"] == n,
f"smear {length_smear}f last hold {s['holds'][-1]} | "
f"insert {dil2}f last hold {h2[-1]} -> {td2} tokens")
# ---- 5. noise mode
lat2 = base_latent()
nout, _nm, _u, nrep = node.insert(samples=lat2, hold_map=T2A_MAP,
init_mode="noise", noise_seed=11,
expand_to_end=False)
nv = nout["samples"].unbind()[0]
worst_n = max(float((nv[:, :, n] - v0[:, :, plan[n][4]]).abs().max())
for n in T2A_EXACT)
check("noise mode still bit-copies the originals", worst_n == 0.0,
f"maxabs {worst_n!r}")
check("noise mode fills the inserted tokens with unit noise",
abs(float(nv[:, :, ins].std()) - 1.0) < 0.05 and
not torch.equal(nv[:, :, 26], ov[:, :, 26]),
f"std {float(nv[:, :, ins].std()):.4f}")
again, _, _, _ = node.insert(samples=base_latent(), hold_map=T2A_MAP,
init_mode="noise", noise_seed=11,
expand_to_end=False)
check("noise mode is reproducible from noise_seed",
torch.equal(again["samples"].unbind()[0], nv))
diff, _, _, _ = node.insert(samples=base_latent(), hold_map=T2A_MAP,
init_mode="noise", noise_seed=12,
expand_to_end=False)
check("...and a different seed gives a different init",
not torch.equal(diff["samples"].unbind()[0], nv))
# ---- 6. audio: nested passthrough vs plain input
torch.manual_seed(5)
audio_in = torch.randn(1, 32, 2, 207) # 124f clip's audio latent
audio_ref = audio_in.clone()
Nested = sys.modules["comfy.nested_tensor"].NestedTensor
av = {"samples": Nested([base_latent()["samples"], audio_in])}
aout, _am, _au, arep = node.insert(samples=av, hold_map=T2A_MAP,
expand_to_end=False)
av_v, av_a = aout["samples"].unbind()
check("nested AV: the audio comes out bit-exact",
torch.equal(av_a, audio_ref) and tuple(av_a.shape) == (1, 32, 2, 207),
str(tuple(av_a.shape)))
check("nested AV: the audio is passed by reference, not copied",
av_a is audio_in)
check("nested AV: the video is still expanded", av_v.shape[2] == 47)
check("nested AV: the audio mask matches the audio it guards",
tuple(aout["noise_mask"].unbind()[1].shape) == (1, 32, 2, 207))
check("a plain video latent gets a ZERO audio track on the DILATED clock",
tuple(oa.shape) == (1, 32, 2, motion._audio_latent_t(158)) and
bool((oa == 0).all()),
f"{tuple(oa.shape)} vs base clock {motion._audio_latent_t(124)}")
# ---- 7. hold_map passthrough + report
u = _json.loads(used)
check("hold_map passes through in H3TimeSmear's format, already snapped",
u["holds"] == holds and u["world_len"] == 124,
f"world_len {u['world_len']}, {len(u['holds'])} holds")
check("the passthrough map drives H3TrueClock to the dilated token count",
len(motion.true_clock_spans(u["holds"])) == 47,
str(len(motion.true_clock_spans(u["holds"]))))
for frag in ("world 124f -> dilated 158f", "t_lat 37 -> 47 tokens",
"copied verbatim (mask 0, frozen): 29 token-times [0-25,35,45-46]",
"inserted (mask 1, regenerate): 18 token-times [26-34,36-44]",
"T2a rule 1", "all on 17-multiples", "AUDIO CAVEAT",
"sigma >= ~0.5"):
check(f"report says {frag!r}", frag in rep)
check("the report flags an OFF-anchor hold span",
"OFF the 17-multiple phase" in node.insert(
samples=base_latent(), expand_to_end=False,
hold_map=_json.dumps({"holds": [1] * 80 + [2] * 39 + [1] * 5,
"world_len": 124}))[3])
check("the nested-AV report states the audio is on the BASE clock",
"audio passed through untouched" in arep and
"BASE clip's clock" in arep)
print("\n--- report (T2a map, lerp)\n" + rep)
print()
if FAILS:
print(f"FAILED: {len(FAILS)} check(s): " + ", ".join(FAILS))
return 1
print("all checks passed")
return 0
if __name__ == "__main__":
sys.exit(main())
|