| |
| """ |
| 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 |
|
|
| |
| _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, |
| ) |
|
|
| |
| _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 |
|
|
|
|
| |
| |
| |
|
|
| 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_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, |
| } |
|
|
|
|
| |
| |
| |
|
|
| 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 |
| |
| |
| 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)} |
| |
| |
| 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)} |
| |
| |
| 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 |
| |
| |
| 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 |
| |
| |
| U, S, Vt = np.linalg.svd(ppmi, full_matrices=False) |
| svd_vecs = U[:, :n_dims] * S[:n_dims] |
| |
| |
| medians = np.median(svd_vecs, axis=0) |
| bit_vecs = (svd_vecs > medians).astype(int) |
| |
| |
| 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] |
| |
| 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 |
|
|
|
|
| |
| |
| |
|
|
| 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 |
| |
| 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_POSITIONS = [(r, c) for r in range(4) for c in range(6)] |
|
|
| 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] |
|
|