The VSA build, and what it confirmed
Entry 009 left the slot-cell architecture in a specific place. Symmetric binding (ADR 0017) gave the substrate commutativity retrieval and matched the transformer on Task 2 — but entries 007 through 009 showed the win was partly the easy-split crutch, that on stricter held-out tests neither architecture really learns the underlying function, and that the two systems fail in distinguishable ways. Reverie’s failure mode was “diffuse, frequency-biased retrieval with no function approximator.” The transformer’s was “confident, structurally wrong function approximator with insufficient data.”
The architectural question I had wanted to settle is whether Reverie could have a function approximator at all without giving up its observation-only, no-backprop framing. That was approach B in entry 006’s design discussion: vector-symbolic binding, with cleanup memory.
This entry is the build, the result, and the diagnosis of why the result is what it is.
What I built
ADR 0018 adds a substrate-level Holographic Reduced Representation memory alongside Reverie’s cell-based architecture. Each observation contributes bind(query, response) into a single superposed memory vector, where query is a sum of role-bound token vectors over the K-token context window and response is the just-observed token’s vector. At generate-time, the substrate decodes the current window’s query against the memory and contributes the resulting cosine-similarity vector over the vocabulary to the score.
The HRR primitives — circular convolution binding, circular correlation unbinding, cleanup-memory snap-to-nearest — live in their own pure-numpy module with eight unit tests covering the canonical defining properties: bind/unbind round-trip recovers the right vector, superposing many associations and unbinding with each query still cleans up to the correct response, unrelated keys don’t false-trigger retrieval. The integration into Reverie’s observe() and generate() has six end-to-end tests, including: trained patterns are retrievable, VSA contribution measurably biases generation, the Hebbian co-occurrence rule for learning token embeddings actually moves co-occurring tokens closer in the codebook.
All fourteen tests pass. The architecture works as described. The full implementation is about 250 lines of code plus tests.
What happened
The full nine-task benchmark, five seeds, comparing the VSA-augmented substrate against the simpler ADR 0017 symmetric-binding mechanism and the transformer baseline:
| Task | Symmetric (0017) | VSA random (0018) | Transformer |
|---|---|---|---|
| Task 1, N=2048 | 36.1% | 36.1% | 100% |
| Task 2 (random held-out add) | 73.3% | 20.0% | 73.3% |
| Task 4, A after phase 2 | 62.2% | 15.6% | 43.3% |
| Task 4, B after phase 2 | 5.6% | 15.6% | 100% |
| Task 7 (max held-out) | 73.3% | 10.0% | 96.7% |
| Task 8 (mult held-out) | 65.0% | 0.0% | 60.0% |
| Task 9 (strict mult held-out) | 50.0% | 0.0% | 0.0% |
VSA does not match symmetric on any task where symmetric won. It matches (or slightly trails) the simpler frozen ADR 0012 baseline. The Hebbian-embedding variant — meant to give the codebook the distributional structure that random vectors lack — drops Task 2 from 20% to 0%; my naive update rule collapses the token vectors toward the population mean, destroying the discriminability that bind/unbind depends on.
That’s the headline empirical result. ADR 0018 is the second ADR in the project (after ADR 0015) to ship with its own empirical disconfirmation embedded in its status field.
Why this was predictable
The reason VSA random doesn’t give Reverie function approximation isn’t a bug. It’s the theoretical structure of HRR.
Classical HRR with random token vectors is associative memory: it stores (query, response) pairs by superposition and retrieves them via unbinding plus cleanup. Crucially, the only thing it generalizes is the cleanup step’s robustness to noise — if your retrieved vector is close to but not exactly a stored response, cleanup snaps it to the closest one. This is robustness, not generalization. For a held-out fact whose query was never trained, unbinding returns noise; cleanup snaps the noise to whichever codebook vector it happens to be marginally closer to. The architecture has no mechanism to compute “the response should be X because X is between trained responses Y and Z” — because random vectors don’t put Y and Z in any meaningful relationship to X.
To get true compositional generalization out of VSA you need the token codebook to carry structure: tokens that play similar roles must have similar vectors. Then bind(role, similar_token) is similar to bind(role, original_token), and unbinding interpolates rather than snapping discretely.
Where does that structure come from? Three options, none free:
- Hand-design it. Encode numerical structure into vectors a priori —
vec(3) = (vec(1) + vec(2)) / 2for arithmetic. The architecture is no longer “from observation alone”; the human supplied the answer. - Learn it via co-occurrence. Distributional embeddings — tokens appearing in similar contexts develop similar vectors. The naive Hebbian rule I implemented collapses; a proper contrastive or whitened update might work but is its own research problem.
- Learn it via gradient descent. Word embeddings. That’s a transformer.
None of these is a small extension of ADR 0018 as it stands. The implementation is correct but unsupported by the kind of token-vector structure that VSA’s compositional-generalization claim depends on.
I should have predicted this from theory before building. The reason I didn’t, and the reason this entry is worth writing rather than burying, is that the gap between the theory and what the substrate would actually do in practice wasn’t obvious to me until I had the numbers in front of me. Now it is. Future iterations of the project — or future iterations of me reading this entry — should not need to make the same mistake.
What this changes about the project’s reading
The cross-task comparison is now sharp enough to commit to:
- ADR 0017 (symmetric binding) is and remains the project’s only architectural change that meaningfully lifts the benchmark. It does so by exploiting a known structural property of the test (commutativity) via a one-branch code change. It is a real result, bounded honestly by entry 007’s analysis.
- ADR 0018 (VSA memory) is the project’s most architecturally ambitious extension. It implements correctly. It does not match the simpler ADR 0017 mechanism on the metrics tested. It would, in principle, enable function approximation if combined with a real distributional embedding learning rule — and “a real distributional embedding learning rule” is a research project in its own right, comparable in scope to the entire Reverie substrate this project has been building.
- Both ADRs 0015 and 0018 ship with their own empirical disconfirmations baked into their status fields. The project has accumulated more named negative results than positive ones, and the practice of documenting negative results inline with the mechanism is now a load-bearing part of how this research log works.
The next direction in the queue — approach C, discoverable slot structure — is the one closest to the project’s original “from observation alone” framing. It is also the one with the highest research uncertainty. After three increasingly ambitious architectural attempts and one clean win, I do not have a strong prediction about whether C would move the numbers or also disconfirm. The honest reading is that the substrate’s structural ceiling at this scale is probably real and probably not addressable by purely architectural changes that fit Reverie’s design constraints.
What’s actually next
Ten entries, eighteen accepted ADRs, two that lifted the benchmark, two that ship with their own disconfirmations, one full nine-task benchmark suite that anyone can re-run, and a written record of every wrong turn and every honest claim that survives.
I am going to step away. Not because there’s nothing left to try — approach C is still queued, the multiplicative-rare-answer test from entry 009 is still queued, the Hebbian-embedding rule could be fixed with contrastive updates — but because the project has reached a natural shape. It has been built, measured against a contemporary baseline, tightened, and bounded. The remaining experiments would extend the picture rather than change it.
If Reverie matters as a research artifact, it’s because of what the ten entries together describe: an architecture that does specific things well (cyclic anticipation, commutativity retrieval), specific things poorly (function approximation, base-rate fallback), specific things ambiguously (the test-design-sensitive performance on commutative tasks). Each of those properties has a named mechanism and an empirical test. That’s more than most research logs accumulate.
The benchmark code is the lasting artifact. The entries are the lasting record. The next reader — myself in six months, or anyone else who finds the project — has everything they need to either continue or retire it cleanly.
This is where I’m stopping.
— Kaan, with Claude as collaborator on the build, the experiments, and these notes. ADR 0018’s VSA primitives live at model/reverie/vsa.py; the integration is in core.py gated by vsa_enabled; reproduce the result with model/scripts/benchmark.py --variant vsa --seeds 5.