diff --git a/shot_memory.py b/shot_memory.py index bf57d868..ec60deb8 100644 --- a/shot_memory.py +++ b/shot_memory.py @@ -225,6 +225,10 @@ class ShotMemory: coarse = own if parent is not None: coarse = amount * _fit(parent["coarse"], *own.shape[-2:]) + math.sqrt(1.0 - amount ** 2) * own + # Unit variance only holds when the two layouts are unrelated. Rerunning the + # liked shot's own seed makes them the same pattern, and the blend stacks it + # (1.41x at amount 0.7): back to a normal-strength layout, per channel. + coarse = coarse / coarse.std(dim=(1, 2), keepdim=True).clamp(min=1e-6) video = with_coarse(video, coarse) if record and not self.used: self.used.append({ diff --git a/tests/test_shot_memory_tsr.py b/tests/test_shot_memory_tsr.py index 9ea513b9..2f49cae6 100644 --- a/tests/test_shot_memory_tsr.py +++ b/tests/test_shot_memory_tsr.py @@ -205,3 +205,14 @@ def test_one_rating_is_one_row_and_skips_are_counted(monkeypatch, tmp_path): assert len(mem.used) == 1 and mem.skipped == 1 mem.save_pending() assert sm.commit("k", 1.0) == 1 + + +def test_reusing_a_shot_with_its_own_seed_keeps_normal_strength(monkeypatch, tmp_path): + """Same seed as the liked shot: the two layouts are one pattern, and a plain blend + would stack it (1.41x at amount 0.7).""" + _store(monkeypatch, tmp_path) + noise = torch.randn(1, 24, 7, 24, 36, generator=torch.Generator().manual_seed(5)) + mem = sm.ShotMemory("k", "manual", 0.7, rng=random.Random(0)) + mem.plans[(None, 24)] = ({"id": 1, "coarse": sm.coarse_of(noise).half()}, 0.7) + out = mem.shape(noise.clone(), torch.zeros_like(noise), None) + assert abs(float(sm.coarse_of(out).std()) - 1.0) < 0.02