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
-
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support