~/satyajit

Library of Layers: a transformer that picks its own layer order

mdjsonmcp

2026-11-09 · 36 min · looped-transformers · mixture-of-experts · interpretability · routing

Why read this

Hightop 30%

Walks LoL's forward pass and router code, checks its MoE match, and runs 53 CPU toy runs: given routing transfers, five learned-router schemes do not.

  • Interactive explanations
  • Original analysis
  • Runs on a laptop CPU

LLM architectureMITResearch paper

How this was scored
Is it new?
2 of 3: A real new idea, method or capability
Can I trust it?
2 of 3: Measures key facts from files, code or configs
Can I run it?
2 of 3: Open code or weights with real limits
Will I understand it?
3 of 3: Mechanism carried by interactives built from real code or data
Can I act on it?
2 of 3: A concrete recipe, numbers or comparison
Will it last?
2 of 3: A reference for a year or more
Does it affect many?
1 of 3: A specialist community
Only here?
2 of 3: A teardown or measurement few others did

Score 71 of 100, ranked 117 of 701 rated articles. Each question is answered 0–3 by hand, and a 3 is rare. How articles are scored

Micah Goldblum's thread opens with the pitch: "Programmers build wildly different algorithms by chaining the same few primitives in different orders. LLMs can't: every input runs through the same layers in the same order." The paper behind it is Library of Layers (LoL) (arXiv 2611.03526, Rahmani, McLeish, Li, Lotfi, Frossard, Goldblum; EPFL, NYU, Columbia, Maryland), and the code is at arahmani1/library-of-layers under MIT.

The model keeps a shelf of transformer blocks. At every depth of its middle section, a small router picks which block runs next. Different windows of text can take different paths through the same shelf, and the same block can run twice or not at all.

I wanted to answer three things:

  1. How is the order chosen? A learned router, a search, or RL? (A router. It is an MoE gate with top-1 selection.)
  2. Do the paper's runs match its text? (Mostly. A few configs differ from what the paper says, listed below.)
  3. Does a learned order actually behave like composing primitives? For this I trained a small version on CPU on a task built from four primitive functions, then went back for a second round: a rerun under different optimiser settings, an ablation of which kinds of routing transfer, and five ways of training the router.

Short version: the mechanism is clean and the routing paths it learns are readable and steerable, as reported. In my toy, a router that has to learn the order on its own did not find the primitives, under any of five training schemes. Giving the model the right routing did help on unseen combinations, by less than my first round suggested, and the routing that helped most reused no block across depth at all. That gap is the interesting part.

arahmani1/library-of-layers@be07d22 · snapshot 2026-11-10
tracked files
112
license
MIT
branch
main
tests
none found
source
785.7 kB
commit date
2026-11-03
source by language
Python735.1 kB(89)Shell50.7 kB(11)

by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded

local clone, 2026-11-10 at be07d22 — branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile, shallow

shallow clone: counts describe the pinned tree, not the history

What a "library of layers" is

A LoL model has three parts (paper, Section 3.1):

LL counts blocks stored; DD counts blocks executed. With L=3L = 3 and D=3D = 3, a window might run E1→E3→E1E_1 \to E_3 \to E_1, repeating one block and skipping another. The paper calls that sequence a routing path, and each library block an expert.

Diagram: eight tokens enter a prelude with full causal attention, then pass through routed layers. At layer 1, tokens 1 to 4 run Expert 2 and tokens 5 to 8 run Expert 4. At layer 2 a router per window picks Expert L, Expert 1 and Expert 2 for three shifted windows. Layer 3 runs Expert 4 and Expert 3. A coda with full causal attention produces the output. A shared library of Experts 1 to L sits above.
The LoL forward pass: a fixed prelude and coda around a routed core, where each window of tokens runs one block from a shared library at every depth. Window boundaries shift between depths (paper, Figure 1).

This is not mixture of experts in the usual sense. A standard MoE layer routes each token to a few of that layer's MLPs, and layer 7 always runs before layer 8. In LoL, every depth draws whole blocks (attention and MLP) from one global pool. So routing decides which computation runs and in what order. Looping, as in Huginn and Ouro, is the special case where the path is fixed and repeats one block.

The forward pass, term by term

The paper's Algorithm 1 is short enough to write out whole. Take a sequence of nn tokens and width dd. The prelude runs first, with full causal attention:

s(0)=P(x)∈Rn×d\mathbf{s}^{(0)} = P(\mathbf{x}) \in \mathbb{R}^{n \times d}

Then, for each routed depth ℓ=1,…,D\ell = 1, \dots, D, five steps.

1. Cut the sequence into windows. With window size WW and stride SS, the boundaries shift by δℓ=(ℓ−1)S mod W\delta_\ell = (\ell-1)S \bmod W, and window kk is Ik={ i:⌊(i+δℓ)/W⌋=k }I_k = \{\, i : \lfloor (i+\delta_\ell)/W \rfloor = k \,\}. With the natural-language settings (n=1,024n = 1{,}024, W=128W = 128, S=64S = 64), odd depths have 8 windows of 128 tokens. Even depths have 9: a half window of 64 at each end and seven full ones between (reasoned from the formula).

2. Summarise what came before. Each window's mean hidden state is μk=mean⁡(sIk(ℓ−1))∈Rd\boldsymbol{\mu}_k = \operatorname{mean}(\mathbf{s}^{(\ell-1)}_{I_k}) \in \mathbb{R}^d. The router's input for window kk is the mean of the earlier windows' means:

r0=12(e(ℓ)+s0(ℓ−1)),rk=1k∑j=0k−1μj(k>0)\mathbf{r}_0 = \tfrac12\left(\mathbf{e}^{(\ell)} + \mathbf{s}^{(\ell-1)}_0\right), \qquad \mathbf{r}_k = \frac{1}{k}\sum_{j=0}^{k-1} \boldsymbol{\mu}_j \quad (k > 0)

e(ℓ)\mathbf{e}^{(\ell)} is a learned vector per depth, and s0\mathbf{s}_0 is the window's first token. The code computes rk\mathbf{r}_k with a cumulative sum over windows that restarts at each document boundary (RouterInputEncoder.build_features). Note what rk\mathbf{r}_k is not: it never looks at window kk itself. The block that runs on a window is chosen from its past. That is what keeps generation causal, and it is why the route for a window is fixed before its first token is generated.

3. Score the library. The router is one linear layer, zk=Wrrk+br∈RL\mathbf{z}_k = W_r \mathbf{r}_k + \mathbf{b}_r \in \mathbb{R}^{L}. In training, the code adds noise whose scale is itself a function of the logits:

z~k=zk+ϵ⊙min⁡ ⁣(softplus⁡(Wnzk+bn), 10),ϵ∼N(0,I),ak=softmax⁡(z~k)\tilde{\mathbf{z}}_k = \mathbf{z}_k + \boldsymbol{\epsilon} \odot \min\!\big(\operatorname{softplus}(W_n \mathbf{z}_k + \mathbf{b}_n),\ 10\big), \quad \boldsymbol{\epsilon} \sim \mathcal{N}(0, I), \qquad \mathbf{a}_k = \operatorname{softmax}(\tilde{\mathbf{z}}_k)

WnW_n and bn\mathbf{b}_n start at zero, so the noise starts at a standard deviation of softplus⁡(0)≈0.69\operatorname{softplus}(0) \approx 0.69 on every logit. The whole router is d⋅L+L2+2Ld \cdot L + L^2 + 2L parameters: about 16k for the 1B math model (d=2,048d = 2{,}048, L=8L = 8), against 51.4M in one of its blocks (reasoned from the code's layer definitions).

4. Pick one block. The balancing bias b\mathbf{b} enters the choice and nothing else:

ρk=arg⁡max⁡m(ak,m+bm),αk=ak,ρk\rho_k = \arg\max_m \left(a_{k,m} + b_m\right), \qquad \alpha_k = a_{k,\rho_k}

5. Run it on the window and blend. Attention inside EρkE_{\rho_k} is causal and confined to IkI_k:

sIk(ℓ)=sIk(ℓ−1)+αk(Eρk ⁣(sIk(ℓ−1))−sIk(ℓ−1))\mathbf{s}^{(\ell)}_{I_k} = \mathbf{s}^{(\ell-1)}_{I_k} + \alpha_k\left(E_{\rho_k}\!\big(\mathbf{s}^{(\ell-1)}_{I_k}\big) - \mathbf{s}^{(\ell-1)}_{I_k}\right)

EE is a full pre-norm residual block, so E(s)−sE(\mathbf{s}) - \mathbf{s} is its attention-plus-MLP update, and αk\alpha_k is a step size on it. Early in training, when the router is unsure, αk\alpha_k sits near 1/L1/L: with 24 experts, each depth applies about 4% of its block's update (reasoned). After the last depth, the coda runs with full attention again and produces p=softmax⁡(C(s(D)))\mathbf{p} = \operatorname{softmax}(C(\mathbf{s}^{(D)})).

Three panels. Top left: the prelude feeds a windowing step that splits tokens 1 to 8 into Window 1 and Window 2; a per-window top-1 router turns each into a probability bar chart and selects Expert 2 and Expert 4 from a library of Experts 1 to L; the result loops back D times with windows shifted by S, then goes to the coda. Top right: attention masks for layers 1 to 3 with window 4 and stride 2; layer 2's blocks are offset so token 5 shares a window with tokens 3 and 4. Bottom: the first window's router input is the first token plus a learned per-depth vector; later windows use the mean of prior hidden states; each feeds a mean-plus-router step that outputs probabilities and a top-1 expert.
The routed core in detail: per-window top-1 routing from a shared library (top left), strided window attention with W = 4, S = 2 (top right), and the router's two input rules for the first and later windows (bottom) (paper, Figure 2).

The cost of the core's attention follows from step 1 (reasoned). Each window computes a causal W×WW \times W score block, so a depth costs about nW/2n W / 2 query-key products against n2/2n^2/2 for full attention: one eighth at n=1,024n = 1{,}024, W=128W = 128. The MLP cost is unchanged, and at this context length the MLP dominates, so the saving matters less than the locality it buys.

Why it routes windows, not tokens

The obvious design is to route each token on its own, as MoE does. The paper says this fails (reported): if neighbouring tokens use different blocks, a query from block E2E_2's attention gets compared with keys produced by block E4E_4's attention, which live in a different space. Training was unstable.

The opposite design, one path for the whole sequence chosen from the first token, keeps attention consistent but decides the computation before most of the input exists.

LoL takes the middle road. At each routed depth, the sequence is cut into windows of WW tokens. Every token in a window runs the same block, and attention inside the core is causal within the window only. Between depths the boundaries shift:

δℓ=(ℓ−1) S mod W,wi(ℓ)=⌊i+δℓW⌋\delta_\ell = (\ell - 1)\,S \bmod W, \qquad w^{(\ell)}_i = \left\lfloor \frac{i + \delta_\ell}{W} \right\rfloor

where SS is the stride and wi(ℓ)w^{(\ell)}_i is the window of position ii at depth ℓ\ell. Shifting the boundaries lets information cross from one window to the next as depth grows, the way the receptive field of sliding-window attention grows with layers. The natural-language runs use W=128W = 128, S=64S = 64 in a context of 1,024 tokens (paper, Appendix A).

The router for window kk sees only what generation would also have: the mean hidden state of windows 0,…,k−10,\dots,k-1 at that depth. Window 0 has no history, so it uses its first token averaged with a learned per-depth vector e(ℓ)e^{(\ell)} (Algorithm 1). The widget below computes the windows and the reach of one token through the core from that rule.

Two things the widget makes plain (reasoned from the formula):

The router is small and ordinary. From model/routers.py and model/lol_window.py in the released code:

# routers.py, NoisyRouter.forward (abridged)
logits = self.c_fc(x.float())                      # n_embd -> n_experts
if self.training:
    noise_std = softplus(self.noise(logits)).clamp(max=10.0)
    logits = logits + noise_std * torch.randn_like(logits)
return softmax(logits, dim=-1)
 
# routers.py, _biased_topk: the bias picks, the unbiased score weights
indices = topk(scores + routing_bias, k=1).indices
weights = scores.gather(-1, indices)
 
# lol_window.py, _combine_expert_output
x = x + weight * (expert(x) - x)

So, per window and per depth:

  1. A linear layer scores all LL blocks. In training, noise is added to the logits. The paper says "Gaussian noise"; the code's noise scale is itself learned, input-dependent and capped at 10, with its weights initialised to zero (so it starts at softplus(0), about 0.69).
  2. Top-1 selection, with a per-expert balancing bias added only for the choice. This is DeepSeek's loss-free load balancing: after each optimizer step, every expert's bias moves by 0.001 against the sign of its load relative to the mean (router.py, bias_update_speed), clamped to ±16. The updates stop at 96.6% of training (bias_update_stop_fraction), so the last few hundred steps run on frozen biases.
  3. The chosen block's residual update is scaled by its unbiased router probability α\alpha. That scaling is the only path by which the language-modelling loss reaches the router. No gradient flows through the discrete choice.

Two details in that list are easy to miss and matter later.

The balancing is global across depths. finish_aux_step in router.py sums each expert's load over every routed depth (per_depth_load.sum(dim=0)) before comparing it with the mean, and _router_bias ignores its depth argument and returns the one expert_bias buffer. So the balancer is satisfied when each block is used equally in total. A router that sends everything at depth 2 to block 2 and everything at depth 3 to block 1 is perfectly balanced. Nothing in the objective pushes a depth to use more than one block.

There is a second, small gradient into the router. "Loss-free" is not quite loss-free in the code. With the default strategy: loss_free, the training loss is ce_loss + sequence_aux_coeff * sequence_aux, where sequence_aux is DeepSeek-V3's per-sequence balance term: L∑mfmaˉmL \sum_m f_m \bar{a}_m, with fmf_m the share of a document's windows that picked block mm and aˉm\bar{a}_m its mean router probability. The coefficient is 10−410^{-4} by default and 10−210^{-2} in the Huginn comparison runs. It pushes routing towards balance within each document, again summed over depths. The paper's appendix describes loss-free balancing only. The alternative strategy, aux_loss, uses a global auxiliary loss with coefficient 0.02, and Figure 8 reports that it ends at a slightly higher loss than the loss-free run.

That answers the first question. There is no search over orders, no RL, no straight-through estimator. The order is learned the way an MoE gate is learned, and the router is tied across depths by default (weight_tie_router: true). With a tied router, the only things that tell depth 3 apart from depth 7 are the hidden state itself and the first window's depth vector. The paper's Figure 9 reports that untying it changes the loss very little.

Two training details matter for cost:

One knob from looped to sparse

Library size is the paper's single new hyperparameter. With depth fixed, a library smaller than the depth forces reuse, which is a looped model. A library larger than the depth leaves blocks idle on every pass, which is sparsity. Per-token compute depends on DD only; stored parameters depend on LL.

The dial uses block sizes computed from the code's own layer definitions and the configurations the shell scripts launch.

A check on the arithmetic (reasoned): one block at width 1,024 is 12.8M parameters (attention 4.2M, SwiGLU with hidden width 2,816 at 8.7M). With 4 prelude and 4 coda layers, L=2,4,8L = 2, 4, 8 give 128.5M, 154.1M and 205.5M non-embedding parameters. Figure 5's x-axis puts the three LoL points at about 128M, 154M and 206M. The released config matches the figure.

What the comparisons report

Left: evaluation cross-entropy against non-embedding parameters. LoL with L equal to 2, 4 and 8 sits below the matching Huginn models (2 by 8, 4 by 4, 8 by 2), at about 2.85, 2.82 and 2.80 against 2.87, 2.84 and 2.81. Right: training loss against ExaFLOPs, with all six curves converging near 2.81 to 2.85 by 22.5 ExaFLOPs.
LoL against Huginn-style looped models at matched unique layers: lower evaluation loss at each size, with min/max error bars over 3 seeds (left), and similar training loss per FLOP (right). Both are 360M-effective models trained on 10B tokens of FineWeb (paper, Figure 5).

All of these are reported, from the paper:

ModelARC-EARC-CHellaSwagPIQAGSM8K
Vanilla40.8 ± 1.028.2 ± 1.331.4 ± 0.556.6 ± 1.229.7 ± 1.3
Vanilla (sliding window)49.5 ± 1.028.5 ± 1.331.3 ± 0.558.8 ± 1.127.5 ± 1.2
LoL50.3 ± 1.029.1 ± 1.331.2 ± 0.559.7 ± 1.127.9 ± 1.2
Left: cross-entropy training loss on a log scale against ExaFLOPs from 0 to about 90 for Vanilla, Vanilla with sliding window, and LoL. LoL drops faster in the first 20 ExaFLOPs and all three end near 0.7. Right: a heatmap of expert utilisation, core layers L0 to L7 against experts E0 to E7, with each cell the percentage of windows. Most rows are dominated by one or two experts: L1 uses E4 for 50 percent, L5 uses E3 for 55 percent, L6 uses E5 for 68 percent and L7 uses E6 for 66 percent.
The 1B math runs: LoL's training loss falls faster early, which the paper credits to growing, and all three end close together (left). Expert utilisation by routed depth in one LoL model: most depths lean on one or two experts (right) (paper, Figure 4).

The fair reading is the paper's own: competitive, within about two standard errors, with the full-attention baseline's ARC-Easy score as the outlier. HellaSwag at 31% and ARC-Challenge at 28-29% sit a few points above chance (25%), so at this scale these benchmarks separate the three models very little. Nothing in the paper claims LoL is better per FLOP. It claims parity plus a property dense models lack.

Checking the MoE match from the scripts

The MoE comparison is the one where "matched" carries the most weight, so I recomputed it from shells/lol_vs_moe.sh and the layer definitions (reasoned from the code, not run). Width 1,024; attention is 4.19M parameters per layer; a dense SwiGLU block with hidden width 2,816 is 12.85M.

MoE baselineLoL
Layout24 MoE layers, 3 experts of hidden 1,408, top-24 + 4 MoE prelude/coda layers (same MoE) + 24 dense blocks, 16 routed depths
Stored parameters (non-embedding)412.1M445.6M
Active per token308.3M308.3M
Step cap at batch 26 × 5 × 8 GPUs × 1,0247,269 steps, about 7.7B tokens9,393 steps, about 10.0B tokens

Two observations. The active counts match exactly, because two experts of hidden 1,408 are exactly one dense MLP of 2,816. And at the same 17.3 ExaFLOP budget, LoL's step cap allows about 29% more tokens, because growing makes its early steps cheap. Training stops at the FLOP budget, so the token figures are upper bounds; the paper's own phrase is "approximately 10B tokens for the most efficient model". Read that way, the 0.05-nat gap in the MoE's favour comes with LoL holding 8% more stored parameters and seeing more data. That is not a contradiction of the paper, which claims competitiveness, but it is the honest size of the comparison.

What the paths encode, and forcing them

That property is the paper's real case: the routing path is a discrete, low-dimensional record of what the model did, and you can overwrite it.

Left: a PCA plot of routing paths for ten languages; Korean, Mandarin, Arabic and Russian, and Hindi with Japanese form separate clusters, and Latin-script languages (German, French, Spanish, Turkish) cluster together inside a dashed ellipse. Middle and right: confusion matrices for sudoku and mazes, forced strategy against generated strategy, with 99.5 to 100 percent on the diagonal.
Routing paths separate languages, with Latin-script languages close together (left). On sudoku and mazes, forcing a strategy's mode path makes the model produce that strategy's trace almost every time (middle, right) (paper, Figure 3).

All reported:

The maze result is the strongest evidence in the paper. The output vocabulary is identical across strategies, so the path is the only thing selecting the algorithm.

Note the setup, though. The strategy label reaches only the router, so the blocks are trained to depend on routing to know which algorithm to run. The experiment shows a path can carry the strategy. It does not show that the five strategies are built from a shared set of reusable block-primitives, which is what the programming analogy in the thread suggests.

Where the configs and the text disagree

From the shell scripts (shells/*.sh) and configs. All of this is checkable in the repository, and none of it overturns a result, but a reproducer would trip on it.

A toy library on CPU

The paper's experiments need 8-GPU nodes. The question I could ask on a 4-core CPU was the one the thread raises: if the task really is "chain primitives in some order", does a learned router discover the primitives and reuse them?

Task. Input: 8 digits mod 10 and a 3-step program. Each step is one of four primitives: inc (add 1 to every digit), rev (reverse), rot (rotate left by one), mul3 (multiply every digit by 3). They do not commute, so order matters. There are 64 programs. I train on 48 and hold out 16, chosen so that every primitive still appears at every step in training. Program tokens are step-specific, so a mean over tokens still says which primitive sits at which step.

Models. Each is a 64-wide, 4-head encoder with 3 block applications:

Round one (the first version of this article): 3,000 AdamW steps at batch 256, 3 seeds per model, PyTorch 2.14.1 on 2 CPU threads, 2-3 minutes per run. Accuracy is exact match of all 8 output digits on 4,096 fresh examples. Script: one PyTorch file of about 200 lines. The routing is a re-implementation of the code above, not the repo's model, because the repo's model needs FlexAttention and CUDA-scale configs.

What I measured in round one:

That matches the paper's own utilisation heatmaps (Figures 4 and 6), where experts "mainly tend to be used in groups of neighboring depths". A router with an option to specialise by depth takes it, because depth is the easiest signal to separate on.

The last pair of runs copies the paper's sudoku setup: the program reaches only the router, so the blocks can only learn which primitive to apply through the path they are on.

So the toy reproduces the paper's direction: hide the task from everything but the router and the path has to carry it. It also shows how far a learned path stays from the clean one: 15-16% against 44.0%, with a seed-to-seed spread of about 28 points.

Round two: the toy again, and two ablations

Round one ended on a reasoned guess about why the router fails, and on a single training setup. For this update I went back to test both. Everything in this section is measured.

First, the rerun did not reproduce round one's gap

The round-one script was not kept. I rewrote it from the description above: same task, same 48/16 split, same widths, the same 3-step encoder and the same routing rule, now with the optimiser written down (AdamW at 3×10−33 \times 10^{-3}, 100 warmup steps then cosine decay, weight decay 0.01, gradient clipping at 1.0). Each run uses one CPU thread with two runs side by side, PyTorch 2.14.1, about 4-5 minutes for a top-1 model and about 14 minutes for one that runs every block at every depth. Before adding anything I reran the three models the conclusion rests on, then varied the learning rate and the weight decay.

Held-out accuracy, mean over seeds (trained programs: 99.9-100% for every model in every row):

SettingDense, fixed orderRouting given, by primitiveLearned router (as in the code)
Round one (script lost)9.8%44.0%4.2%
Round two, lr 3e-3, wd 0.01, 5 seeds25.5% (13.9-33.8)38.4% (29.8-49.0)12.3% (2.9-20.7)
Round two, lr 1e-3, wd 0.01, 3 seeds12.8%19.4%9.0%
Round two, lr 3e-3, wd 0.1, 3 seeds23.8%40.5%not run

The ordering held in every row: routing given by primitive beats the dense model, and the dense model beats a learned router. The size of the gap did not hold. Round one put the given routing 34 points above dense; round two puts it 7-17 points above. The dense baseline in particular moves with the optimiser, from 9.8% to 25.5% between two scripts that differ only in details I did not record the first time. So the round-one sentence "the blocks partly become reusable primitives" overstated the effect. The direction is reproducible; the 44-against-10 contrast is not.

Experiment 1: which kind of path transfers

The paper's thread frames the idea as chaining a few primitives in different orders. The toy can take that apart, because I can hand the model any routing I like and see which kinds help on unseen programs. Five schemes, all with routing given and nothing learned about the order:

What I measured (held-out, lr 3e-3, wd 0.01):

The third result is the one that matters for LoL's framing. In this toy, what transfers to new programs is a path that follows the program's structure. Reusing one block across depths is not where the benefit comes from; it is a constraint that costs accuracy, because the shared inc block has to work on hidden states from three different depths. A library smaller than its depth buys parameter efficiency, which is the looped end of the paper's dial. It does not, on this evidence, buy composition.

Experiment 2: is it the router's gradient?

Round one's explanation was that the router only learns through α\alpha, the weight of the block it already picked, so it never hears that a different block would have helped. The code reading above adds a second suspect: the balancing bias is shared across depths, so a router that gives each depth its own block is perfectly balanced. Both can be tested. Five ways to train the same L=4L = 4 tied router, three seeds each (five for the first):

To read the routing, I use two numbers per run, averaged over the three steps. Separation is how well the chosen block identifies the primitive: the share of examples whose block's most common primitive is their own. It is 0.25 when blocks ignore the primitive and 1.0 for the oracle. Depth concentration is how much each block is owned by one depth: the share of each block's use that falls at its busiest step. It is 1/3 when blocks are spread evenly over the three steps and 1.0 when every block belongs to one step.

What I measured:

Router trainingHeld-out, top-1SeparationDepth concentration
As in the code (5 seeds)12.3%0.460.59
+ sequence balance loss12.4%0.460.41
Balance per depth11.2%0.460.35
Soft mixture in training0.2% (15.1% if kept soft at test)0.470.54
Gumbel-softmax, annealed1.3% (17.5% if kept soft at test)0.360.67

So neither suspect explains the failure on its own. Separation sat between 0.34 and 0.51 in every run of every scheme, against 1.0 for the routing that transfers. Reasoned, not measured: every one of these models fits the 48 trained programs, so the training loss cannot tell a compositional routing apart from any other routing that fits. Only the held-out programs can, and the router never sees them. A learned router finds a routing that works on what it was trained on. Nothing in a top-1 gate, soft or hard, balanced globally or per depth, prefers the one that factorises the task. The paper's sudoku and maze runs get structure into the path by a different route: the strategy label is visible to the router and to nothing else, which forces the path to carry it. Round one's router-only runs (14.8-16.5%) were the toy's version of that, and they were the only learned-router configurations whose mean beat the dense baseline in round one.

What it costs

What I could not check

Where this sits

The paper's related work places it between two lines (reported, from its Section 2):

LoL's distinct choices are the combination: whole blocks rather than MLPs, one global library for every depth, top-1 per window, and window boundaries that shift with depth. The looped transformer and mixture of experts pages describe the two ends of its dial, and ALoDLM is the adaptive-depth cousin: it varies how many loops a token gets, not which layer runs.

What the paper shows, as reported: a model with input-dependent layer order trains stably at 360M and 1B, matches looped, dense and MoE baselines closely, and exposes a path you can probe and overwrite. The maze result is the evidence that a path can select an algorithm.

What it does not show, and what my toy suggests is hard: that the blocks become reusable primitives which recombine for unseen tasks. In the toy, a routing handed to the model that follows the program's primitives beats a dense model on unseen programs in every setting I tried (by 7-17 points in round two), and a routing that names the program instead of describing it transfers nothing. A learned router, trained five different ways, never got its blocks to line up with the primitives (separation 0.34-0.51, against 1.0) and never beat the dense model on average. The best routing I found reused no block across depth at all, which says the benefit comes from structure in the path, not from reuse.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "Library of Layers: a transformer that picks its own layer order", ai.thesatyajit.com, November 2026.

bibtex
@misc{ghana2026libraryoflayers,
  author = {Satyajit Ghana},
  title  = {Library of Layers: a transformer that picks its own layer order},
  url    = {https://ai.thesatyajit.com/articles/library-of-layers},
  year   = {2026}
}
share