gemma_glm / llm_glm /vector_engine.py
DigitalEuan's picture
Upload 14 files
9870a25 verified
Raw
History Blame Contribute Delete
12.6 kB
#!/usr/bin/env python3
"""
Vector Engine β€” Core 24-bit Substrate Operations
=================================================
Maps words, tokens, and patches to 24-bit Golay codewords.
Provides NRCI scoring, Hamming distance, and lattice classification.
"""
import sys, os, hashlib, re, json
from typing import List, Dict, Tuple, Optional, Set
from fractions import Fraction
from collections import Counter
# Add parent dir for UBP engine
_this_dir = os.path.dirname(os.path.abspath(__file__))
_parent = os.path.dirname(_this_dir)
if _parent not in sys.path:
sys.path.insert(0, _parent)
from ubp_unified_v5 import (
GolayCodeEngine, LeechLatticeEngine, UBPSourceCodeParticlePhysics,
)
# ── Singletons (expensive to create, reuse forever) ──
_GOLAY: Optional[GolayCodeEngine] = None
_LEECH: Optional[LeechLatticeEngine] = None
_PP: Optional[UBPSourceCodeParticlePhysics] = None
def get_golay() -> GolayCodeEngine:
global _GOLAY
if _GOLAY is None:
_GOLAY = GolayCodeEngine()
return _GOLAY
def get_leech() -> LeechLatticeEngine:
global _LEECH
if _LEECH is None:
g = get_golay()
_LEECH = LeechLatticeEngine(g)
return _LEECH
def get_pp() -> UBPSourceCodeParticlePhysics:
global _PP
if _PP is None:
_PP = UBPSourceCodeParticlePhysics()
return _PP
# ══════════════════════════════════════════════════════════════════════════════
# VECTOR MAPPING
# ══════════════════════════════════════════════════════════════════════════════
def word_to_hash24(word: str) -> List[int]:
"""Hash a word to 24 bits via SHA-256 prefix. Deterministic."""
h = hashlib.sha256(word.lower().strip().encode()).digest()
return [(h[i // 8] >> (7 - i % 8)) & 1 for i in range(24)]
def snap_to_codeword(vec: List[int]) -> Tuple[List[int], dict]:
"""Snap any 24-bit vector to nearest Golay codeword."""
g = get_golay()
return g.snap_to_codeword(list(vec))
def hamming(a: List[int], b: List[int]) -> int:
"""Hamming distance between two 24-bit vectors."""
return sum(x ^ y for x, y in zip(a, b))
def vector_to_hex(vec: List[int]) -> int:
"""Convert 24-bit vector to integer."""
return sum((1 << (23 - i)) for i in range(24) if vec[i])
def hex_to_vector(h: int) -> List[int]:
"""Convert integer to 24-bit vector."""
return [(h >> (23 - i)) & 1 for i in range(24)]
# ══════════════════════════════════════════════════════════════════════════════
# LATTICE CLASSIFICATION
# ══════════════════════════════════════════════════════════════════════════════
LATTICE_CLASSES = {
0: "Identity", 8: "Octad", 10: "Decad", 12: "Dodecad",
14: "Tetradecad", 16: "Hexadecad", 20: "Icosad", 24: "Edge"
}
def lattice_class(hw: int) -> str:
return LATTICE_CLASSES.get(hw, f"HW={hw}")
def classify(vec: List[int]) -> dict:
"""Full classification of a 24-bit vector."""
l = get_leech()
hw = sum(vec)
nrci = float(l.calculate_nrci(vec))
tax = float(l.calculate_symmetry_tax(vec))
return {
"hw": hw,
"nrci": nrci,
"tax": tax,
"lattice": lattice_class(hw),
"hex": f"0x{vector_to_hex(vec):06X}",
"in_band": nrci >= 0.70,
"on_octad": hw == 8,
}
# ══════════════════════════════════════════════════════════════════════════════
# SVD VOCABULARY BUILDER
# ══════════════════════════════════════════════════════════════════════════════
class SVDVocabulary:
"""Builds distributional 24-bit vectors from a corpus using PPMI + SVD.
Closely follows GLM20_svd_vocab.py methodology."""
def __init__(self):
self.word_vectors: Dict[str, List[int]] = {}
self.word_snapped: Dict[str, List[int]] = {}
self.word_meta: Dict[str, dict] = {}
self.context_words: List[str] = []
self._built = False
def build_from_definitions(self, definitions: Dict[str, str],
context_size: int = 100,
window: int = 8,
n_dims: int = 24) -> int:
"""Build SVD vocabulary from word→definition mappings.
Returns number of words mapped."""
try:
import numpy as np
except ImportError:
return 0
# Tokenize corpus
tokens = []
for defn in definitions.values():
tokens.extend(re.findall(r"[a-z]+", defn.lower()))
tokens = [t for t in tokens if len(t) >= 3]
target_words = sorted(definitions.keys())
vocab_idx = {w: i for i, w in enumerate(target_words)}
# Context words (most frequent non-target)
freq = Counter(tokens)
self.context_words = [w for w, _ in freq.most_common(context_size + len(target_words))
if w not in vocab_idx][:context_size]
ctx_idx = {w: i for i, w in enumerate(self.context_words)}
# PPMI co-occurrence
cooc = np.zeros((len(target_words), len(self.context_words)))
all_tokens = re.findall(r"[a-z]+", " ".join(definitions.values()).lower())
for i, tok in enumerate(all_tokens):
if tok not in vocab_idx:
continue
wi = vocab_idx[tok]
for j in range(max(0, i - window), min(len(all_tokens), i + window + 1)):
if j == i:
continue
ctx = all_tokens[j]
if ctx in ctx_idx:
cooc[wi, ctx_idx[ctx]] += 1
# PPMI transform
total = cooc.sum()
if total == 0:
return 0
row_sums = cooc.sum(axis=1, keepdims=True)
col_sums = cooc.sum(axis=0, keepdims=True)
row_sums[row_sums == 0] = 1
col_sums[col_sums == 0] = 1
ppmi = np.log2((cooc * total) / (row_sums * col_sums) + 1e-10)
ppmi[ppmi < 0] = 0
# SVD β†’ n_dims
U, S, Vt = np.linalg.svd(ppmi, full_matrices=False)
svd_vecs = U[:, :n_dims] * S[:n_dims]
# Median-quantize to bits
medians = np.median(svd_vecs, axis=0)
bit_vecs = (svd_vecs > medians).astype(int)
# Snap to Golay
g = get_golay()
l = get_leech()
for i, word in enumerate(target_words):
raw = [int(b) for b in bit_vecs[i]]
self.word_vectors[word] = raw
snapped, meta = g.snap_to_codeword(raw)
self.word_snapped[word] = snapped
self.word_meta[word] = {
"raw_hw": sum(raw),
"snapped_hw": sum(snapped),
"nrci": float(l.calculate_nrci(snapped)),
"lattice": lattice_class(sum(snapped)),
"hex": f"0x{vector_to_hex(snapped):06X}",
"method": "svd",
}
self._built = True
return len(self.word_snapped)
def get_vector(self, word: str) -> List[int]:
"""Get 24-bit vector for a word. Falls back to hash if not in SVD vocab."""
w = word.lower().strip()
if w in self.word_snapped:
return self.word_snapped[w]
# Fallback: hash + snap
raw = word_to_hash24(w)
snapped, _ = snap_to_codeword(raw)
return snapped
def get_meta(self, word: str) -> dict:
"""Get metadata for a word."""
w = word.lower().strip()
if w in self.word_meta:
return self.word_meta[w]
raw = word_to_hash24(w)
snapped, _ = snap_to_codeword(raw)
l = get_leech()
return {
"raw_hw": sum(raw),
"snapped_hw": sum(snapped),
"nrci": float(l.calculate_nrci(snapped)),
"lattice": lattice_class(sum(snapped)),
"hex": f"0x{vector_to_hex(snapped):06X}",
"method": "hash_fallback",
}
def hamming_between(self, word_a: str, word_b: str) -> int:
return hamming(self.get_vector(word_a), self.get_vector(word_b))
def save(self, path: str):
"""Save vocabulary to JSON."""
data = {
"word_snapped": self.word_snapped,
"word_meta": self.word_meta,
"context_words": self.context_words,
}
with open(path, "w") as f:
json.dump(data, f)
def load(self, path: str) -> bool:
"""Load vocabulary from JSON."""
if not os.path.exists(path):
return False
with open(path) as f:
data = json.load(f)
self.word_snapped = {k: list(v) for k, v in data["word_snapped"].items()}
self.word_meta = data.get("word_meta", {})
self.context_words = data.get("context_words", [])
self._built = True
return True
# ══════════════════════════════════════════════════════════════════════════════
# ZONE CENTROID (Idea Zone from GLM07)
# ══════════════════════════════════════════════════════════════════════════════
class IdeaZone:
"""Maintains a running EMA centroid for the current conversation topic."""
def __init__(self, alpha: float = 0.3):
self.alpha = alpha
self.centroid: List[float] = [0.0] * 24
self.words: List[str] = []
self._snapped: Optional[List[int]] = None
def update(self, word: str, vocab: SVDVocabulary):
"""Add a word to the zone, updating the EMA centroid."""
vec = vocab.get_vector(word)
self.centroid = [self.alpha * v + (1 - self.alpha) * c
for v, c in zip(vec, self.centroid)]
self.words.append(word.lower())
self._snapped = None # invalidate cache
def get_centroid(self) -> List[int]:
"""Get the snapped centroid vector."""
if self._snapped is None:
bits = [1 if c > 0.5 else 0 for c in self.centroid]
self._snapped, _ = snap_to_codeword(bits)
return self._snapped
def get_centroid_nrci(self) -> float:
l = get_leech()
return float(l.calculate_nrci(self.get_centroid()))
def distance_to(self, vec: List[int]) -> int:
return hamming(self.get_centroid(), vec)
def reset(self):
self.centroid = [0.0] * 24
self.words = []
self._snapped = None
# ══════════════════════════════════════════════════════════════════════════════
# MOG (Miracle Octad Generator) Layout
# ══════════════════════════════════════════════════════════════════════════════
MOG_POSITIONS = [(r, c) for r in range(4) for c in range(6)] # 24 positions
def mog_quadrants(vec: List[int]) -> List[int]:
"""Split 24-bit vector into 4 sextets (Reality/Information/Activation/Potential)."""
return [sum(vec[i:i+6]) for i in range(0, 24, 6)]
def mog_dominant_layer(vec: List[int]) -> Tuple[str, int]:
"""Return the dominant ontological layer and its bit count."""
layers = ["Reality", "Information", "Activation", "Potential"]
q = mog_quadrants(vec)
idx = q.index(max(q))
return layers[idx], q[idx]