import gradio as gr import torch import sys, os, time, hashlib sys.path.insert(0, "src") from mulattice.protocol import SynthCfg, build_model, force_fast_scan, logits_at, synth_batch DEVICE = "cpu" CFG = SynthCfg() print("Loading models...", flush=True) MODELS = {} for name in ["capacity8k", "forgetting", "realtext"]: path = f"checkpoints/{name}.pt" if os.path.exists(path): ck = torch.load(path, map_location="cpu", weights_only=False) c = SynthCfg() if ck.get("proto_cfg", {}).get("addr_scale"): c.addr_scale = float(ck["proto_cfg"]["addr_scale"]) m = build_model(c) m.load_state_dict(ck["model"]) m = m.to(DEVICE).eval() force_fast_scan(m) MODELS[name] = m print(f" {name}.pt loaded", flush=True) print("All models ready", flush=True) INK = "#1c1e26"; MUT = "#6b7280"; HAIR = "#e5e7eb"; BG = "#fcfcfa" ACC = "#2547d0"; OK = "#0e8a5f"; BAD = "#c2413b"; SOFT = "#f4f4f0"; UPD = "#b07807" with open("demo.css") as f: DEMO_CSS = f.read() def run_custom(text, n1, v1, nv1, n2, v2, nv2): m = MODELS.get("forgetting", MODELS.get("capacity8k")) seed = int(time.time()) % 100000 pairs = [] for n, v, nv in [(n1, v1, nv1), (n2, v2, nv2)]: if n.strip() and v.strip(): pairs.append({"name": n.strip(), "value": v.strip(), "new_value": nv.strip() if nv.strip() else None}) if not pairs: return "Enter at least one fact (a name and a value).", "" used = set() def h(s, span, off): x = int(hashlib.md5(f"{seed}:{s}".encode()).hexdigest(), 16) % span while (off + x) in used: x = (x + 1) % span used.add(off + x) return off + x kmap, vmap = {}, {} for p in pairs: kmap[p["name"]] = h("k" + p["name"], CFG.NK, 0) for p in pairs: vmap[p["value"]] = h("v" + p["value"], CFG.NV, CFG.NK) if p["new_value"]: vmap[p["new_value"]] = h("v2" + p["new_value"], CFG.NV, CFG.NK) words = [w for w in text.split() if w.strip()][:500] or ["..."] fid = {w: h("f" + w, CFG.NK, 0) for w in set(words)} fill = [fid[w] for w in words] store_rows, upd_rows = [], [] for p in pairs: vid = vmap[p["value"]] row = dict(p); row["kid"] = kmap[p["name"]]; row["vid"] = vid if p["new_value"]: row["nid"] = vmap[p["new_value"]] upd_rows.append(row) store_rows.append(row) F = 6; block = F + 2 n_upd = len(upd_rows); Q = len(store_rows) L = (len(store_rows) + n_upd) * block + 2 * Q toks = torch.zeros(1, L, dtype=torch.long) def take_f(n): out = [] while len(out) < n: i = int(torch.randint(0, len(fill), (1,))) out.extend(fill[i:i+n-len(out)]) return out[:n] for i, r in enumerate(store_rows): s = i * block toks[0, s:s+F] = torch.tensor(take_f(F)) toks[0, s+F], toks[0, s+F+1] = r["kid"], r["vid"] for j, r in enumerate(upd_rows): s = (len(store_rows) + j) * block toks[0, s:s+F] = torch.tensor(take_f(F)) toks[0, s+F], toks[0, s+F+1] = r["kid"], r["nid"] S2 = (len(store_rows) + n_upd) * block ans_l = [] for q, r in enumerate(store_rows): toks[0, S2+2*q] = r["kid"] toks[0, S2+2*q+1] = CFG.SEP ans_l.append(S2 + 2*q + 1) t0 = time.time() with torch.no_grad(): pred = logits_at(m, toks, torch.tensor([ans_l])).argmax(-1).cpu() dt = time.time() - t0 cells = m.blocks[0].mixer.scan.last_n_cells inv = {v: k for k, v in vmap.items()} correct = 0 rows_html = '' for q, r in enumerate(store_rows): got = inv.get(int(pred[0, q]), "?") exp = r["new_value"] or r["value"] ok = got == exp correct += ok old = f' was {r["value"]}' if r["new_value"] else "" cls = "y" if ok else "n" icon = "✅" if ok else "❌" rows_html += f'' rows_html += '
AskedAnsweredExpected
{r["name"]}{got}{exp}{old}{icon}
' stats_html = ('
' f'
{correct}/{len(store_rows)}correct
' f'
{L:,}words read
' f'
{dt:.1f}stime
' f'
{cells:,}memory slots
' '
') return rows_html, stats_html def run_scale(n_pairs, gap, pct_changed): m = MODELS.get("forgetting", MODELS.get("capacity8k")) g = torch.Generator().manual_seed(42) n = int(n_pairs) b = synth_batch(1, n, min(32, n), CFG, filler=int(gap), overwrite_frac=float(pct_changed)/100, generator=g) t0 = time.time() with torch.no_grad(): pred = logits_at(m, b["tokens"], b["ans_pos"]).argmax(-1).cpu() dt = time.time() - t0 ok = (pred == b["gold"]).squeeze(0) up = b["was_updated"].squeeze(0) cells = m.blocks[0].mixer.scan.last_n_cells acc = ok.float().mean().item() acc_up = ok[up].float().mean().item() if bool(up.any()) else 1.0 acc_keep = ok[~up].float().mean().item() if bool((~up).any()) else 1.0 result_html = f'''
{acc:.1%}
{int(ok.sum())} of {ok.numel()} recalled correctly
Changed facts: {acc_up:.1%} · Unchanged: {acc_keep:.1%}
{b["L"]:,}words read
{cells:,}memory slots
{dt:.1f}stime
''' return result_html with gr.Blocks(title="μLattice — AI Memory That Grows Without the Cost Growing", css=DEMO_CSS + f""" .gradio-container {{ max-width: 980px !important; margin: auto !important; background: {BG} !important; padding: 0 24px 60px !important; font-family: -apple-system, BlinkMacSystemFont, Inter, sans-serif !important; }} footer {{ display: none !important; }} """) as demo: gr.HTML(f"""
Tensorbend Research · Eric E. · Live proof · Real weights

μLattice — the growing memory, running

A model that reads text and keeps every fact in it — stores associations, replaces them on sight, and answers from memory. Every word costs the same, whether the model holds ten facts or eight thousand.

3 trained models loaded: capacity8k · forgetting · realtext · 4.2M parameters
""") # ── Section 01 ── gr.HTML("""
01

Try it with your own facts

Paste any text. Define facts as name → value pairs. Mark any to be changed mid-stream. The model reads your words, stores the facts, and must return the latest value.

""") with gr.Row(): with gr.Column(scale=1): doc = gr.Textbox(label="Your text (the model reads this)", lines=5, value="The harbour was quiet that morning. Gulls circled the masts while the tide went out. A clerk stamped papers without looking up. Somewhere a bell rang twice, then stopped. The road east had been washed out again. Dust settled over the market stalls.") with gr.Column(scale=1): n1 = gr.Textbox(label="Name", value="the vault password") v1 = gr.Textbox(label="Value", value="swordfish") nv1 = gr.Textbox(label="Changed to (optional)", value="bluedragon") n2 = gr.Textbox(label="Name", value="the meeting time") v2 = gr.Textbox(label="Value", value="3pm") nv2 = gr.Textbox(label="Changed to (optional)", value="") btn1 = gr.Button("Store, overwrite, and answer", variant="primary") out1 = gr.HTML() stats1 = gr.HTML() gr.HTML(f"""
The model reads your words as the stream, stores your facts as it passes them, replaces marked ones, then answers from memory. Uses the forgetting checkpoint (best at overwrite tasks).
""") # ── Section 02 ── gr.HTML(f"""
02

The store at scale

The same mechanism under load. Store facts, change some mid-stream, then ask for them all back. The cost per word stays flat no matter how many are stored.

""") with gr.Row(): np_s = gr.Slider(64, 512, value=128, step=64, label="How many facts", info="Capped at 512 for CPU (GPU handles 8,192+)") gap_s = gr.Slider(0, 14, value=8, step=1, label="Words between each fact") ow_s = gr.Slider(0, 100, value=50, step=10, label="% of facts changed mid-stream") btn2 = gr.Button("Run the stress test", variant="primary") out2 = gr.HTML() gr.HTML(f"""
Uses the forgetting checkpoint. On GPU this scales to 8,192 facts in one 133K-token pass. On free-tier CPU, capped at 512.
""") btn1.click(run_custom, inputs=[doc, n1, v1, nv1, n2, v2, nv2], outputs=[out1, stats1]) btn2.click(run_scale, inputs=[np_s, gap_s, ow_s], outputs=[out2]) demo.launch()