Skip to content

Commit a2730e7

Browse files
authored
chore: add comments to Fixpoints.lean (#19)
* chore: add comments * chore: remove unnecessary whitespace
1 parent 20830cd commit a2730e7

1 file changed

Lines changed: 106 additions & 0 deletions

File tree

LeroyCompilerVerificationCourse/Fixpoints.lean

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ import Batteries.Data.List.Perm
1010

1111
universe u
1212

13+
/-
14+
9. More about fixpoints
15+
16+
9.1 From Knaster-Tarski to effective computation of fixpoints
17+
-/
18+
19+
/-
20+
Consider a type `α` equipped with a decidable equality `eq` and a
21+
transitive ordering `le`.
22+
-/
1323
@[grind] class OrderStruct (α : Sort u) where
1424
eq : α → α → Prop
1525
le : α → α → Prop
@@ -20,14 +30,24 @@ universe u
2030
gt_wf : WellFounded (fun x y : α => le y x ∧ ¬eq y x)
2131
open OrderStruct
2232

33+
/-
34+
This is the strict order induced by `le`. We assume it is well-founded:
35+
all strictly ascending chains are finite.
36+
-/
2337
@[grind] def gt {α : Sort u} [OrderStruct α] (x y : α) : Prop := le y x ∧ ¬eq y x
2438

2539
def gt_well_founded {α : Sort u} [OrderStruct α] : WellFounded (gt : α → α → Prop) := by apply @gt_wf
2640

41+
/-
42+
Let `bot` be a smallest element of `α`.
43+
-/
2744
class OrderWithBot (α : Sort u) extends OrderStruct α where
2845
bot : α
2946
bot_smallest : ∀ x, le bot x
3047

48+
/-
49+
Let `F` be a monotonic function from `α` to `α`.
50+
-/
3151
class Monotone (α : Sort u) (F : α → α) [OrderStruct α] where
3252
F_mon : ∀ {x y : α}, le x y → le (F x) (F y)
3353

@@ -38,6 +58,10 @@ section FixpointExistence
3858
variable (α : Sort u) (F : α → α) [OrderWithBot α]
3959

4060
open OrderStruct OrderWithBot
61+
/-
62+
Let's show the existence of a fixpoint, as in the Knaster-Tarski theorem.
63+
The proof is by well-founded induction.
64+
-/
4165
theorem fixpoint_exists_1 [Monotone α F] : ∃ x : α, eq x (F x) := by
4266
have REC : ∀ x : α, le x (F x) -> ∃ y : α , eq y (F y) := by
4367
intro x
@@ -59,6 +83,32 @@ theorem fixpoint_exists_1 [Monotone α F] : ∃ x : α, eq x (F x) := by
5983
apply REC
6084
apply bot_smallest
6185

86+
/-
87+
Unfortunately, we cannot extract the value of the fixpoint.
88+
-/
89+
/--
90+
error: Tactic `cases` failed with a nested error:
91+
Tactic `induction` failed: recursor `Exists.casesOn` can only eliminate into `Prop`
92+
93+
α : Sort u
94+
F : α → α
95+
inst✝ : OrderWithBot α
96+
motive : (∃ x, eq x (F x)) → Sort ?u.1192
97+
h_1 : (x : α) → (P : eq x (F x)) → motive ⋯
98+
fixpoint✝ : ∃ x, eq x (F x)
99+
⊢ motive fixpoint✝ after processing
100+
_
101+
the dependent pattern matcher can solve the following kinds of equations
102+
- <var> = <term> and <term> = <var>
103+
- <term> = <term> where the terms are definitionally equal
104+
- <constructor> = <constructor>, examples: List.cons x xs = List.cons y ys, and List.cons x xs = List.nil
105+
-/
106+
#guard_msgs in
107+
def cannotExtractFixpoint [Monotone α F] : α :=
108+
let fixpoint := fixpoint_exists_1 α F
109+
match fixpoint with
110+
| ⟨x, P⟩ => x
111+
62112
end FixpointExistence
63113

64114
section Iterate
@@ -71,12 +121,29 @@ instance : WellFoundedRelation α where
71121
rel := gt
72122
wf := gt_wf
73123

124+
/-
125+
We can alternatively write the iteration algorithm explicitly,
126+
proving later that it is correct.
127+
128+
The algorithm is simple: we iterate `F` starting from a pre-fixpoint `x`
129+
130+
The `iterate` function takes as argument not just `x`, but also two proofs:
131+
- that `x` is a pre-fixpoint, i.e. `le x (F x)`
132+
- that `x` is below any post-fixpoint `z`.
133+
134+
Likewise, it returns as result not just the fixpoint `y`, but also two proofs:
135+
- that `y` is a fixpoint, i.e. `eq y (F y)`
136+
- that `y` is below any post-fixpoint `z`.
137+
-/
74138
@[grind] def iterate (x : α) (PRE : le x (F x)) (SMALL : ∀ z, le (F z) z -> le x z) : α :=
75139
if beq x (F x) then x else iterate (F x) (by apply F_mon; exact PRE) (by intro z hyp; specialize SMALL z hyp; apply le_trans; apply F_mon; exact SMALL; exact hyp)
76140
termination_by x
77141
decreasing_by
78142
grind [beq_false']
79143

144+
/-
145+
We then prove that the algorithm implemented above indeed calculates the least fixpoint of `F`.
146+
-/
80147
@[grind] theorem iterate_correct (x : α) (PRE : le x (F x)) (SMALL : ∀ z, le (F z) z -> le x z) (heq : y = iterate _ F x PRE SMALL ) : eq y (F y) ∧ ∀ z, le (F z) z → le y z := by
81148
fun_induction iterate
82149
case case1 x' PRE SMALL isTrue =>
@@ -96,8 +163,14 @@ section Fixpoint
96163
open OrderWithBot
97164
variable {α : Sort u} [i : OrderWithBot α] (F : α → α) [Monotone α F]
98165

166+
/-
167+
The fixpoint is obtained by iterating from `bot`.
168+
-/
99169
@[grind] def fixpoint' : α := iterate α F bot (by apply bot_smallest) (by grind [bot_smallest])
100170

171+
/-
172+
It is therefore both a fixpoint and the smallest post-fixpoint.
173+
-/
101174
theorem fixpoint_correct :
102175
eq (fixpoint' F) (F (fixpoint' F)) ∧ ∀ z : α, le (F z) z → le (fixpoint' F) z := by
103176
unfold fixpoint'
@@ -109,6 +182,13 @@ theorem fixpoint_correct :
109182
· rfl
110183
end Fixpoint
111184

185+
/-
186+
9.2 Application to constant propagation analysis
187+
188+
First, we equip the type of abstract stores with the required equality
189+
and ordering predicates. `le` and `beq` are defined in `Constprop.lean`,
190+
under the names `Le` and `Equal`.
191+
-/
112192
section Constprop
113193

114194
open Std.HashMap
@@ -136,6 +216,9 @@ def Eq'_sym : ∀ S1 S2, Eq' S1 S2 → Eq' S2 S1 := by
136216

137217
@[grind] def Gt (S1 S2 : Store) := Le S2 S1 ∧ ¬ Eq' S2 S1
138218

219+
/-
220+
We will use monotonically increasing functions a lot.
221+
-/
139222
@[grind] def Increasing (F : Store → Store) := ∀ x y, Le x y → Le (F x) (F y)
140223

141224
theorem hash_set_incl_size_leq (S1 S2 : Store) : Le S2 S1 → List.Subperm (S1.toList) (S2.toList) := by
@@ -150,6 +233,12 @@ theorem hash_set_incl_size_leq (S1 S2 : Store) : Le S2 S1 → List.Subperm (S1.t
150233
specialize LE k v (by grind)
151234
grind
152235

236+
/-
237+
The really hard proof is to show that the strict order `Gt` is
238+
well-founded. For this we reason on the cardinal of the finite maps
239+
representing abstract stores, that is, the number of "`x` is mapped to `n`" facts
240+
contained in abstract stores.
241+
-/
153242
@[grind] theorem Le_cardinal :
154243
∀ S T : Store,
155244
Le T S ->
@@ -197,6 +286,11 @@ noncomputable instance : OrderStruct Store where
197286
gt_wf := Gt_wf
198287
end Constprop
199288

289+
/-
290+
Another difficulty is that our type of abstract stores does not have
291+
a smallest element `bot`. But for the kind of functions we take fixpoints of,
292+
we know a pre-fixpoint we can start iterating with.
293+
-/
200294
section FixpointJoin
201295
variable (Init : Store)
202296
variable (F : Store → Store) [Monotone Store F]
@@ -263,6 +357,14 @@ theorem fixpoint_join_smallest :
263357
· unfold fixpoint_join
264358
dsimp
265359

360+
/-
361+
Now we can try to use the `fixpoint_join` function above in the `Cexec`
362+
static analyzer. Howver, we need to simulateneously define the `Cexec` function, while
363+
showing that it is increasing.
364+
365+
This can be done, but we'll need a lot of lemmas about increasing
366+
functions first.
367+
-/
266368
@[grind] theorem Join_increasing :
267369
∀ S1 S2 S3 S4,
268370
Le S1 S2 -> Le S3 S4 -> Le (Join S1 S3) (Join S2 S4) := by grind
@@ -312,6 +414,10 @@ theorem fixpoint_join_increasing (_ : Store) (F : Store → Store) (F_mon : ∀
312414
· exact hyp
313415
· grind
314416

417+
/-
418+
At long last, we can define `Cexec` while at the same time showing that
419+
it is increasing.
420+
-/
315421
noncomputable def Cexec' : (c : com) → {F : Store → Store // ∀ x y, le x y → le (F x) (F y)}
316422
| .SKIP => ⟨(fun S => S), by grind⟩
317423
| .ASSIGN x a => ⟨(fun S => Update x (Aeval S a) S), by

0 commit comments

Comments
 (0)