Proof of `Courcelle's theorem on a word RAM`
groundedproofs/Lax11Proofs/CourcelleMain.lean · lax-11
What this proof establishes
no assumptions
Lax11.CourcelleAssuming the claims on the left, the claim on the right holds — checked by the archive's pipeline. Proof code is not displayed here.
Description
Model checking monadic second-order logic is linear time on a word random access machine for graphs presented together with a -expression: for every sentence and every width bound there is one program which, on a compressed sparse row block followed by a -expression that evaluates to it, writes if the sentence holds in the graph and if it does not, within a constant multiple of the length of the input, at every word length at which that constant multiple of the length and of each entry of the input fits into a word.
Proof strategy
The witness is the compiled driver , with — the tree-fold schema of , instantiated with the type table of and given the instance word's front end.
The mathematics is finished before the program is looked at. says the fold's value at a node is the number of the -type of the subexpression rooted there, by structural induction on the expression with one congruence of per constructor; turns the value at the root into the truth of the sentence, by adequacy and the root conditions of . Both are statements about the pure fold, with no environment in them.
What is left is the word. decomposes an admissible input into the segments the reads consume — two header entries, the rest of the graph block, the node count, and three arrays of one entry per node — and forgets the surface encoding relation to the one the induction is stated against. then runs the phases in a row: the graph block and the vertex-name array are read into arrays nothing ever reads again, the parents and the op codes into the schema's own, the four tables are materialized by , and and do the fold. The epilogue is one of , which is in range because the root's value is the number of a type.
discharges the compiler, the layout invariant and the machine, charging machine steps per unit of IMP+ cost, plus one for the final . The array extents are chosen per input, as that lemma allows.
Where the constant comes from
The bound is per entry of the input word. The first factor is the compiler — an array access compiles to four instructions, whatever the layout — and the hundred is the driver's own per-entry cost, every loop bounded loosely and nothing fought over. The third term is the price of materializing the four tables, three units per entry, and it is where the tower lives: the type table has one row and one column per -type of a -labelled region, a number that grows faster than any tower of exponentials in . It is paid once, before the input is read, which the order of the quantifiers permits — the sentence and the width bound come first, then the program and the constant, then the graph.
The input is nonempty and the table setup cost is positive. Spreading that one-time cost over the whole input leaves enough slack in the same constant to pay the final instruction.
What the theorem claims about that constant is that it depends on the sentence and the width bound alone: the tables are materialized before the input is read, and the per-node work of the fold is a fixed number of array accesses whatever the size of the alphabet. What it does not claim is any bound on it. None is computed anywhere in the development, and the absence is deliberate rather than an oversight — every known proof of Courcelle's theorem makes the dependence non-elementary.
Where the word length is paid for
The machine truncates every value modulo , so the run on the machine is the run in the unbounded semantics only as long as nothing the program computes reaches . The bound the driver is proved under is : the length of the input word, plus its largest entry, plus the size of the tables. All three summands are needed and none of them is loose.
The length. Every count the program keeps — the number of nodes, the length of the graph block, the loop counters, the offsets into the four arrays of the expression block — is a count of entries of the word.
The largest entry. Most entries of an instance are small: vertex numbers, offsets and node numbers are below the length of the word, and an operation code is below the number of operations at width , which the tables' size already covers. Two families are not, because the encoding deliberately does not constrain them — the parent entry of the root, which nothing points through, and the vertex name at a node whose operation creates no vertex. The machine reads its whole tape whatever it does with it, so those entries pass through the accumulator and have to be words; there is nothing to prove about them and nothing to be gained by pretending they are small, so the bound names the largest entry and the statement's fitting condition supplies it. That is the only reason the condition quantifies over the entries of the word rather than over its length alone.
The tables. A label indexes the array of seeds and a cell of the square combination table is indexed by two values at once, so and are numbers the machine holds. This is the one place where the size of the type alphabet — the tower — reaches the word length rather than only the constant, and it is honest that it does: a machine whose words cannot number the types cannot run the table. It costs the statement nothing, because the constant is chosen after the sentence and the width bound, and says the constant that pays for materializing the table already pays for holding its indices.
So the compiled program needs cells to be words, and the statement's hypothesis — that is a word for every entry — gives that at the largest entry, with a margin nobody has to compute.
Formalization notes
These are the honesty items of the theorem: what the statement claims, where it and a textbook proof part company, and what a reader is entitled to know was decided rather than proved. The definitions carry their own notes — the logic in , -expressions in , the input format in — and what is said there is not repeated here.
The width measure is cliquewidth, and the conversion from treewidth is not formalized. What is proved is the Courcelle–Makowsky–Rotics form: MSO₁ model checking in linear time on graphs presented with a -expression. Bounded treewidth implies bounded cliquewidth, so the class covered here contains every class of bounded treewidth; but getting the treewidth form of the statement out of this one needs a conversion of a tree decomposition of width into a -expression with bounded in terms of , and that conversion is not formalized. Anyone who wants the treewidth statement should treat it as unproved here. The same holds one level up, at the input: the theorem takes a -expression, it does not compute one. Deciding cliquewidth is NP-hard and approximating it is the theorem of Oum and Seymour; neither is in this submission. That is exactly the status a linear-time treewidth algorithm would have had — a separate theorem, with its own proof, which composes with this one to give an algorithm that takes only the graph.
The expression is a certificate, and the program reads only two of its arrays. The program never looks at the compressed sparse row block, and never at the vertex-name array of the expression block: it reads the whole word, because it must get past the graph block to reach the expression block and because the machine reads its tape in order, but it uses only the parent array and the operation-code array. The other two go into arrays no later expression of the program mentions. This is not laziness dressed up: the type of a subexpression is a function of the types of its children and of the operation at its root, and of nothing else. The graph block is what makes refer to a graph in the first place, and the vertex names are what make the second block a -expression rather than the shape of one, so that a reviewer can check the certificate against the graph. Both only lengthen the input, so reading past them costs a constant per entry and the bound is unaffected.
The type table is noncomputable, and that is the shape of the statement. The fold is driven by a table — a finite value alphabet, an initial value per operation symbol, a binary combination. Here the values are the -types of -labelled regions, and the table is extracted from the composition congruences by together with choice: for each pair of types the entry is a type realized by some gluing of two regions with those types, and the congruences say that the choice does not matter. Nothing in the development computes this table, and nothing could — the statement is an existential over programs, and the truth of a monadic second-order sentence in an arbitrary graph is not something the meta level decides on the way to constructing one. A reader who wants the machine to print the table is asking for an effective bound on the type space, which is the tower this development declines to estimate. What the noncomputability does not touch is the program: is a noncomputable inhabitant of an ordinary structure type and the generator consumes it as data, so the same generator applied to a computable table produces a program that runs. The table's content is carried by proof; the program text around it is carried by evaluation.
The multiplication instruction is never used. The machine of has one, and under a unit-cost measure a linear-time claim that leans on it is at risk of being an artifact of the model. The fold indexes a two-dimensional table, which is where a multiplication would naturally appear — the entry for and sits at . Instead the row bases are themselves materialized, once, in the prologue, as the array , and a lookup is : two array reads and an addition. The prologue that fills is a sequence of stores whose length is the table's size, a constant fixed before the input is read. So the compiled program contains no multiplication anywhere — checks that by filtering the generated instruction list, since it is a property of the program text and the program text is the same for every table — and the claim survives the strictest reading of the cost model, the one that would charge for a product of two words, rather than depending on a generous one.
The machine-versus-model check runs a stand-in table. House discipline in this submission is that every program is run — by `#eval` inside the build, against the pure model it is proved to implement — before anything about it is proved. The driver is run that way, but it cannot be run with the type table, which is noncomputable by construction. therefore instantiates the generic driver with : a hand-written table over the same operation alphabet, decoded by the same , whose values are three bits — class is nonempty, class is nonempty, there is an edge — together with the partial states the sequential fold needs. That is a genuine cliquewidth dynamic program, and the two sentences it decides on the path are "some two vertices are adjacent" and its negation; the machine writes and , and `#guard`s check that against the pure fold. So every line of program text is exercised: the same reads, the same prologue, the same seed and push loops, the same epilogue, the same decoder. What is not exercised is the content of the real table, and that is exactly what carries. Plumbing is machine-checked, mathematics is proof-carried, and neither is asked to vouch for the other.
is imported by nothing in the proof. It is a self-contained file of tree-decomposition theory — descendants as parent-map iteration, validity and width, the tree order, the highest node containing a vertex, subtrees as unions of bags, the separation lemma and the edge-placement lemmas — stated in the numbering the encoding uses, and it is named here so that no reviewer spends time looking for the place where it is used.
One device on the trust surface is not textbook. Everything a reader has to check by eye — the machine, what it means to compute within a time bound, the graph encoding, the syntax and satisfaction of the logic, -expressions and their evaluation, the input format, and the statement itself — is written to be the object a paper would write, with one exception, and it should be named plainly. Variables in formulas are de Bruijn positions, so a reader checking against a textbook must translate between and , and must hold in mind that a quantifier binds at the last position, so that the outermost variable is and no index is ever shifted. Every other deviation in this development is inside the proof package, where the kernel is the reviewer. This one is not, and it is the price paid for keeping substitution off the surface entirely.
Attribution
Courcelle's theorem in the Courcelle–Makowsky–Rotics form: monadic second-order model checking in linear time on graphs of bounded cliquewidth, given a -expression. The type algebra and the composition lemma are the standard Ehrenfeucht–Fraïssé argument; the program is the textbook bottom-up fold.