Investigation · matrix-polygon-shapes

Can square matrices be replaced by n-gons?

A matrix is a rectangle of numbers. What if the numbers were laid out as a triangle, a pentagon or a hexagon? "Multiplying shapes" can mean three precise things, and each gives a different, sometimes surprising answer.

Shape = nonzero pattern of a matrix
Up to 5 corners

Rectangles, triangles, trapezoids and certain pentagons survive multiplication. Hexagons and larger shapes cannot stay stable: products square them up.

Shape = array, multiplied by convolution
Any polygon works

Shapes add (Minkowski sum). Cost is exactly 4|P| − B − 3. Triangles and hexagons are as natural as squares. Without wrap-around the centre of a hexagon learns 30× faster than its corners.

Shape = ring of matrices
Odd beats even

Even rings (square, hexagon) admit no shortcut: rank 2^k. Odd rings (triangle, pentagon) do. The pentagon's exact cost (25 to 31) is an open problem.

Shaped weights in a language model
Close, not better

Triangles and cubes of weights beat a plain MLP but not SwiGLU. A whole hexagon-pyramid model with triangle layers comes close to a standard transformer: the best version (wrapped) is 0.013 bits/char behind at 4,000 steps, within seed noise. Matrix-product versions lead per iteration in short runs, but not per second.

The common thread: the rectangle is what multiplication converges to, and other shapes earn their place only with a different multiplication. See Takeaways.

Overview

The page asks whether the rectangle of a matrix can be replaced by another shape. Sections 1–3 answer it mathematically. Sections 4–6 try shaped weights in language models, first one layer in a tiny model, then whole models, ending in a long series of experiments on a pyramid model with matrix products as its feed-forward layers.

Headline results

Most section 6 results are single runs at 1,000 iterations; the page says so wherever it matters. Differences below about 0.05 bits per character between single runs are noise.

Shapes and multiplication
Sections 1–3: what happens to a shape when you multiply
  • Shapes inside a matrix: only boxes cut by at most one diagonal survive a matrix product, so at most 5 corners.
  • What survives multiplication: triangles, trapezoids and some pentagons stay; hexagons, bands and disks grow into rectangles.
  • Shaped arrays under convolution: under convolution every convex polygon survives; a product costs exactly 4|P| − B − 3.
  • Rings of matrices: even rings (square, hexagon) cannot be shortcut; odd ones (triangle, pentagon) can; the pentagon's cost is open.
Shaped layers in small language models
Sections 4–5: one layer swapped in a tiny model
A whole model in a shape: the shapes
Section 6: the feed-forward layer of a full model
Changing a layer from one iteration to the next
Section 6: rotations, staircases and masks
Why it worked, and what the model wants
Section 6: opening up the trained models
Cheaper, smaller, and the bottom line
Section 6: cost, precision, and the comparison
Takeaways
What it adds up to

1 · Shapes as nonzero patterns inside a matrix

Keep ordinary matrix multiplication, but allow nonzeros only inside a shape drawn on the N×N grid. The product of two such matrices has nonzeros where (x,z) can be reached through some middle index y with both (x,y) and (y,z) inside the shape. That is the relational composition R∘R. A shape is useful as a "matrix type" only if it is closed: the product of two such matrices has the same shape.

Boolean products on a 64×64 grid, computed live. Cost counts Σy |column y| · |row y| scalar multiplications for a sparse product, relative to the dense N³.

What survives multiplication

2 · Shaped arrays multiplied by convolution

A non-rectangular array has no natural "row times column" product. It does have a natural product, though: treat it as a two-variable polynomial (or an image filter) and convolve. The shape of the result is the Minkowski sum P + Q, so a pentagon times a pentagon is a pentagon twice the size. The exact minimum number of multiplications is the number of lattice points in P + Q. By Pick's theorem that is 4|P| − B − 3, where B counts the lattice points on the boundary.

Blue: the support P of one factor. Orange: the support of the product, P + P. Each orange point is one output coefficient, so one unavoidable multiplication.
Two arrays of the same shape, about 100 coefficients each. "Exact" is the proven minimum |P+P|, which equals 4|P| − B − 3 in every row.

3 · A polygon of matrices: trace(A₁A₂···Ak)

Put a matrix on each edge of a k-gon and multiply around the ring. This k-linear form is the k-gon tensor network. The triangle is ordinary matrix multiplication, trace(ABC), with Strassen's 7 products for 2×2. The square is the four-way form trace(ABCD) from the three-way project. Its rank N⁴ means no fused algorithm beats the naive one. The tensor rank is the number of products a fused algorithm needs. For 2×2 matrices (bond dimension 2):

The button builds Christandl–Zuiddam's decompositions in your browser from Strassen's: 31 products for the pentagon, 127 for the heptagon. It then checks them entry by entry against the ring tensor.
exact search over GF(2) (SAT)result
triangle, 7 productsexists (Strassen)
triangle, 6 productsimpossible (proved by SAT, as Hopcroft–Kerr)
square ring, 15 productsno answer; stopped after 15 min (yet it is certainly impossible, by a flattening argument)
pentagon ring, 31 productsno answer; stopped after 7.5 h (31 does exist, by tensor surgery)
pentagon ring, 30 productsno answer; stopped after 7.5 h

4 · Triangle-shaped weights, swept three ways

Store weights on a triangular lattice: cells (i, j, k) with i + j + k = n − 1, about n²/2 of them. Each cell lies on three lines, at 0°, 60° and 120°. A matrix entry lies on only two: its row and its column. Sweeping each direction multiplies the two other coordinates' inputs and sums along the line. So one set of weights gives three outputs:

y₀[i] = Σ W·hj·hk   y₁[j] = Σ W·hi·hk   y₂[k] = Σ W·hi·hj

Together the three sweeps are exactly the gradient of the cubic energy E(h) = Σ Wijk·hi·hj·hk. That makes it a multiplicative layer, in the same family as the gated layers (GLU, SwiGLU) that modern language models use.

Click any cell (n = 8, 36 weights). Blue: the 0° line (output y₀[i]). Orange: the 60° line (y₁[j]). Green: the 120° line (y₂[k]). The check computes all three sweeps and compares their sum with a finite-difference gradient of E.

Does it help a language model?

A 2-layer, width-64 character-level transformer was trained on this repository's markdown (2.8 MB, 1,500 steps, CPU). Only the feed-forward block differed, and every variant had about 65.5k feed-forward weights. Lower is better:

Validation bits per character after 1,500 steps. Dots are individual seeds (two each, one for the one-sweep triangle). Script: llm/triangle_ffn.py.

In 3D: a cube and a tetrahedron

A cube of weights W[i, j, k] is a full 3-way array. Each weight sits on 3 lines, one per axis, and the triangle above is just its diagonal slice i + j + k = n − 1. A tetrahedron (cells with i + j + k + l = n − 1) has 4 face directions. Each sweep collapses a triangular slice and multiplies the other three inputs, giving a 4th-order (quartic) interaction. In general a d-dimensional simplex has d + 1 sweeps and d(d + 1) multiplications per weight read.

Counted by running each shape's sweep code (n = 5), which also checks that the sweeps add up to the gradient of the shape's energy.

In the same tiny language model, at the same 65.5k feed-forward weights (cube n = 29, tetrahedron n = 46):

feed-forward500 steps (2 seeds)1,500 steps
MLP3.9723.389
triangle, 3 sweeps3.9653.374
tetrahedron, 4 sweeps3.966not run (3.6 s per step on CPU)
cube, 3 sweeps3.9633.350
SwiGLU3.9713.325
Validation bits per character, lower is better. At 500 steps all five are within seed noise (about ±0.02).

Fewer bits?

Does a shaped weight array, used three ways, keep its quality with fewer bits per weight? The trained models' feed-forward weights were rounded to b bits after training (symmetric, per tensor, round to nearest) and the language model re-evaluated. "Gated" is the SwiGLU-style feed-forward block: Swish-gated linear unit, then a projection.

weights rounded (one group at a time)weights3 bits2 bits
cube's three-way weights48.8k+0.082+1.07
triangle's weights11.3k+0.065+0.95
MLP output matrix32.8k+0.115+1.77
MLP input matrix32.8k+0.170+1.49
triangle's input projection13.6k+0.197+1.85
Increase in validation bits per character, mean of 2 seeds.
total feed-forward storageMLPgatedcube, mixed precision
≈ 262k bits (4 bits per weight)3.4163.380—
≈ 221–241k bits3.5213.4623.460 (with 8% less storage)
≈ 200k bits3.6703.6023.530
Absolute validation bits per character after rounding. The cube stores its three-way weights at 3 bits and its small input projection at 6–8 bits.

5 · Hexagons: wrap around or not, and hexagonal attention

Multiply two hexagonal arrays by convolution. With wrap-around (hexagons tiling a torus) every output cell receives exactly the same number of multiplications, |P|, just like an ordinary cyclic product. Without wrap-around the count is the overlap of two hexagons: |P| at the centre, falling to 1 at the corners.

Darker means more multiplications land on that output cell. Wrapped, the doubled hexagon folds back onto the original one and every cell gets the same count.

Does the centre learn faster?

Yes. Under gradient descent an output's learning speed is its neural-tangent-kernel diagonal, Σp b²s−p + a²p, which grows with the number of products landing on it. Both factors were trained (y = a ∗ b, random targets, 12 seeds, hexagons of radius 4):

Average error removed per output in the first 3 gradient steps, by ring (0 = centre). Orange: no wrap-around. Blue: wrap-around (only rings 0–4 exist after folding).

A hexagonal attention or convolution mask

The idea: let each cell attend strongly to its 6 neighbours, skip the next ring, and attend to a few random far cells. That is a hexagonal cousin of BigBird's local + random sparse attention.

Hexagonal map of radius 8 (217 cells). Dark: the centre cell. Blue: what it attends to directly. Shaded: what has reached it after the chosen number of layers. The table gives links per cell (cost) and layers until every cell has heard from every other.
Max/min over directions of the 4th moment E[(x·u)⁴] after repeated blurring, computed live. 1.000 means perfectly round up to 4th order.

In a language model: does the non-uniformity help?

Arrange the hidden units of the feed-forward block on a hexagon and let the block convolve two projections of the input across it: y = C·((A·x) ∗ (B·x)). Without wrap-around the output hexagon has twice the radius and is centre-heavy; with wrap-around every output gets the same number of products. This is computed exactly with an FFT, because a hexagon of radius R on a torus is the cyclic group ZN, N = 3R² + 3R + 1, via φ(q, r) = ((R+1)·q − R·r) mod N. The question is not whether it saves operations, but whether the shape gives the model a property it can use.

feed-forward block (≈ 65k weights)val bits/char
plain MLP3.389
gated (SwiGLU-style)3.325
hexagon, no wrap (centre-heavy)3.324
element-wise bilinear, C·((A·x) ⊙ (B·x))3.301
hexagon, wrap (uniform)3.289 (seeds 3.312, 3.265)
Tiny character-level model, 1,500 steps, 2 seeds. Competitive, but not a clear win.
Increase in validation bits/char when output cells of the trained layer are zeroed, against the fraction of cells kept (mean of 2 seeds). Solid: trim the outer rings first. Dashed: drop the same number of random cells. Orange: no wrap-around (centre-heavy). Blue: wrap-around (uniform).
The trained no-wrap layer, by ring of the output hexagon (0 = centre). Orange: average activation size. Blue: size of the output weights attached to the cell. Dashed blue: how far those weights moved from their initial values. Each series is scaled to its own maximum. With wrap-around all three are flat.

Where this matters: hexagonal grids are standard in geospatial indexing (Uber's H3), board and strategy game maps, telescope and detector arrays, and hexagonal CNNs (HexagDLy). A hexagonal local + skip + random mask is a natural sparse attention for such data. Choosing wrap-around or not decides whether the border learns as fast as the middle.

6 · A whole language model in a shape

The experiments above swap one layer. shape-llm builds a whole small character-level model around a shape. Every token's hidden state is a hexagon of feature cells, shrinking through the network like an image pyramid.

blockshexagon radiuscellschannels per cellfeatures
1–2124692938
3–461276762
5–633716592

Hexagonal feed-forward: clearly worse

model (2,000 steps, 2 seeds)parametersval bits/char
standard transformer2.59M2.992
hexagonal, one shared 7-tap kernel (attention holds 89% of the parameters)2.63M3.342
standard transformer2.25M3.023
hexagonal, each cell its own neighbour weights2.33M3.259
… plus neighbour-count normalisation (edge cells boosted)2.33M3.254

Turning the centre down

Centre outputs collect many more products, so perhaps more noise. The non-wrapped hexagonal layer of section 5 was scaled so the corners keep weight 1 and the centre gets less:

hexagonal layer, no wrap (tiny model, 1,500 steps)val bits/chareffect
unscaled (centre 91 products, corners 1)3.324centre-first order; training grows the edge weights to compensate
centre × 1/√fan-in3.330balanced: centre and edges contribute equally, no order, no compensation
centre × 1/fan-in3.392over-corrected: the edges become most important

No sign that the centre's larger sums were harmful noise: 1/√ balances the layer at no cost, and 1/fan-in overshoots.

Triangle feed-forward: almost a standard transformer

Same pyramid and bottlenecks, but every block uses the triangle layer, swept at 0°, 60° and 120°. Without wrap-around the line at index i holds n − i weights, so long lines are the triangle's "centre". With wrap-around the triangle closes into a torus and every line is equally long. "Centre lowered" scales each output by (products)^−½; "reversed" by (products ÷ max)^½; "average 1" renormalises so only the balance changes, not the overall size.

Which triangle is which. The names in the tables and the chart combine two choices: wrap or not, and how the signal is spread from the long "centre" lines to the short edge lines. The weights shown are for the smallest triangle (side 50); the larger ones are similar.

name in tables and chartwrapsignal across the linescentre linelength-1 edge line
triangle (unscaled)nofull everywhere11
triangle, wrapyesfull everywhere (all lines equally long, so there is no centre)11
triangle, wrap, scaledyesone constant factor for every line0.140.14
triangle, centre lowerednolow in the centre, full at the edges0.141
triangle, centre lowered, average 1nolow in the centre, high at the edges, average 10.553.9
triangle, reversednofull in the centre, lower at the edges10.14
triangle, reversed, average 1 (“rev., avg 1”)nohigh in the centre, lower at the edges, average 11.480.21
full pyramid model (1,000 steps, 2 seeds)val bits/char
standard transformer3.384
triangle, no wrap, unscaled3.407
triangle, wrap3.441
triangle, centre lowered, average 13.465
triangle, centre lowered3.554
hexagonal feed-forward3.587
One batch, same training text. A later batch (below and in the chart) re-ran the unscaled triangle and the transformer next to the reversed variants: transformer 3.404, triangle 3.423, reversed 3.420, reversed with average 1 3.391.

Longer run: every triangle variant, 4,000 steps

The same full pyramid model, trained 4× longer with 3 seeds per variant, all on the same text. The letters are the two choices: A1 no wrap, A2 wrap; B1 full signal in the centre and lower at the edges, B2 low in the centre and full at the edges, B3 full everywhere. With wrap every line is equally long, so there is no centre: A2 + B1 is the same model as A2 + B3, and A2 + B2 only turns every line down by one factor.

model (4,000 steps, 3 seeds)choicesmean bits/charthe 3 seedsbehind the transformer
standard transformer–2.7262.713 – 2.737–
triangle, wrapA2 + B32.7392.732 – 2.7510.013
triangle, reversed, average 1A1 + B12.7702.754 – 2.7830.044
triangle, reversedA1 + B12.7732.760 – 2.7930.047
triangle, no wrap, unscaledA1 + B32.7782.761 – 2.7930.052
triangle, centre lowered, average 1A1 + B22.7892.787 – 2.7910.063
triangle, centre loweredA1 + B22.8172.787 – 2.8320.091
Validation bits per character at step 4,000, lower is better. The transformer has 2.25M parameters, the triangles 2.31M (2.33M with wrap).
Hover a point to see its value and its seeds.
Validation bits per character (lower is better). Bold lines are the mean of the seeds (3 in the 4,000-step runs, 1 or 2 elsewhere); tick “show individual seeds” to add each seed as a dashed line. Use the boxes to choose which lines to compare; the y-scale fits the lines shown. Only lines from the same experiment share training text, so compare within an experiment.

What the longer runs revealed

  1. Uniform beats every centre/edge weighting. The best triangle is the one with no centre at all (wrapped). Every no-wrap profile ranks below it: full centre, flat or lowered centre. The hexagon said the same thing in section 5: its wrapped single layer also beat the unwrapped one (3.289 against 3.324, seed ranges apart). In both shapes, the version where every output collects the same number of products wins. That is also the property a dense matrix has.
  2. The 1,000-step runs got the ranking wrong twice. There, wrap finished 0.034 behind no wrap, and the reversed triangle looked ahead of the transformer. In the 4,000-step runs wrap pulls ahead from step 800 and the lead grows (0.009 at step 800, 0.031 at step 1,600, about 0.04 from step 2,400 on). The reversed triangle ends 0.044 behind the transformer. Seed spreads also shrank, from 0.19 at the end of a 1,000-step run to at most 0.045 at the end of a 4,000-step run. Short runs with 2 seeds were not enough to rank variants 0.03 apart.
  3. The triangles start fast, fall behind, then close in. At step 400 every triangle except the two centre-lowered ones is slightly ahead of the transformer. By step 1,200 they are 0.07–0.09 behind (0.12 with the centre lowered). From there the gap shrinks steadily as the learning rate anneals: the wrapped triangle goes from 0.073 behind to 0.013. In the last 1,600 steps the triangles improve by 0.16–0.17 bits/char against 0.14 for the transformer, while in steps 400–2,000 the transformer improved more (0.99 against 0.87–0.95). A guess, not tested: the three-way products train less well at a high learning rate, so a lower peak rate or a longer schedule could help them.
  4. The shape is mostly wiring, not weights. The triangle's own weights are only 14,150 numbers, 0.6% of the model (27,900 with wrap). Almost all parameters sit in the projections into and out of the triangle. Yet switching the wiring from "no wrap" to "wrap" moves the loss by 0.04, more than any reweighting of the centre and edges. What matters is which features get multiplied together.
  5. Renormalising made a bad idea consistent. The centre-lowered triangle rescaled to average 1 had by far the tightest seeds (2.787–2.791), while the same profile without rescaling had the widest (2.787–2.832). Rescaling did not make it good, only predictable. This is one observation on 3 seeds.
  6. Per unit of time, the transformer wins easily. Matched on parameters (the triangles even have 3–4% more), the triangle is close to the transformer. But each step is about 4–6× slower on a CPU, so at equal training time the transformer is far ahead. The triangle's case would rest on a fused gather–multiply kernel, which is the same open question as in section 4.

If you try triangle layers: wrap them, leave the weights unscaled, keep all three sweeps in every step and do not expect a fourth direction to help (see below), compare against a dense baseline at 4,000+ steps with 3 or more seeds, and test the learning rate before reading anything into gaps of 0.02. The fair next test of wrap is a smaller wrapped triangle doing the same number of products as the unwrapped one, so that wrap and extra work are separated.

Two of three sweeps per step

An idea to make the triangle cheaper: every training step uses only two of the three directions, and the pair rotates so that all directions get exercised.

angles = [0°, 60°, 120°]
a = angles[i % 3]            # i = training step, or a random number
b = angles[(i + 1) % 3]
sweep only a and b; skip the third direction
Each line is one output of the layer: it sums the products along that line. Solid lines are computed, dashed lines are skipped. Over three steps every direction is used twice and skipped once.

Tested on the wrapped and the unwrapped triangle, with the cyclic rule above and with a randomly chosen skipped direction. All layers skip the same direction on a given step. The two remaining sweeps are scaled by 3/2 during training (as dropout does), so the model can be evaluated with all three. 4,000 steps, 3 seeds, same text as the longer run.

trianglesweeps per training stepevaluated with all 3the 3 seedsevaluated with only 2cost of skipping
wrapall 32.7392.732 – 2.751––
wrap2 of 3, cyclic2.8662.846 – 2.9002.897+0.127
wrap2 of 3, random2.8542.839 – 2.8622.884+0.115
no wrapall 32.7782.761 – 2.793––
no wrap2 of 3, cyclic2.8792.859 – 2.8932.906+0.101
no wrap2 of 3, random2.8912.855 – 2.9102.922+0.113
Validation bits per character at step 4,000, lower is better. "Evaluated with only 2" is the mean over the three possible pairs. The standard transformer is at 2.726. The curves are in the longer-run chart above: tick the “2 of 3” boxes.

A square with four directions (4 of 4)

The hypothesis: two of three sweeps was worse than three of three, so a fourth direction might do better still, and beat the standard transformer, whose matrices only pair rows with columns.

The layer keeps its weights on an n × n grid that wraps around, and sweeps it at 0°, 45°, 90° and 135°: rows, columns and both diagonals. With n odd, every cell lies on exactly one line of each direction. The first three directions are exactly the wrapped triangle of the sections above, so this is the wrapped triangle plus the second diagonal.

The four line families of the wrapped square. The thick line is a single line: a diagonal that leaves through one edge re-enters at the opposite edge.

Each cell sits on four lines, one per direction. For the output of one of its lines, a cell contributes its weight times the features of its other three lines. Those three can be combined in two ways, and both were trained:

model (4,000 steps, 3 seeds)directionsmean bits/charthe 3 seedsbehind the transformer
standard transformer–2.7262.713 – 2.737–
triangle, wrap3 of 32.7392.732 – 2.7510.013
square, one product across all four4 of 42.7542.747 – 2.7620.028
square, two directions at a time4 of 42.7812.751 – 2.8010.055
Validation bits per character at step 4,000, lower is better, same text as the other 4,000-step runs. The square's sides are 41, 53 and 67 (the triangle's are 50, 65 and 85), so that both have 2.33M parameters. The curves are in the longer-run chart above.

What the square revealed

Is the fourth direction actually used? In each trained model, one direction's outputs were switched off in every layer (at evaluation only), and the rise in loss measured.

wrapped triangle, 3 directions square with one product across all four directions
Hover a bar to see its seeds.
Rise in validation bits per character when one direction is switched off in a trained model. Bars are the mean of 3 seeds, dots the individual seeds. A longer bar means the model leans on that direction more.
  1. The fourth direction is fully used, but it divides the work instead of adding to it. Switching off any one of the square's four directions costs 0.23–0.25 bits/char, and the added diagonal (0.23) counts about as much as the other three. In the triangle each of the three directions costs 0.40. So each direction matters less in the square: the same job is shared by four directions instead of three, and the final loss is no better.
  2. Removing and adding are not mirror images. Taking a direction away from a model built for three costs 0.10–0.13 even when the model is trained for it. Adding a fourth gains nothing. "Fewer is worse" did not imply "more is better".
  3. At equal parameters, each added direction cost a little. Transformer 2.726, three directions 2.739, four directions 2.754: about 0.014 per added direction, the opposite of the hypothesis. Each single difference is close to the seed noise, and the transformer is not literally the two-direction member of the family (its layers use an activation function instead of products), so read this as a trend, not a law.
  4. Uniform layouts take the top three places. Transformer, wrapped triangle, wrapped square with one product. Every non-wrapped triangle ranks below that square. Whether every output collects the same number of products mattered more than how many directions are swept.
  5. The bolder product trained better. Multiplying three features at once could have been unstable. Instead it beat the cautious two-at-a-time version (2.754 against 2.781), with tighter seeds (spread 0.015 against 0.050).
  6. The fourth sweep was free in time. The square with one product takes 1.05 s per step against 1.14 s for the wrapped triangle, because its smaller grid (41 × 41 instead of 50 × 50 at the first level) more than pays for the extra sweep.
  7. Same learning pattern as the triangles. A fast start (level with the best model at step 400), then 0.07 behind the transformer around step 1,200–1,600, closing to 0.028 at the end.

All 4,000-step models side by side

All thirteen models trained for 4,000 steps on the same text, ranked by their final loss. The transformer is first, the wrapped triangle second and the square with one product across all four directions third. The two-of-three models are worse than every model that sweeps all its directions.

standard transformer triangle, all 3 sweeps every step triangle, 2 of 3 sweeps per step square, 4 directions the same model evaluated with only 2 sweeps
Hover a row to see its seeds.
Validation bits per character at step 4,000; further left is better. Large dot: mean of 3 seeds. Small dots and the bar: the individual seeds. For the two-of-three models the second number and the diamond show the loss when evaluating with only 2 sweeps. The dashed line marks the transformer. The axis starts at 2.70, not 0.

Time against loss. Seconds per training step against final loss, for the models with a clean timing.

standard transformer triangle, all 3 sweeps every step triangle, 2 of 3 sweeps per step square, 4 directions
Hover a point to see its numbers.
Lower left is better. Each arrow goes from sweeping all 3 directions to sweeping 2 of 3 (cyclic rule): about 15% less time per step, and 0.10–0.13 bits/char worse. The squares cost about as much as the wrapped triangle. Timed on one CPU thread on an idle machine; the random rule does the same work as the cyclic one.

A square matrix with the usual product, and a rotating offset

A different kind of layer: no sweeps, just ordinary matrix multiplication. Each feed-forward layer turns a token's features into a small square matrix M, computes the usual product M·M, and maps the result back. In the product, the destination cell [x, y] is row y times column x.

In blocks 1–2 the row and the column are taken from neighbours instead, and which neighbours depends on the training iteration. The destination cell itself is not moved:

xoffset = (iteration >> 1) & 1
yoffset = iteration & 1
row = matrix[(y + yoffset) % height]
col = matrix[(x + xoffset) % width]
dest_cell[x, y] = row · col
the row that is used the column that is used the destination cell [x, y]
The offsets cycle through four states, one per training iteration: the cell's own row and column, the next row, the next column, and both. Rows and columns wrap around at the edges.

The matrices are 10 × 10 in blocks 1–2, 11 × 11 in blocks 3–4 and 13 × 13 in blocks 5–6, which gives 2.27M parameters (transformer: 2.25M). Blocks 3–6 use no offset, and validation uses no offset. One run of 1,000 iterations each, trained one after the other on the same text with the same seed, plus the same model with no offset at all. The first run used a mistyped formula, xoffset = (iteration*2) & 2, which only alternates between two states: no offset, and row y + 1 with column x + 2. It is kept as a second column. A third rule, xoffset = yoffset = iteration & 1, moves the row and the column together: no offset, then row y + 1 with column x + 1. A fourth does the same with a shift of 2: row y + 2 with column x + 2. A fifth cycles through three states: no offset, (x+1, y+1), (x+2, y+2). In the table, a column named (x+a, y+b) is a two-state rule: no offset on even iterations, that offset on odd ones.

iteration(x+2, y+1)(x+2, y+2)four statesthree states(x+1, y+1)no offsettransformer
2004.0784.0904.0934.0874.1224.0654.104
4003.7783.7673.8223.8413.8993.9413.759
6003.4673.4623.4893.4953.5323.7143.503
8003.3113.3093.3243.3403.3583.5323.386
1,0003.2583.2593.2753.2903.3053.4823.350
Validation bits per character, lower is better; one seed each. The curves are in the loss-curve chart above: pick “Square matrix products vs transformer” in the experiment menu.

A staircase instead of straight rows and columns

The offsets above only move the result. This idea changes the product itself. In a matrix product, a destination cell is a sum along its row and along its column, both straight lines. Here both lines become staircases: two cells along, one across, two along, one across. That is a slope of 1 in 2, an angle of 26.565°.

The tilt alternates: +26.5° on even iterations, −26.5° on odd ones. The row path and the column path turn by the same angle, so they stay at right angles to each other. Everything wraps around at the edges.

s = +1 if iteration % 2 == 0 else -1
f(k) = k // 2                      # 2 cells along, 1 across

# through the output cell: the row path is at row y when k == x, the column path at column x when k == y
dest_cell[x, y] = sum over k of
    matrix[(y + s*(f(k) - f(x))) % height][k]  *  matrix[k][(x - s*(f(k) - f(y))) % width]

# from the edge (the first runs): the paths start at row y and column x
dest_cell[x, y] = sum over k of
    matrix[(y + s*f(k)) % height][k]  *  matrix[k][(x - s*f(k)) % width]
the staircase that replaces the row the staircase that replaces the column the destination cell [x, y]
The 10 cells of each path are multiplied pairwise in order (first with first, second with second, and so on) and added. First two grids: each path is the staircase that passes through the output cell. Last two grids: the first runs, where each path starts at the edge of the matrix and so usually misses the output cell. Every cell of the matrix lies on exactly one row staircase and one column staircase.

The first table and findings are for the paths from the edge; the version through the output cell follows below. As with the offsets, the staircase is used in blocks 1–2 and blocks 3–6 keep the plain product. One run, seed 0, 1,000 iterations, same text and same starting weights as the runs above. A second run keeps the tilt fixed at +26.5° on every iteration, to separate the bent paths from the alternation. A third cycles through three states: −26.5°, 0° (straight rows and columns) and +26.5°. A fourth uses a shallower staircase, 3 cells along and 1 across, which is ±18.435°. A fifth keeps that shallower tilt fixed at +18.4°. A sixth is steeper: 3 cells along, then 2 across, which is ±33.69°. Its path skips a cell each time it steps, so it still visits one cell per column.

iteration±33.7°±26.5°±18.4°three statesfixed +26.5°fixed +18.4°best offsetplaintransformer
2004.0944.0884.0894.1074.0924.1084.0784.0654.104
4003.8573.8563.8923.9563.9613.9553.7783.9413.759
6003.5213.5003.5853.6143.6813.6913.4673.7143.503
8003.3543.3373.4083.4393.4913.5133.3113.5323.386
1,0003.3033.2923.3593.3883.4353.4593.2583.4823.350
Validation bits per character, lower is better; one seed each. The first six columns are staircases. ±33.7°: two states, 3 cells along then 2 across (the path skips a cell when it steps, so it still has one cell per column). ±26.5°: two states, +26.5° and −26.5° in turn (2 cells along, 1 across). ±18.4°: the same with 3 cells along per 1 across. Three states: −26.5°, 0° (the straight product) and +26.5° in turn. Fixed: the same tilt on every iteration. Best offset: the two-state rule (x+2, y+1). Plain: the straight product with no offset. The curves are in the loss-curve chart above: pick “Square matrix products vs transformer” in the experiment menu.

Staircases through the output cell

In the runs above each path starts at the edge of the matrix, so it usually does not touch the cell it computes. Here every path is shifted so that it is the staircase of its family that passes through the output cell, as in the first two grids of the diagram above. All six staircase variants were trained again this way: same seed, same starting weights, same text, one run each.

through the output cell from the edge
Hover a row to see its numbers.
Validation bits per character at iteration 1,000; further left is better. Each row is one staircase variant trained twice, differing only in where the paths are placed. Dashed lines: the best offset rule (3.258), the standard transformer (3.350) and the plain product (3.482). The axis starts at 3.24, not 0.
staircase variantfrom the edgethrough the output cellimprovement
two states, ±26.5°3.2923.2700.022
two states, ±33.7°3.3033.2850.018
two states, ±18.4°3.3593.3320.027
three states, −26.5° / 0° / +26.5°3.3883.2620.127
fixed +26.5°3.4353.3690.066
fixed +18.4°3.4593.4020.057
One seed each. For reference: best offset rule 3.258, standard transformer 3.350, plain product 3.482.

Fewer multiplications: a queens mask

The hypothesis: more multiplications did not help, so perhaps fewer do. A regular matrix product multiplies every row with every column. Here only N cells of the N × N matrix take part. This is no longer a matrix multiplication. The question is whether such a cheap operation is good enough as the non-linear step of the layer, the job an activation function normally does.

The N cells are picked as in the eight queens puzzle: one cell in every row and every column, and no two on the same diagonal. A new placement is drawn at random for every layer on every iteration, from a pool of 200 that covers every cell of the matrix, so over time every cell contributes.

a picked cell (one per row and column) a cell that receives a result
One of the 200 placements for the 10 × 10 matrix. Left: the 10 picked cells. Middle: with the mask on both sides, the 10 picked values are multiplied in pairs, which gives 10 results. Right: with the mask on one side, every cell receives one product.

How the picked cells are used was open, so both readings were trained. With p[y] the picked column of row y:

feed-forward (1,000 iterations, seed 0)multiplications for a 10 × 10 matrixval bits/chartime per step
full product, four-state offset (the other offset rules: 3.258, 3.259, 3.290 and 3.305)1,0003.2750.12 s
standard transformer–3.3500.058 s
full product, no offset (the reference here)1,0003.4820.13 s
queens mask on both sides, a new placement every iteration103.7550.095 s
queens mask on one side, a new placement every iteration1003.9080.095 s
queens mask on both sides, the same placement always103.5640.08 s
queens mask on one side, the same placement always1003.5180.08 s
Validation bits per character, lower is better; one seed each, same text and same starting weights. The random-placement runs are evaluated with random placements as well; the fixed ones with their own placement. Each layer's fixed placement is the first of its pool, so the two blocks of a level share one. Times for the fixed runs are with the optimised code described further down. The curves are in the loss-curve chart above.

What the alternating models actually learned

Every model above that alternates between states beats the plain matrix product. The trained models were opened up to find out why. No new training was needed for the three measurements; the fourth item is one control run.

never changes (plain product, fixed staircases) alternates between states the layer removed outright
Hover a point to see which model it is.
Each point is one trained model (1,000 iterations, seed 0). Across: how much the loss rises when the feed-forward of blocks 1–2 is switched off in the trained model, i.e. how much the model relies on it. Up: its final validation loss, lower is better. The less a model relies on those two layers, the better it is.
  1. The alternating models have switched the layer off. In the plain model, switching off the feed-forward of blocks 1–2 costs 0.405 bits/char. In the alternating models it costs between 0.002 and 0.14, and the layer's output is two to four times smaller. The offsets and staircases were applied to exactly those two blocks. A layer whose output keeps being rearranged is one the rest of the model learns not to depend on.
  2. Removing the layer outright is better than any of them: 3.220. One control run with no feed-forward at all in blocks 1–2 (same seed, same starting weights, same text) beats the best offset rule (3.258), the best staircase (3.262) and the transformer (3.350). It is the best 1,000-iteration model on this page. Each block has two halves, attention and feed-forward. Only the feed-forward half of blocks 1 and 2 is removed. The model still has six blocks and six attention layers, but four feed-forward layers instead of six, and 17% fewer parameters in use (1.89M against 2.27M; the transformer has 2.25M). So it is a smaller model that does better, not a like-for-like swap.
  3. The transformer does not gain from the same removal: 3.378. The standard transformer with the feed-forward half of its blocks 1 and 2 removed (same seed, same starting weights, 1.70M parameters in use instead of 2.25M) ends 0.028 behind the full transformer (3.350). So this is not a general rule that early feed-forward layers are useless. An ordinary feed-forward layer in the first two blocks does a little good; a matrix-product layer there does harm.
    feed-forward in blocks 1–2matrix-product modelstandard transformer
    present3.4823.350
    removed3.2203.378
    effect of removing it0.262 better0.028 worse
    With the two layers removed from both, the matrix-product model is 0.158 ahead of the transformer (3.220 against 3.378) at a similar size (1.89M against 1.70M parameters). The two models differ in more than the feed-forward, though: the matrix-product model also has the pyramid, its bottlenecks and a different attention.
  4. So the offsets and staircases helped by taking something away, not by adding something. In this model the matrix-product feed-forward in the first two blocks does harm, and each alternating rule is a roundabout way of disabling it. Across the 19 models the link between "relies on the layer" and "final loss" is strong (correlation 0.90).
  5. The gain is in learning, not in generalising. The better models also have a lower loss on the training text, by a wide margin:
    modelloss on training textloss on held-out text
    plain matrix product3.1503.482
    standard transformer3.0123.350
    offset (x+2, y+1)2.8583.258
    staircase through cell, three states2.8563.262
    feed-forward removed from blocks 1–22.7853.220
    A regulariser would raise the training loss and lower the held-out loss. Here both fall together, so the alternating models simply learn faster. (In 1,000 iterations a model sees less than half of the training text, so "training text" here is mostly unseen too; the held-out text comes from different documents.)
  6. The models became tolerant of shifts they never saw. Shifting the result of blocks 1–2 by any offset costs the plain model 0.67 to 0.90. A model trained on two states loses about 0.10 on shifts it never saw, and the four-state model only 0.02 to 0.03:
    modela shift it was trained onshifts it never saw
    plain product (trained on none)–+0.67 to +0.90
    offset (x+2, y+1), two states+0.003+0.09 to +0.10
    offset (x+1, y+1), two states−0.001+0.05 to +0.16
    offset, three states+0.004+0.02 to +0.10
    offset, four states+0.002 to +0.006+0.02 to +0.03
    This is the same finding seen from another side: a model that hardly uses the layer does not care where its results land.
  7. It also explains the queens mask. There the mask changed on every iteration in all six blocks, and the models learned to ignore every feed-forward: switching off blocks 1–2 changes nothing, and the outputs of the later layers are four to ten times smaller than in the other models. But blocks 3–6 are layers the model needs (switching them off costs 1.0 to 1.6 in the other models). Disabling the first two helps; disabling all six is why the queens models are so far behind.

What this means for the ideas above.

Letting the model decide: a gain that starts at zero

Instead of removing the feed-forward of blocks 1–2, or disturbing it with an offset, the layer's output is multiplied by one learnable number per layer, a gain, that starts at zero. The layer begins switched off, and training turns it up only as far as it helps. This is the ReZero idea from the published work below. Nine runs, seed 0, 1,000 iterations, same text. The staircase is the two-state one, ±26.5° in turn, with its paths through the output cell. The triangle is the wrapped triangle layer of section 4, sweeping all three directions on every iteration (3 of 3) or two of the three in rotation (2 of 3).

modelblocks 1–2 feed-forwardblocks 3–6 feed-forwardval bits/chargains learned
standard transformerqueens-mask product × gainstandard3.417+0.005, +0.004
standard transformeralternating staircase × gainstandard3.408−0.045, +0.049
standard transformertriangle, 2 of 3 sweeps × gainstandard3.389−0.093, +0.081
standard transformertriangle, 3 of 3 sweeps × gainstandard3.385−0.109, +0.102
pyramidtriangle, 3 of 3 sweeps × gainfull matrix product3.212+0.036, +0.054
pyramidtriangle, 2 of 3 sweeps × gainfull matrix product3.212+0.020, +0.026
pyramidqueens-mask product × gainfull matrix product3.205−0.001, +0.001
pyramidalternating staircase × gainfull matrix product3.193+0.011, +0.013
pyramidfull matrix product × gainfull matrix product3.192−0.032, −0.036
For reference: standard transformer 3.350, the same without the feed-forward of blocks 1–2 3.378; pyramid with the layer removed 3.220, with the layer at full strength 3.482. A gain of 1 would be full strength; the sign of a gain does not matter. One seed each.

A local product: only the nearby cells

The best model so far has no feed-forward half in blocks 1–2 (their attention half stays) and plain matrix products in blocks 3–6. In a matrix product, every output cell multiplies its whole row with its whole column. Here the cell only uses the cells next to it: about N/3 of them along its row and N/3 along its column, centred on the cell and wrapping around at the edges. That makes it similar to a small cross-shaped convolution.

h = half the window          # window = 2h + 1 ≈ N/3 cells: 3 for N = 10 and 11, 5 for N = 13
dest_cell[x, y] = sum over d in -h..h of
    matrix[y][(x + d) % width]  *  matrix[(y + d) % height][x]
cells used from the row cells used from the column the destination cell [x, y]
First grid: the full product uses the whole row and column of the output cell (10 products per cell). Second: the local product uses the 3 nearest cells of each, including the cell itself (3 products per cell). Third: the local product in the shifted state of an offset rule, with the two arms moved separately. For a 10 × 10 matrix that is 300 multiplications instead of 1,000. Unlike the full product, the local one behaves like a convolution: shifting the matrix shifts the result by the same amount.

Six runs, each the local version of a model above: same seed, same starting weights, same text, 1,000 iterations. With the full product, an offset can be read two ways that come to the same thing. With the local product they differ, so both were run: result shifted moves the whole cross to another cell; arms moved follows the original rule literally, taking the row arm from row y + yoffset and the column arm from column x + xoffset while the output cell stays put (the third grid above).

what is in blocks 1–2local productfull productlocal: cost of switching the layer off afterwards
no feed-forward (the best model)3.2553.220–
four-state offset, arms moved3.3853.275+0.011
four-state offset, result shifted3.3993.275+0.037
two-state offset (x+2, y+1), arms moved3.4223.258+0.021
no offset3.4613.482+0.499
two-state offset (x+2, y+1), result shifted3.4953.258+0.089
Validation bits per character at iteration 1,000, lower is better; one seed each. Blocks 3–6 use the same kind of product as blocks 1–2, with no offset. Standard transformer: 3.350. A step takes 0.09–0.11 s with the local product against 0.11–0.13 s with the full one.

Few bits: how little precision do these models need?

A model stored and run with very few bits per number is smaller and can run without floating-point hardware. Three measurements on the 1,000-iteration models above, one seed each. The matrix-product feed-forward has a head start: it only multiplies and adds, so on whole numbers it is exact. A standard feed-forward layer needs an activation function (GELU), which is a floating-point function.

1. Rounding the weights after training. Every weight matrix of the trained model is rounded to b bits, with no retraining.

standard transformer matrix product, no feed-forward in blocks 1–2 local product, no feed-forward in blocks 1–2
Hover a point to see its value.
Rise in validation bits per character when all weight matrices are rounded to b bits after training; lower is better. At 2 bits (not shown) every model breaks, rising by 0.6 to 0.7.

2. Training with few-bit weights. Models that really run at one or two bits are trained for it: the weights are rounded in every forward pass during training, as in BitNet. "Three values" means every weight is −1, 0 or +1 times one scale per matrix; "one bit" means ±1.

full-precision weights three values (−1, 0, +1) one bit (±1)
Hover a row to see its numbers.
Validation bits per character at iteration 1,000; further left is better. The dashed line is the full-precision transformer (3.350). The local product was not trained with one-bit weights. The axis starts at 3.15, not 0.
modelfull precisionthree valuesone bitcost of few bits
standard transformer3.3503.6123.5900.24 to 0.26
matrix product, no feed-forward in blocks 1–23.2203.3643.3610.14
local product, no feed-forward in blocks 1–23.2553.348not run0.09

3. Rounding the numbers that flow through the model. The input to every attention, feed-forward and output layer is rounded to b bits at evaluation. Every model behaves the same, including the few-bit-weight ones:

bits per value865432
rise in loss+0.001+0.002+0.005+0.02+0.10 to +0.13+1.0 to +1.8

Robustness to disruptions

A hypothesis: the triangle multiplies three things at a time (a weight and two values), so it may be more robust to disruptions than an ordinary layer. Tested on the trained 1,000-iteration models, with no retraining, by disturbing them in five ways and measuring how much the loss rises:

triangle, no wrap (loss 3.351) matrix product, blocks 1–2 removed (loss 3.216) standard transformer (loss 3.350)
Hover a bar to see its numbers.
The rise in loss caused by each disruption, as a fraction of the rise the standard transformer suffers from the same disruption. Left of the dashed line means more robust than the transformer. One trained model each, seed 0.
modellossweight noisevalue noisevalues dropped3 bits2 bits
standard transformer3.350+0.086+0.387+0.397+0.141+0.718
triangle, no wrap3.351+0.065+0.348+0.348+0.075+0.657
triangle, wrap3.407+0.075+0.366+0.366+0.075+0.598
matrix product, every block3.482+0.060+0.313+0.309+0.067+0.464
matrix product, blocks 1–2 removed3.216+0.081+0.396+0.401+0.075+0.677
Rise in validation bits per character; lower is more robust. Milder versions of each disruption (5% and 10% weight noise, value noise of 0.1 and 0.2, 10% dropped, 4 bits) give the same order. One seed each.

All 1,000-step models side by side

Every full model on this page that was trained for 1,000 iterations (55 of them), shown as its difference from the standard transformer. Bars to the left are better than the transformer, bars to the right are worse.

The runs come from three batches trained on slightly different versions of the text, so the raw losses cannot be compared across batches. Each model is therefore compared with the transformer trained in its own batch, on the same text.

better than the standard transformer worse than the standard transformer one run too close to call (±0.05)
Hover a row to see its numbers.
Difference in validation bits per character at iteration 1,000 (model minus transformer); negative is better. A bar is the mean of the model's runs, a dot is one run. The matrix-product, staircase and queens models have one run each (seed 0); the triangle and hexagon models have two or three, the plain triangle five, and the triangle with a window one. At this run length, runs of the same model have differed by 0.05 to 0.19, so differences inside the shaded band are too small to trust. The 1,000-step ranking of the triangles also changed when they were trained for 4,000 steps.

Where the matrix product helps, and where it hurts

Two questions were still open: which part of the pyramid model gives it its lead over the transformer, and whether the blocks 1–2 layer is unwanted because it squeezes 938 features into a 10 × 10 matrix. Three control runs answer both. Same seed, same starting weights, same text, 1,000 iterations.

blocks 1–2 feed-forwardblocks 3–6 feed-forwardval bits/charwhat it tests
nonematrix product3.220the reference (the removed-layer model)
noneordinary layer with an activation function, same size3.321is the matrix product in blocks 3–6 doing anything?
10 × 10 matrix product, full strengthmatrix product3.482the earlier result
30 × 30 matrix product, full strengthmatrix product3.547is the squeeze to blame?
30 × 30 matrix product × gainmatrix product3.221does the model want it when it is wide? (gains +0.011, −0.019)
One seed each. The standard transformer is at 3.350. The "ordinary layer" uses the same two projections as the matrix-product layer, with a GELU activation in place of the product, so the two models start from identical weights. A 30 × 30 matrix has 900 values for 938 features, so nothing is squeezed; that model has 5.27M parameters.

The triangle with a local window

So far the triangle layer came in two forms. Without wrap, the line at index i holds n − i weights, so outputs collect anything from 1 to n products. With wrap, every line holds n weights. A third form keeps the wrap but shortens every line: each output collects only the weights in a window of n/2 + 1 cells centred on its own index, wrapping around at the ends. For the side-50 triangle that is 26 weights per output instead of 50.

weights that some output of this sweep collects the weights collected by one output (index 3)
The weights of a side-12 triangle laid out as a 12 × 12 grid, for the 0° sweep: each row is one output. Without wrap the rows shrink from 12 weights to 1. With wrap every row has 12. With the window every row has 7 (12/2 + 1), centred on the row's own index and wrapping round the edge. The other two sweeps use the same rule along their own direction. The window and the no-wrap triangle do the same number of products; the wrapped one does twice as many.

All three were trained as the pyramid model with the triangle layer in every block: 1,000 iterations, seed 0, the same text as the standard transformer run.

iterationtriangle, no wraptriangle, wraptriangle, window n/2 + 1standard transformer
2004.1054.1014.1024.104
4003.9003.9013.9183.759
6003.5683.6253.6403.503
8003.3993.4583.4793.386
1,0003.3513.4073.4313.350
Validation bits per character, lower is better; one seed each. Products per token in the triangle layers: 42,450 without wrap, 83,700 with wrap, 42,600 with the window.
no wrapwrapwindowtransformer
loss after 1,000 iterations3.3513.4073.4313.350
against the transformerlevel0.057 behind0.080 behind–
products collected per output1 to 505026–
time per iteration (4 threads)0.30 s0.48 s0.40 sabout 0.05 s
loss rise when blocks 1–2 feed-forward is switched off+0.237+0.287+0.241–
"Products collected per output" is for the side-50 triangle of blocks 1–2. One seed each.

Three of three, at uneven rotating strengths

Skipping one of the triangle's three sweeps per iteration made the model worse. A gentler version keeps all three sweeps on every iteration but gives them uneven strengths that rotate:

contributions = [3/6, 2/6, 1/6]
a = contributions[i % 3]            # i = iteration
b = contributions[(i + 1) % 3]
c = contributions[(i + 2) % 3]

The three are scaled by 3 so that they average 1 and the layer's output keeps its size: the sweeps run at strengths 1.5, 1.0 and 0.5, and the roles rotate every iteration. On the same scale, "2 of 3" is 1.5, 1.5, 0 and plain "3 of 3" is 1, 1, 1. Evaluation always uses all three sweeps at equal strength. The triangle layer is in every block; 1,000 iterations, seed 0, same text.

triangle, wrap triangle, no wrap
Hover a point to see its value.
Validation bits per character at iteration 1,000; lower is better. From left to right the sweep strengths get more uneven. The dashed line is the standard transformer (3.350). One seed each.
sweep strengths during trainingtriangle, wraptriangle, no wrap
equal: 1, 1, 1 (3 of 3)3.4073.351
uneven, rotating: 1.5, 1.0, 0.53.448 (+0.041)3.454 (+0.103)
one skipped, rotating: 1.5, 1.5, 0 (2 of 3)3.509 (+0.102)3.529 (+0.178)
In brackets: the rise against equal strengths. Standard transformer: 3.350.

Compared with the standard transformer

The best models of this section are the pyramid with matrix products in blocks 3–6 and a blocks 1–2 layer that is removed, or switched almost off by a gain ("the pyramid" below). They lead the standard transformer after 1,000 iterations. A lead per iteration is only half the story, because the two models do not take the same time per iteration.

How fast is each model? That depends on what is measured. The first version of this comparison used wall-clock time, which measures the code as much as the model, so three things are reported here: the arithmetic each model needs, which does not depend on the code; the time per iteration after optimising the code; and the loss at equal training time.

standard transformerpyramid, layer removedpyramid, layer with gain
multiplications per token2.31M1.77M2.15M
… of which in the matrix products–7,0569,056
values each token carries through the model184938, 762, 592938, 762, 592
time per iteration, CPU (2 threads)50 ms80 ms94 ms
time per iteration, GPU, batch of 1619 ms24 ms29 ms
time per iteration, GPU, batch of 25688 ms291 ms332 ms
parameters in use2.25M1.89M2.27M
Times are for one training iteration (forward, backward and update) on this machine, an Apple-silicon Mac, with the optimised code. The batch of 16 is the one used in every run on this page.

Loss at equal iterations and at equal time. Both models were also trained for 2,000 iterations, and the pyramid for 1,250, which takes it as long as the transformer needs for 2,000. Same seed and text.

standard transformer pyramid, matrix product, blocks 1–2 layer removed the shorter run the 2,000-iteration run
Hover a point to see its numbers.
Validation bits per character against training time on the CPU (iterations × time per iteration, 50 ms and 80 ms); lower is better. Each run has its own learning-rate schedule, so a shorter run is not the first part of a longer one. One seed each.
standard transformerpyramid, layer removedpyramid, layer with gain
loss after 1,000 iterations3.3503.2163.192
loss after 2,000 iterations2.9782.9432.907
loss after about 100 seconds on the CPU2.978 (2,000 iterations)3.101 (1,250 iterations)about 3.19 (1,000 iterations)
loss with one-bit weights (1,000 iterations)3.5903.361not run
loss added by rounding weights to 3 bits+0.141+0.075not run
One seed each. The removed-layer runs in the first three rows use the optimised code, which changes the rounding order; that moved its 1,000-iteration loss from 3.220 to 3.216. Earlier on this page, at 4,000 steps with 3 seeds, the transformer beat every pyramid variant that was tested then; these models have not been run at that length.

Takeaways

The common thread: the rectangle is the attractor. A matrix is a rectangle because a linear map pairs every input with every output. Shapes drawn inside that rectangle drift back towards rectangles when you multiply. Every centred shape becomes its bounding box within 2–3 products. Other shapes stay themselves only when the multiplication changes. Convolution adds shapes instead of composing them, and keeps every convex polygon. And when the polygon describes how matrices are wired together (a ring), what matters is not the shape but its parity.

Surprises

  1. A pentagon can be a stable matrix shape, a hexagon cannot. The more symmetric shape loses. A stable shape is a box cut by at most one diagonal line, and a rectangle cut by one line has at most 5 corners.
  2. Geometry gives the exact cost. Multiplying two P-shaped arrays needs exactly 4|P| − B − 3 multiplications, by Pick's theorem. So the number of lattice points on the edge sets the cost. That's why pentagons, octagons and disks, which can't be regular on a lattice, are about 4% more expensive. The crystallographic restriction shows up as a price.
  3. Odd versus even decides everything for rings. Square, hexagon and octagon rings are incompressible: every fused algorithm needs all 2^k terms. Triangle, pentagon and heptagon rings are compressible. Strassen's speed-up is, in this light, an odd-cycle phenomenon.
  4. Hexagons are made of triangles: a triangular array correlated with its mirror image gives a hexagonal array.
  5. 2D is special. A doubled lattice polygon never has gaps in 2D, but a doubled tetrahedron can (Reeve), so 3D shaped arrays are less tidy.
  6. The natural symmetric generalisation of Strassen doesn't simply appear for the pentagon. A rotation-symmetric 31-term fit froze at error 0.0003. The only lead below 31 is an unconfirmed 30-term approximate decomposition.
  7. The three-way weights tolerate coarse rounding, the projections don't. The cube's 49k three-way weights at 2 bits hurt less than one 33k MLP matrix at 2 bits. The sensitive part is the input projection, whose errors are multiplied together.
  8. Without wrap-around, a hexagon's centre learns about 30× faster than its corners, because more products land there. Wrap-around makes learning uniform.
  9. Hexagonal blurs are exactly round up to 4th order at every step; square blurs only become round slowly.
  10. In a whole model, the triangle beats the hexagon by a wide margin. A pyramid built on hexagonal feature cells with 3 → 1 bottlenecks came close to a standard transformer once its feed-forward was the triangle layer (the wrapped triangle is 0.013 bits/char behind at 4,000 steps, within seed noise), and stayed 0.2 behind with the hexagon's local mixing. The pyramid was never the problem.
  11. The "centre" means different things in the two shapes. In the triangle the long lines are signal (lowering them hurts, raising them is free); in the hexagon the centre-heavy sums were neither harmful nor needed (1/√ balancing changes nothing).
  12. A centre-heavy hexagonal layer learns an importance order without being asked. In a language model, trimming its outer rings hurts 2–3× less than trimming at random. Meanwhile training partly fights the non-uniformity, growing the edge weights to make up for weaker edge activations.
  13. Uniform wins in both shapes. Given the choice between a centre-heavy and a uniform (wrapped) layout, the uniform one won for the hexagonal layer and, in 4,000-step runs, for the triangle feed-forward (2.739 against 2.778 bits/char). No reweighting of the centre and edges beat it.
  14. The shape's own weights are about 1% of the model, yet changing how they are wired (wrap or not) moved the loss more than any centre/edge weighting. The wiring matters more than the weights.
  15. All three sweep directions are needed. Training the triangle with only two of its three sweeps per step, rotating which one is skipped, cost 0.10–0.13 bits/char and saved only about 15% of the step time. Each direction carries information the other two do not.
  16. More directions are not automatically better. A wrapped square swept along rows, columns and both diagonals, at the same parameter count, ended 0.028 bits/char behind the transformer and 0.015 behind the 3-direction wrapped triangle. Dropping a direction hurts, but adding one does not help: the trained square uses all four directions equally, yet each one matters less than in the triangle (0.23–0.25 against 0.40 when switched off). The extra direction divides the work instead of adding to it.
  17. The best trick turned out to be a subtraction. Rotating offsets and staircase paths made a matrix-product model beat the transformer at 1,000 steps. Opening the trained models showed why: they had learned to ignore the layer the tricks were applied to. Removing it outright did better than every trick (3.220 against 3.258, transformer 3.350; one seed). What is removed is the feed-forward half of blocks 1 and 2. Their attention half stays, so the model keeps its six blocks but has four feed-forward layers instead of six, and 17% fewer parameters. The standard transformer does not gain from the same removal (3.378 against 3.350), so the harm is specific to the matrix-product layer.
  18. A model with one-bit weights matched the full-precision transformer. Trained with every weight restricted to ±1, the matrix-product model scores 3.361 against 3.350 for the transformer with 32-bit weights, and the transformer loses twice as much when it is restricted the same way (one seed, 1,000 steps).
  19. Ahead per iteration, behind per second. The best pyramid model beats the standard transformer by 0.16 after 1,000 iterations and 0.07 after 2,000. It needs fewer multiplications per token, yet takes 1.6 times as long per iteration even after optimising the code, because each token carries a vector 3 to 5 times wider. In the same 100 seconds the transformer ends 0.12 better.
  20. Exact search hits a wall quickly. A SAT solver settles the triangle instantly. But it couldn't prove within 15 minutes that the square ring needs more than 15 products, a fact that a one-line flattening argument proves.

The language-model experiments: what stood out

Section 6 grew long. These are its main results, most surprising first. Each says how far it can be trusted: solid means 4,000 steps with 3 seeds; one seed means a single 1,000-step run, usually paired with a control that started from the same weights.

  1. The tricks worked by switching a layer off. Rotating offsets and staircase paths made a matrix-product model beat the transformer. The trained models turned out to ignore the layer the tricks were applied to, and removing that layer did better than any trick (3.220 against 3.258). One seed.
  2. The same layer helps late and hurts early. A matrix product as the feed-forward layer is worth 0.10 in blocks 3–6 against an ordinary layer of the same size, and costs 0.26 in blocks 1–2. Making the early layer wider makes it worse, so it is not a matter of size. One seed.
  3. Asked directly, the model says no. With a learnable gain that starts at zero, the model leaves every blocks 1–2 layer nearly off: queens mask 0.005, staircase 0.01 to 0.05, matrix product 0.03, triangle 0.02 to 0.11. That held at 2,000 steps and for a wide matrix. One seed each.
  4. A model with one-bit weights matched the full-precision transformer. The matrix-product model with every weight restricted to ±1 scores 3.361 against 3.350, and loses half as much as the transformer does under the same restriction. Its feed-forward needs no activation function, so on whole numbers it is exact. One seed.
  5. Ahead per iteration, behind per second. The best pyramid model leads the transformer by 0.16 after 1,000 iterations and 0.07 after 2,000. It needs fewer multiplications per token (1.77M against 2.31M), but each token carries a vector 3 to 5 times wider, and that makes an iteration 1.6 times as long even after optimising the code. In the same 100 seconds the transformer ends 0.12 better. One seed.
  6. A fixed tenth of the multiplications costs almost nothing; a random tenth costs everything. A local product that uses only the cells near the output cell matched the full product, and so did a queens mask kept the same on every iteration (0.036 behind with a tenth of the products). The same queens mask redrawn at random every iteration was the worst of the 1,000-step models. What matters is that the layer computes the same thing every time, not how much it computes. One seed.
  7. Ending up ignoring a layer is not the same as never having it. With the local product, offset models switched the layer off and still finished 0.13 to 0.17 behind the model without it. One seed.
  8. How much a disturbed layer hurts depends on how loud it is and how big the disturbance is. Skipping one of the triangle's three sweeps cost 0.10 to 0.13 at full strength in every block (solid), and nothing at all behind a gain of a few percent (one seed). Running all three at uneven rotating strengths cost about half as much as skipping one (one seed).
  9. Uniform beats centred. For the triangle and the hexagon, the wrapped layout in which every output collects the same number of products came out best. No reweighting of centre against edge beat it. Solid for the triangle; the hexagon was tested only in a tiny model. At 1,000 steps the picture is the reverse: an evenly loaded triangle with a local window was 0.08 behind the uneven no-wrap triangle doing the same number of products (one seed). Uneven layers start faster; even ones caught up later.
  10. More directions did not help. A square swept along rows, columns and both diagonals ended behind the transformer and no better than the three-direction triangle. The fourth direction was fully used, but it divided the work instead of adding to it. Solid.
  11. Short runs mislead, and the noise is now measured. Rankings from 1,000 steps and 2 seeds were wrong twice at 4,000 steps. Models that are identical in effect differ by 0.015 to 0.04 between single runs, so a gap below about 0.05 means nothing.
  12. What survived the longest test. At 4,000 steps with 3 seeds the standard transformer beat every shaped layer that was run at that length. The best models of the later experiments have not been run that long.

Where this is useful

How new is this?

Most of it is known in some field: triangular matrices, receptive fields, Newton polytopes and Minkowski sums, Pick's theorem, hexagonal sampling, and Christandl–Zuiddam's cycle tensors. What this page adds is the side-by-side view, the classification of stable matrix shapes (at most 5 corners), the closed cost formula 4|P| − B − 3 for shaped convolution, and the unconfirmed hint that the pentagon ring might be approximated with 30 terms. Also new are the small experiments: shaped weight layers swept three or four ways; the finding that their three-way weights are robust to low precision while the input projection is not; the hexagonal wrap / no-wrap learning-speed contrast; the emergent importance order of a centre-heavy hexagonal layer; and a hexagonal pyramid language model whose wrapped-triangle feed-forward lands within seed noise of a parameter-matched transformer. The isotropy of hexagonal blurs and the power of random long-range links (BigBird, small-world graphs) are known.

How the language-model findings compare with published work

On raw performance there is no comparison. Published transformers reach just under 1 bit per character on the standard enwik8 benchmark (0.97 for the Compressive Transformer with 277M parameters, 0.98 for the Hourglass model). The models in section 6 have about 2M parameters, train for 1,000 to 4,000 steps on 2.5 MB of text and score 2.7 to 3.5. Nothing here competes with the state of the art. The findings are about how small models behave relative to each other.

finding on this pagepublished workhow they relate
Feed-forward layers that multiply features (triangle, cube, square, matrix product) are about as good as a standard layer, not better. Gated feed-forward layers such as SwiGLU multiply two projections and give modest quality gains (Shazeer 2020). Bilinear layers with no activation function are reported as competitive (Pearce et al. 2024). Agrees. The best shaped layer was 0.013 behind the transformer after 4,000 steps.
Rotating offsets and staircases helped because they taught the model to do without a layer. Stochastic depth drops whole layers at random during training (Huang et al. 2016). LayerDrop does the same in transformers and lets layers be pruned afterwards (Fan et al. 2019). Same idea, reached by accident. The offsets rearrange a layer's output instead of dropping it.
The transformer loses only 0.028 without the feed-forward half of its first two blocks. Feed-forward layers are highly redundant: removing them from the decoder and sharing one across the encoder costs little in translation (Pires et al. 2023). Large language models keep most of their performance with many layers removed, after a little retraining (Gromov et al. 2024). Agrees in spirit. Those results are for larger models, and the second removes deeper layers, not early ones.
A local product, using only the cells near the output cell, is about as good as the full row and column. Locality is the premise of convolutions. Criss-cross attention lets each position attend only to its own row and column to save computation (Huang et al. 2018). Agrees. The full product's cross is the criss-cross path; the local product shortens its arms.
Short runs with few seeds ranked the variants wrongly, twice. Standard practice is to report several seeds and train to convergence. Agrees.

What is less standard. These four do not appear in the papers above. A wider literature search was not done, and each rests on one seed and 1,000 iterations.

A mundane explanation, tested and ruled out. In blocks 1–2 the matrix-product layer squeezes 938 features into a 10 × 10 matrix, which is 100 values, about a ninth of the width. The harm could have come from that squeeze. It does not: a 30 × 30 matrix, which squeezes nothing, is just as unwanted and does more harm at full strength (section 6, “Where the matrix product helps, and where it hurts”).

What current practice would suggest trying. Give the layer a learnable gain that starts at zero, so the model switches it on only if it helps. That is ReZero (Bachlechner et al. 2020); LayerScale is the per-channel version (Touvron et al. 2021). It is the principled form of what the offsets did by accident. It was run in “Letting the model decide” in section 6: the gains stay near zero (about 0.03 for the full matrix product, 0.001 to 0.005 for the queens mask), so the model keeps the layer almost off.

Pros and cons by shape

shapeas a matrix patternas a convolution arrayas a ring of matrices
Triangle + closed, N³/6 per product, the basis of LU/Cholesky and triangular solves. − upper × lower is dense; only half the linear maps. + natural for total-degree polynomials (Taylor series), lattice-regular, cheapest per coefficient at equal size (rank/|P| 3.60). − an FFT on a box wastes about half the space. + this is ordinary matrix multiplication; Strassen's 7. − the exact rank for N ≥ 3 is a famous open problem.
As weights swept three ways: + 6× weight reuse; beats an MLP. − behind SwiGLU; three sweeps no better than one per weight.
Square + all linear maps; closed. − nothing to exploit. + separable, FFT-native, all the tooling. − anisotropic: corners waste coefficients for round problems. − rank exactly 2^k (N⁴ in general): no fused shortcut.
Pentagon + the special pentagon "upper triangle ∩ row/column window" is closed. − regular or centred pentagons spread into rectangles. + P + P is again a pentagon, with no gaps. − cannot be regular on a lattice; few boundary points make it about 4% more expensive; no FFT. + odd, so it has shortcuts: 31 instead of 32. − the true optimum (25 to 31) is unknown.
Hexagon − band matrices are hexagons and grow with every product; never stable. + regular on the hex lattice, isotropic, needs 13.4% fewer samples for round band-limited signals; hex FFTs exist; triangle ⊕ reflected triangle. − less tooling than squares. − even ring: rank exactly 2^k, no shortcut.
Octagon, n-gon, disk − centred shapes become rectangles after 1–2 products. + closest to isotropic. − not lattice-regular; fewest boundary points, so most expensive per coefficient. odd n: shortcuts of 2^n − 1 or better; even n: none.

So can square matrices be replaced by n-gons?

What is proven, what is numerical

Boxes with overlapping ranges, cut by x ≤ y or not, are closed and stable under multiplicationproof
No other convex shape is stable (so at most 5 corners)random search
Convolution cost of P-shaped arrays is exactly |P+Q| = 4|P| − B − 3 (2D lattice polygons)proof + tests
2D lattice polygons have no gaps in P + P; 3D can (Reeve)known theorem + check
Even rings have rank 2^k; odd rings ≤ 2^k − 1 (Christandl–Zuiddam), rebuilt and verified hereproof + tests
Pentagon ring of 2×2 matrices needs 31 productsopen (25–31)
Pentagon ring border rank ≤ 30 (rotation-symmetric fit, error falling as coefficients grow)unconfirmed hint
Three sweeps of a triangle of weights equal the gradient of the cubic energy Σ W·hihjhkproof + tests
Triangle and cube feed-forward beat an MLP, trail SwiGLU (tiny LM, 1,500 steps); tetrahedron undecidedmeasured, small
Cube and tetrahedron sweeps equal the gradients of cubic and quartic energies; 6 and 12 multiplications per weight readproof + tests
Three-way weights robust to 2–3-bit rounding, input projection sensitive; mixed-precision cube beats the gated block at ≈ 200k stored bitsmeasured, small
Hexagon product: wrap-around gives every output |P| multiplications; without it the centre gets |P| and corners 1proof + tests
Without wrap-around the centre outputs learn faster (NTK diagonal 41 vs 2 at radius 4)tests
Hexagonal blur exactly isotropic in 4th moments; square blur notsymmetry + tests
Ring 1 + random far links connect a radius-8 hex map in 5 layers (one random draw)measured
A hexagon of radius R on a torus is the cyclic group Z3R²+3R+1 (hexagonal convolution = 1-D FFT)proof + tests
Hexagonal feed-forward layers are competitive with gated and bilinear blocks; the centre-heavy one orders its outputs by importance; ring-wise precision gives no gainmeasured, small
The up-triangles on q − r ≡ 0 (mod 3) tile the hexagonal grid exactly and their base points form a coarser hexagonal gridproof + tests
Hexagon-pyramid model: hexagonal feed-forward 0.2–0.35 bits/char behind a standard transformer; triangle feed-forward at 4,000 steps (3 seeds): wrapped 0.013 behind (within noise), unwrapped 0.044–0.091 behind; wrap beats no wrap and lowering the centre hurts (ranges do not overlap); two of three sweeps per step 0.10–0.13 worse; a square with 4 sweep directions 0.028 behind. A matrix-product feed-forward with a rotating row/column offset is 0.045–0.093 ahead of the transformer at 1,000 steps (five offset rules), and 0.18–0.22 ahead of the same layer without the offset; a staircase version of the product (paths tilted ±26.5°, starting at the edge) reaches 3.292 when the tilt alternates between two states (3.359 with a shallower ±18.4° tilt, 3.303 with a steeper ±33.7° one), 3.388 with three states and 3.435–3.459 when the tilt is fixed; with the paths through the output cell all six staircase variants improve (by 0.018 to 0.127; three states 3.262, ±26.5° 3.270); a sparse queens-mask product is 0.27–0.42 worse than the full product. The alternating models rely far less on the feed-forward of blocks 1–2 (ablation cost 0.002–0.14 against 0.405), and removing that layer outright gives 3.220, the best 1,000-step model, while the same removal in the standard transformer makes it 0.028 worse (3.378). With a learnable gain that starts at zero the model keeps the layer almost off (gain 0.03) and reaches 3.192. After 2,000 steps that model is 0.071 ahead of the transformer (2.907 against 2.978), but it is 1.6 to 1.9 times slower per step, and at equal training time the transformer is 0.12 ahead. A local product (each cell uses only the N/3 nearest cells of its row and column) is within 0.04 of the full product at a third of the multiplications. Trained with one-bit or three-valued weights the matrix-product models lose 0.09–0.14 against 0.24–0.26 for the transformer, and with one-bit weights match the full-precision transformer (all one seed each)measured, small
Lowering the centre: neutral at 1/√ for the hexagon, harmful for the triangle; raising it (triangle) free or slightly helpful (differences ≤ 0.03, within seed noise)measured, noisy