Source: tests/test_vision_probe_adaptation.py · Plain text

tests/test_vision_probe_adaptation.py

import torch

from mcss.vision_probe.adaptation import (
    FastState,
    Readout,
    masks,
    materialize,
    proposals,
    residual_selector,
)


def fixture():
    rng = torch.Generator().manual_seed(41)
    z = torch.randn(8, 8, 6, generator=rng)
    decoder = torch.randn(6, 3, generator=rng) * 0.1
    readout = Readout(torch.zeros(6), torch.eye(6), torch.ones(6), decoder, torch.zeros(3))
    rgb = z @ decoder + 0.1
    return z, rgb, readout


def test_off_preserves_content_and_proposals_are_branch_private():
    z, rgb, r = fixture()
    old = FastState.zero(6)
    support, _ = masks(8, 8, 0)
    before = materialize(z, old).clone()
    c = proposals(z, rgb, r, old, support, 0.4)
    assert torch.equal(materialize(z, c["OFF"]), before)
    assert torch.equal(materialize(z, old), before)
    assert c["A"].a.norm() > 0
    assert torch.equal(c["ALL"].a, c["A"].a)
    assert torch.equal(c["ALL"].b, c["B"].b)
    c["OFF"].a.fill_(17)
    assert old.a.count_nonzero() == 0
    assert not torch.equal(c["A"].a, c["OFF"].a)


def test_update_uses_only_support_targets_and_selection_has_no_geometry_input():
    z, rgb, r = fixture()
    support, probe = masks(8, 8, 0)
    changed = rgb.clone()
    changed[~support] += 100
    old = FastState.zero(6)
    first = proposals(z, rgb, r, old, support, 0.4)
    second = proposals(z, changed, r, old, support, 0.4)
    for key in first:
        assert torch.equal(first[key].a, second[key].a)
        assert torch.equal(first[key].b, second[key].b)
    assert not (support & probe).any()
    assert residual_selector(z, rgb, r, first, probe) in first


def test_zero_residual_produces_zero_write():
    z, _, r = fixture()
    rgb = r.predict(z)
    support, _ = masks(8, 8, 0)
    for state in proposals(z, rgb, r, FastState.zero(6), support, 0.4).values():
        assert state.a.count_nonzero() == 0
        assert state.b.count_nonzero() == 0


def test_second_step_all_is_simultaneous_not_sequential():
    z, rgb, readout = fixture()
    support0, _ = masks(8, 8, 0)
    support1, _ = masks(8, 8, 1)
    old = proposals(z, rgb, readout, FastState.zero(6), support0, 2.0)["ALL"]
    snapshot = old.fork()
    candidates = proposals(z, rgb, readout, old, support1, 2.0)
    sequential_b = proposals(z, rgb, readout, candidates["A"], support1, 2.0)["B"]
    assert old.a.norm() > 0 and old.b.norm() > 0
    assert torch.equal(old.a, snapshot.a) and torch.equal(old.b, snapshot.b)
    assert torch.equal(candidates["ALL"].a, candidates["A"].a)
    assert torch.equal(candidates["ALL"].b, candidates["B"].b)
    assert not torch.allclose(candidates["ALL"].b, sequential_b.b)