hv-acausal-memory
A sparse ternary memory bank with an adaptive forget-gate, resurrection probes, and acausal prefetch. NumPy only. No training data required.
Size: ~14 KB source, no weights. Runtime: ~30 s for the full benchmark. Dependencies: NumPy only.
The three mechanisms
Forget-gate. Each anchor's weight decays at a rate β that scales with memory pressure. Empty banks decay at β = 0.02; full banks at β = 0.30. This prevents the "nostalgia loop" — the memory does not grow without bound.
Resurrection. Dead anchors (weight below threshold) can be revived when a reminder matches their key. Exact-key reminders revive 100%; partial reminders revive a fraction proportional to overlap.
Acausal prefetch. Given a partial query (some dimensions zeroed), rank live anchors by similarity on the known dimensions only. Returns speculative candidates before the full query arrives.
Headline numbers
| diagnostic | result |
|---|---|
| Noise-free retrieval (200 anchors) | 1.000 |
| Retrieval at p=0.2 noise | 1.000 |
| Retrieval collapse point | p ≈ 0.5 |
| Capacity at noise-free, N=4000 | degrades below 1.0 |
| Capacity at p=0.2, N=4000 | still 1.0 |
| Exact-key resurrection rate | 100% |
| Partial-key resurrection (keep=0.5) | ~70% |
| Acausal prefetch top-1 at 50% keep, no noise | 1.000 |
| 16/16 consistency checks pass |
The capacity bound
The classical VSA capacity bound for D-dimensional sparse ternary vectors
is N ≈ D/(2 log D). For D=5000 this is ~290 anchors — but only for
noise-free recall. With query noise, the effective bound is larger,
because the query still has 0.4–0.6 similarity to its own key while
random cross-anchor similarity remains ~0.014.
The two regimes:
- Noise-free: retrieval at N=4000 begins to degrade (bound visible).
- p=0.2 noise: retrieval at N=4000 is still 1.0 (bound not visible).
Storage
| layout | bytes per anchor | 200 anchors |
|---|---|---|
| Dense (D=5000, int8 × 2) | 10,000 | 1,953 KB |
| Sparse (nnz=200, uint16 idx + int8 val × 2) | 1,200 | 234 KB |
The source issue's "187 KB" figure corresponds to ~160 anchors at nnz=200 in sparse layout.
How to use
from hv_acausal_memory import (
HyperNSV, sparse_random_ternary, add_noise, make_partial_query,
)
import numpy as np
rng = np.random.default_rng(0)
# Build a bank
mem = HyperNSV(D=5000, nnz=200, max_anchors=500, seed=0)
# Store anchors
for _ in range(200):
key = sparse_random_ternary(5000, 200, rng)
val = sparse_random_ternary(5000, 200, rng)
mem.store(key, val)
# Retrieve with a noisy query
query = add_noise(mem.keys[42].copy(), 0.2, rng)
results = mem.retrieve(query, top_k=5)
# results[0] = (anchor_idx, weighted_score, raw_similarity)
# Mark the top result as retrieved (boosts weight)
mem.mark_retrieved(results[0][0])
# Acausal prefetch: rank candidates from a partial query
partial = make_partial_query(mem.keys[17].copy(), keep_fraction=0.5,
noise=0.0, rng=rng)
candidates = mem.acausal_prefetch(partial, top_k=3)
# Advance time (applies forget-gate)
for _ in range(100):
mem.step_time(dt=1.0)
# Resurrect a dead anchor
revived = mem.resurrect(mem.keys[42].copy(), threshold=0.3, boost=0.5)
# Bank statistics
print(mem.stats())
- Downloads last month
- -