zeechimp commited on
Commit
76f2dbe
Β·
verified Β·
1 Parent(s): bb6c496

Create README.md

Browse files
Files changed (1) hide show
  1. README.md +415 -0
README.md ADDED
@@ -0,0 +1,415 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ license: apache-2.0
5
+ library_name: holo-program
6
+ tags:
7
+ - memory
8
+ - programmable-memory
9
+ - membrane-computing
10
+ - p-systems
11
+ - cryptographic-memory
12
+ - keyed-access
13
+ - associative-memory
14
+ - hyperdimensional-computing
15
+ - vector-symbolic-architecture
16
+ - numpy
17
+ - educational
18
+ - research
19
+ datasets: []
20
+ metrics:
21
+ - chain-execution-accuracy
22
+ - membrane-inheritance
23
+ - key-separation
24
+ pipeline_tag: feature-extraction
25
+ ---
26
+
27
+ # holo-program
28
+
29
+ **Programs as items, membranes as regions, keys as gates.**
30
+
31
+ A memory substrate with three stacked layers under one interface.
32
+ Programs are stored as key-value items and executed by
33
+ unbind-then-classify. Membranes are nested regions with child-to-parent
34
+ inheritance and P-systems operations. Cryptographic keying derives item
35
+ vectors from a secret; without the key, the trace is noise. Everything
36
+ is addressable through a single `ask()` primitive.
37
+
38
+ The three layers are views of one vector space. The caller does not
39
+ specify which layer to search. `ask()` returns heterogeneous hits.
40
+
41
+ ## What it does
42
+
43
+ ```python
44
+ from holo_program import HoloProgram
45
+
46
+ prog = HoloProgram(d=2048, key=None)
47
+
48
+ # Programs as items
49
+ prog.add_op("root", "inc", "0", "1")
50
+ prog.add_op("root", "inc", "1", "2")
51
+ prog.add_op("root", "double", "2", "4")
52
+
53
+ # Execute a chain
54
+ result = prog.run_chain("root", ["inc", "inc", "double"], "0")
55
+ # result = {"path": ["0", "1", "2", "4"], "ok": True}
56
+
57
+ # Membranes with inheritance
58
+ prog.add_membrane("alice", parent="root")
59
+ prog.store("alice", "alice_lives_in_paris")
60
+ prog.query("alice", "alice_lives_in_paris") # ~1.0
61
+ prog.query("root", "alice_lives_in_paris") # ~0.0
62
+
63
+ # Keyed access
64
+ p_secret = HoloProgram(key="secret")
65
+ p_secret.store("root", "classified")
66
+ p_wrong = HoloProgram(key="wrong")
67
+ p_wrong.membranes["root"].trace = p_secret.membranes["root"].trace.copy()
68
+ p_secret.query("root", "classified") # ~1.0
69
+ p_wrong.query("root", "classified") # ~0.0
70
+
71
+ # Non-flat query
72
+ hits = prog.ask("inc")
73
+ # [operation] membrane=root role=op inc(0) -> 1
74
+ # [operation] membrane=root role=op inc(1) -> 2
75
+ ```
76
+
77
+ ## Why three layers
78
+
79
+ Standard memory tools are flat. A vector database accepts an
80
+ embedding; a KV cache accepts a key; a graph database accepts a
81
+ pattern. Each tool has one interface and one data layer. The user
82
+ adapts the problem to the tool.
83
+
84
+ The substrate here stacks three layers over the same vector space.
85
+ Each layer has been built before. The composition is what is new.
86
+ A user stores a fact, defines an operation, puts it in a membrane,
87
+ and locks it behind a key β€” all through the same primitives, all
88
+ retrievable through the same `ask()` call.
89
+
90
+ ## Installation
91
+
92
+ ```bash
93
+ pip install numpy
94
+ ```
95
+
96
+ No other dependencies. Single file, approximately 600 lines.
97
+
98
+ ## Usage
99
+
100
+ ### CLI
101
+
102
+ ```bash
103
+ python holo_program.py
104
+ python holo_program.py --output results/
105
+ ```
106
+
107
+ Runs eight demonstrations and writes a JSON state file.
108
+
109
+ ### Python
110
+
111
+ ```python
112
+ from holo_program import HoloProgram
113
+
114
+ prog = HoloProgram(d=2048, key=None, threshold=0.05)
115
+
116
+ # Membranes
117
+ prog.add_membrane("alice", parent="root")
118
+ prog.add_membrane("alice_kitchen", parent="alice")
119
+
120
+ # Storage
121
+ prog.store("root", "earth_round")
122
+ prog.store("alice", "alice_lives_in_paris")
123
+ prog.store("alice_kitchen", "manager_is_bob")
124
+
125
+ # Operations
126
+ prog.add_op("root", "serve", "customer", "coffee")
127
+ prog.add_op("alice_kitchen", "brew", "coffee", "ready")
128
+
129
+ # Execute
130
+ result = prog.run_chain("root", ["inc", "inc"], "5")
131
+ result = prog.apply("alice_kitchen", "brew", "coffee") # "ready"
132
+
133
+ # Data-driven walk
134
+ walk = prog.run_data_driven("root", "cold") # ["cold", "warm", ...]
135
+
136
+ # P-systems operations
137
+ prog.move("private_note", "alice", "root")
138
+ prog.dissolve("alice_kitchen", "alice")
139
+
140
+ # Non-flat query
141
+ hits = prog.ask("coffee", top_k=8)
142
+
143
+ # Cryptographic layer
144
+ commitment = prog.commit("root", "secret_value", salt="random_salt_123")
145
+ ok = prog.verify("root", "secret_value", "random_salt_123", commitment)
146
+ ```
147
+
148
+ ## Results
149
+
150
+ All results at D=2048, threshold=0.05. Self-test verifies four
151
+ primitives before any demonstration: bind/unbind identity, key
152
+ separation, projection recovery, and random-key key-value retrieval
153
+ (49/50 at 50 superposed pairs).
154
+
155
+ ### Program execution
156
+
157
+ Twenty `inc` operations (i β†’ i+1 for i = 0..19), twenty `double`
158
+ operations (i β†’ 2i), ten `square` operations (i β†’ iΒ²) stored as
159
+ key-value pairs.
160
+
161
+ | Chain | Start | Path | Result |
162
+ |---|---|---|---|
163
+ | inc, inc, double | 3 | 3 β†’ 4 β†’ 5 β†’ 10 | correct |
164
+ | square, inc | 4 | 4 β†’ 16 β†’ 17 | correct |
165
+ | double, inc, inc | 5 | 5 β†’ 10 β†’ 11 β†’ 12 | correct |
166
+ | inc, square | 2 | 2 β†’ 3 β†’ 9 | correct |
167
+
168
+ Single applies: `inc(7) = 8`, `double(7) = 14`, `square(7) = 49`.
169
+
170
+ The chains compose across different operations. The substrate does not
171
+ know which operations exist; it reads the key, retrieves the value,
172
+ classifies the out, and passes the result forward.
173
+
174
+ ### Data-driven execution
175
+
176
+ State transitions: `cold β†’ warm β†’ hot β†’ boiling β†’ evaporated`.
177
+
178
+ Starting at `cold` with no predefined chain, the substrate walks by
179
+ finding whichever operation matches the current value at each step:
180
+
181
+ ```
182
+ cold -> warm -> hot -> boiling -> evaporated
183
+ steps: 4
184
+ ```
185
+
186
+ Starting at `evaporated` (no matching operation), the walk terminates
187
+ immediately.
188
+
189
+ ### Membrane inheritance
190
+
191
+ Three nested membranes: `root`, `alice`, `alice_kitchen`.
192
+
193
+ | Query | root | alice | alice_kitchen |
194
+ |---|---|---|---|
195
+ | earth_round | +1.028 | +1.028 | +1.028 |
196
+ | alice_lives_in_paris | +0.023 | +1.004 | +1.004 |
197
+ | kitchen_floor_5 | -0.026 | -0.026 | +1.020 |
198
+ | manager_is_bob | -0.004 | -0.004 | +1.020 |
199
+
200
+ The child sees the parent's facts. The parent does not see the child's.
201
+ Inheritance is one-way, from child to parent.
202
+
203
+ ### Movement and dissolution
204
+
205
+ **Move.** `private_note` stored in `inner`:
206
+
207
+ | | root | inner |
208
+ |---|---|---|
209
+ | before | +0.027 | +1.000 |
210
+ | after | +1.027 | +1.027 |
211
+
212
+ The content transferred completely.
213
+
214
+ **Dissolution.** `middle_fact` in `middle`, then dissolve `middle` into
215
+ `root`:
216
+
217
+ | | root | middle |
218
+ |---|---|---|
219
+ | before | +0.009 | +1.000 |
220
+ | after | +1.009 | (gone) |
221
+
222
+ Content transferred, membrane removed.
223
+
224
+ ### Cryptographic keying
225
+
226
+ Two programs share the same trace. One has the correct key; the other
227
+ has a wrong key.
228
+
229
+ | Candidate | Correct key | Wrong key |
230
+ |---|---|---|
231
+ | classified_meeting_place | +0.997 | +0.004 |
232
+ | classified_meeting_time | +0.997 | -0.007 |
233
+ | paris | -0.018 | -0.022 |
234
+ | 3pm | -0.028 | +0.034 |
235
+ | unrelated_word | -0.028 | +0.010 |
236
+
237
+ With the correct key, stored items read at +0.997 and non-stored at
238
+ near zero. With the wrong key, all items read near zero. The trace
239
+ carries no usable signal without the key.
240
+
241
+ ### Commitment scheme
242
+
243
+ Commitment = `bind(vector(label), vector(salt))`. Verification:
244
+
245
+ | Correct (label, salt) | Wrong salt | Wrong label |
246
+ |---|---|---|
247
+ | True | False | False |
248
+
249
+ Commitment vector magnitude is exactly 1.0000 (phase-only binding
250
+ preserves unit modulus).
251
+
252
+ ### Unified `ask()`
253
+
254
+ Five example queries, each returning heterogeneous hits:
255
+
256
+ | Query | Result |
257
+ |---|---|
258
+ | `alice` | `[membrane] alice` |
259
+ | `alice_lives_in_paris` | `[item] alice sim=+1.004` |
260
+ | `coffee` | `[operation] serve(customer) β†’ coffee` in root; `[operation] brew(coffee) β†’ ready` in alice_kitchen |
261
+ | `alice_kitchen` | `[membrane] alice_kitchen` |
262
+ | `manager_is_bob` | `[item] alice_kitchen sim=+1.020` |
263
+
264
+ The caller does not specify which layer to search. The substrate
265
+ reports what it has.
266
+
267
+ ### Programs as items
268
+
269
+ Querying `ask("inc")` returns the two stored `inc` operations.
270
+
271
+ Querying `ask("3")` returns both the operation where `3` is an
272
+ output (`inc(2) β†’ 3`) and the operation where `3` is an input
273
+ (`double(3) β†’ 6`). The `role` field distinguishes them.
274
+
275
+ The same primitive reads items and operations. There is no separate
276
+ "program layer" from the caller's perspective.
277
+
278
+ ## API reference
279
+
280
+ ### `HoloProgram`
281
+
282
+ ```python
283
+ HoloProgram(d=2048, key=None, threshold=0.05)
284
+ ```
285
+
286
+ **Membrane management**
287
+
288
+ - `add_membrane(name, parent="root")` β€” create a nested membrane.
289
+
290
+ **Storage**
291
+
292
+ - `store(membrane, label, weight=1.0)` β€” store an item.
293
+ - `add_op(membrane, op_name, in_val, out_val)` β€” store an operation.
294
+ - `move(label, src, dst)` β€” transfer an item between membranes.
295
+ - `dissolve(child, into)` β€” merge a child membrane into its parent.
296
+
297
+ **Execution**
298
+
299
+ - `apply(membrane, op_name, in_val) -> out_val | None` β€” single step.
300
+ - `run_chain(membrane, ops, start) -> dict` β€” fixed sequence.
301
+ - `run_data_driven(membrane, start, max_steps=20) -> dict` β€” follow
302
+ whichever operation matches at each step.
303
+
304
+ **Query**
305
+
306
+ - `query(membrane, label) -> float` β€” similarity in the membrane's
307
+ inheritance chain.
308
+ - `ask(query, top_k=8) -> list[dict]` β€” heterogeneous search across
309
+ all membranes and all layers.
310
+
311
+ **Cryptographic**
312
+
313
+ - `commit(membrane, label, salt) -> np.ndarray` β€” generate commitment.
314
+ - `verify(membrane, label, salt, commitment) -> bool` β€” verify.
315
+
316
+ **Diagnostics**
317
+
318
+ - `stats() -> dict` β€” per-membrane counts and magnitudes.
319
+ - `save_json(path)` β€” full state.
320
+
321
+ ## Design notes
322
+
323
+ ### Random keys, not structured keys
324
+
325
+ Operations use fully random keys derived from a hash of `(op_name,
326
+ in_val)`, not from a structured binding of role vectors. An earlier
327
+ version used `norm(bind(op, op_name) + bind(in, in_val))`, which
328
+ produced correlated keys for operations sharing a component. With
329
+ twenty `inc` operations, the shared `inc` component made the keys
330
+ correlated and cross-talk swamped the signal; only single applies
331
+ worked.
332
+
333
+ The random-key variant removes the cross-talk at the cost of treating
334
+ every `(op, in)` pair as structurally distinct. The `op` role is
335
+ still used for the value (`bind(out, out_val)`), but not for the key.
336
+
337
+ ### Parent direction
338
+
339
+ Inheritance goes from child to parent. A child sees its parent's
340
+ facts; a parent does not see its child's facts. This is the natural
341
+ direction for a "context" relation: a specific context inherits from
342
+ a general one, not the other way around.
343
+
344
+ ### Keyed codebook
345
+
346
+ Item vectors are derived from `random_vector(key, label)`. Different
347
+ keys produce uncorrelated vectors for the same label. A trace built
348
+ with key A, queried with key B, produces similarity near zero because
349
+ the two codebooks are independent.
350
+
351
+ This is access control at the codebook level. Without the key, the
352
+ trace is a random superposition of unknown vectors.
353
+
354
+ ## Limitations
355
+
356
+ **Programs are not Turing-complete.** Chains and data-driven walks
357
+ compose operations. Branching, iteration, and recursion must be
358
+ encoded as larger operation sets or implemented outside the substrate.
359
+
360
+ **No catalysis.** An operation cannot require a third object to be
361
+ present. The structure supports it (a catalyst could be a role in the
362
+ operation and a check against the local trace) but it is not
363
+ implemented.
364
+
365
+ **No priority.** Multiple applicable operations are returned by
366
+ `find_applicable` sorted by similarity. There is no specificity-based
367
+ ordering or explicit precedence.
368
+
369
+ **No timed rules.** Operations have no validity windows.
370
+
371
+ **No provenance.** The substrate does not track which call produced
372
+ which weight. Two sources that store the same fact look identical.
373
+
374
+ **Keys are not secret against an adversary with the codebook.** The
375
+ key separation prevents a wrong-key query from retrieving items. It
376
+ does not prevent brute-force matching against the trace if the label
377
+ space is small.
378
+
379
+ **Commitments are not cryptographic.** `bind(vector(label),
380
+ vector(salt))` is a hash-like operation in the substrate's algebra.
381
+ It is not a cryptographically secure commitment in the formal sense.
382
+ It is useful for cooperative verification, not for adversarial
383
+ security.
384
+
385
+ **No cross-membrane queries.** A query goes to one membrane.
386
+ `ask()` searches all membranes for the query string, but it does not
387
+ propagate the query through the inheritance chain.
388
+
389
+ **No consolidation.** Traces grow linearly. Long-running stores need
390
+ external management. The four-axis tool provides consolidation; it is
391
+ not integrated here.
392
+
393
+ ## Citation
394
+
395
+ ```bibtex
396
+ @misc{holo-program2026,
397
+ title = {holo-program: Programs as items, membranes as regions,
398
+ keys as gates},
399
+ author = {zeechimp},
400
+ year = {2026},
401
+ note = {A memory substrate with three stacked layers under a
402
+ single non-flat interface.}
403
+ }
404
+ ```
405
+
406
+ ## References
407
+
408
+ - Plate, T. A. "Holographic Reduced Representations." *IEEE Transactions on Neural Networks* 6:3 (1995), 623–641.
409
+ - Kanerva, P. "Hyperdimensional Computing." *Cognitive Computation* 1:2 (2009), 139–159.
410
+ - PΔƒun, Gh. "Computing with Membranes." *Journal of Computer and System Sciences* 61:1 (2000), 108–143.
411
+ - Gayler, R. W. "Vector Symbolic Architectures Answer Jackendoff's Challenges." *ICCS/ASCS* (2003).
412
+
413
+ ## License
414
+
415
+ Apache 2.0