Skip to content

Commit 66e8c1d

Browse files
committed
Add Aiur formal verification framework with #aiur_gen command
Add G field arithmetic (Add, Sub, Mul, eqZero), u8/u32 semantic functions with range predicates, and commutativity theorems to Goldilocks.lean. Enable precompileModules on Ix library for native evaluation under Lean's `module` system. Add `#aiur_gen` command that evaluates an Aiur Toplevel at elaboration time and writes a .lean file with: - Semantic types: Lean inductives from Aiur enums with pointers erased, type aliases as abbrevs, topologically sorted by type references with mutual blocks for cyclic dependencies - Semantic propositions: one inductive per function, one constructor per control-flow path, with assertEq as Prop conjuncts, store/load erased, u8/u32 ops with range constraints, or-patterns expanded, IO operations as unconstrained fresh variables, data constructors inlined, nested match patterns resolved transitively - Topological sorting of both types and functions by dependency graph, with mutual blocks only for actual cycles Add multi-file example: - ExampleBase.lean: Expr type, eval, swap, double_eval (forward ref), simplify_neg (nested match) → ExampleBaseSem.lean - ExampleMain.lean: ExprList, Tree/Forest (mutual types), list_sum, assert_eval, tree_sum/forest_sum (mutual functions), is_even/is_odd (mutual recursion) → ExampleMainSem.lean (imports base) - Example.lean: 10 theorems over generated semantic propositions (determinism, totality, involution, assertEq semantics, spec soundness, mutual type/function soundness, mutual recursion path exclusivity)
1 parent 7cbe144 commit 66e8c1d

8 files changed

Lines changed: 1161 additions & 0 deletions

File tree

Ix/Aiur/Formal/Example.lean

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
module
2+
public import Ix.Aiur.Formal.ExampleMainSem
3+
4+
/-!
5+
# Aiur Formal Verification — Example Proofs
6+
7+
Proves properties about the Aiur programs defined across two toplevels:
8+
- `ExampleBase.lean`: `Pair`, `Expr`, `eval`, `swap` → `ExampleBaseSem.lean`
9+
- `ExampleMain.lean`: `ExprList`, `list_sum`, `assert_eval` → `ExampleMainSem.lean`
10+
-/
11+
12+
public section
13+
14+
open Aiur exampleBase exampleMain
15+
16+
-- ============================================================
17+
-- 1. Eval is deterministic
18+
-- ============================================================
19+
20+
theorem eval_deterministic {e : Expr} {v₁ v₂ : G} {P₁ P₂ : Prop}
21+
(h₁ : eval e v₁ P₁) (h₂ : eval e v₂ P₂)
22+
(hP₁ : P₁) (hP₂ : P₂) : v₁ = v₂ := by
23+
induction h₁ generalizing v₂ P₂ with
24+
| path0 => cases h₂; rfl
25+
| path1 _ _ ih₁ ih₂ =>
26+
cases h₂ with
27+
| path1 h₂a h₂b =>
28+
show _ + _ = _ + _
29+
congr 1
30+
· exact ih₁ h₂a hP₁.1 hP₂.1
31+
· exact ih₂ h₂b hP₁.2 hP₂.2
32+
| path2 _ ih =>
33+
cases h₂ with
34+
| path2 h₂a =>
35+
show (0 : G) - _ = (0 : G) - _
36+
congr 1; exact ih h₂a hP₁ hP₂
37+
38+
-- ============================================================
39+
-- 2. Eval is total
40+
-- ============================================================
41+
42+
theorem eval_total (e : Expr) :
43+
∃ (v : G) (P : Prop), eval e v P ∧ P := by
44+
induction e with
45+
| Lit n => exact ⟨n, True, .path0, trivial⟩
46+
| Add a b iha ihb =>
47+
obtain ⟨va, Pa, ha, hPa⟩ := iha
48+
obtain ⟨vb, Pb, hb, hPb⟩ := ihb
49+
exact ⟨va + vb, Pa ∧ Pb, .path1 ha hb, hPa, hPb⟩
50+
| Neg a iha =>
51+
obtain ⟨va, Pa, ha, hPa⟩ := iha
52+
exact ⟨(0 : G) - va, Pa, .path2 ha, hPa⟩
53+
54+
-- ============================================================
55+
-- 3. Swap is an involution
56+
-- ============================================================
57+
58+
theorem swap_involution {p q r : Pair} {P₁ P₂ : Prop}
59+
(h₁ : swap p q P₁) (h₂ : swap q r P₂)
60+
(_ : P₁) (_ : P₂) : r = p := by
61+
cases h₁; cases h₂; rfl
62+
63+
-- ============================================================
64+
-- 4. assert_eval semantics
65+
-- ============================================================
66+
67+
theorem assert_eval_correct {e : Expr} {expected : G} {P : Prop}
68+
(h : assert_eval e expected () P) (hP : P) :
69+
∃ v Pv, eval e v Pv ∧ Pv ∧ v = expected := by
70+
cases h with
71+
| path0 h_eval => exact ⟨_, _, h_eval, hP.1, hP.2
72+
73+
-- ============================================================
74+
-- 5. list_sum is deterministic (uses eval_deterministic cross-toplevel)
75+
-- ============================================================
76+
77+
theorem list_sum_deterministic {xs : ExprList} {v₁ v₂ : G} {P₁ P₂ : Prop}
78+
(h₁ : list_sum xs v₁ P₁) (h₂ : list_sum xs v₂ P₂)
79+
(hP₁ : P₁) (hP₂ : P₂) : v₁ = v₂ := by
80+
induction h₁ generalizing v₂ P₂ with
81+
| path0 => cases h₂; rfl
82+
| path1 h₁e _ ih =>
83+
cases h₂ with
84+
| path1 h₂e h₂r =>
85+
show _ + _ = _ + _
86+
congr 1
87+
· exact eval_deterministic h₁e h₂e hP₁.1 hP₂.1
88+
· exact ih h₂r hP₁.2 hP₂.2
89+
90+
-- ============================================================
91+
-- 6. Spec-based: eval matches a Lean function
92+
-- ============================================================
93+
94+
def evalSpec : Expr → G
95+
| .Lit n => n
96+
| .Add a b => evalSpec a + evalSpec b
97+
| .Neg a => (0 : G) - evalSpec a
98+
99+
theorem eval_sound {e : Expr} {v : G} {P : Prop}
100+
(h : eval e v P) (hP : P) : v = evalSpec e := by
101+
induction h with
102+
| path0 => rfl
103+
| path1 _ _ ih₁ ih₂ => simp only [evalSpec, ih₁ hP.1, ih₂ hP.2]
104+
| path2 _ ih => simp only [evalSpec, ih hP]
105+
106+
-- ============================================================
107+
-- 7. Mutual types: tree_sum / forest_sum match Lean specs
108+
-- ============================================================
109+
110+
mutual
111+
def treeSpec : Tree → G
112+
| .Leaf n => n
113+
| .Node n f => n + forestSpec f
114+
115+
def forestSpec : Forest → G
116+
| .Empty => 0
117+
| .Cons t rest => treeSpec t + forestSpec rest
118+
end
119+
120+
mutual
121+
def tree_sum_sound {t : Tree} {v : G} {P : Prop}
122+
(h : tree_sum t v P) (hP : P) : v = treeSpec t := by
123+
cases h with
124+
| path0 => rfl
125+
| path1 hf => simp only [treeSpec, forest_sum_sound hf hP]
126+
127+
def forest_sum_sound {f : Forest} {v : G} {P : Prop}
128+
(h : forest_sum f v P) (hP : P) : v = forestSpec f := by
129+
cases h with
130+
| path0 => rfl
131+
| path1 ht hf => simp only [forestSpec, tree_sum_sound ht hP.1, forest_sum_sound hf hP.2]
132+
end
133+
134+
-- ============================================================
135+
-- 8. Mutual recursion: is_even and is_odd constraints are exclusive
136+
--
137+
-- The path0 constraint (`G.eqZero n = 1`) and the path1 constraint
138+
-- (`G.eqZero n = 0`) cannot both hold. This means path0 and path1
139+
-- can never fire for the same `n`, which is what makes the mutual
140+
-- semantic propositions deterministic.
141+
-- ============================================================
142+
143+
theorem even_odd_path_exclusive {n : G}
144+
(h0 : G.eqZero n = (1 : G)) (h1 : G.eqZero n = (0 : G)) : False :=
145+
absurd (h0.symm.trans h1) G.one_ne_zero
146+
147+
theorem even_zero {n r : G} {P : Prop}
148+
(h : is_even n r P) (hP : P) (hz : G.eqZero n = (1 : G)) :
149+
r = (1 : G) := by
150+
cases h with
151+
| path0 => rfl
152+
| path1 _ => exact (even_odd_path_exclusive hz hP.2).elim
153+
154+
theorem odd_zero {n r : G} {P : Prop}
155+
(h : is_odd n r P) (hP : P) (hz : G.eqZero n = (1 : G)) :
156+
r = (0 : G) := by
157+
cases h with
158+
| path0 => rfl
159+
| path1 _ => exact (even_odd_path_exclusive hz hP.2).elim
160+
161+
end

Ix/Aiur/Formal/ExampleBase.lean

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
module
2+
public import Ix.Aiur.Meta
3+
public import Ix.Aiur.Formal.GenSem
4+
5+
/-!
6+
# Aiur Formal Verification — Example Base
7+
8+
Defines a small expression language with an evaluator.
9+
`Example.lean` builds on this with composite types.
10+
-/
11+
12+
open Aiur
13+
14+
def exampleBase := ⟦
15+
type Pair = (G, G)
16+
17+
enum Expr {
18+
Lit(G),
19+
Add(&Expr, &Expr),
20+
Neg(&Expr)
21+
}
22+
23+
fn double_eval(e: Expr) -> G {
24+
eval(e) + eval(e)
25+
}
26+
27+
fn eval(e: Expr) -> G {
28+
match e {
29+
Expr.Lit(n) => n,
30+
Expr.Add(&a, &b) => eval(a) + eval(b),
31+
Expr.Neg(&a) => 0 - eval(a),
32+
}
33+
}
34+
35+
fn swap(p: Pair) -> Pair {
36+
(proj(p, 1), proj(p, 0))
37+
}
38+
39+
fn simplify_neg(e: Expr) -> Expr {
40+
match e {
41+
Expr.Neg(&inner) =>
42+
match inner {
43+
Expr.Neg(&x) => x,
44+
Expr.Lit(n) => Expr.Lit(0 - n),
45+
_ => e,
46+
},
47+
_ => e,
48+
}
49+
}
50+
51+
52+
#aiur_gen exampleBase "ExampleBaseSem.lean" #

Ix/Aiur/Formal/ExampleBaseSem.lean

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
-- Auto-generated by #aiur_gen. Regenerate, don't edit.
2+
module
3+
public import Ix.Aiur.Goldilocks
4+
5+
public section
6+
7+
namespace exampleBase
8+
9+
open Aiur
10+
11+
-- ══════ Semantic types ══════
12+
13+
abbrev Pair := (G × G)
14+
15+
inductive Expr where
16+
| Lit : G → Expr
17+
| Add : Expr → Expr → Expr
18+
| Neg : Expr → Expr
19+
20+
-- ══════ Semantic propositions ══════
21+
22+
inductive eval : Expr → G → PropProp where
23+
| path0 :
24+
eval (.Lit n) n True
25+
| path1 :
26+
eval a r0 P0 →
27+
eval b r1 P1 →
28+
eval (.Add a b) (r0 + r1) (P0 ∧ P1)
29+
| path2 :
30+
eval a r0 P0 →
31+
eval (.Neg a) ((0 : G) - r0) (P0)
32+
33+
inductive swap : Pair → Pair → PropProp where
34+
| path0 :
35+
swap p ((p).2, (p).1) True
36+
37+
inductive simplify_neg : Expr → Expr → PropProp where
38+
| path0 :
39+
simplify_neg (.Neg (.Neg x)) x True
40+
| path1 :
41+
simplify_neg (.Neg (.Lit n)) (.Lit ((0 : G) - n)) True
42+
| path2 :
43+
simplify_neg (.Neg _) e True
44+
| path3 :
45+
simplify_neg _ e True
46+
47+
inductive double_eval : Expr → G → PropProp where
48+
| path0 :
49+
eval e r0 P0 →
50+
eval e r1 P1 →
51+
double_eval e (r0 + r1) (P0 ∧ P1)
52+
53+
end exampleBase
54+
55+
end

Ix/Aiur/Formal/ExampleMain.lean

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
module
2+
public import Ix.Aiur.Meta
3+
public import Ix.Aiur.Formal.GenSem
4+
5+
/-!
6+
# Aiur Formal Verification — Example Main
7+
8+
Defines `ExprList` and functions that use both `Expr` (from `ExampleBase`)
9+
and `ExprList`, demonstrating cross-toplevel type references and mutual
10+
recursion (`is_even` / `is_odd`). Also demonstrates mutual types
11+
(`Tree` / `Forest`).
12+
-/
13+
14+
open Aiur
15+
16+
def exampleMain := ⟦
17+
enum ExprList {
18+
Nil,
19+
Cons(&Expr, &ExprList)
20+
}
21+
22+
enum Tree {
23+
Leaf(G),
24+
Node(G, &Forest)
25+
}
26+
27+
enum Forest {
28+
Empty,
29+
Cons(&Tree, &Forest)
30+
}
31+
32+
fn list_sum(xs: ExprList) -> G {
33+
match xs {
34+
ExprList.Nil => 0,
35+
ExprList.Cons(&e, &rest) => eval(e) + list_sum(rest),
36+
}
37+
}
38+
39+
fn assert_eval(e: Expr, expected: G) {
40+
let result = eval(e);
41+
assert_eq!(result, expected);
42+
}
43+
44+
fn tree_sum(t: Tree) -> G {
45+
match t {
46+
Tree.Leaf(n) => n,
47+
Tree.Node(n, &f) => n + forest_sum(f),
48+
}
49+
}
50+
51+
fn forest_sum(f: Forest) -> G {
52+
match f {
53+
Forest.Empty => 0,
54+
Forest.Cons(&t, &rest) => tree_sum(t) + forest_sum(rest),
55+
}
56+
}
57+
58+
fn is_even(n: G) -> G {
59+
match eq_zero(n) {
60+
1 => 1,
61+
0 => is_odd(n - 1),
62+
}
63+
}
64+
65+
fn is_odd(n: G) -> G {
66+
match eq_zero(n) {
67+
1 => 0,
68+
0 => is_even(n - 1),
69+
}
70+
}
71+
72+
73+
#aiur_gen exampleMain "ExampleMainSem.lean"
74+
importing Ix.Aiur.Formal.ExampleBaseSem
75+
opening exampleBase #

0 commit comments

Comments
 (0)