File size: 4,523 Bytes
021d920
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""feat-R probe: R's construction (object-toggle signal / temporal+spatial clutter)
measured in the UNTRAINED ENCODER's feature space instead of 8x8 pooled pixels.
Mirrors compute_R.py line-for-line, swapping pool() -> enc(). Trainless.
Tests: does the random conv encoder preserve the pixel signal/clutter ratio (R)
into the feature space the linear head learns from, and does that predict learning?
Pre-registration: PREREGISTRATION_why_gradientSNR.md.
"""
import sys, json, glob, re
sys.path.insert(0, ".")
import numpy as np, cv2, torch
from triclock.model import LogLensNet

DEV = "cuda" if torch.cuda.is_available() else "cpu"
B = np.load("simreal/frames64.npy", mmap_mode="r")
NSAMP = 80

def make_enc(seed):
    torch.manual_seed(seed)
    enc = LogLensNet().enc.to(DEV).eval()
    @torch.no_grad()
    def f(imgs):                      # imgs: list of HxWx3 float -> [len,64]
        x = torch.from_numpy(np.stack(imgs)).permute(0, 3, 1, 2).float().to(DEV)
        return enc(x).cpu().numpy()
    return f

def draw(img, pos, c, size, ct):
    img = img.copy(); x, y = pos
    img[y:y+size, x:x+size] = c*ct + img[y:y+size, x:x+size].mean(axis=(0, 1))*(1-ct)
    return img

def feat_R(mode, size, ct, enc, rng):
    sig, den = [], []
    for _ in range(NSAMP):
        idx = rng.integers(len(B))
        f0 = np.asarray(B[idx], np.float32) / 255.0
        frames = [f0]*9 if mode == "static" else [np.asarray(B[rng.integers(len(B))], np.float32)/255.0 for _ in range(9)]
        pos = (int(rng.integers(2, 60-size)), int(rng.integers(2, 60-size)))
        c = rng.uniform(0.15, 0.95, 3).astype(np.float32)
        # signal: object color-toggle delta in feature space
        a, b = enc([draw(f0, pos, c, size, ct), draw(f0, pos, 1-c, size, ct)])
        sig.append(np.sum((a - b)**2) / 4)
        # clutter: temporal (across frames) + spatial (shifted probes) feature variance
        P = enc(frames)
        probes = enc([np.roll(np.roll(f0, int(rng.integers(64)), 0), int(rng.integers(64)), 1) for _ in range(6)])
        den.append(P.var(0).sum() + probes.var(0).sum())
    return float(np.mean(sig) / (np.mean(den) + 1e-9))

def parse_cell(fn):
    b = "static" if "_static_" in fn else "moving"
    m = re.search(r"_o(\d+)_ct([\d.]+)_", fn)
    return (b, int(m.group(1)), float(m.group(2))) if m else None

if __name__ == "__main__":
    smoke = "--smoke" in sys.argv
    R = json.load(open("results_compiler_v2/R_grid.json"))
    if smoke:
        enc = make_enc(0)
        for c in [("static", 14, 1.0), ("static", 5, 0.4), ("static", 10, 0.4)]:
            print(f"{c}: featR={feat_R(*c, enc, np.random.default_rng(0)):.4f}  R={R[f'{c[0]}_o{c[1]}_ct{c[2]:g}']:.4f}", flush=True)
        sys.exit(0)
    SIZES, CTS, SEEDS = [5, 6, 8, 10, 12, 14, 16], [1.0, 0.4], [0, 1, 2]
    grid = {}
    for bg in ["static", "moving"]:
        for ct in CTS:
            for s in SIZES:
                vals = [feat_R(bg, s, ct, make_enc(sd), np.random.default_rng(100+sd)) for sd in SEEDS]
                key = f"{bg}_o{s}_ct{ct:g}"
                grid[key] = {"featR_mean": float(np.mean(vals)), "featR_seeds": vals, "R": R.get(key)}
                print(f"{key:16s} R={R.get(key):.4f}  featR={np.mean(vals):.4f}", flush=True)
    outcomes = {}
    for fn in glob.glob("results_*/*an0c0_s0.json") + glob.glob("results_*/*_an0_s0.json"):
        c = parse_cell(fn)
        if c:
            try: outcomes[f"{c[0]}_o{c[1]}_ct{c[2]:g}"] = json.load(open(fn))["heldout_mean"]
            except Exception: pass
    p3 = {sd: feat_R("static", 10, 0.4, make_enc(sd), np.random.default_rng(999)) for sd in range(6)}
    json.dump({"grid": grid, "direct_outcomes": outcomes, "p3_static10ct0.4_featR_by_seed": p3},
              open("results_compiler_v2/featR.json", "w"), indent=2)
    from scipy.stats import spearmanr
    keys = [k for k in grid if grid[k]["R"] is not None]
    rho1, pv1 = spearmanr([grid[k]["R"] for k in keys], [grid[k]["featR_mean"] for k in keys])
    ok = [k for k in keys if k in outcomes]
    rho2 = spearmanr([grid[k]["featR_mean"] for k in ok], [outcomes[k] for k in ok])[0] if len(ok) >= 4 else None
    print(f"\nP1 Spearman(R, featR) over {len(keys)} cells = {rho1:.3f} (p={pv1:.2e})")
    print(f"P2 Spearman(featR, trained_acc) over {len(ok)} cells = {rho2}")
    print(f"P3 static10@0.4 featR by seed: {p3}")
    print("controls: static14ct1 featR=%.4f (HIGH?)  static5ct0.4 featR=%.4f (low?)"
          % (grid['static_o14_ct1']['featR_mean'], grid['static_o5_ct0.4']['featR_mean']))