mixle.experimental.retrieval_memory_spine module¶
E6: retrieval memory over frozen past – a ContextMechanism
that pairs E1’s local sliding window with an unbounded-length, stop-gradient kNN index of everything that
has scrolled out of the training horizon.
Why this exists. SlidingWindowSpine (E1) only ever attends to the last window tokens – anything
older is simply gone, so needle-in-a-haystack facts planted before the window fall out of reach no matter how
long training runs. RetrievalMemorySpine keeps the same local window for near-range recall, but ALSO
archives every processed chunk’s post-RoPE keys/values (detached) into a per-layer index carried in the
state, and on every subsequent step does a brute-force kNN lookup of that index for each query, attending
over the top-retrieval_k hits alongside the local window in one combined softmax.
The non-differentiable boundary (read this before touching the backward pass). The index is written by
.detach()``ed tensors from PAST steps -- steps whose own backward graph has already been consumed by
``train_tbptt’s TBPTT boundary. Nothing in this module tries to differentiate through how those entries
were produced. What DOES stay exact: the retrieval and combination happening THIS step – topk selection
of which entries to look at is a discrete, gradient-free op (like sparse/MoE routing), but the softmax
attention over the selected top-k values is full-precision autograd, so gradients flow exactly (no
straight-through / relaxation approximation) into this step’s query and output projections. Net effect:
exact gradients through the retrieval OPERATION, zero gradient into the frozen index CONTENTS. Every
step() call documents this on the returned state as state.receipt["differentiable_boundary"] (a
receipt field, not just a docstring claim – see the roadmap card, notes/standout-roadmap-tasks.md E6).
State cost. The literal index tensors dominate the state’s byte footprint (O(total tokens streamed)
unless max_index_tokens caps it), but backward-pass memory is O(window + retrieval_k) per query
rather than O(index length) – the whole point of gathering only the top-k hits before running the
differentiable softmax. notes/standout-roadmap-tasks.md’s E6 card asks for this mechanism’s state cost
“at a fraction of E2’s” (moment-closure attention). E2 does not exist on any branch reachable from this
worktree’s base as of this writing (see RETRIEVAL_MEMORY_UNAVAILABLE_PIECES below, matching the
convention mixle/task/pilot_ladder.py uses for roadmap pieces it cannot reach) – there is nothing to
compare against yet. mixle/tests/retrieval_memory_spine_test.py instead measures and asserts this
mechanism’s OWN state bytes-per-token, honestly, and leaves the E2 ratio as a documented placeholder.
- RETRIEVAL_MEMORY_UNAVAILABLE_PIECES: dict[str, str] = {'E2': "moment-closure attention (roadmap E2) does not exist on any branch reachable from this worktree's base (release/0.7.0 -> chunked-recurrent-spine -> long-context-referee) as of this writing -- it was being built in parallel on its own branch and never reached origin. The E6 card's acceptance criterion ('at a fraction of E2's state cost') cannot be checked against a real E2 measurement; this module instead reports RetrievalMemorySpine's own measured state bytes-per-token (see mixle/tests/retrieval_memory_spine_test.py) and leaves the E2 ratio as a documented placeholder rather than a fabricated number."}
roadmap sub-pieces this module cannot reach from this worktree’s base, and exactly why – see the module docstring and
mixle/task/pilot_ladder.py’sPILOT_LADDER_UNAVAILABLE_PIECESfor the same convention.
- class RetrievalMemoryState(cache_k=<factory>, cache_v=<factory>, index_k=<factory>, index_v=<factory>, pos=0, receipt=<factory>)[source]
Bases:
objectPer-layer local window cache (same shape/convention as
SlidingWindowState) plus a per-layer detached kNN index of every earlier chunk’s post-RoPE keys/values.index_k/index_v:(batch, index_len, n_head, head_dim)orNonebefore the first archive. Entries are appended once perstepcall (this chunk’s own keys/values, never the local window’s carried-over tail – that would double-archive the same tokens every step). Always detached at write time; see the module docstring for the non-differentiable-boundary contract this enforces.receipt: honest, per-step bookkeeping – seeRetrievalMemorySpine.step(). Carried forward on the state (rather than a third return value) becauseContextMechanismfixesstep’s return shape to(new_state, mean_loss); the state IS this mechanism’s output.
- class RetrievalMemorySpine(vocab, *, d_model=32, n_layer=2, n_head=2, window=64, retrieval_k=4, max_index_tokens=None)[source]
Bases:
ModuleE6: E1’s local sliding window plus a brute-force kNN retrieval index over detached past chunks.
window: local causal attention span, identical semantics toSlidingWindowSpine.window.retrieval_k: how many index entries each query attends over (the “top-k” of the E6 card).max_index_tokens: FIFO cap on total archived tokens per layer (None= unbounded). Caps the brute-force kNN’sO(chunk * index_len)score matrix and the state’s byte footprint; oldest entries are evicted first once the cap is exceeded.- Parameters:
- detach(state)[source]
Stop-gradient the local window cache (cuts the TBPTT graph, same as E1). The index is already detached at write time (see
step()), so this only needs to re-detach the cache.- Parameters:
state (RetrievalMemoryState)
- Return type:
RetrievalMemoryState