File size: 11,757 Bytes
1272654
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# SPDX-License-Identifier: BSD-3-Clause-Clear
# Copyright (c) 2026 Ronald Lee Hampton. All rights reserved.
# No express or implied licenses to any party's patent rights are granted by this license.
#
# read_shuffle_cost.py: re-measures the shuffle cost of every shipped brain and re-counts the switchboards.
#
# Usage (from the package folder):
#   python -I reader/read_shuffle_cost.py --brains brains --data data --expect results/per_brain_record.csv
# Needs: Python 3, numpy, torch, safetensors. CPU only, one thread, about a minute for the 80 brains. No network.
# Side effects: none; it prints only (redirect the output to keep it).
#
# THE MEASURE. Each brain is run on the 256 held-out windows (66 tokens each: two context words, then 64 targets), 16,384 positions in
# one batch. The reasoning core's quantized output (qk, one 32-number vector per position) is then shuffled across positions with
# numpy default_rng(0).permutation(16384), everything else held fixed, and the network is run again. The SHUFFLE COST is the mean, over
# the positions whose target is not one of the 40 excluded ids, of (cross-entropy shuffled minus cross-entropy as trained), in nats.
# THE LINE (fixed before the run): a core is a SWITCHBOARD if its shuffle cost is below 0.025 nats, else a CONTEXT WORKER.
# A RESCUE is a seed whose plain (ST-0) core is a switchboard and whose sleep-form (S-SLEEP) core is a context worker; a HARM is the
# reverse. THE BAR (fixed before the run): S-SLEEP ends with fewer switchboards than ST-0, AND at least 5 seeds are discordant, AND the
# exact one-sided sign test on the discordant seeds gives p at or below 0.05.
#
# This file holds only the forward pass the measure needs (no training code). Tensor names keep the run's own prefixes:
#   tiny.*  the routed base network: two word embeddings summed, a router to one of 48 experts (top 1, its score nudged by the pull
#           0.4 x (mean size minus the expert's size), sizes in "y"), each expert a two-layer block, a layer norm and a shared head;
#   rc.*    the reasoning core: written from the integer part's hidden state, one gated pass, quantized to whole numbers in -127..127;
#   cc.*    the small integer part with ternary weights: reads the two context words' ids as 12 bits each, and writes a gain on the
#           base network's expert output from its own hidden state and the core's quantized output (zero gain at the start).
#   cc.Wv and cc.bv, a two-output head the measure does not use, were removed from the shipped files (results/head_removed_record.csv).
# Every operation below is written in the same order as the run's own code so that the numbers can match it bit for bit on the same
# software and hardware; on other hardware or torch versions the last digits may differ (the --expect check reports by how much).
import sys, os, csv, glob, math, hashlib, argparse
import numpy as np
import torch
import torch.nn.functional as F

V, D, T, CTX, NBITS = 4096, 32, 64, 2, 12
S_PULL, RHO = 0.4, 0.5
LINE = 0.025
FORMS = ("ST-0", "S-SLEEP")
P_BAR, MIN_DISC = 0.05, 5
BITW = 2 ** torch.arange(NBITS)


def sha256_file(p):
    h = hashlib.sha256()
    with open(p, "rb") as f:
        for blk in iter(lambda: f.read(1 << 20), b""):
            h.update(blk)
    return h.hexdigest()


def sign_p(r, n):
    """exact one-sided p of at least r rescues among n discordant seeds, each a fair coin."""
    return float(sum(math.comb(n, k) for k in range(r, n + 1)) / 2 ** n) if n else 1.0


def tern(w):
    return (w > 0.5).float() - (w < -0.5).float()


def ste_tern(w):                           # the training-time expression, kept so the floating-point result is the same
    return w + (tern(w) - w)


def ste_round(x):
    return x + (torch.round(x) - x)


def ste_floor(x):
    return x + (torch.floor(x) - x)


def rms(x, w):
    return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + 1e-6) * w


def msg(xs):
    """the two context words' token ids as 12 bits each, +1/-1, newest word first."""
    parts = [((x[:, None] // BITW) % 2).float() * 2 - 1 for x in xs[:2]]
    return torch.cat(parts, dim=-1)


def hidden(P, xs):
    """the integer part's hidden state: ternary weights, whole-number biases, clamps; every value a whole number."""
    m = msg(xs)
    a1 = m @ ste_tern(P["cc.W1"]).t() + ste_round(P["cc.b1"] * 1.0)
    h1 = torch.clamp(a1, 0.0, 31.0)
    a2 = h1 @ ste_tern(P["cc.W2"]).t() + ste_round(P["cc.b2"] * 16.0)
    return torch.clamp(ste_floor(a2 / 4.0), 0.0, 127.0)


def pull_bias(P):
    y = P["y"].numpy()
    return torch.tensor(S_PULL * (y.mean() - y), dtype=torch.float32)


def forward(P, xs, bias, h2, perm=None):
    """logits at every position; with perm, the reasoning core's quantized output is shuffled across positions first."""
    # the routed base network
    x = F.embedding(xs[0], P["tiny.emb.0.weight"])
    x = x + F.embedding(xs[1], P["tiny.emb.1.weight"])
    r = F.linear(x, P["tiny.router.weight"], P["tiny.router.bias"])
    score = r + bias
    p = F.softmax(r, dim=-1)
    sel = torch.argmax(score, dim=-1)
    g = p.gather(1, sel[:, None]).squeeze(1)
    h = F.gelu(torch.bmm(x[:, None, :], P["tiny.w1"][sel])).squeeze(1)
    y = torch.bmm(h[:, None, :], P["tiny.w2"][sel]).squeeze(1)
    contrib = g[:, None] * y
    # the reasoning core, written from the integer part's hidden state
    K0 = (1 - RHO) * torch.tanh(rms(h2 / 127.0, P["rc.nwc.weight"]) @ P["rc.wkc"])
    z = rms(K0, P["rc.ns.weight"])
    u = F.silu(z @ P["rc.w1"]) * (z @ P["rc.w3"])
    K2 = RHO * K0 + (1 - RHO) * torch.tanh(u @ P["rc.wh"])
    qk = torch.clamp(ste_round(K2 * 127.0), -127.0, 127.0)
    if perm is not None:
        qk = qk[perm]                      # THE SHUFFLE: the core's output moved across positions, nothing else changed
    # the integer part's write: a gain on the expert output
    c = h2 @ ste_tern(P["cc.Wcg"]).t()
    c = c + qk @ ste_tern(P["cc.Wck"]).t()
    gain = 2.0 * torch.sigmoid(c / 256.0) - 1.0
    o = x + contrib * (1.0 + gain)
    o = F.layer_norm(o, (D,), P["tiny.norm.weight"], P["tiny.norm.bias"], 1e-5)
    return F.linear(o, P["tiny.head.weight"], P["tiny.head.bias"])


def heldout(data_dir):
    win = np.load(os.path.join(data_dir, "heldout_windows_uint16.npy"), allow_pickle=False).astype(np.int64)
    if win.shape != (256, T + CTX):
        raise SystemExit("REFUSED: held-out windows have shape %s" % (win.shape,))
    xs = [torch.from_numpy(np.ascontiguousarray(win[:, CTX - 1 - k:T + CTX - 1 - k])).reshape(-1) for k in range(CTX)]
    tgt = torch.from_numpy(np.ascontiguousarray(win[:, CTX:])).reshape(-1)
    ids = [int(l) for l in open(os.path.join(data_dir, "excluded_target_ids.txt"), encoding="utf-8") if l.strip() and not l.startswith("#")]
    excl = torch.zeros(V, dtype=torch.bool)
    excl[torch.tensor(ids)] = True
    allowed = (~excl[tgt]).numpy()
    return win, xs, tgt, allowed, ids


def main(a):
    from safetensors.torch import load_file
    torch.set_num_threads(1)
    win, xs, tgt, allowed, ids = heldout(a.data)
    perm = torch.from_numpy(np.random.default_rng(0).permutation(len(tgt)))
    print("held-out windows %d, positions %d, scored positions %d, excluded ids %d" % (win.shape[0], len(tgt), int(allowed.sum()), len(ids)))
    print("torch %s, numpy %s, threads %d" % (torch.__version__, np.__version__, torch.get_num_threads()))
    paths = sorted(glob.glob(os.path.join(a.brains, "*.safetensors")))
    per = {}
    for path in paths:
        name = os.path.basename(path)[:-len(".safetensors")]
        form, s = name.split("_")[0], int(name.split("_")[1][1:])
        P = load_file(path)
        with torch.no_grad():
            bias = pull_bias(P)
            h2 = hidden(P, xs)
            lg_on = forward(P, xs, bias, h2)
            lg_sh = forward(P, xs, bias, h2, perm)
        ce_on = F.cross_entropy(lg_on, tgt, reduction="none").double().numpy()
        ce_sh = F.cross_entropy(lg_sh, tgt, reduction="none").double().numpy()
        cost = float((ce_sh - ce_on)[allowed].mean())
        per[(form, s)] = {"name": name, "cost": cost, "label": "SWITCHBOARD" if cost < LINE else "CONTEXT WORKER",
                          "ce_on": float(ce_on[allowed].mean()),
                          "logits_sha256": hashlib.sha256(lg_on.contiguous().numpy().tobytes()).hexdigest(),
                          "file_sha256": sha256_file(path)}
        print("%-8s s%d  shuffle cost %.6f  %-14s  (held-out loss as trained %.6f)" % (form, s, cost, per[(form, s)]["label"], per[(form, s)]["ce_on"]))
    seeds = sorted({s for (_f, s) in per})
    if not seeds or not all((f, s) in per for f in FORMS for s in seeds):
        print("REFUSED: no brains, or a seed is missing one of its two brains")
        return 2
    sb = {f: [s for s in seeds if per[(f, s)]["label"] == "SWITCHBOARD"] for f in FORMS}
    res = [s for s in seeds if per[("ST-0", s)]["label"] == "SWITCHBOARD" and per[("S-SLEEP", s)]["label"] == "CONTEXT WORKER"]
    harm = [s for s in seeds if per[("ST-0", s)]["label"] == "CONTEXT WORKER" and per[("S-SLEEP", s)]["label"] == "SWITCHBOARD"]
    n = len(res) + len(harm)
    p = sign_p(len(res), n)
    passed = len(sb["S-SLEEP"]) < len(sb["ST-0"]) and n >= MIN_DISC and p <= P_BAR
    print("")
    print("seeds %d (%d to %d); THE LINE: switchboard below %.3f nats of shuffle cost" % (len(seeds), seeds[0], seeds[-1], LINE))
    for f in FORMS:
        print("  %-8s switchboards %d of %d: %s" % (f, len(sb[f]), len(seeds), sb[f]))
    print("  rescues %d %s; harms %d %s; discordant %d; exact one-sided sign test p %.5f" % (len(res), res, len(harm), harm, n, p))
    print("THE BAR (S-SLEEP fewer, at least %d discordant, p <= %.2f): %s" % (MIN_DISC, P_BAR, "CONFIRMED" if passed else "NOT CONFIRMED"))
    near = sorted((abs(v["cost"] - LINE), "%s s%d" % k, round(v["cost"], 6)) for k, v in per.items())[:3]
    print("  nearest the line: %s" % [(k, c) for _d, k, c in near])
    rc = 0
    if a.expect:
        rows = {(r["form"], int(r["seed"])): r for r in csv.DictReader(open(a.expect, encoding="utf-8", newline=""))}
        keys = [k for k in per if k in rows]
        lab_eq = sum(1 for k in keys if rows[k]["label"] == per[k]["label"])
        file_eq = sum(1 for k in keys if rows[k]["safetensors_sha256"] == per[k]["file_sha256"])
        cost_eq = sum(1 for k in keys if float(rows[k]["shuffle_cost"]) == per[k]["cost"])
        ce_eq = sum(1 for k in keys if float(rows[k]["ce_on_allowed_mean"]) == per[k]["ce_on"])
        lg_eq = sum(1 for k in keys if rows[k]["logits_sha256_on"] == per[k]["logits_sha256"])
        dmax = max(abs(float(rows[k]["shuffle_cost"]) - per[k]["cost"]) for k in keys) if keys else float("nan")
        cmax = max(abs(float(rows[k]["ce_on_allowed_mean"]) - per[k]["ce_on"]) for k in keys) if keys else float("nan")
        print("")
        print("AGAINST %s (%d rows):" % (os.path.basename(a.expect), len(rows)))
        print("  brain files equal to the record's sha256: %d of %d" % (file_eq, len(per)))
        print("  labels equal: %d of %d" % (lab_eq, len(per)))
        print("  shuffle costs equal bit for bit: %d of %d; largest absolute difference %.3g nats" % (cost_eq, len(per), dmax))
        print("  held-out loss as trained equal bit for bit: %d of %d; largest absolute difference %.3g nats" % (ce_eq, len(per), cmax))
        print("  logits as trained equal bit for bit (sha256): %d of %d" % (lg_eq, len(per)))
        if lab_eq != len(per) or file_eq != len(per) or len(rows) != len(per):
            rc = 1
    return rc


if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--brains", default="brains")
    ap.add_argument("--data", default="data")
    ap.add_argument("--expect", default="")
    sys.exit(main(ap.parse_args()))