zeechimp commited on
Commit
cdfe973
·
verified ·
1 Parent(s): b371ed9

Create holo_program.py

Browse files
Files changed (1) hide show
  1. 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()