Skip to content

Commit cdfe655

Browse files
thomaskwaringtwwarchenson2018
authored
feat(Foundations/Data/Relation): strongly normalising elements of a type equipped with a relation (#549)
This PR defines a predicate `SN r x` (better naming suggestions welcome) expressing that there is no infinite chain of `r`-related elements starting with `x`. This extends the definition of `Terminating` using `WellFounded` to individual elements, using `Acc`. --- I'm very open to being told this is not a useful generalisation / abstraction, but the existing API for `Acc` does seem to make certain proofs easier. For instance, `sn_app_left` in `LambdaCalculus/LocallyNameless/Untyped/StrongNorm` falls out as reduction on `M` is a subrelation of reduction on `M.app N`. --------- Co-authored-by: twwar <tom.waring@unimelb.edu.au> Co-authored-by: Chris Henson <46805207+chenson2018@users.noreply.github.com>
1 parent 02052d4 commit cdfe655

3 files changed

Lines changed: 113 additions & 64 deletions

File tree

Cslib/Foundations/Data/Relation.lean

Lines changed: 75 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -203,38 +203,91 @@ theorem Confluent.equivalence_join_reflTransGen (h : Confluent r) :
203203
apply equivalence_join
204204
grind
205205

206+
/-- An element `x` is `SN` (for strongly-normalising) for a relation `r` if it is accesible under
207+
the inverse of `r`. -/
208+
abbrev SN (r : α → α → Prop) := Acc (fun a b => r b a)
209+
210+
lemma SN_iff_SN_of_rel (x : α) : SN r x ↔ ∀ y, r x y → SN r y := by grind [Acc]
211+
212+
lemma SN.intro : (h : ∀ y, r x y → SN r y) → SN r x := (SN_iff_SN_of_rel x).mpr
213+
214+
lemma SN.of_rel (hx : SN r x) (h : r x y) : SN r y := Acc.inv hx h
215+
216+
@[grind →]
217+
lemma SN.of_rel_reflTransGen (hx : SN r x) (h : ReflTransGen r x y) : SN r y := by
218+
induction h with
219+
| refl => exact hx
220+
| tail _ h ih => exact ih.of_rel h
221+
222+
lemma SN.transGen (hx : SN r x) : SN (TransGen r) x := by
223+
have eq : TransGen (Function.swap r) = (fun a b => TransGen r b a) := by
224+
ext
225+
exact transGen_swap
226+
simpa [eq] using Acc.transGen hx
227+
228+
lemma SN.of_le {r' : α → α → Prop} (hx : SN r x) (h : r' ≤ r) : SN r' x := by
229+
refine Subrelation.accessible ?_ hx
230+
exact subrelation_iff_le.mpr fun {x y} => h y x
231+
232+
@[simp]
233+
lemma SN.iff_transGen (x : α) : SN (TransGen r) x ↔ SN r x :=
234+
fun hx => hx.of_le <| fun _ _ => TransGen.single, transGen⟩
235+
236+
/-- `SN r x` is equivalent to the more elementary definition, that there is no infinite sequence
237+
of reductions starting with `x`. -/
238+
theorem SN.iff_isEmpty_chain :
239+
SN r x ↔ IsEmpty {f : ℕ → α | f 0 = x ∧ ∀ n, r (f n) (f (n + 1))} :=
240+
acc_iff_isEmpty_descending_chain
241+
242+
lemma SN.onFun_of_image {r : β → β → Prop} {f : α → β} (hx : SN r (f x)) :
243+
SN (Function.onFun r f) x := InvImage.accessible f hx
244+
245+
lemma SN.of_normal (hx : Normal r x) : SN r x := SN.intro fun y hy => (hx ⟨y, hy⟩).elim
246+
206247
/-- A relation is terminating when the inverse of its transitive closure is well-founded.
207248
Note that this is also called Noetherian or strongly normalizing in the literature. -/
208249
abbrev Terminating (r : α → α → Prop) := WellFounded (fun a b => r b a)
209250

251+
lemma Terminating.apply (hr : Terminating r) (x : α) : SN r x := WellFounded.apply hr x
252+
253+
lemma Terminating.iff_forall_sn : Terminating r ↔ ∀ x, SN r x :=
254+
⟨WellFounded.apply, WellFounded.intro⟩
255+
210256
theorem Terminating.toTransGen (ht : Terminating r) : Terminating (TransGen r) := by
211-
suffices _ : (fun a b => TransGen r b a) = TransGen (Function.swap r) by grind
212-
grind [transGen_swap]
257+
simp_rw [iff_forall_sn, SN.iff_transGen] at ht ⊢
258+
exact ht
213259

214260
theorem Terminating.ofTransGen : Terminating (TransGen r) → Terminating r := by
215-
suffices _ : (fun a b => TransGen r b a) = TransGen (Function.swap r) by grind
216-
grind [transGen_swap]
261+
simp_rw [iff_forall_sn, SN.iff_transGen]
262+
exact id
263+
264+
theorem Terminating.iff_transGen : Terminating (TransGen r) ↔ Terminating r := by
265+
simp_rw [iff_forall_sn, SN.iff_transGen]
217266

218-
theorem Terminating.iff_transGen : Terminating (TransGen r) ↔ Terminating r :=
219-
⟨ofTransGen, toTransGen⟩
267+
theorem Terminating.iff_isEmpty_chain :
268+
Terminating r ↔ IsEmpty {f : ℕ → α // ∀ n, r (f n) (f (n + 1))} :=
269+
wellFounded_iff_isEmpty_descending_chain
220270

221-
theorem Terminating.subrelation {r' : α → α → Prop} (hr : Terminating r) (h : Subrelation r' r) :
271+
theorem Terminating.of_le {r' : α → α → Prop} (hr : Terminating r) (h : r' ≤ r) :
222272
Terminating r' := by
223-
rw [Terminating, wellFounded_iff_isEmpty_descending_chain] at hr ⊢
224-
rw [isEmpty_subtype]
225-
intro f hf
226-
exact hr.elim ⟨f, fun n ↦ by exact h (hf n)⟩
227-
228-
theorem Terminating.isNormalizing (h : Terminating r) : Normalizing r := by
229-
unfold Terminating at h
230-
intro t
231-
apply WellFounded.induction h t
232-
intro a ih
233-
by_cases ha : Reducible r a
234-
· obtain ⟨b, hab⟩ := ha
235-
obtain ⟨n, hbn, hn⟩ := ih b hab
236-
exact ⟨n, ReflTransGen.head hab hbn, hn⟩
237-
· use a
273+
rw [iff_forall_sn] at hr ⊢
274+
exact fun x => (hr x).of_le h
275+
276+
lemma Terminating.subtype_sn (r : α → α → Prop) :
277+
Terminating (α := {x // SN r x}) (fun a b => r a b) :=
278+
iff_forall_sn.mpr fun x => x.property.onFun_of_image
279+
280+
theorem SN.isNormalizable (hx : SN r x) : Normalizable r x := by
281+
-- restrict to the subtype where all elements are `SN`, so `flip r` is well-founded
282+
obtain ⟨⟨y, hsn⟩, hred : ReflTransGen r x y, hnorm⟩ :=
283+
(Terminating.subtype_sn r).has_min
284+
(s := Subtype.val ⁻¹' ({y | ReflTransGen r x y})) ⟨⟨x, hx⟩, ReflTransGen.refl⟩
285+
use y, hred
286+
intro ⟨z, hyz⟩
287+
exact hnorm ⟨z, hsn.of_rel hyz⟩ (.tail hred hyz) hyz
288+
289+
theorem Terminating.isNormalizing (hr : Terminating r) : Normalizing r :=
290+
fun x => (hr.apply x).isNormalizable
238291

239292
theorem Terminating.isConfluent_iff_all_unique_Normal (ht : Terminating r) :
240293
Confluent r ↔ ∀ a : α, ∃! n : α, ReflTransGen r a n ∧ Normal r n := by

Cslib/Languages/LambdaCalculus/LocallyNameless/Stlc/StrongNorm.lean

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ universe u v
2626

2727
namespace LambdaCalculus.LocallyNameless.Stlc
2828

29-
open Untyped Typing LambdaCalculus.LocallyNameless.Untyped.Term
29+
open Untyped Typing LambdaCalculus.LocallyNameless.Untyped.Term Relation
3030

3131
variable {Var : Type u} {Base : Type v} [DecidableEq Var] [HasFresh Var]
3232

@@ -43,9 +43,9 @@ open scoped Term
4343
@[scoped grind]
4444
structure Saturated (S : Set (Term Var)) : Prop where
4545
lc : ∀ M ∈ S, LC M
46-
sn : ∀ M ∈ S, SN M
46+
sn : ∀ M ∈ S, SN FullBeta M
4747
neutal_lc : ∀ M, Neutral M → LC M → M ∈ S
48-
multiApp : ∀ M N P, LC N → SN N → multiApp (M ^ N) P ∈ S → multiApp (M.abs.app N) P ∈ S
48+
multiApp : ∀ M N P, LC N → SN FullBeta N → multiApp (M ^ N) P ∈ S → multiApp (M.abs.app N) P ∈ S
4949

5050
/-- The semantic map maps each type to a corresponding saturated set of terms.
5151
For the strong normalization proof to work, we must ensure that
@@ -56,7 +56,7 @@ structure Saturated (S : Set (Term Var)) : Prop where
5656
-/
5757
@[simp, scoped grind =]
5858
def semanticMap : Ty Base → Set (Term Var)
59-
| .base _ => { t | SN t ∧ LC t }
59+
| .base _ => { t | SN FullBeta t ∧ LC t }
6060
| .arrow τ₁ τ₂ => { t | ∀ s, s ∈ semanticMap τ₁ → app t s ∈ semanticMap τ₂ }
6161

6262
/-- The sets constructed by semanticMap are saturated -/
@@ -117,7 +117,7 @@ lemma soundness {Γ : Context Var (Ty Base)} (derivation_t : Γ ⊢ t ∶ τ) :
117117
/-- Using soundness and the fact that the empty context
118118
is entailed by any environment, we can conclude that
119119
a well-typed term is strongly normalizing. -/
120-
theorem strong_norm {t : Term Var} {τ : Ty Base} (der : Γ ⊢ t ∶ τ) : SN t := by
120+
theorem strong_norm {t : Term Var} {τ : Ty Base} (der : Γ ⊢ t ∶ τ) : SN FullBeta t := by
121121
apply (semanticMap_saturated τ).sn
122122
apply (soundness der [] (by grind) entails_context_empty)
123123

Cslib/Languages/LambdaCalculus/LocallyNameless/Untyped/StrongNorm.lean

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,59 +24,52 @@ namespace LambdaCalculus.LocallyNameless.Untyped.Term
2424

2525
variable {Var : Type u} {t t' : Term Var}
2626

27-
open FullBeta
27+
open FullBeta Relation
2828

2929
attribute [grind =] Finset.union_singleton
3030

31-
/-- A term is strongly normalizing if every reduction sequence terminates at some point.
32-
This is ensured by the following type as inductive data must always be finite. -/
33-
inductive SN {α} : Term α → Prop
34-
| sn t : (∀ t', t ⭢βᶠ t' → SN t') → SN t
35-
36-
attribute [scoped grind .] SN.sn
37-
3831
/-- A single β-reduction step preserves strong normalization. -/
39-
lemma sn_step (t_st_t' : t ⭢βᶠ t') (sn_t : SN t) : SN t' := by
40-
grind [cases SN]
32+
lemma sn_step (t_st_t' : t ⭢βᶠ t') (sn_t : SN FullBeta t) : SN FullBeta t' :=
33+
sn_t.of_rel t_st_t'
4134

4235
/-- Multiple β-reduction steps also preserve strong normalization. -/
43-
lemma sn_steps (t_st_t' : t ↠βᶠ t') (sn_t : SN t) : SN t' := by
44-
induction t_st_t' with grind [sn_step]
36+
lemma sn_steps (t_st_t' : t ↠βᶠ t') (sn_t : SN FullBeta t) : SN FullBeta t' :=
37+
sn_t.of_rel_reflTransGen t_st_t'
4538

4639
/-- Free variables are strongly normalizing. -/
47-
lemma sn_fvar {x : Var} : SN (fvar x) := by
48-
grind only [cases Xi, cases Beta, SN]
40+
lemma sn_fvar {x : Var} : SN FullBeta (fvar x) := by
41+
rw [SN_iff_SN_of_rel]
42+
grind only [cases Xi, cases Beta]
4943

5044
/-- An application is strongly normalizing if the left and right terms are strongly normalizing,
5145
as well as all possible future top level abstraction application beta reductions -/
52-
lemma sn_app (t s : Term Var) (sn_t : SN t) (sn_s : SN s)
53-
(hβ : ∀ {t' s' : Term Var}, t ↠βᶠ t'.abs → s ↠βᶠ s' → SN (t' ^ s')) : SN (t.app s) := by
46+
lemma sn_app (t s : Term Var) (sn_t : SN FullBeta t) (sn_s : SN FullBeta s)
47+
(hβ : ∀ {t' s' : Term Var}, t ↠βᶠ t'.abs → s ↠βᶠ s' → SN FullBeta (t' ^ s')) :
48+
SN FullBeta (t.app s) := by
5449
induction sn_t generalizing s with
55-
| sn t ht ih_t =>
50+
| intro t ht ih_t =>
5651
induction sn_s with
57-
| sn s hs ih_s =>
52+
| intro s hs ih_s =>
5853
constructor
5954
intro u hstep
6055
cases hstep with
6156
| base h => cases h; grind
6257
| appL _ h_s_red => apply ih_s _ h_s_red
6358
grind [Relation.ReflTransGen.head]
64-
| appR _ h_t_red => apply ih_t _ h_t_red _ (SN.sn s hs)
59+
| appR _ h_t_red => apply ih_t _ h_t_red _ (SN.intro hs)
6560
grind [Relation.ReflTransGen.head]
6661

6762
/-- The left side of a strongly normalizing application is strongly normalizing. -/
68-
lemma sn_app_left (M N : Term Var) (lc_N : Term.LC N) (sn_MN : SN (M.app N)) :
69-
SN M := by
70-
generalize Heq : M.app N = P
71-
rw [Heq] at sn_MN
72-
induction sn_MN generalizing M N with grind
63+
lemma sn_app_left (M N : Term Var) (lc_N : Term.LC N) (sn_MN : SN FullBeta (M.app N)) :
64+
SN FullBeta M := by
65+
refine sn_MN.onFun_of_image (f := (·.app N)) |>.of_le fun _ _ => ?_
66+
exact Xi.appR lc_N
7367

7468
/-- The right side of a strongly normalizing application is strongly normalizing. -/
75-
lemma sn_app_right (M N : Term Var) (lc_N : Term.LC M) (sn_MN : SN (M.app N)) :
76-
SN N := by
77-
generalize Heq : M.app N = P
78-
rw [Heq] at sn_MN
79-
induction sn_MN generalizing M N with grind
69+
lemma sn_app_right (M N : Term Var) (lc_M : Term.LC M) (sn_MN : SN FullBeta (M.app N)) :
70+
SN FullBeta N := by
71+
refine sn_MN.onFun_of_image (f := M.app) |>.of_le fun _ _ => ?_
72+
exact Xi.appL lc_M
8073

8174
/-- A neutral term is a term of the form v t₁ … t_n where
8275
v is a variable and t₁ … t_n are strongly normalizing terms. -/
@@ -87,7 +80,7 @@ inductive Neutral : Term Var → Prop
8780
/-- Just a free variable is neutral. -/
8881
| fvar : ∀ x, Neutral (fvar x)
8982
/-- Applying a strongly normalizing term to a neutral term yields a neutral term. -/
90-
| app : ∀ t1 t2, Neutral t1 → SN t2 → Neutral (app t1 t2)
83+
| app : ∀ t1 t2, Neutral t1 → SN FullBeta t2 → Neutral (app t1 t2)
9184

9285
--attribute [scoped grind .] Neutral.bvar Neutral.fvar Neutral.app
9386

@@ -100,17 +93,19 @@ lemma neutral_steps (Hneut : Neutral t) (Hsteps : t ↠βᶠ t') : Neutral t' :=
10093
induction Hsteps <;> grind [neutral_step]
10194

10295
/-- Neutral terms are strongly normalizing. -/
103-
lemma sn_neutral (Hneut : Neutral t) : SN t := by
96+
lemma sn_neutral (Hneut : Neutral t) : SN FullBeta t := by
10497
induction Hneut with
10598
| app => grind [→ neutral_steps, sn_app]
106-
| _ => grind only [SN, cases Xi]
99+
| _ =>
100+
rw [SN_iff_SN_of_rel]
101+
grind only [cases Xi]
107102

108103
/-- A lambda abstraction is strongly normalizing if its body is strongly normalizing. -/
109-
lemma sn_abs [DecidableEq Var] [HasFresh Var] {M N : Term Var} (sn_MN : SN (M ^ N)) (lc_N : LC N) :
110-
SN (abs M) := by
104+
lemma sn_abs [DecidableEq Var] [HasFresh Var] {M N : Term Var} (sn_MN : SN FullBeta (M ^ N))
105+
(lc_N : LC N) : SN FullBeta (abs M) := by
111106
generalize h : (M ^ N) = M_open at sn_MN
112107
induction sn_MN generalizing M N with
113-
| sn =>
108+
| intro =>
114109
constructor
115110
intro _ h_step
116111
cases h_step with
@@ -123,8 +118,9 @@ lemma sn_abs [DecidableEq Var] [HasFresh Var] {M N : Term Var} (sn_MN : SN (M ^
123118
1. N is locally closed,
124119
1. M ^ N P₁ … Pₙ is locally closed -/
125120
lemma sn_abs_app_multiApp [DecidableEq Var] [HasFresh Var] {Ps} {M N : Term Var}
126-
(sn_N : SN N) (sn_MNPs : SN (multiApp (M ^ N) Ps))
127-
(lc_N : LC N) (lc_MNPs : LC (multiApp (M ^ N) Ps)) : SN (multiApp (M.abs.app N) Ps) := by
121+
(sn_N : SN FullBeta N) (sn_MNPs : SN FullBeta (multiApp (M ^ N) Ps))
122+
(lc_N : LC N) (lc_MNPs : LC (multiApp (M ^ N) Ps)) :
123+
SN FullBeta (multiApp (M.abs.app N) Ps) := by
128124
induction Ps with
129125
| nil =>
130126
apply sn_app

0 commit comments

Comments
 (0)