lean4-htt/src/Init/WF.lean
Joachim Breitner 977ae92e43
fix: module system: remove WellFounded-specific hacks (#9143)
This PR removes a rather ugly hack in the module system, exposing the
bodies of theorems whose type mention `WellFounded`.

The original motivation was that reducing well-founded definitions (e.g.
in `by rfl`) requires reducing proofs, so they need to be available.

But reducing proofs is generally fraught with peril, and we have been
nudging our users away from using it for a while, e.g. in #5182. Since
the module system is opt-in and users will gradually migrate to it, it
may be reasonable to expect them to avoid reducing well-founded
recursion in the process

This way we don't need hacks like this (which, without evidence, I
believe would be incomplete anyways) and we get the nice guarantee that
within the module system, theorems bodies are always private.
2025-07-02 11:58:50 +00:00

457 lines
16 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
module
prelude
public import Init.SizeOf
public import Init.BinderNameHint
public import Init.Data.Nat.Basic
public section
@[expose] section
universe u v
/--
`Acc` is the accessibility predicate. Given some relation `r` (e.g. `<`) and a value `x`,
`Acc r x` means that `x` is accessible through `r`:
`x` is accessible if there exists no infinite sequence `... < y₂ < y₁ < y₀ < x`.
-/
inductive Acc {α : Sort u} (r : αα → Prop) : α → Prop where
/--
A value is accessible if for all `y` such that `r y x`, `y` is also accessible.
Note that if there exists no `y` such that `r y x`, then `x` is accessible. Such an `x` is called a
_base case_.
-/
| intro (x : α) (h : (y : α) → r y x → Acc r y) : Acc r x
noncomputable abbrev Acc.ndrec.{u1, u2} {α : Sort u2} {r : αα → Prop} {C : α → Sort u1}
(m : (x : α) → ((y : α) → r y x → Acc r y) → ((y : α) → (a : r y x) → C y) → C x)
{a : α} (n : Acc r a) : C a :=
n.rec m
noncomputable abbrev Acc.ndrecOn.{u1, u2} {α : Sort u2} {r : αα → Prop} {C : α → Sort u1}
{a : α} (n : Acc r a)
(m : (x : α) → ((y : α) → r y x → Acc r y) → ((y : α) → (a : r y x) → C y) → C x)
: C a :=
n.rec m
namespace Acc
variable {α : Sort u} {r : αα → Prop}
theorem inv {x y : α} (h₁ : Acc r x) (h₂ : r y x) : Acc r y :=
h₁.recOn (fun _ ac₁ _ h₂ => ac₁ y h₂) h₂
end Acc
/--
A relation `r` is `WellFounded` if all elements of `α` are accessible within `r`.
If a relation is `WellFounded`, it does not allow for an infinite descent along the relation.
If the arguments of the recursive calls in a function definition decrease according to
a well founded relation, then the function terminates.
Well-founded relations are sometimes called _Artinian_ or said to satisfy the “descending chain condition”.
-/
inductive WellFounded {α : Sort u} (r : αα → Prop) : Prop where
/--
If all elements are accessible via `r`, then `r` is well-founded.
-/
| intro (h : ∀ a, Acc r a) : WellFounded r
/--
A type that has a standard well-founded relation.
Instances are used to prove that functions terminate using well-founded recursion by showing that
recursive calls reduce some measure according to a well-founded relation. This relation can combine
well-founded relations on the recursive function's parameters.
-/
class WellFoundedRelation (α : Sort u) where
/-- A well-founded relation on `α`. -/
rel : αα → Prop
/-- A proof that `rel` is, in fact, well-founded. -/
wf : WellFounded rel
namespace WellFounded
theorem apply {α : Sort u} {r : αα → Prop} (wf : WellFounded r) (a : α) : Acc r a :=
wf.rec (fun p => p) a
section
variable {α : Sort u} {r : αα → Prop} (hwf : WellFounded r)
noncomputable def recursion {C : α → Sort v} (a : α) (h : ∀ x, (∀ y, r y x → C y) → C x) : C a := by
induction (apply hwf a) with
| intro x₁ _ ih => exact h x₁ ih
include hwf in
theorem induction {C : α → Prop} (a : α) (h : ∀ x, (∀ y, r y x → C y) → C x) : C a :=
recursion hwf a h
variable {C : α → Sort v}
variable (F : ∀ x, (∀ y, r y x → C y) → C x)
noncomputable def fixF (x : α) (a : Acc r x) : C x := by
induction a with
| intro x₁ _ ih => exact F x₁ ih
theorem fixF_eq (x : α) (acx : Acc r x) : fixF F x acx = F x (fun (y : α) (p : r y x) => fixF F y (Acc.inv acx p)) := by
induction acx with
| intro x r _ => exact rfl
@[deprecated fixF_eq (since := "2025-04-04")]
abbrev fixFEq := @fixF_eq
end
variable {α : Sort u} {C : α → Sort v} {r : αα → Prop}
/--
A well-founded fixpoint. If satisfying the motive `C` for all values that are smaller according to a
well-founded relation allows it to be satisfied for the current value, then it is satisfied for all
values.
This function is used as part of the elaboration of well-founded recursion.
-/
-- Well-founded fixpoint
noncomputable def fix (hwf : WellFounded r) (F : ∀ x, (∀ y, r y x → C y) → C x) (x : α) : C x :=
fixF F x (apply hwf x)
-- Well-founded fixpoint satisfies fixpoint equation
theorem fix_eq (hwf : WellFounded r) (F : ∀ x, (∀ y, r y x → C y) → C x) (x : α) :
fix hwf F x = F x (fun y _ => fix hwf F y) :=
fixF_eq F x (apply hwf x)
end WellFounded
open WellFounded
-- Empty relation is well-founded
def emptyWf {α : Sort u} : WellFoundedRelation α where
rel := emptyRelation
wf := by
apply WellFounded.intro
intro a
apply Acc.intro a
intro b h
cases h
-- Subrelation of a well-founded relation is well-founded
namespace Subrelation
variable {α : Sort u} {r q : αα → Prop}
theorem accessible {a : α} (h₁ : Subrelation q r) (ac : Acc r a) : Acc q a := by
induction ac with
| intro x _ ih =>
apply Acc.intro
intro y h
exact ih y (h₁ h)
theorem wf (h₁ : Subrelation q r) (h₂ : WellFounded r) : WellFounded q :=
⟨fun a => accessible @h₁ (apply h₂ a)⟩
end Subrelation
-- The inverse image of a well-founded relation is well-founded
namespace InvImage
variable {α : Sort u} {β : Sort v} {r : β → β → Prop}
theorem accAux (f : α → β) {b : β} (ac : Acc r b) : (x : α) → f x = b → Acc (InvImage r f) x :=
Acc.recOn ac fun _ _ ih => fun _ e => Acc.intro _ (fun y lt => ih (f y) (e ▸ lt) y rfl)
theorem accessible {a : α} (f : α → β) (ac : Acc r (f a)) : Acc (InvImage r f) a :=
accAux f ac a rfl
theorem wf (f : α → β) (h : WellFounded r) : WellFounded (InvImage r f) :=
⟨fun a => accessible f (apply h (f a))⟩
end InvImage
/--
The inverse image of a well-founded relation is well-founded.
-/
@[reducible] def invImage (f : α → β) (h : WellFoundedRelation β) : WellFoundedRelation α where
rel := InvImage h.rel f
wf := InvImage.wf f h.wf
-- The transitive closure of a well-founded relation is well-founded
open Relation
theorem Acc.transGen (h : Acc r a) : Acc (TransGen r) a := by
induction h with
| intro x _ H =>
refine Acc.intro x fun y hy ↦ ?_
cases hy with
| single hyx =>
exact H y hyx
| tail hyz hzx =>
exact (H _ hzx).inv hyz
theorem acc_transGen_iff : Acc (TransGen r) a ↔ Acc r a :=
⟨Subrelation.accessible TransGen.single, Acc.transGen⟩
theorem WellFounded.transGen (h : WellFounded r) : WellFounded (TransGen r) :=
⟨fun a ↦ (h.apply a).transGen⟩
namespace Nat
-- less-than is well-founded
def lt_wfRel : WellFoundedRelation Nat where
rel := (· < ·)
wf := by
apply WellFounded.intro
intro n
induction n with
| zero =>
apply Acc.intro 0
intro _ h
apply absurd h (Nat.not_lt_zero _)
| succ n ih =>
apply Acc.intro (Nat.succ n)
intro m h
have : m = n m < n := Nat.eq_or_lt_of_le (Nat.le_of_succ_le_succ h)
match this with
| Or.inl e => subst e; assumption
| Or.inr e => exact Acc.inv ih e
/--
Strong induction on the natural numbers.
The induction hypothesis is that all numbers less than a given number satisfy the motive, which
should be demonstrated for the given number.
-/
@[elab_as_elim] protected noncomputable def strongRecOn
{motive : Nat → Sort u}
(n : Nat)
(ind : ∀ n, (∀ m, m < n → motive m) → motive n) : motive n :=
Nat.lt_wfRel.wf.fix ind n
/--
Case analysis based on strong induction for the natural numbers.
-/
@[elab_as_elim] protected noncomputable def caseStrongRecOn
{motive : Nat → Sort u}
(a : Nat)
(zero : motive 0)
(ind : ∀ n, (∀ m, m ≤ n → motive m) → motive (succ n)) : motive a :=
Nat.strongRecOn a fun n =>
match n with
| 0 => fun _ => zero
| n+1 => fun h₁ => ind n (λ _ h₂ => h₁ _ (lt_succ_of_le h₂))
end Nat
abbrev measure {α : Sort u} (f : α → Nat) : WellFoundedRelation α :=
invImage f Nat.lt_wfRel
abbrev sizeOfWFRel {α : Sort u} [SizeOf α] : WellFoundedRelation α :=
measure sizeOf
attribute [instance low] sizeOfWFRel
namespace Prod
open WellFounded
section
variable {α : Type u} {β : Type v}
variable (ra : αα → Prop)
variable (rb : β → β → Prop)
/--
A lexicographical order based on the orders `ra` and `rb` for the elements of pairs.
-/
protected inductive Lex : α × β → α × β → Prop where
/--
If the first projections of two pairs are ordered, then they are lexicographically ordered.
-/
| left {a₁} (b₁) {a₂} (b₂) (h : ra a₁ a₂) : Prod.Lex (a₁, b₁) (a₂, b₂)
/--
If the first projections of two pairs are equal, then they are lexicographically ordered if the
second projections are ordered.
-/
| right (a) {b₁ b₂} (h : rb b₁ b₂) : Prod.Lex (a, b₁) (a, b₂)
theorem lex_def {r : αα → Prop} {s : β → β → Prop} {p q : α × β} :
Prod.Lex r s p q ↔ r p.1 q.1 p.1 = q.1 ∧ s p.2 q.2 :=
⟨fun h => by cases h <;> simp [*], fun h =>
match p, q, h with
| _, _, Or.inl h => Lex.left _ _ h
| (_, _), (_, _), Or.inr ⟨e, h⟩ => by subst e; exact Lex.right _ h⟩
namespace Lex
instance [αeqDec : DecidableEq α] {r : αα → Prop} [rDec : DecidableRel r]
{s : β → β → Prop} [sDec : DecidableRel s] : DecidableRel (Prod.Lex r s)
| (a, b), (a', b') =>
match rDec a a' with
| isTrue raa' => isTrue $ left b b' raa'
| isFalse nraa' =>
match αeqDec a a' with
| isTrue eq => by
subst eq
cases sDec b b' with
| isTrue sbb' => exact isTrue $ right a sbb'
| isFalse nsbb' =>
apply isFalse; intro contra; cases contra <;> contradiction
| isFalse neqaa' => by
apply isFalse; intro contra; cases contra <;> contradiction
-- TODO: generalize
theorem right' {a₁ : Nat} {b₁ : β} (h₁ : a₁ ≤ a₂) (h₂ : rb b₁ b₂) : Prod.Lex Nat.lt rb (a₁, b₁) (a₂, b₂) :=
match Nat.eq_or_lt_of_le h₁ with
| Or.inl h => h ▸ Prod.Lex.right a₁ h₂
| Or.inr h => Prod.Lex.left b₁ _ h
end Lex
-- relational product based on ra and rb
inductive RProd : α × β → α × β → Prop where
| intro {a₁ b₁ a₂ b₂} (h₁ : ra a₁ a₂) (h₂ : rb b₁ b₂) : RProd (a₁, b₁) (a₂, b₂)
end
section
variable {α : Type u} {β : Type v}
variable {ra : αα → Prop} {rb : β → β → Prop}
theorem lexAccessible {a : α} (aca : Acc ra a) (acb : (b : β) → Acc rb b) (b : β) : Acc (Prod.Lex ra rb) (a, b) := by
induction aca generalizing b with
| intro xa _ iha =>
induction (acb b) with
| intro xb _ ihb =>
apply Acc.intro (xa, xb)
intro p lt
cases lt with
| left _ _ h => apply iha _ h
| right _ h => apply ihb _ h
-- The lexicographical order of well founded relations is well-founded
@[reducible] def lex (ha : WellFoundedRelation α) (hb : WellFoundedRelation β) : WellFoundedRelation (α × β) where
rel := Prod.Lex ha.rel hb.rel
wf := ⟨fun (a, b) => lexAccessible (WellFounded.apply ha.wf a) (WellFounded.apply hb.wf) b⟩
instance [ha : WellFoundedRelation α] [hb : WellFoundedRelation β] : WellFoundedRelation (α × β) :=
lex ha hb
-- relational product is a Subrelation of the Lex
theorem RProdSubLex (a : α × β) (b : α × β) (h : RProd ra rb a b) : Prod.Lex ra rb a b := by
cases h with
| intro h₁ h₂ => exact Prod.Lex.left _ _ h₁
-- The relational product of well founded relations is well-founded
def rprod (ha : WellFoundedRelation α) (hb : WellFoundedRelation β) : WellFoundedRelation (α × β) where
rel := RProd ha.rel hb.rel
wf := by
apply Subrelation.wf (r := Prod.Lex ha.rel hb.rel) (h₂ := (lex ha hb).wf)
intro a b h
exact RProdSubLex a b h
end
end Prod
namespace PSigma
section
variable {α : Sort u} {β : α → Sort v}
variable (r : αα → Prop)
variable (s : ∀ a, β a → β a → Prop)
-- Lexicographical order based on r and s
inductive Lex : PSigma β → PSigma β → Prop where
| left : ∀ {a₁ : α} (b₁ : β a₁) {a₂ : α} (b₂ : β a₂), r a₁ a₂ → Lex ⟨a₁, b₁⟩ ⟨a₂, b₂⟩
| right : ∀ (a : α) {b₁ b₂ : β a}, s a b₁ b₂ → Lex ⟨a, b₁⟩ ⟨a, b₂⟩
end
section
variable {α : Sort u} {β : α → Sort v}
variable {r : αα → Prop} {s : ∀ (a : α), β a → β a → Prop}
theorem lexAccessible {a} (aca : Acc r a) (acb : (a : α) → WellFounded (s a)) (b : β a) : Acc (Lex r s) ⟨a, b⟩ := by
induction aca with
| intro xa _ iha =>
induction (WellFounded.apply (acb xa) b) with
| intro xb _ ihb =>
apply Acc.intro
intro p lt
cases lt with
| left => apply iha; assumption
| right => apply ihb; assumption
-- The lexicographical order of well founded relations is well-founded
@[reducible] def lex (ha : WellFoundedRelation α) (hb : (a : α) → WellFoundedRelation (β a)) : WellFoundedRelation (PSigma β) where
rel := Lex ha.rel (fun a => hb a |>.rel)
wf := WellFounded.intro fun ⟨a, b⟩ => lexAccessible (WellFounded.apply ha.wf a) (fun a => hb a |>.wf) b
instance [ha : WellFoundedRelation α] [hb : (a : α) → WellFoundedRelation (β a)] : WellFoundedRelation (PSigma β) :=
lex ha hb
end
section
variable {α : Sort u} {β : Sort v}
def lexNdep (r : αα → Prop) (s : β → β → Prop) :=
Lex r (fun _ => s)
theorem lexNdepWf {r : αα → Prop} {s : β → β → Prop} (ha : WellFounded r) (hb : WellFounded s) : WellFounded (lexNdep r s) :=
WellFounded.intro fun ⟨a, b⟩ => lexAccessible (WellFounded.apply ha a) (fun _ => hb) b
end
section
variable {α : Sort u} {β : Sort v}
-- Reverse lexicographical order based on r and s
inductive RevLex (r : αα → Prop) (s : β → β → Prop) : @PSigma α (fun _ => β) → @PSigma α (fun _ => β) → Prop where
| left : {a₁ a₂ : α} → (b : β) → r a₁ a₂ → RevLex r s ⟨a₁, b⟩ ⟨a₂, b⟩
| right : (a₁ : α) → {b₁ : β} → (a₂ : α) → {b₂ : β} → s b₁ b₂ → RevLex r s ⟨a₁, b₁⟩ ⟨a₂, b₂⟩
end
section
open WellFounded
variable {α : Sort u} {β : Sort v}
variable {r : αα → Prop} {s : β → β → Prop}
theorem revLexAccessible {b} (acb : Acc s b) (aca : (a : α) → Acc r a): (a : α) → Acc (RevLex r s) ⟨a, b⟩ := by
induction acb with
| intro xb _ ihb =>
intro a
induction (aca a) with
| intro xa _ iha =>
apply Acc.intro
intro p lt
cases lt with
| left => apply iha; assumption
| right => apply ihb; assumption
theorem revLex (ha : WellFounded r) (hb : WellFounded s) : WellFounded (RevLex r s) :=
WellFounded.intro fun ⟨a, b⟩ => revLexAccessible (apply hb b) (WellFounded.apply ha) a
end
section
def SkipLeft (α : Type u) {β : Type v} (s : β → β → Prop) : @PSigma α (fun _ => β) → @PSigma α (fun _ => β) → Prop :=
RevLex emptyRelation s
def skipLeft (α : Type u) {β : Type v} (hb : WellFoundedRelation β) : WellFoundedRelation (PSigma fun _ : α => β) where
rel := SkipLeft α hb.rel
wf := revLex emptyWf.wf hb.wf
theorem mkSkipLeft {α : Type u} {β : Type v} {b₁ b₂ : β} {s : β → β → Prop} (a₁ a₂ : α) (h : s b₁ b₂) : SkipLeft α s ⟨a₁, b₁⟩ ⟨a₂, b₂⟩ :=
RevLex.right _ _ h
end
end PSigma
/--
The `wfParam` gadget is used internally during the construction of recursive functions by
wellfounded recursion, to keep track of the parameter for which the automatic introduction
of `List.attach` (or similar) is plausible.
-/
def wfParam {α : Sort u} (a : α) : α := a
/--
Reverse direction of `dite_eq_ite`. Used by the well-founded definition preprocessor to extend the
context of a termination proof inside `if-then-else` with the condition.
-/
@[wf_preprocess] theorem ite_eq_dite [Decidable P] :
ite P a b = (dite P (fun h => binderNameHint h () a) (fun h => binderNameHint h () b)) := rfl