Induction and the checked theorem ladder
Contents
Induction and the checked theorem ladder#
Peano Lab begins with six arithmetic rule constants, but the interesting mathematics starts where those equations stop. PA3 says
while the apparently symmetric equation \(0+n=n\) is not an axiom. The difference is intentional: addition is specified by recursion on its right argument,
The expression \(0+n\) is stuck when \(n\) is a variable. We must reason about how \(n\) is built. This small asymmetry is the first moment the lab stops behaving like a calculator and starts behaving like a theorem prover.
The implementation is short enough to inspect: the tactic-side construction lives in
engine/induction.py,
while the trusted interpretation of its proof term is in
kernel/checker.py.
Induction is a schema, not a magic command#
For each arithmetic formula \(\varphi(n,\bar a)\), with any additional parameters \(\bar a\), induction permits the inference
The first-order language cannot quantify over formulas. There is therefore no object-language
variable called \(\varphi\) and no single first-order sentence saying “induct over every property.”
Instead, every chosen motive supplies one instance of an infinite effective schema. Peano Lab
makes that choice visible: an Ind certificate stores the motive formula, and the checker reconstructs
its zero and successor instances before accepting it.
Run the smallest useful example yourself:
pa> pa prove forall n. 0 + n = n
pa> induction n
pa> simp
pa> simp [IH]
pa> qed
The first command creates the original closed goal. induction n replaces its one hole by an Ind
node with two holes. The base goal \(0+0=0\) follows from PA3. In the step, the context contains a
rigid \(n\) and the induction hypothesis \(IH:0+n=n\); PA4 changes \(0+S(n)\) to \(S(0+n)\), and congruence
transports the induction hypothesis beneath \(S\). simp is merely a convenient certificate builder.
The last line checks the completed Ind tree against the formula supplied on the first line.
This also explains why “induction on a variable” is not a textual substitution trick. The tactic must keep outer parameters at the right de Bruijn indices, shift hypotheses beneath the new term binder, and construct the step as universal introduction followed by implication introduction. A scoping bug produces a certificate the checker rejects; it does not produce a theorem.
The recursive side predicts the proof#
The same right-argument choice explains the shape of the next rungs. To prove commutativity,
the library inducts on \(m\). The left side then unfolds directly. The base case becomes
\(n=0+n\), so it needs the previously proved zero_add; PA3 only knows \(n+0=n\). In the step, PA4
turns \(n+S(m)\) into \(S(n+m)\), while the other side \(S(m)+n\) is stuck for exactly the same reason
\(0+n\) was stuck. The separate add_succ_left lemma moves that successor through addition, and
the induction hypothesis identifies the terms underneath it. See the exact four-command body at
pa lib add_comm.
Multiplication repeats the lesson at a larger scale. PA5 and PA6 define \(n\cdot m\) by examining \(m\):
Thus \(0\cdot n=0\) needs induction even though \(n\cdot0=0\) is immediate. Commutativity becomes readable only after proving the mirror equation \(S(n)\cdot m=n\cdot m+m\). Associativity and distributivity similarly choose the variable that exposes PA6 and then use earlier addition facts to rearrange the resulting sums. There is no general normalizer secretly solving semiring algebra: the scripts record which recursive equation was opened and which checked lemma repaired the inconvenient orientation.
This is a useful proof-planning heuristic beyond this lab. Before trying tactics, ask which argument the defining equations inspect. Inducting on that argument tends to expose computation; inducting on the other one tends to demand a mirror lemma first.
Twenty core rungs, plus a checked semiring basis#
The theorem library is an ordered regression suite. Its complete data is in
library/theorems.py,
and tests/test_ladder.py
replays every entry. The order is pedagogical rather than a claim that every row depends on the row
immediately above it:
Rung |
Statement or purpose |
Earlier checked facts used |
|---|---|---|
|
\(\forall n.\;0+n=n\) |
none |
|
\(S(n)+m=S(n+m)\) |
none |
|
\(n+m=m+n\) |
|
|
\((n+m)+k=n+(m+k)\) |
none |
|
\(0\cdot n=0\) |
none |
|
\(S(n)\cdot m=n\cdot m+m\) |
|
|
\(n\cdot m=m\cdot n\) |
the two multiplication-side lemmas |
|
\(n\cdot(m+k)=n\cdot m+n\cdot k\) |
|
|
\((n\cdot m)\cdot k=n\cdot(m\cdot k)\) |
|
|
\(1\cdot n=n\) |
none |
|
\(n\cdot1=n\) |
|
|
\((n+m)\cdot k=n\cdot k+m\cdot k\) |
|
|
\(S(n)\ne0\) |
PA1 |
|
\(S(n)=S(m)\to n=m\) |
PA2 |
|
\(n\le n\) |
|
|
\(n\le m\to m\le k\to n\le k\) |
|
|
\(S(p)+n=n\to\bot\) |
none |
|
remove \(b\) from a fixed-sum equation |
three earlier additive facts |
|
opposing additive witnesses force equality |
two earlier additive facts |
|
\(n\le m\to m\le n\to n=m\) |
the witness lemma |
|
\(n\le m\lor m\le n\) |
none |
|
\(a+b=0\to b=0\) |
none |
|
\(n\cdot m=0\to n=0\lor m=0\) |
|
Here order is not primitive syntax in the kernel. The surface parser expands
Consequently, le_refl supplies witness \(0\); le_trans composes two witnesses by addition; and
antisymmetry must do real arithmetic to show that opposing witnesses cannot contain a positive
successor. The five M7 helper rows name those genuine proof obligations rather than hiding them
inside opaque automation.
M11 appends exactly the three multiplication rows shown after mul_assoc. Together with PA3/PA5,
zero_add, mul_zero_left, and the existing associativity, commutativity, and distributivity
certificates, they form the complete oriented basis used by the later ring tactic. No separate
numeral lemma is needed: a numeral is an iterated successor of zero, and closed coefficient
arithmetic already builds PA3–PA6 proof terms.
Browse the executable index at pa lib.
Each card displays the closed statement, earlier dependencies, generated prelude, authored tactic body,
certificate size, and the result of the independent check.
Reuse without a trusted theorem oracle#
A conventional prover keeps a persistent environment of checked declarations. Peano Lab’s binding kernel intentionally has no theorem table and no proof-ascription constructor. Giving the checker a special “trust library theorem by name” case would make the library part of the trusted computing base.
Instead, suppose a new entry claims \(T\) and uses already checked facts \(D_1,\ldots,D_r\). Replay first proves the temporary goal
The generated intro commands make dependencies ordinary local hypotheses, so all authored commands
go through the normal tactic engine. After replay, the untrusted library layer simultaneously replaces
those hypothesis slots with the earlier closed certificates. It shifts proposition and term indices to
avoid capture and contracts the universal- and implication-introduction/elimination redexes exposed by
substitution. This is a small, explicit cut-elimination pass.
The key is what happens next:
The empty context matters. No dependency assumption remains, and the target is the entry’s original statement—not the temporary curried formula. If replay, substitution, normalization, or dependency bookkeeping is wrong, the final check fails. Library composition therefore increases convenience but does not increase logical authority.
Reading the capstone#
Open pa lib mul_eq_zero
to see its exact script. The proof inducts on \(m\). At zero, the right disjunct \(m=0\) is reflexive;
the specialized premise is the PA5 equation, but this branch need not use it. At a successor, PA6
rewrites the assumption as
The checked helper add_eq_zero_right extracts \(n=0\), giving the left disjunct. Its own proof is an
induction on the right addend: a successor sum rewrites to a successor, which PA1 says cannot equal
zero. Notice that no excluded middle is needed. The entire twenty-entry M7 core replays in the
intuitionistic checker.
Finally, pa lean mul_eq_zero
exports this exact closed formula over Lean’s Nat, with the Peano Lab script in comments and one
deliberate sorry. That is an invitation to cross-check the destination. The authority for the
Peano Lab theorem remains the closed certificate that its own small kernel accepted.