emerging.systems / hyper_vocab_memory.py
compunerd's picture
Full-vocab HVM cache (128K tokens) via chunked bf16 projection
f7a2fac verified
Raw
History Blame Contribute Delete
12.9 kB
#!/usr/bin/env python3
"""
Hyperdimensional Vocab Memory — oscillator associative memory for a language model.
- Encode vocab as 256 complex oscillator hypervectors (random projection).
- Burn in token co-occurrence from text corpus (Hebbian relationship modulation).
- Query: context string → recall associated tokens by phase-coherent pattern
completion.
- Fuse: memory scores boost model logits (3B params for reasoning, memory for
knowledge → functions like a larger model).
Real Llama 3B tokenizer + bf16 embeddings, text corpus from the local disk.
python3 hyper_vocab_memory.py
"""
import glob, json, math, os, struct, sys, time
import numpy as np
# ── tokenizer + embeddings (llama 3B, mmap'd bf16) ──────────────────────
BASE = glob.glob("/home/compunerd/.cache/huggingface/hub/"
"models--huihui-ai--Hermes-3-Llama-3.2-3B-abliterated/"
"snapshots/*")[0]
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)),
"bqsm_assist"))
# use the Safetensors helper inline rather than import (avoids directory issues)
C = json.load(open(os.path.join(BASE, "config.json")))
D = C["hidden_size"]
TK = json.load(open(os.path.join(BASE, "tokenizer.json")))
VOCAB = TK["model"]["vocab"] # token -> id
INV = {v: k for k, v in VOCAB.items()}
class ST:
"""Minimal safetensors reader — mmap, no copy on parse."""
def __init__(self):
self.shards, self.idx = [], {}
for p in sorted(glob.glob(os.path.join(BASE, "*.safetensors"))):
mm = np.memmap(p, dtype=np.uint8, mode="r")
n = struct.unpack("<Q", bytes(mm[:8]))[0]
hdr = json.loads(bytes(mm[8:8 + n]).decode())
st = 8 + n
si = len(self.shards)
self.shards.append((mm, st))
for k, v in hdr.items():
if k != "__metadata__":
self.idx[k] = (si, v)
def get(self, name):
si, v = self.idx[name]
mm, st = self.shards[si]
a, b = v["data_offsets"]
raw = np.asarray(mm[st + a: st + b])
dt = v["dtype"]
if dt == "BF16":
return ((raw.view(np.uint16).astype(np.uint32) << 16)
).view(np.float32).reshape(v["shape"])
elif dt == "F16":
return raw.view(np.float16).astype(np.float32).reshape(v["shape"])
return raw.view(np.float32).reshape(v["shape"])
st = ST()
# ── embed-row helper (bf16 zero-copy per row) ───────────────────────────
def embed_row(token_id):
si, v = st.idx["model.embed_tokens.weight"]
mm, st_off = st.shards[si]
D_emb = v["shape"][1]
a, b = v["data_offsets"]
off = st_off + a + token_id * D_emb * 2 # bf16 = 2 bytes
raw = np.asarray(mm[off: off + D_emb * 2])
return ((raw.view(np.uint16).astype(np.uint32) << 16)
).view(np.float32) # → f32, D-dim
# ── tokenizer ────────────────────────────────────────────────────────────
def encode(text):
ids, words = [128000], text.split()
for i, w in enumerate(words):
w = w.strip(".,;:!?'\"()[]{}<>/\\|+-=*&^%$#@~`").strip()
if not w:
continue
key = ("Ġ" + w) if i else w
if key in VOCAB:
ids.append(VOCAB[key])
elif w in VOCAB:
ids.append(VOCAB[w])
else:
for ch in key:
if ch in VOCAB:
ids.append(VOCAB[ch])
return ids
def dec(i):
return INV.get(i, f"[{i}]").replace("Ġ", " ").replace("Ċ", "\n")
# ── Hyperdimensional encoding: token embedding → oscillator state ───────
N_OSC = 256
rng = np.random.default_rng(42)
# random projection matrix [2*N_OSC, D]
PROJ = rng.standard_normal((N_OSC * 2, D), dtype=np.float32) / np.sqrt(D)
def osc_vector(tok, cache):
"""Complex oscillator state for a token (from cache), or zeros if unseen."""
v = cache.get(tok)
return v if v is not None else np.zeros(N_OSC, dtype=np.complex64)
def build_cache(token_ids, chunk=4096, label=""):
"""Encode token IDs into normalized complex oscillator states (batched).
Reads the bf16 embedding matrix via mmap in chunks, projects each chunk,
and stores a compact complex64 vector per token. Full vocab (~128K) is
~262 MB of cache — built chunk-wise so peak memory stays ~100 MB."""
si, v = st.idx["model.embed_tokens.weight"]
mm, st_off = st.shards[si]
D_emb = v["shape"][1]
a, b = v["data_offsets"]
base = st_off + a
ids = list(token_ids)
cache = {}
for i in range(0, len(ids), chunk):
batch = ids[i:i + chunk]
lo, hi = batch[0], batch[-1]
off = base + lo * D_emb * 2
raw = np.asarray(mm[off: off + (hi - lo + 1) * D_emb * 2])
E = ((raw.view(np.uint16).astype(np.uint32) << 16)
).view(np.float32).reshape(hi - lo + 1, D_emb)
P = E[[t - lo for t in batch]] @ PROJ.T # [batch, 2*N_OSC]
P /= (np.linalg.norm(P, axis=1, keepdims=True) + 1e-8)
C = (P[:, :N_OSC] + 1j * P[:, N_OSC:]).astype(np.complex64)
for j, t in enumerate(batch):
cache[t] = C[j]
if label and (i == 0 or (i + chunk) >= len(ids)):
print(f" {label} {min(i + chunk, len(ids))}/{len(ids)}", flush=True)
return cache
# ── Gather the tokens that actually matter (corpus + queries) ───────────
corpus_files = [
"/home/compunerd/agent_framework/README.md",
"/home/compunerd/agent_framework/bqsm_assist/WAVE_RIDER_BREAKTHROUGH.md",
"/home/compunerd/Desktop/bqsm/basin-quotient-machine/LENS_CONTROL_METHODS.md",
"/home/compunerd/Desktop/bqsm/basin-quotient-machine/README.md",
]
test_queries = [
"The capital of France is",
"BQSM uses coupled",
"The ring computes through mode",
"lens site 0 enhances the",
"Phase 0 Gate",
"a transformer forward pass as",
"the model with real bf16",
"attention becomes geometric",
]
used_ids = set()
corpus_texts = []
for fp in corpus_files:
if os.path.exists(fp):
text = open(fp).read()[:50000]
corpus_texts.append(text)
used_ids.update(encode(text))
for q in test_queries:
used_ids.update(encode(q))
# Full-dictionary coverage: encode the entire vocab so ANY token the model
# emits can be scored/recalled. Corpus-derived associations still come from
# `used_ids`, but every token now has a hypervector. BQSM_FULL_VOCAB=off
# reverts to the corpus-subset cache for fast smoke tests.
FULL_VOCAB = os.environ.get("BQSM_FULL_VOCAB", "on") != "off"
if FULL_VOCAB:
V = st.idx["model.embed_tokens.weight"][1]["shape"][0]
print(f"encoding full vocab ({V} tokens) — one-time, ~30s...")
t0 = time.time()
cache = build_cache(range(V), label="vocab")
print(f" {time.time()-t0:.1f}s ({len(cache)} tokens cached)")
else:
print(f"encoding {len(used_ids)} distinct tokens (corpus + queries)...")
t0 = time.time()
cache = build_cache(sorted(used_ids))
print(f" {time.time()-t0:.1f}s")
# Burn-in + build sparse "following" index (skip-gram, distance-decayed)
from collections import Counter, defaultdict
print("\nBurn-in corpus (skip-gram PMI, window=3)...")
MAX_DIST = 3
unigram = Counter()
skipgram = {d: Counter() for d in range(1, MAX_DIST + 1)}
total_tokens = 0
for text in corpus_texts:
ids = encode(text)
total_tokens += len(ids)
unigram.update(ids)
for d in range(1, MAX_DIST + 1):
skipgram[d].update(zip(ids[:-d], ids[d:]))
# Distance-decayed PMI: tokens d apart get weight 1/d. This captures
# "France -> is -> Paris" as "France -> Paris" (d=2, weight 0.5), which is
# what a pure bigram memory misses.
W = np.zeros((N_OSC, N_OSC), dtype=np.complex64)
following = defaultdict(list) # token_id -> [(target_id, weight), ...]
n_pairs = 0
for d in range(1, MAX_DIST + 1):
decay = 1.0 / d
for (a, b), cnt in skipgram[d].items():
za = cache.get(a); zb = cache.get(b)
if za is None or zb is None:
continue
pmi = math.log((cnt * total_tokens) / (unigram[a] * unigram[b]) + 1e-12)
if pmi <= 0:
continue
w = decay * pmi
W += w * np.outer(za, np.conj(zb))
following[a].append((b, w))
n_pairs += 1
print(f" {n_pairs} associations burned in (PMI>0), from {total_tokens} tokens")
norm = np.linalg.norm(W)
if norm > 0:
W /= norm
# ── Sparse recall: per context token, aggregate its strongest followers ───
def query_sparse(context_str, top_k=20):
ids = encode(context_str)
scores = defaultdict(float)
for i, cid in enumerate(ids):
if cid not in following:
continue
# last token gets 2× weight for next-token prediction
w = 2.0 if i == len(ids) - 1 else 1.0
for tid, pmi in following[cid]:
scores[tid] += w * pmi
ranked = sorted(scores.items(), key=lambda x: x[1], reverse=True)
return [r for r in ranked if r[0] in cache][:top_k]
# ── Query: context → associative recall ──────────────────────────────────
def query(context_str, top_k=20):
"""Encode context as oscillator state, recall associated tokens via W."""
ids = encode(context_str)
ctx = np.zeros(N_OSC, dtype=np.complex64)
n_valid = 0
for t in ids:
z = cache.get(t)
if z is not None:
ctx += z
n_valid += 1
if n_valid == 0:
return []
ctx /= n_valid + 1e-8
recalled = W @ ctx
recalled = recalled / (np.linalg.norm(recalled) + 1e-8)
scores = []
for t, zt in cache.items():
score = float(abs(np.dot(np.conj(zt), recalled)))
scores.append((t, score))
scores.sort(key=lambda x: x[1], reverse=True)
return scores[:top_k]
# ── Demo (only when run directly) ─────────────────────────────────────────
if __name__ == "__main__":
print("\n" + "=" * 66)
print("HYPERDIMENSIONAL VOCAB MEMORY — recall demo")
print("=" * 66)
tests = [
("The capital of France is", "Paris"),
("BQSM uses coupled", "oscillator"),
("The ring computes through mode", "coupling"),
("lens site 0 enhances the", "channel"),
("Phase 0 Gate", "FAILURE"),
("a transformer forward pass as", "coupled"),
("the model with real bf16", "weights"),
("attention becomes geometric", "adjacency"),
]
def find_token(text):
for t in cache:
if dec(t).strip() == text:
return t
return None
for context, expected in tests:
results = query_sparse(context)
expected_id = find_token(expected)
rank = None
for i, (t, s) in enumerate(results):
if t == expected_id:
rank = i + 1
break
print(f"\n \"{context}\"")
print(f" expect: \"{expected}\" rank: "
f"{rank if rank else '-- (not in top %d)' % len(results)}")
print(f" top 5: ", end="")
for t, s in results[:5]:
print(f"{dec(t)!r}({s:.4f})", end=" ")
print()
print("\n" + "=" * 66)
print("FUSION — memory boosts model logits (simulated)")
print("=" * 66)
context = "The capital of France is"
model_logits = {t: float(rng.standard_normal()) * 0.5 for t in cache}
results = query_sparse(context)
for t, mem_score in results:
model_logits[t] = model_logits.get(t, 0.0) + 2.0 * mem_score
top_after = sorted(model_logits, key=lambda t: model_logits[t],
reverse=True)[:10]
print(f" context: {context!r}")
print(f" top-10 after fusion: {[dec(t) for t in top_after]}")
paris_id = find_token("Paris")
if paris_id:
rank = top_after.index(paris_id) + 1 if paris_id in top_after else None
print(f" 'Paris' rank after fusion: "
f"{'#' + str(rank) if rank else '-- (out of top 10)'}")
print("\n" + "=" * 66)
print("HOW IT SCALES TO 30B-CLASS:")
print(" - 3B model: grammar, reasoning, common patterns (its parameters)")
print(" - Oscillator memory: facts, entity links, co-occurrence (burn-in)")
print(" - The memory costs N² oscillators (~256² = 65K couplings), not GBs")
print(" - Continually learns: new facts burn in without retraining the model")
print(" - Hyperdimensional encoding: near-orthogonal random projections")
print(" = associative memory for 128K vocab in ~65K complex couplings")
print("=" * 66)