holo-working
A working memory substrate: fast buffer, slow long-term, and rehearsal between them.
Biological working memory has two components. A small, fast-decaying buffer holds what is currently in mind. A wide, slow long-term store holds everything that was ever processed. Consolidation between the two is what turns a momentary experience into a persistent memory.
This substrate implements that architecture over the holographic vector space.
What it does
from holo_working import WorkingMemory
mem = WorkingMemory(d=2048, buffer_decay=0.85,
consolidation_rate=0.2)
# Present items
mem.present("alpha")
mem.present("beta")
mem.present("gamma")
# Query the buffer, the long-term trace, or both
mem.query("alpha", source="buffer") # recent items loud
mem.query("alpha", source="ltm") # consolidated items loud
mem.query("alpha", source="max") # the higher of the two
# Serial position test
seq = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"]
report = mem.serial_position_test(seq, source="max")
mem.print_position_curve(report)
The serial position effect
Twelve items presented one at a time. Query each item with the max
readout.
| Position | Buffer | LTM | Max |
|---|---|---|---|
| 0 | +0.125 | +1.043 | +1.043 |
| 5 | +0.392 | +0.821 | +0.821 |
| 8 | +0.619 | +0.502 | +0.619 |
| 11 | +1.018 | +0.017 | +1.018 |
The curve is U-shaped. Primacy from LTM at early positions. Recency from buffer at late positions. Middle dip where neither is strong. This is the classical serial position effect, emergent from the two decay rules.
The four readouts
The same substrate state produces four different curves depending on which readout is chosen.
| Readout | Shape |
|---|---|
buffer |
Recency-dominated |
ltm |
Primacy-dominated |
sum |
Monotonic |
max |
U-shaped (classical) |
The readout is the substrate's most consequential interface parameter.
Installation
pip install numpy
No other dependencies. Single file, approximately 600 lines.
Usage
CLI
python holo_working.py
python holo_working.py --output results/
Runs ten demonstrations.
Python
from holo_working import WorkingMemory
mem = WorkingMemory(
d=2048,
buffer_decay=0.85, # fast decay
ltm_decay=1.0, # no decay on long-term
consolidation_rate=0.2, # 20% of buffer to LTM per step
focus_weight=0.5, # rehearsal strength
threshold=0.05,
max_focus=4,
)
# Present
for label in ["alpha", "beta", "gamma"]:
mem.present(label)
mem.step()
# Focus (attention)
mem.focus("beta")
mem.unfocus("beta")
mem.clear_focus()
# Chunk (capacity expansion)
mem.chunk(["a1", "a2", "a3", "a4"], "chunk_A")
mem.present("chunk_A")
# Query
r = mem.query("alpha", source="max")
print(r["buffer_raw"], r["ltm_raw"], r["combined"], r["verdict"])
# Serial position
report = mem.serial_position_test(
["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l"],
retention_steps=0,
source="max",
)
mem.print_position_curve(report)
Results
All results at D=2048, buffer_decay=0.85, consolidation_rate=0.2.
Self-test
| Check | Result |
|---|---|
| bind/unbind identity | PASS |
| raw projection | PASS |
| buffer holds presented | PASS |
| LTM holds after decay | PASS |
Buffer decay over time
Single item presented, then 15 steps.
| Step | Buffer | LTM |
|---|---|---|
| 0 | 1.000 | 0.000 |
| 5 | 0.444 | 0.742 |
| 10 | 0.197 | 1.071 |
| 15 | 0.087 | 1.217 |
Buffer decays geometrically. LTM accumulates and asymptotes.
Serial position curve
See the table above. U-shape confirmed.
Readout comparison
| Readout | Recency/mean | Middle/mean | Primacy/mean |
|---|---|---|---|
| buffer | 1.18 | 0.86 | 1.21 |
| ltm | 0.62 | 0.77 | 1.44 |
| sum | 0.88 | 1.04 | 1.34 |
| max | 1.02 | 0.62 | 1.09 |
Four curves, one state.
Buffer decay parameter sweep
| buffer_decay | Recency/mean | Primacy/mean |
|---|---|---|
| 0.60 | 1.97 | 0.91 |
| 0.85 | 1.18 | 1.21 |
| 0.98 | 0.82 | 1.51 |
Fast decay β strong recency. Slow decay β strong primacy.
Consolidation rate parameter sweep
| consolidation | Recency/mean | Primacy/mean |
|---|---|---|
| 0.02 | 2.14 | 0.26 |
| 0.20 | 1.18 | 1.21 |
| 0.40 | 0.68 | 1.39 |
Low consolidation β strong recency. High consolidation β strong primacy.
Attention spotlight
Items c (position 2) and j (position 9) held in focus
throughout presentation.
| Position | Score | Focused? |
|---|---|---|
| 2 | +5.196 | yes |
| 9 | +4.557 | yes |
| Neighbors | 0.71β1.12 | no |
Focused items are ~5Γ their neighbors.
Chunking
Eight items stored without chunking: buffer scores 0.29β0.63 across positions.
Eight items stored as two chunks: buffer scores 0.83 and 0.99. Two slots carry eight items at full strength.
Two interleaved streams
Two streams L and R presented alternately. The buffer treats
both by recency only. Stream membership is irrelevant. To keep
streams separate, use distinct traces.
API reference
WorkingMemory
WorkingMemory(
d=2048,
buffer_decay=0.85,
ltm_decay=1.0,
consolidation_rate=0.2,
focus_weight=0.5,
threshold=0.05,
max_focus=4,
seed=0,
)
Presentation
present(label, weight=1.0)β consolidate, decay, add, rehearse.step()β consolidate, decay, rehearse without adding.
Focus
focus(label) -> boolβ add to focus set if room.unfocus(label)clear_focus()
Chunking
chunk(labels, name)β bind multiple items into a chunk vector.
Query
query(label, source="max") -> dictβ source is one ofbuffer,ltm,sum,max,weighted,auto.recall_scores(labels, source)β batch query.
Serial position
serial_position_test(sequence, retention_steps=0, source="max") -> dictprint_position_curve(report, width=60)
Diagnostics
stats() -> dictsave_json(path)reset()
Design notes
Two stores, one substrate
The buffer and long-term traces share the same vector space. They are separate traces but the same codebook. Items presented to the buffer are the same vectors that accumulate in LTM. This means a query can ask about an item in either store using the same label.
Consolidation is not lossy
The buffer decays but its content is not lost; a fraction moves to LTM before decay. Over many steps, all presented items accumulate in LTM. The buffer is a short-term view; the LTM is the long-term record.
Attention is rehearsal
Focusing an item means adding it to the buffer at every step. The item doesn't decay because its buffer contribution is repeatedly refreshed. This is the substrate's model of what deliberate attention does: it keeps an item active.
The readout choice
The four readouts are not four different substrates; they are four
projections of the same state. A user who wants a memory that
emphasizes recent items picks buffer. A user who wants a memory
that emphasizes consolidated items picks ltm. A user who wants
the classical shape picks max.
Limitations
No hard capacity limit. The buffer holds as many items as presented, subject to interference. Real working memory has a capacity around 4Β±1.
No item-item interference model. Similar items interfere more than dissimilar ones, but this is emergent from geometry, not designed.
No temporal coding. Items do not carry position tags. Order is preserved only by recency.
Middle dip is at 70% of mean, not zero. Real working memory shows the middle dip near chance. The substrate's interference floor is higher.
Chunks are bundles, not structures. Unbinding a chunk returns a superposition, not clean parts.
ltm_decay is exposed but rarely changed. Default 1.0 means
LTM never forgets. Setting it below 1.0 is possible but the
parameter's effect is not characterized here.
Citation
@misc{holo-working2026,
title = {holo-working: A working memory substrate with buffer,
long-term store, and rehearsal},
author = {zeechimp},
year = {2026},
note = {Two-store architecture reproducing the serial position
effect.}
}
References
- Miller, G. A. "The Magical Number Seven, Plus or Minus Two." Psychological Review 63:2 (1956).
- Atkinson, R. C., Shiffrin, R. M. "Human Memory: A Proposed System and its Control Processes." Psychology of Learning and Motivation 2 (1968).
- Murdock, B. B. "The Serial Position Effect of Free Recall." Journal of Experimental Psychology 64:5 (1962).
- Plate, T. A. "Holographic Reduced Representations." IEEE Transactions on Neural Networks 6:3 (1995).
License
Apache 2.0