pns-bind-25m / src /pns /data /binding.py
nur-dev's picture
PNS-Bind-25M: implementation, configs, eval, results, reproduction
f930dac verified
Raw History Blame Contribute Delete
3.96 kB
"""Exact binding addresses for E3-BIND.
Computes, for each event of a lifetime, which learned slot (if any) that event
is allowed to write, and for each semantic question which slot holds the
currently authoritative version of the queried binding.
Contract (preregistered):
* the address carries entity id, attribute/relation type, version and
slot id - NEVER the semantic value or the answer;
* one slot per (entity, attribute, version); NO slot is ever reused;
* slot indices are drawn from a per-lifetime random permutation seeded by the
lifetime seed alone, so the index cannot encode the label;
* a revert moves the AUTHORITY POINTER to an earlier version's slot; it does
not move or rewrite any payload.
"""
from __future__ import annotations
import numpy as np
from ..world.schema import ET, Fam
N_BIND_SLOTS = 192 # E3-A2: measured max 143 versions/lifetime, p99 139
def binding_plan(lt: dict, n_slots: int = N_BIND_SLOTS) -> dict:
"""-> per-event write slot, per-question read slot, per-slot exact key."""
events = lt["events"]
rng = np.random.default_rng(int(lt["seed"])) # label-independent
perm = rng.permutation(n_slots)
nxt = 0
write_slot = np.full(len(events), -1, np.int16)
read_slot = np.full(len(events), -1, np.int16)
slot_ent = np.full(n_slots, -1, np.int16)
slot_attr = np.full(n_slots, -1, np.int16)
slot_ver = np.full(n_slots, -1, np.int16)
active: dict[tuple, int] = {} # (ent, attr) -> slot of active version
versions: dict[tuple, list] = {} # (ent, attr) -> [(clock, source, slot)]
clock = 0
overflow = 0
def alloc(key, clock, source):
nonlocal nxt, overflow
if nxt >= n_slots:
overflow += 1
return -1
s = int(perm[nxt]); nxt += 1
slot_ent[s] = key[0] % 4096
slot_attr[s] = ATTR_ID.get(key[1], 0)
slot_ver[s] = len(versions.get(key, []))
versions.setdefault(key, []).append((clock, source, s))
active[key] = s
return s
for i, ev in enumerate(events):
clock += ev.dt
p = ev.payload
if ev.etype == ET.OBSERVATION:
key = (p["ent"], p["attr"])
write_slot[i] = alloc(key, clock, p.get("source", ""))
elif ev.etype == ET.CORRECTION:
key = (p["ent"], p["key"])
if p["mode"] == "replace":
write_slot[i] = alloc(key, clock, p.get("source", ""))
else:
# revert: authority pointer only, no payload written anywhere
hist = versions.get(key, [])
tgt = None
if p.get("ref_kind") == "source":
for c, src, s in hist:
if src == p.get("ref_source"):
tgt = s
else:
for c, src, s in hist:
if c == p.get("ref_clock"):
tgt = s
if tgt is not None:
active[key] = tgt
elif ev.etype == ET.QUESTION:
tk = lt["tickets"][i] if "tickets" in lt else None
d_ = tk.data if tk is not None else {}
if ev.family == int(Fam.SEM_LATEST):
read_slot[i] = active.get((d_.get("ent"), d_.get("attr")), -1)
elif ev.family == int(Fam.SEM_2HOP):
# ONLY the first hop is addressed; hop 2 stays a learned lookup
read_slot[i] = active.get((d_.get("src"), d_.get("rel")), -1)
return dict(write_slot=write_slot, read_slot=read_slot, slot_ent=slot_ent,
slot_attr=slot_attr, slot_ver=slot_ver,
n_used=nxt, overflow=overflow)
ATTR_ID = {a: i + 1 for i, a in enumerate(sorted(
["location", "team", "status", "phase", "priority", "destination",
"owner", "project", "vault", "access code", "firmware tag", "reading",
"quantity", "budget"]))}