Create holo_program.py
Browse files- holo_program.py +768 -0
holo_program.py
ADDED
|
@@ -0,0 +1,768 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
holo-program
|
| 3 |
+
============
|
| 4 |
+
A memory where programs are first-class items, membranes are nested
|
| 5 |
+
regions, and writes can be cryptographically keyed.
|
| 6 |
+
|
| 7 |
+
Three layers, one substrate:
|
| 8 |
+
|
| 9 |
+
Program layer Operations are stored as key-value pairs:
|
| 10 |
+
key = random vector derived from (op, in)
|
| 11 |
+
value = bind(out, out_val)
|
| 12 |
+
Execution is unbind-then-classify. Chaining is
|
| 13 |
+
composition.
|
| 14 |
+
|
| 15 |
+
Membrane layer Regions have parents. Reads inherit from child
|
| 16 |
+
to parent. Writes isolate to the innermost
|
| 17 |
+
membrane. Dissolution merges a child into its
|
| 18 |
+
parent (P-systems operation). Movement transfers
|
| 19 |
+
an object between membranes.
|
| 20 |
+
|
| 21 |
+
Cryptographic layer
|
| 22 |
+
Item vectors are derived from a key. Without
|
| 23 |
+
the key, a trace is noise. A commitment scheme
|
| 24 |
+
lets a store prove that an item was written
|
| 25 |
+
without revealing the item.
|
| 26 |
+
|
| 27 |
+
Everything is addressable through `ask()`.
|
| 28 |
+
|
| 29 |
+
Requires: numpy.
|
| 30 |
+
"""
|
| 31 |
+
|
| 32 |
+
from __future__ import annotations
|
| 33 |
+
|
| 34 |
+
import argparse
|
| 35 |
+
import json
|
| 36 |
+
import os
|
| 37 |
+
import zlib
|
| 38 |
+
from typing import Any, Dict, List, Optional, Tuple
|
| 39 |
+
|
| 40 |
+
import numpy as np
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
D_DEFAULT = 2048
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
# ============================================================
|
| 47 |
+
# Primitives
|
| 48 |
+
# ============================================================
|
| 49 |
+
def rvec(seed: int, d: int = D_DEFAULT) -> np.ndarray:
|
| 50 |
+
r = np.random.default_rng(seed)
|
| 51 |
+
return np.exp(1j * r.uniform(0, 2 * np.pi, d))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def povec(seed: int, d: int = D_DEFAULT) -> np.ndarray:
|
| 55 |
+
v = rvec(seed, d)
|
| 56 |
+
F = np.fft.fft(v)
|
| 57 |
+
return np.fft.ifft(F / np.abs(F))
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def bind(a: np.ndarray, b: np.ndarray) -> np.ndarray:
|
| 61 |
+
return np.fft.ifft(np.fft.fft(a) * np.fft.fft(b))
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def unbind(a: np.ndarray, c: np.ndarray) -> np.ndarray:
|
| 65 |
+
return np.fft.ifft(np.fft.fft(c) * np.conj(np.fft.fft(a)))
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def norm(v: np.ndarray) -> np.ndarray:
|
| 69 |
+
F = np.fft.fft(v)
|
| 70 |
+
return np.fft.ifft(F / (np.abs(F) + 1e-12))
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def cos(a: np.ndarray, b: np.ndarray) -> float:
|
| 74 |
+
na = np.linalg.norm(a)
|
| 75 |
+
nb = np.linalg.norm(b)
|
| 76 |
+
if na < 1e-12 or nb < 1e-12:
|
| 77 |
+
return 0.0
|
| 78 |
+
return float(np.real(np.vdot(a, b)) / (na * nb))
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def project(u: np.ndarray, v: np.ndarray) -> float:
|
| 82 |
+
nv2 = float(np.real(np.vdot(v, v)))
|
| 83 |
+
if nv2 < 1e-12:
|
| 84 |
+
return 0.0
|
| 85 |
+
return float(np.real(np.vdot(u, v)) / nv2)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def cos_batch(u: np.ndarray, X: np.ndarray) -> np.ndarray:
|
| 89 |
+
X = X.reshape(len(X), -1)
|
| 90 |
+
u = u.ravel()
|
| 91 |
+
return np.real(X @ np.conj(u)) / (
|
| 92 |
+
np.linalg.norm(X, axis=1) * np.linalg.norm(u) + 1e-12)
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
# ============================================================
|
| 96 |
+
# Cryptographic layer
|
| 97 |
+
# ============================================================
|
| 98 |
+
def keyed_seed(key: Optional[str], label: str) -> int:
|
| 99 |
+
s = f"{key}::{label}" if key is not None else f"_::{label}"
|
| 100 |
+
return zlib.crc32(s.encode()) & 0xffffffff
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def make_vector(key: Optional[str], label: str, d: int) -> np.ndarray:
|
| 104 |
+
return povec(keyed_seed(key, label), d)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
# ============================================================
|
| 108 |
+
# Global role vectors (cached per dimension)
|
| 109 |
+
# ============================================================
|
| 110 |
+
_ROLES_CACHE: Dict[int, Dict[str, np.ndarray]] = {}
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def get_roles(d: int) -> Dict[str, np.ndarray]:
|
| 114 |
+
if d not in _ROLES_CACHE:
|
| 115 |
+
_ROLES_CACHE[d] = {
|
| 116 |
+
"op": povec(1000, d),
|
| 117 |
+
"in": povec(1001, d),
|
| 118 |
+
"out": povec(1002, d),
|
| 119 |
+
"salt": povec(1003, d),
|
| 120 |
+
}
|
| 121 |
+
return _ROLES_CACHE[d]
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
# ============================================================
|
| 125 |
+
# Membrane
|
| 126 |
+
# ============================================================
|
| 127 |
+
class Membrane:
|
| 128 |
+
"""
|
| 129 |
+
A region of the substrate with an optional parent.
|
| 130 |
+
"""
|
| 131 |
+
|
| 132 |
+
def __init__(self, name: str, d: int, key: Optional[str] = None,
|
| 133 |
+
parent: Optional["Membrane"] = None,
|
| 134 |
+
threshold: float = 0.05):
|
| 135 |
+
self.name = name
|
| 136 |
+
self.d = d
|
| 137 |
+
self.key = key
|
| 138 |
+
self.parent = parent
|
| 139 |
+
self.threshold = threshold
|
| 140 |
+
self.roles = get_roles(d)
|
| 141 |
+
self.trace = np.zeros(d, dtype=complex)
|
| 142 |
+
self.ops_trace = np.zeros(d, dtype=complex)
|
| 143 |
+
self.weights: Dict[str, float] = {}
|
| 144 |
+
self.labels: set = set()
|
| 145 |
+
self.operations: List[Tuple[str, str, str]] = []
|
| 146 |
+
|
| 147 |
+
def _vec(self, label: str) -> np.ndarray:
|
| 148 |
+
return make_vector(self.key, label, self.d)
|
| 149 |
+
|
| 150 |
+
def _op_key(self, op_name: str, in_val: str) -> np.ndarray:
|
| 151 |
+
"""
|
| 152 |
+
Random key for an operation, derived from the pair.
|
| 153 |
+
|
| 154 |
+
Fully random: no two distinct pairs share a component, so
|
| 155 |
+
cross-talk between operations with the same op name is
|
| 156 |
+
negligible. Same computation in add_op and apply.
|
| 157 |
+
"""
|
| 158 |
+
pair_id = f"__op__{op_name}::{in_val}"
|
| 159 |
+
return self._vec(pair_id)
|
| 160 |
+
|
| 161 |
+
# ---------------- items ----------------
|
| 162 |
+
def store(self, label: str, weight: float = 1.0) -> None:
|
| 163 |
+
v = self._vec(label)
|
| 164 |
+
self.trace += weight * bind(v, v)
|
| 165 |
+
self.weights[label] = self.weights.get(label, 0.0) + weight
|
| 166 |
+
self.labels.add(label)
|
| 167 |
+
|
| 168 |
+
def similarity(self, label: str) -> float:
|
| 169 |
+
v = self._vec(label)
|
| 170 |
+
u = unbind(v, self.trace)
|
| 171 |
+
return project(u, v)
|
| 172 |
+
|
| 173 |
+
def query_inherited(self, label: str) -> float:
|
| 174 |
+
sim = self.similarity(label)
|
| 175 |
+
if sim >= self.threshold:
|
| 176 |
+
return sim
|
| 177 |
+
if self.parent is not None:
|
| 178 |
+
return self.parent.query_inherited(label)
|
| 179 |
+
return sim
|
| 180 |
+
|
| 181 |
+
# ---------------- operations ----------------
|
| 182 |
+
def add_op(self, op_name: str, in_val: str, out_val: str) -> None:
|
| 183 |
+
key = self._op_key(op_name, in_val)
|
| 184 |
+
value = bind(self.roles["out"], self._vec(out_val))
|
| 185 |
+
self.ops_trace += bind(key, value)
|
| 186 |
+
self.operations.append((op_name, in_val, out_val))
|
| 187 |
+
|
| 188 |
+
def apply(self, op_name: str, in_val: str) -> Optional[str]:
|
| 189 |
+
if not self.operations:
|
| 190 |
+
return None
|
| 191 |
+
key = self._op_key(op_name, in_val)
|
| 192 |
+
u = unbind(key, self.ops_trace)
|
| 193 |
+
out = unbind(self.roles["out"], u)
|
| 194 |
+
candidates = sorted({o[2] for o in self.operations})
|
| 195 |
+
stack = np.stack([self._vec(c) for c in candidates])
|
| 196 |
+
sims = np.abs(cos_batch(out, stack))
|
| 197 |
+
best = int(np.argmax(sims))
|
| 198 |
+
if sims[best] < self.threshold:
|
| 199 |
+
return None
|
| 200 |
+
return candidates[best]
|
| 201 |
+
|
| 202 |
+
def find_applicable(self, in_val: str
|
| 203 |
+
) -> List[Tuple[str, str, float]]:
|
| 204 |
+
hits: List[Tuple[str, str, float]] = []
|
| 205 |
+
for op_name, in_v, out_v in self.operations:
|
| 206 |
+
if in_v != in_val:
|
| 207 |
+
continue
|
| 208 |
+
key = self._op_key(op_name, in_val)
|
| 209 |
+
u = unbind(key, self.ops_trace)
|
| 210 |
+
out = unbind(self.roles["out"], u)
|
| 211 |
+
sim = project(out, self._vec(out_v))
|
| 212 |
+
if sim >= self.threshold:
|
| 213 |
+
hits.append((op_name, out_v, float(sim)))
|
| 214 |
+
hits.sort(key=lambda h: -h[2])
|
| 215 |
+
return hits
|
| 216 |
+
|
| 217 |
+
# ---------------- cryptographic ----------------
|
| 218 |
+
def commit(self, label: str, salt: str) -> np.ndarray:
|
| 219 |
+
v_label = self._vec(label)
|
| 220 |
+
v_salt = self._vec(salt)
|
| 221 |
+
return bind(v_label, v_salt)
|
| 222 |
+
|
| 223 |
+
def verify(self, label: str, salt: str,
|
| 224 |
+
commitment: np.ndarray) -> bool:
|
| 225 |
+
expected = self.commit(label, salt)
|
| 226 |
+
return cos(commitment, expected) > 0.99
|
| 227 |
+
|
| 228 |
+
# ---------------- diagnostics ----------------
|
| 229 |
+
def stats(self) -> Dict:
|
| 230 |
+
return {
|
| 231 |
+
"name": self.name,
|
| 232 |
+
"d": self.d,
|
| 233 |
+
"has_key": self.key is not None,
|
| 234 |
+
"n_labels": len(self.labels),
|
| 235 |
+
"n_operations": len(self.operations),
|
| 236 |
+
"trace_magnitude": float(np.linalg.norm(self.trace)),
|
| 237 |
+
"ops_magnitude": float(np.linalg.norm(self.ops_trace)),
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
# ============================================================
|
| 242 |
+
# HoloProgram
|
| 243 |
+
# ============================================================
|
| 244 |
+
class HoloProgram:
|
| 245 |
+
def __init__(self, d: int = D_DEFAULT,
|
| 246 |
+
key: Optional[str] = None,
|
| 247 |
+
threshold: float = 0.05):
|
| 248 |
+
self.d = d
|
| 249 |
+
self.key = key
|
| 250 |
+
self.threshold = threshold
|
| 251 |
+
self.membranes: Dict[str, Membrane] = {}
|
| 252 |
+
self.add_membrane("root", parent=None)
|
| 253 |
+
|
| 254 |
+
def add_membrane(self, name: str,
|
| 255 |
+
parent: Optional[str] = "root") -> None:
|
| 256 |
+
if name in self.membranes:
|
| 257 |
+
return
|
| 258 |
+
parent_m = self.membranes.get(parent) if parent else None
|
| 259 |
+
self.membranes[name] = Membrane(
|
| 260 |
+
name=name, d=self.d, key=self.key,
|
| 261 |
+
parent=parent_m, threshold=self.threshold,
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
def store(self, membrane: str, label: str,
|
| 265 |
+
weight: float = 1.0) -> None:
|
| 266 |
+
self.membranes[membrane].store(label, weight=weight)
|
| 267 |
+
|
| 268 |
+
def add_op(self, membrane: str, op_name: str,
|
| 269 |
+
in_val: str, out_val: str) -> None:
|
| 270 |
+
self.membranes[membrane].add_op(op_name, in_val, out_val)
|
| 271 |
+
|
| 272 |
+
def query(self, membrane: str, label: str) -> float:
|
| 273 |
+
return self.membranes[membrane].query_inherited(label)
|
| 274 |
+
|
| 275 |
+
def apply(self, membrane: str, op_name: str,
|
| 276 |
+
in_val: str) -> Optional[str]:
|
| 277 |
+
return self.membranes[membrane].apply(op_name, in_val)
|
| 278 |
+
|
| 279 |
+
def run_chain(self, membrane: str, ops: List[str],
|
| 280 |
+
start: str) -> Dict:
|
| 281 |
+
cur = start
|
| 282 |
+
path = [cur]
|
| 283 |
+
for op in ops:
|
| 284 |
+
nxt = self.apply(membrane, op, cur)
|
| 285 |
+
if nxt is None:
|
| 286 |
+
return {"path": path, "ok": False, "failed_at": op}
|
| 287 |
+
path.append(nxt)
|
| 288 |
+
cur = nxt
|
| 289 |
+
return {"path": path, "ok": True}
|
| 290 |
+
|
| 291 |
+
def run_data_driven(self, membrane: str, start: str,
|
| 292 |
+
max_steps: int = 20) -> Dict:
|
| 293 |
+
cur = start
|
| 294 |
+
path = [cur]
|
| 295 |
+
for _ in range(max_steps):
|
| 296 |
+
hits = self.membranes[membrane].find_applicable(cur)
|
| 297 |
+
if not hits:
|
| 298 |
+
break
|
| 299 |
+
op_name, out_val, sim = hits[0]
|
| 300 |
+
path.append(out_val)
|
| 301 |
+
cur = out_val
|
| 302 |
+
return {"path": path, "steps": len(path) - 1}
|
| 303 |
+
|
| 304 |
+
def commit(self, membrane: str, label: str,
|
| 305 |
+
salt: str) -> np.ndarray:
|
| 306 |
+
return self.membranes[membrane].commit(label, salt)
|
| 307 |
+
|
| 308 |
+
def verify(self, membrane: str, label: str, salt: str,
|
| 309 |
+
commitment: np.ndarray) -> bool:
|
| 310 |
+
return self.membranes[membrane].verify(label, salt, commitment)
|
| 311 |
+
|
| 312 |
+
def move(self, label: str, src: str, dst: str) -> bool:
|
| 313 |
+
s = self.membranes[src]
|
| 314 |
+
d = self.membranes[dst]
|
| 315 |
+
if label not in s.labels:
|
| 316 |
+
return False
|
| 317 |
+
w = s.weights.get(label, 1.0)
|
| 318 |
+
if w <= 0:
|
| 319 |
+
return False
|
| 320 |
+
v = s._vec(label)
|
| 321 |
+
s.trace -= w * bind(v, v)
|
| 322 |
+
s.weights[label] = 0.0
|
| 323 |
+
s.labels.discard(label)
|
| 324 |
+
d.store(label, weight=w)
|
| 325 |
+
return True
|
| 326 |
+
|
| 327 |
+
def dissolve(self, child: str, into: str) -> None:
|
| 328 |
+
c = self.membranes[child]
|
| 329 |
+
t = self.membranes[into]
|
| 330 |
+
t.trace = t.trace + c.trace
|
| 331 |
+
t.ops_trace = t.ops_trace + c.ops_trace
|
| 332 |
+
for lbl, w in c.weights.items():
|
| 333 |
+
t.weights[lbl] = t.weights.get(lbl, 0.0) + w
|
| 334 |
+
t.labels.add(lbl)
|
| 335 |
+
t.operations.extend(c.operations)
|
| 336 |
+
for m in self.membranes.values():
|
| 337 |
+
if m.parent is c:
|
| 338 |
+
m.parent = t
|
| 339 |
+
del self.membranes[child]
|
| 340 |
+
|
| 341 |
+
def ask(self, query: str, top_k: int = 8) -> List[Dict]:
|
| 342 |
+
hits: List[Dict] = []
|
| 343 |
+
|
| 344 |
+
for m_name, mem in self.membranes.items():
|
| 345 |
+
if query in mem.labels:
|
| 346 |
+
sim = mem.query_inherited(query)
|
| 347 |
+
hits.append({
|
| 348 |
+
"type": "item",
|
| 349 |
+
"membrane": m_name,
|
| 350 |
+
"label": query,
|
| 351 |
+
"similarity": sim,
|
| 352 |
+
})
|
| 353 |
+
|
| 354 |
+
for m_name, mem in self.membranes.items():
|
| 355 |
+
for op_name, in_v, out_v in mem.operations:
|
| 356 |
+
if query in (op_name, in_v, out_v):
|
| 357 |
+
role = ("op" if query == op_name else
|
| 358 |
+
"in" if query == in_v else "out")
|
| 359 |
+
hits.append({
|
| 360 |
+
"type": "operation",
|
| 361 |
+
"membrane": m_name,
|
| 362 |
+
"role": role,
|
| 363 |
+
"op": op_name,
|
| 364 |
+
"in": in_v,
|
| 365 |
+
"out": out_v,
|
| 366 |
+
})
|
| 367 |
+
|
| 368 |
+
if query in self.membranes:
|
| 369 |
+
hits.append({
|
| 370 |
+
"type": "membrane",
|
| 371 |
+
"membrane": query,
|
| 372 |
+
})
|
| 373 |
+
|
| 374 |
+
def sort_key(h):
|
| 375 |
+
if h["type"] == "item":
|
| 376 |
+
return (0, -h["similarity"])
|
| 377 |
+
return (1, 0)
|
| 378 |
+
|
| 379 |
+
hits.sort(key=sort_key)
|
| 380 |
+
return hits[:top_k]
|
| 381 |
+
|
| 382 |
+
def stats(self) -> Dict:
|
| 383 |
+
return {
|
| 384 |
+
"d": self.d,
|
| 385 |
+
"has_key": self.key is not None,
|
| 386 |
+
"threshold": self.threshold,
|
| 387 |
+
"n_membranes": len(self.membranes),
|
| 388 |
+
"membranes": {n: m.stats()
|
| 389 |
+
for n, m in self.membranes.items()},
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
def save_json(self, path: str) -> None:
|
| 393 |
+
out = self.stats()
|
| 394 |
+
out["membrane_detail"] = {
|
| 395 |
+
n: {
|
| 396 |
+
"labels": sorted(m.labels),
|
| 397 |
+
"operations": [
|
| 398 |
+
{"op": o, "in": i, "out": r}
|
| 399 |
+
for o, i, r in m.operations
|
| 400 |
+
],
|
| 401 |
+
"parent": m.parent.name if m.parent else None,
|
| 402 |
+
}
|
| 403 |
+
for n, m in self.membranes.items()
|
| 404 |
+
}
|
| 405 |
+
with open(path, "w") as f:
|
| 406 |
+
json.dump(out, f, indent=2)
|
| 407 |
+
print(f" saved: {path}")
|
| 408 |
+
|
| 409 |
+
|
| 410 |
+
# ============================================================
|
| 411 |
+
# Helpers
|
| 412 |
+
# ============================================================
|
| 413 |
+
def section(title: str) -> None:
|
| 414 |
+
print()
|
| 415 |
+
print("=" * 78)
|
| 416 |
+
print(title)
|
| 417 |
+
print("=" * 78)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
def self_test() -> bool:
|
| 421 |
+
print(" self-test:")
|
| 422 |
+
d = 2048
|
| 423 |
+
a = povec(1, d)
|
| 424 |
+
b = povec(2, d)
|
| 425 |
+
ok1 = cos(unbind(a, bind(a, b)), b) > 0.99
|
| 426 |
+
print(f" bind/unbind identity: {'PASS' if ok1 else 'FAIL'}")
|
| 427 |
+
|
| 428 |
+
v1 = make_vector("key1", "label", d)
|
| 429 |
+
v2 = make_vector("key2", "label", d)
|
| 430 |
+
ok2 = cos(v1, v2) < 0.1
|
| 431 |
+
print(f" keys separate vectors: {'PASS' if ok2 else 'FAIL'}")
|
| 432 |
+
|
| 433 |
+
bb = 0.7 * bind(a, a)
|
| 434 |
+
recovered = project(unbind(a, bb), a)
|
| 435 |
+
ok3 = abs(recovered - 0.7) < 0.02
|
| 436 |
+
print(f" raw projection: {'PASS' if ok3 else 'FAIL'}")
|
| 437 |
+
|
| 438 |
+
# Fully random key-value storage: many pairs sharing nothing
|
| 439 |
+
r = get_roles(d)
|
| 440 |
+
trace = np.zeros(d, dtype=complex)
|
| 441 |
+
pairs = []
|
| 442 |
+
for i in range(50):
|
| 443 |
+
key = norm(bind(r["op"], povec(2000 + i, d)))
|
| 444 |
+
val = povec(3000 + i, d)
|
| 445 |
+
trace += bind(key, val)
|
| 446 |
+
pairs.append((key, val))
|
| 447 |
+
correct = 0
|
| 448 |
+
for key, val in pairs:
|
| 449 |
+
rec = unbind(key, trace)
|
| 450 |
+
if cos(rec, val) > cos(rec, pairs[0][1]):
|
| 451 |
+
correct += 1
|
| 452 |
+
ok4 = correct >= 48
|
| 453 |
+
print(f" random-key key-value: "
|
| 454 |
+
f"{'PASS' if ok4 else 'FAIL'} ({correct}/50)")
|
| 455 |
+
|
| 456 |
+
return ok1 and ok2 and ok3 and ok4
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
# ============================================================
|
| 460 |
+
# DEMO 1. Program execution (chains)
|
| 461 |
+
# ============================================================
|
| 462 |
+
def demo_program_chains():
|
| 463 |
+
section("DEMO 1. PROGRAM EXECUTION — chained operations")
|
| 464 |
+
prog = HoloProgram(d=2048)
|
| 465 |
+
|
| 466 |
+
for i in range(20):
|
| 467 |
+
prog.add_op("root", "inc", str(i), str(i + 1))
|
| 468 |
+
for i in range(20):
|
| 469 |
+
prog.add_op("root", "double", str(i), str(2 * i))
|
| 470 |
+
for i in range(10):
|
| 471 |
+
prog.add_op("root", "square", str(i), str(i * i))
|
| 472 |
+
|
| 473 |
+
print(" stored: 20 inc, 20 double, 10 square operations")
|
| 474 |
+
print()
|
| 475 |
+
|
| 476 |
+
chains = [
|
| 477 |
+
(["inc", "inc", "double"], "3"),
|
| 478 |
+
(["square", "inc"], "4"),
|
| 479 |
+
(["double", "inc", "inc"], "5"),
|
| 480 |
+
(["inc", "square"], "2"),
|
| 481 |
+
]
|
| 482 |
+
for ops, start in chains:
|
| 483 |
+
result = prog.run_chain("root", ops, start)
|
| 484 |
+
path_str = " -> ".join(result["path"])
|
| 485 |
+
ok = "OK" if result["ok"] else f"FAILED at {result['failed_at']}"
|
| 486 |
+
print(f" [{', '.join(ops)}] from '{start}': {path_str} {ok}")
|
| 487 |
+
|
| 488 |
+
print()
|
| 489 |
+
print(" single apply:")
|
| 490 |
+
for op_name, in_val in [("inc", "7"), ("double", "7"), ("square", "7")]:
|
| 491 |
+
out = prog.apply("root", op_name, in_val)
|
| 492 |
+
print(f" {op_name}({in_val}) -> {out}")
|
| 493 |
+
|
| 494 |
+
|
| 495 |
+
# ============================================================
|
| 496 |
+
# DEMO 2. Data-driven execution
|
| 497 |
+
# ============================================================
|
| 498 |
+
def demo_data_driven():
|
| 499 |
+
section("DEMO 2. DATA-DRIVEN EXECUTION — apply any applicable op")
|
| 500 |
+
prog = HoloProgram(d=2048)
|
| 501 |
+
transitions = [
|
| 502 |
+
("t1", "cold", "warm"),
|
| 503 |
+
("t2", "warm", "hot"),
|
| 504 |
+
("t3", "hot", "boiling"),
|
| 505 |
+
("t4", "boiling", "evaporated"),
|
| 506 |
+
]
|
| 507 |
+
for op, src, dst in transitions:
|
| 508 |
+
prog.add_op("root", op, src, dst)
|
| 509 |
+
|
| 510 |
+
result = prog.run_data_driven("root", "cold", max_steps=10)
|
| 511 |
+
print(f" start at 'cold', walk using whichever op matches:")
|
| 512 |
+
print(f" {' -> '.join(result['path'])}")
|
| 513 |
+
print(f" steps: {result['steps']}")
|
| 514 |
+
|
| 515 |
+
print()
|
| 516 |
+
result = prog.run_data_driven("root", "evaporated", max_steps=5)
|
| 517 |
+
print(f" start at 'evaporated' (no matching op):")
|
| 518 |
+
print(f" {' -> '.join(result['path'])}")
|
| 519 |
+
print(f" steps: {result['steps']}")
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
# ============================================================
|
| 523 |
+
# DEMO 3. Membranes with inheritance
|
| 524 |
+
# ============================================================
|
| 525 |
+
def demo_membranes():
|
| 526 |
+
section("DEMO 3. MEMBRANES — nested regions with inheritance")
|
| 527 |
+
prog = HoloProgram(d=2048)
|
| 528 |
+
prog.add_membrane("alice", parent="root")
|
| 529 |
+
prog.add_membrane("alice_kitchen", parent="alice")
|
| 530 |
+
|
| 531 |
+
prog.store("root", "earth_round")
|
| 532 |
+
prog.store("root", "water_wet")
|
| 533 |
+
prog.store("alice", "alice_lives_in_paris")
|
| 534 |
+
prog.store("alice", "alice_works_at_cafe")
|
| 535 |
+
prog.store("alice_kitchen", "kitchen_floor_5")
|
| 536 |
+
prog.store("alice_kitchen", "manager_is_bob")
|
| 537 |
+
|
| 538 |
+
print(" membranes: root <- alice <- alice_kitchen")
|
| 539 |
+
print()
|
| 540 |
+
print(f" {'query':>24} {'root':>8} {'alice':>8} {'kitchen':>8}")
|
| 541 |
+
for q in ["earth_round", "alice_lives_in_paris",
|
| 542 |
+
"kitchen_floor_5", "manager_is_bob"]:
|
| 543 |
+
r_root = prog.query("root", q)
|
| 544 |
+
r_alice = prog.query("alice", q)
|
| 545 |
+
r_kit = prog.query("alice_kitchen", q)
|
| 546 |
+
print(f" {q:>24} {r_root:>+8.3f} {r_alice:>+8.3f} {r_kit:>+8.3f}")
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
# ============================================================
|
| 550 |
+
# DEMO 4. Communication and dissolution
|
| 551 |
+
# ============================================================
|
| 552 |
+
def demo_communication():
|
| 553 |
+
section("DEMO 4. COMMUNICATION AND DISSOLUTION")
|
| 554 |
+
prog = HoloProgram(d=2048)
|
| 555 |
+
prog.add_membrane("inner", parent="root")
|
| 556 |
+
prog.store("root", "common_knowledge")
|
| 557 |
+
prog.store("inner", "private_note")
|
| 558 |
+
|
| 559 |
+
print(" before move:")
|
| 560 |
+
print(f" root sees private_note: {prog.query('root', 'private_note'):+.4f}")
|
| 561 |
+
print(f" inner sees private_note: {prog.query('inner', 'private_note'):+.4f}")
|
| 562 |
+
|
| 563 |
+
prog.move("private_note", "inner", "root")
|
| 564 |
+
print()
|
| 565 |
+
print(" after move (inner -> root):")
|
| 566 |
+
print(f" root sees private_note: {prog.query('root', 'private_note'):+.4f}")
|
| 567 |
+
print(f" inner sees private_note: {prog.query('inner', 'private_note'):+.4f}")
|
| 568 |
+
|
| 569 |
+
prog.add_membrane("middle", parent="root")
|
| 570 |
+
prog.store("middle", "middle_fact")
|
| 571 |
+
print()
|
| 572 |
+
print(" before dissolution:")
|
| 573 |
+
print(f" root sees middle_fact: {prog.query('root', 'middle_fact'):+.4f}")
|
| 574 |
+
print(f" middle sees middle_fact: {prog.query('middle', 'middle_fact'):+.4f}")
|
| 575 |
+
|
| 576 |
+
prog.dissolve("middle", "root")
|
| 577 |
+
print()
|
| 578 |
+
print(" after dissolution (middle -> root):")
|
| 579 |
+
print(f" root sees middle_fact: {prog.query('root', 'middle_fact'):+.4f}")
|
| 580 |
+
print(f" membranes remaining: {list(prog.membranes.keys())}")
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
# ============================================================
|
| 584 |
+
# DEMO 5. Cryptographic layer
|
| 585 |
+
# ============================================================
|
| 586 |
+
def demo_crypto():
|
| 587 |
+
section("DEMO 5. CRYPTOGRAPHIC LAYER — keyed codebook")
|
| 588 |
+
print(" Store items with key 'secret_A'. Copy the trace to two")
|
| 589 |
+
print(" other programs: one with the same key, one with a wrong")
|
| 590 |
+
print(" key. Query both.")
|
| 591 |
+
print()
|
| 592 |
+
|
| 593 |
+
p_correct = HoloProgram(d=2048, key="secret_A")
|
| 594 |
+
p_correct.store("root", "classified_meeting_place")
|
| 595 |
+
p_correct.store("root", "classified_meeting_time")
|
| 596 |
+
|
| 597 |
+
p_wrong = HoloProgram(d=2048, key="wrong_key")
|
| 598 |
+
p_wrong.membranes["root"].trace = \
|
| 599 |
+
p_correct.membranes["root"].trace.copy()
|
| 600 |
+
|
| 601 |
+
candidates = ["classified_meeting_place", "classified_meeting_time",
|
| 602 |
+
"paris", "3pm", "unrelated_word"]
|
| 603 |
+
|
| 604 |
+
print(f" {'candidate':>30} {'correct key':>14} {'wrong key':>12}")
|
| 605 |
+
for c in candidates:
|
| 606 |
+
sim_c = p_correct.query("root", c)
|
| 607 |
+
sim_w = p_wrong.query("root", c)
|
| 608 |
+
print(f" {c:>30} {sim_c:>+14.4f} {sim_w:>+12.4f}")
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
# ============================================================
|
| 612 |
+
# DEMO 6. Commitment scheme
|
| 613 |
+
# ============================================================
|
| 614 |
+
def demo_commit():
|
| 615 |
+
section("DEMO 6. COMMITMENT SCHEME")
|
| 616 |
+
prog = HoloProgram(d=2048, key="K")
|
| 617 |
+
prog.store("root", "secret_value")
|
| 618 |
+
|
| 619 |
+
commitment = prog.commit("root", "secret_value", salt="random_salt_123")
|
| 620 |
+
print(f" commitment vector shape: {commitment.shape}")
|
| 621 |
+
print(f" commitment magnitude: {np.linalg.norm(commitment):.4f}")
|
| 622 |
+
print()
|
| 623 |
+
print(" verification:")
|
| 624 |
+
ok1 = prog.verify("root", "secret_value", "random_salt_123", commitment)
|
| 625 |
+
ok2 = prog.verify("root", "secret_value", "wrong_salt", commitment)
|
| 626 |
+
ok3 = prog.verify("root", "different_value", "random_salt_123", commitment)
|
| 627 |
+
print(f" correct (label, salt): {ok1}")
|
| 628 |
+
print(f" wrong salt: {ok2}")
|
| 629 |
+
print(f" wrong label: {ok3}")
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
# ============================================================
|
| 633 |
+
# DEMO 7. Unified ask()
|
| 634 |
+
# ============================================================
|
| 635 |
+
def demo_ask():
|
| 636 |
+
section("DEMO 7. UNIFIED ask() — non-flat interface")
|
| 637 |
+
prog = HoloProgram(d=2048)
|
| 638 |
+
prog.add_membrane("alice", parent="root")
|
| 639 |
+
prog.add_membrane("alice_kitchen", parent="alice")
|
| 640 |
+
|
| 641 |
+
prog.store("root", "earth_round")
|
| 642 |
+
prog.store("root", "water_wet")
|
| 643 |
+
prog.store("alice", "alice_lives_in_paris")
|
| 644 |
+
prog.store("alice", "alice_works_at_cafe")
|
| 645 |
+
prog.store("alice_kitchen", "kitchen_floor_5")
|
| 646 |
+
prog.store("alice_kitchen", "manager_is_bob")
|
| 647 |
+
|
| 648 |
+
prog.add_op("root", "serve", "customer", "coffee")
|
| 649 |
+
prog.add_op("root", "greet", "customer", "hello")
|
| 650 |
+
prog.add_op("alice_kitchen", "brew", "coffee", "ready")
|
| 651 |
+
|
| 652 |
+
for query in ["alice", "alice_lives_in_paris", "coffee",
|
| 653 |
+
"alice_kitchen", "manager_is_bob"]:
|
| 654 |
+
hits = prog.ask(query, top_k=6)
|
| 655 |
+
print(f" ask('{query}'):")
|
| 656 |
+
if not hits:
|
| 657 |
+
print(f" (no hits)")
|
| 658 |
+
for h in hits:
|
| 659 |
+
if h["type"] == "item":
|
| 660 |
+
print(f" [item] membrane={h['membrane']:<14} "
|
| 661 |
+
f"sim={h['similarity']:+.3f}")
|
| 662 |
+
elif h["type"] == "operation":
|
| 663 |
+
print(f" [operation] membrane={h['membrane']:<14} "
|
| 664 |
+
f"role={h['role']:<4} "
|
| 665 |
+
f"op={h['op']}({h['in']}) -> {h['out']}")
|
| 666 |
+
elif h["type"] == "membrane":
|
| 667 |
+
print(f" [membrane] {h['membrane']}")
|
| 668 |
+
print()
|
| 669 |
+
|
| 670 |
+
|
| 671 |
+
# ============================================================
|
| 672 |
+
# DEMO 8. Programs as items
|
| 673 |
+
# ============================================================
|
| 674 |
+
def demo_program_as_item():
|
| 675 |
+
section("DEMO 8. PROGRAMS AS ITEMS")
|
| 676 |
+
prog = HoloProgram(d=2048)
|
| 677 |
+
prog.add_op("root", "inc", "1", "2")
|
| 678 |
+
prog.add_op("root", "inc", "2", "3")
|
| 679 |
+
prog.add_op("root", "double", "3", "6")
|
| 680 |
+
|
| 681 |
+
print(" The substrate holds operations alongside items.")
|
| 682 |
+
print(" Query for 'inc' through ask():")
|
| 683 |
+
hits = prog.ask("inc", top_k=10)
|
| 684 |
+
for h in hits:
|
| 685 |
+
if h["type"] == "operation":
|
| 686 |
+
print(f" {h['op']}({h['in']}) -> {h['out']} "
|
| 687 |
+
f"(membrane={h['membrane']})")
|
| 688 |
+
|
| 689 |
+
print()
|
| 690 |
+
print(" Query for '3' (input and output of operations):")
|
| 691 |
+
hits = prog.ask("3", top_k=10)
|
| 692 |
+
for h in hits:
|
| 693 |
+
if h["type"] == "operation":
|
| 694 |
+
print(f" {h['op']}({h['in']}) -> {h['out']} "
|
| 695 |
+
f"role={h['role']}")
|
| 696 |
+
|
| 697 |
+
|
| 698 |
+
# ============================================================
|
| 699 |
+
# Main
|
| 700 |
+
# ============================================================
|
| 701 |
+
def run_all_demos(output_dir: str = "holo_program_out") -> None:
|
| 702 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 703 |
+
print("=" * 78)
|
| 704 |
+
print("HOLO-PROGRAM")
|
| 705 |
+
print("Programs as items, membranes as regions, keys as gates.")
|
| 706 |
+
print("=" * 78)
|
| 707 |
+
|
| 708 |
+
demos = [
|
| 709 |
+
demo_program_chains,
|
| 710 |
+
demo_data_driven,
|
| 711 |
+
demo_membranes,
|
| 712 |
+
demo_communication,
|
| 713 |
+
demo_crypto,
|
| 714 |
+
demo_commit,
|
| 715 |
+
demo_ask,
|
| 716 |
+
demo_program_as_item,
|
| 717 |
+
]
|
| 718 |
+
for d in demos:
|
| 719 |
+
try:
|
| 720 |
+
d()
|
| 721 |
+
except Exception as e:
|
| 722 |
+
print(f" EXCEPTION in {d.__name__}: {e}")
|
| 723 |
+
|
| 724 |
+
section("OUTPUT")
|
| 725 |
+
prog = HoloProgram(d=2048, key="demo_key")
|
| 726 |
+
prog.add_membrane("alice", parent="root")
|
| 727 |
+
prog.store("root", "earth_round")
|
| 728 |
+
prog.store("alice", "alice_lives_in_paris")
|
| 729 |
+
prog.add_op("root", "inc", "1", "2")
|
| 730 |
+
prog.save_json(os.path.join(output_dir, "state.json"))
|
| 731 |
+
|
| 732 |
+
print()
|
| 733 |
+
print("=" * 78)
|
| 734 |
+
print(f"All outputs saved to: {output_dir}/")
|
| 735 |
+
print("=" * 78)
|
| 736 |
+
|
| 737 |
+
|
| 738 |
+
def main() -> None:
|
| 739 |
+
parser = argparse.ArgumentParser(
|
| 740 |
+
description="Programs as items, membranes as regions, "
|
| 741 |
+
"keys as gates.",
|
| 742 |
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
| 743 |
+
epilog="""
|
| 744 |
+
Library usage:
|
| 745 |
+
|
| 746 |
+
from holo_program import HoloProgram
|
| 747 |
+
|
| 748 |
+
prog = HoloProgram(d=2048, key=None)
|
| 749 |
+
prog.add_membrane("alice", parent="root")
|
| 750 |
+
prog.store("root", "apple")
|
| 751 |
+
prog.add_op("root", "inc", "0", "1")
|
| 752 |
+
prog.run_chain("root", ["inc", "inc"], "0")
|
| 753 |
+
""")
|
| 754 |
+
parser.add_argument("--output", default="holo_program_out",
|
| 755 |
+
help="output directory (default: holo_program_out)")
|
| 756 |
+
args = parser.parse_args()
|
| 757 |
+
|
| 758 |
+
print("=" * 78)
|
| 759 |
+
print("SELF-TEST")
|
| 760 |
+
print("=" * 78)
|
| 761 |
+
if not self_test():
|
| 762 |
+
print(" primitive test failed; continuing anyway")
|
| 763 |
+
|
| 764 |
+
run_all_demos(output_dir=args.output)
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
if __name__ == "__main__":
|
| 768 |
+
main()
|