mixle.experimental.summary_tree module¶
E4: hierarchical summary tree + multi-scale objective – see notes/designs/E4.md for the full
derivation (persistent append-only tree over evicted tokens, tree-path positional encoding, the
predict-the-summary auxiliary loss, the stop-gradient horizon receipt). This module implements that
note section-by-section; see the note’s “Implementation notes vs. this design” section for the small,
honestly-documented places this module simplifies the note’s scheme for tractability.
What this is. E1’s SlidingWindowSpine keeps an exact
but bounded KV window; anything evicted from that window is gone. E4 keeps it: every evicted token is
folded, one at a time, into a persistent tree of learned summaries via mixed-radix carry propagation
(the fast-multipole-method structure – near field exact, far field via a bounded number of
increasingly coarse representatives – applied to token history). Tree depth grows only as
log_fanout(evicted_count), so the far-field attention set stays bounded regardless of how much
history has streamed through.
Positional encoding. RoPE’s q . k dependence on i - j is well-conditioned only when
i - j is a small, well-scaled number (E1’s window); a far-field summary node represents a range
of possibly billions of original positions, and there is no single j to rotate by. E4 replaces
RoPE for the far field with (a) a content channel – a level embedding plus a sibling-slot embedding
summed into the node’s summary before it’s used as an attention key – and (b) a relative bias
channel – a learned scalar indexed by tree distance (lca_depth, see below), the ALiBi/T5-bias
shape of mechanism but indexed by tree distance instead of linear offset. Near-field window tokens
keep ordinary RoPE unchanged (E1’s _rope_angles/_apply_rope, reused verbatim).
Predict-the-summary auxiliary loss. Every node, the moment it’s finalized, is scored against the
exact additive token-id histogram of the leaves it covers via one shared linear head
(d_model -> vocab) and cross-entropy against the normalized histogram. This is direct supervision
independent of whether any future query ever attends to that node – for a node many levels up the
tree that a training run may never query again, this is its compressor’s only gradient.
Stop-gradient horizon (receipted). A node moves from live to archived once H further
nodes have finalized at its own level after it; .summary is .detach()-ed at that moment and
never un-detaches. Archived nodes stay forward-visible (attendable) but stop receiving gradient. See
SummaryTreeSpine.detach() and mixle/tests/summary_tree_test.py for the exact-accounting
receipt.
- class TreeNode(summary, histogram, path, level, g, finalized_step, finalized_index_within_level, detached=False, detached_at_finalized_count=None)[source]
Bases:
objectOne finalized node of the persistent summary tree (E4.md’s
TreeNode).summarycarries one entry per layer (each(batch, d_model)) since every layer’s tree is built from that layer’s own(k, v)via that layer’s ownqkvprojection, but the bookkeeping fields below (histogram/path/level/g) are identical across layers – they describe which evicted tokens this node covers, not any layer-specific content – so they’re stored once.
- class SummaryTreeState(window, cached_ids, pending_leaf, pending, live, archived, level_finalized_count, evicted_count=0)[source]
Bases:
objectContextMechanismcarried state: E1’s exact near field plus the persistent far-field tree.
- class SummaryTreeSpine(vocab, *, d_model=32, n_layer=2, n_head=2, window=16, fanout=4, max_level_cap=24, detach_horizon_nodes=2, aux_weight=0.1)[source]
Bases:
ModuleE4:
SlidingWindowSpine’s exact near field plus a persistent, bounded far-field tree of learned summaries, merged into one joint softmax per layer (E4.md’s “Far-field attention: merging into E1’s score”). See the module docstring for the positional encoding and auxiliary loss this adds on top of E1 unchanged.- Parameters:
- log_density(x, y)[source]
x, y:(n, T)long tensors. Returns-mean_per_position_nllfor each of thensequences, each scored independently (state re-initialized per row) – one non-streaming forward per row, computed by callinginit_state+steponce per row exactly as a length-T, single-chunk stream would (E5.md’s “GradLeaf citizenship”).
- digits_of(n, base)[source]
Base-
basedigits ofn, least-significant first.digits_of(0, base) == (0,).
- lca_depth(query_pos, node_level, node_g, fanout, *, max_climb=64)[source]
Tree distance between a query at absolute position
query_posand a node covering the contiguous leaf range[g * fanout**level, (g + 1) * fanout**level)(g= the node’s own 0-based sequential index among nodes finalized at its level – see E4.md’s carry-propagation section). Returns how many levels abovenode_levelthe query’s ancestor chain must climb before it lands in the same subtree as the node – seenotes/designs/E4.md’s “Implementation notes” section for why this integer recurrence is the exact algebraic equivalent of “matching leading digits of the two paths” without materializingquery_pos’s (potentially ~1e9-long) digit expansion. Pure function of(query_pos, node_level, node_g, fanout)– no dependence on how the stream was chunked, which is what makes it stable under re-chunking (Acceptance §3).