Part IV · Cost, Information, and the Limits of Grammar
Chapter 13
Compiling Operations: When Grammar Becomes Arithmetic
Proved here The main new result of the current edition — a constant-size normal form for move words in heap-shaped trees, with a matching impossibility result for arbitrary shapes. Novelty not yet verified against the literature.
- Define arithmetically compilable structures and explain what compilation buys: one-time cost proportional to the word, then constant cost per application.
- Prove that every move word over in a heap-shaped binary tree compiles to a map defined exactly on an interval.
- Prove that no constant-size compiled form exists for arbitrary tree shapes, by a counting argument.
- Recognize compilable structures beyond trees: de Bruijn graphs, hypercubes, grids.
- Read the experimental evidence, including the workload under which compilation barely helps.
13.1 The idea
Chapter 3 made an observation about lists: a navigation word such as is determined by three numbers — its net displacement and its minimum and maximum prefix displacements — however long it is. If a representation could jump by a computed displacement, a word of a million moves could be compiled once into those three numbers and then applied in constant time. A linked list cannot jump; an array can.
This is the founding question of the book in its sharpest form. A move word is a sentence of the structure’s operational grammar. Can the sentence itself be turned into machinery — a single cheap operation equivalent to the whole sentence — and on which structures?
A deterministic operational grammar is arithmetically compilable on a class of structures if there are a numeration of elements and a class of partial maps on numerals such that
- every move acts on numerals as some ;
- is closed under composition, including conjunction of domains;
- every is stored in words, and composing, applying and testing the domain of members of cost word operations.
Then every move word compiles in word operations into one element of , which is applied afterwards in , independently of .
The definition is a cost-oriented specialization of an old idea: the meanings of all words form the transition monoid of the structure (CGT-DEF-007), and compilation asks for a representation of that monoid with constant-size elements and constant-time operations. Automatic structures [Blumensath & Grädel 2000; Khoussainov & Nerode 1995] and automatic groups [Epstein et al. 1992] ask a related question — whether elements and moves are recognized by finite automata — but not this one: there, applying a move to a word-encoded element costs time linear in the element’s encoding.
13.2 The heap-shaped tree
Let be the binary tree whose nodes are exactly the numerals (the shape of a binary heap with elements), with moves
A word is defined at if every numeral visited after each step lies in . Evaluating step by step costs (or less, when it falls off the tree early).
For every word and every there are integers , and , computable from with arithmetic operations, such that for every :
With early termination (stop as soon as the domain is empty), all intermediate integers have bits; for compilation costs word operations and each application costs .
Normal form of prefixes. We show by induction on that the map computed by the first letters (ignoring definedness) is with . The empty word gives . Appending or gives with . Appending when gives , because : an up-move cancels the last down-move. Appending when gives .
Monotonicity. Each is non-decreasing in , as a composition of non-decreasing maps.
Domain, lower end. is defined at iff for every prefix . A prefix value can drop below only at an up-move taken with , which requires , i.e. for the new . Since never decreases, these conditions together say (and ).
Domain, upper end. If , the prefix value exceeds for every and the domain is empty. Otherwise, because , iff , i.e.
Take . Each is a threshold because is non-decreasing; the conjunction of all lower and upper conditions is therefore the interval .
Cost. Each letter updates with a constant number of operations. Once or the domain is empty and compilation may stop; until then all quantities have bits, and with needed only while .
∎The proof is short, but the statement was checked mechanically as well: for all words of length at most , all and all , the compiled map agrees with step-by-step evaluation — more than two million cases — and for random long words on trees with up to nodes (sdk/tests/test_addressing.py). The browser laboratory’s implementation is checked against the SDK’s.
import random
from cgtsdk import Cost
from cgtsdk.addressing import HeapWord, heap_walk
n = 10**6
rng = random.Random(1)
x, w = 6000, []
for _ in range(2000): # a 2000-letter walk that stays inside the tree
opts = [(a, y) for a, y in (("L", 2*x), ("R", 2*x + 1), ("U", x // 2)) if 1 <= y <= n]
a, x = rng.choice(opts); w.append(a)
compile_cost = Cost()
f = HeapWord.compile(w, n, compile_cost)
print(f"|w| = {len(w)} -> f(x) = ((x >> {f.k}) << {f.j}) | {f.c}, defined on [{f.lo}, {f.hi}]")
apply_cost, walk_cost = Cost(), Cost()
starts = [rng.randrange(4096, 8192) for _ in range(1000)]
assert all(f(s, apply_cost) == heap_walk(w, s, n, walk_cost) for s in starts)
print(f"compile once: {compile_cost.total} ops; per application: compiled {apply_cost.total/1000:.0f}, step-by-step {walk_cost.total/1000:.0f}")
|w| = 2000 -> f(x) = ((x >> 3) << 9) | 486, defined on [8, 7807]
compile once: 8000 ops; per application: compiled 5, step-by-step 3640
A word of two thousand moves collapses to “shift right by , shift left by , set the low nine bits to ”, defined on the interval . After a one-time cost of operations, each application costs instead of about .
Text alternative: type a word over L, R, U and a tree size; the widget compiles it with the algorithm of the proof, shows , and lists the compiled result next to step-by-step evaluation for the first forty start nodes. They always agree; the widget would flag any disagreement.
13.3 The lower bound: arbitrary shapes
Why does the heap-shaped tree compile and a linked list or an arbitrary tree not? Because in the semantics of every move is determined by a single number, . In an arbitrary tree, even the domain of a single move carries a lot of information.
Let a scheme encode each binary tree with nodes (nodes named by their addresses) as a bit string from which it can be decided, for every node, whether the move is defined there, without access to . Then some tree needs bits.
Consider caterpillars: a right spine with left leaves attached at the spine positions in a set , and so that every position of is on the spine. Then . The caterpillars have pairwise different answer functions, and the counting lemma (Lemma 12.2) applies.
∎So for arbitrary shapes the best possible “compiled” form of a word is essentially a table of its values at all nodes, built by simulating the word from every node at cost . The theorem was also checked by enumerating all binary trees with up to nodes (experiments/check_theorems.py).
Together the two theorems locate the boundary of mechanism M-ARITH for trees precisely: compilation to constant size is possible exactly when the move semantics is fixed by a constant number of parameters of the structure, and impossible when the shape itself carries the information. The general form of this statement — a characterization of compilable classes — is open (CGT-OPEN-012).
13.4 More compilable structures
The pattern is not special to trees. Three further structures compile with density , each with a different algebra:
- De Bruijn graph : vertices are base- numerals of digits; the move “shift in digit ” is . Affine maps modulo are closed under composition: .
- Hypercube : the move “flip bit ” is ; compositions are XORs with the XOR of the masks.
- Grid with moves defined inside the box: a word compiles to a translation together with the per-axis minimum and maximum prefix displacements ; it is defined at iff and for all ; composition adds translations and takes the minimum and maximum of shifted prefixes.
Each item is a direct calculation of the closure property, as stated; the grid case is the multi-dimensional version of the cursor observation of Chapter 3. Each is checked exhaustively on small instances in the SDK’s tests.
∎from cgtsdk import Cost
from cgtsdk.addressing import (AffineMod, BoxTranslation, XorMask, compile_word,
debruijn_moves, grid_moves, hypercube_moves)
import random
rng = random.Random(3)
cases = [("de Bruijn B(2,20)", debruijn_moves(2, 20), AffineMod.identity(2**20)),
("hypercube Q_20", hypercube_moves(20), XorMask(0)),
("grid 1000 x 1000", grid_moves((1000, 1000)), BoxTranslation.identity((1000, 1000)))]
for name, moves, ident in cases:
w = [rng.choice(sorted(moves)) for _ in range(10_000)]
c = Cost(); f = compile_word(moves, w, ident, c)
print(f"{name:18s} 10,000-move word compiled in {c.total} ops into {f}")
de Bruijn B(2,20) 10,000-move word compiled in 10000 ops into AffineMod(a=0, b=782888, N=1048576)
hypercube Q_20 10,000-move word compiled in 10000 ops into XorMask(c=537303)
grid 1000 x 1000 10,000-move word compiled in 10000 ops into BoxTranslation(dims=(1000, 1000), t=(32, -78), lo=(-51, -81), hi=(39, 35))
The de Bruijn word compiled to a constant map (): after more than shifts the starting vertex is entirely forgotten, which the affine form exposes immediately. The grid word compiled to “move by ”, defined for start points with coordinates at least and below .
13.5 What the experiments show
The ledger’s experiment (CGT-EXP-008) measured compilation against step-by-step evaluation under two workloads, a lesson learned from earlier experiments (CGT-D-017).
| Workload on | compiled ops / application | step-by-step ops / application | applications to break even | |
|---|---|---|---|---|
| words generated as walks (≈90% of starts defined) | 8 | 5 | 16 | 2.9 |
| 512 | 5 | 933 | 2.2 | |
| 4096 | 5 | 7,420 | 2.2 | |
| random words (most starts fall off early) | 512 | 5 | 29 | 86 |
| 4096 | 5 | 130 | 131 |
When words stay inside the tree, compilation pays after about two applications. When random words fall off the tree within a few steps, step-by-step evaluation stops early and compilation needs over a hundred applications to pay. On random pointer trees with , the same kind of walk-generated word was defined at only of nodes for — the non-uniformity that Theorem 13.3 lower-bounds (CGT-OBS-014).
Compilation is a statement about repeated application of the same word. It does not make a single evaluation cheaper (compiling costs as much as evaluating), and it applies only to structures whose move semantics is uniform. Its novelty as a normal form has not been verified against the literature on implicit tree navigation and on transition monoids; it is presented as proved here, not as new.
Compile for by hand, following the proof of Theorem 13.2. Check your with the SDK or the laboratory.
Solution to Exercise 13.1
Prefixes: : ; : ; : ; : ; : ; : . No up-move with , so . Thresholds (with ): ; . So on : the walk visits , which forces .
Generalize Theorem 13.2 to heap-shaped -ary trees with moves “child ” and “parent”. What replaces shifts?
Show that the grid compiled form of Proposition 13.4 is closed under composition by proving the composition formula, and give the compiled form of the reverse of a word in terms of the word’s compiled form.
Prove that the moves and on a circular doubly linked list of known length compile into translations modulo — and explain why this does not make a linked list as fast as an array.
Solution to Exercise 13.4
Each move is on positions, so words compile to , defined everywhere. But applying the compiled form requires reaching position , and a linked list offers no way to jump by a computed offset: the compiled meaning is constant-size, while its execution still walks pointers. Compilation helps only when the representation supports arithmetic addressing.
- A structure is arithmetically compilable when its moves act on numerals through a composition-closed class of constant-size maps; then any word compiles once and applies in constant time.
- In heap-shaped trees every word over compiles to on an interval domain (CGT-THM-006).
- For arbitrary shapes no constant-size compiled form exists: one move’s domain can carry bits (CGT-THM-007).
- De Bruijn graphs, hypercubes and grids compile too, via affine maps, XOR masks and bounded translations (CGT-PROP-008).
- Measured: compilation pays after about two applications when words stay defined, and after over a hundred when early exits make step-by-step evaluation cheap.
References cited in this chapter
- Blumensath & Grädel 2000 A. Blumensath, E. Grädel (2000). Automatic structures. Proc. 15th IEEE LICS, pp. 51–62.
- Epstein et al. 1992 D. B. A. Epstein, J. W. Cannon, D. F. Holt, S. V. F. Levy, M. S. Paterson, W. P. Thurston (1992). Word Processing in Groups. Jones and Bartlett (later A K Peters).
- Khoussainov & Nerode 1995 B. Khoussainov, A. Nerode (1995). Automatic presentations of structures. Logic and Computational Complexity, LNCS 960.