From c034a7ea1335705ca1e1a7461fac257df6757d57 Mon Sep 17 00:00:00 2001 From: Son Ho Date: Fri, 9 Jun 2023 16:07:39 +0200 Subject: Start working on extrinsic proofs of termination --- backends/lean/Base/Diverge.lean | 208 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 backends/lean/Base/Diverge.lean (limited to 'backends/lean/Base/Diverge.lean') diff --git a/backends/lean/Base/Diverge.lean b/backends/lean/Base/Diverge.lean new file mode 100644 index 00000000..bd500c25 --- /dev/null +++ b/backends/lean/Base/Diverge.lean @@ -0,0 +1,208 @@ +import Lean +import Base.Primitives + +namespace Diverge + +open Primitives + +section Fix + +open Result + +variable {a b : Type} + +/-! # The least fixed point definition and its properties -/ + +def least_p (p : Nat → Prop) (n : Nat) : Prop := p n ∧ (∀ m, m < n → ¬ p m) +noncomputable def least (p : Nat → Prop) : Nat := + Classical.epsilon (least_p p) + +-- Auxiliary theorem for [least_spec]: if there exists an `n` satisfying `p`, +-- there there exists a least `m` satisfying `p`. +theorem least_spec_aux (p : Nat → Prop) : ∀ (n : Nat), (hn : p n) → ∃ m, least_p p m := by + apply Nat.strongRec' + intros n hi hn + -- Case disjunction on: is n the smallest n satisfying p? + match Classical.em (∀ m, m < n → ¬ p m) with + | .inl hlt => + -- Yes: trivial + exists n + | .inr hlt => + simp at * + let ⟨ m, ⟨ hmlt, hm ⟩ ⟩ := hlt + have hi := hi m hmlt hm + apply hi + +-- The specification of [least]: either `p` is never satisfied, or it is satisfied +-- by `least p` and no `n < least p` satisfies `p`. +theorem least_spec (p : Nat → Prop) : (∀ n, ¬ p n) ∨ (p (least p) ∧ ∀ n, n < least p → ¬ p n) := by + -- Case disjunction on the existence of an `n` which satisfies `p` + match Classical.em (∀ n, ¬ p n) with + | .inl h => + -- There doesn't exist: trivial + apply (Or.inl h) + | .inr h => + -- There exists: we simply use `least_spec_aux` in combination with the property + -- of the epsilon operator + simp at * + let ⟨ n, hn ⟩ := h + apply Or.inr + have hl := least_spec_aux p n hn + have he := Classical.epsilon_spec hl + apply he + +/-! # The fixed point definitions -/ + +def fix_fuel (n : Nat) (f : (a → Result b) → a → Result b) (x : a) : Result b := + match n with + | 0 => .div + | n + 1 => + f (fix_fuel n f) x + +@[simp] def fix_fuel_pred (f : (a → Result b) → a → Result b) (x : a) (n : Nat) := + not (div? (fix_fuel n f x)) + +def fix_fuel_P (f : (a → Result b) → a → Result b) (x : a) (n : Nat) : Prop := + fix_fuel_pred f x n + +noncomputable def fix (f : (a → Result b) → a → Result b) (x : a) : Result b := + fix_fuel (least (fix_fuel_P f x)) f x + +/-! # The proof of the fixed point equation -/ + +-- Monotonicity relation over results +-- TODO: generalize +def result_rel {a : Type u} (x1 x2 : Result a) : Prop := + match x1 with + | div => True + | fail _ => x2 = x1 + | ret _ => x2 = x1 -- TODO: generalize + +-- Monotonicity relation over monadic arrows +-- TODO: generalize +def marrow_rel (f g : a → Result b) : Prop := + ∀ x, result_rel (f x) (g x) + +-- Validity property for a body +def is_valid (f : (a → Result b) → a → Result b) : Prop := + ∀ {{g h}}, marrow_rel g h → marrow_rel (f g) (f h) + +/- + + -/ + +theorem fix_fuel_mono {f : (a → Result b) → a → Result b} (Hvalid : is_valid f) : + ∀ {{n m}}, n ≤ m → marrow_rel (fix_fuel n f) (fix_fuel m f) := by + intros n + induction n + case zero => simp [marrow_rel, fix_fuel, result_rel] + case succ n1 Hi => + intros m Hle x + simp [result_rel] + match m with + | 0 => + exfalso + -- TODO: annoying to do those conversions by hand - try zify? + have : n1 + 1 ≤ (0 : Int) := by simp [*] at * + have : 0 ≤ n1 := by simp [*] at * + linarith + | Nat.succ m1 => + simp_arith at Hle + simp [fix_fuel] + have Hi := Hi Hle + simp [is_valid] at Hvalid + have Hvalid := Hvalid Hi x + simp [result_rel] at Hvalid + apply Hvalid + +@[simp] theorem neg_fix_fuel_P {f : (a → Result b) → a → Result b} {x : a} {n : Nat} : + ¬ fix_fuel_P f x n ↔ (fix_fuel n f x = div) := by + simp [fix_fuel_P, div?] + cases fix_fuel n f x <;> simp + +theorem fix_fuel_fix_mono {f : (a → Result b) → a → Result b} (Hvalid : is_valid f) : + ∀ n, marrow_rel (fix_fuel n f) (fix f) := by + intros n x + simp [result_rel] + have Hl := least_spec (fix_fuel_P f x) + simp at Hl + match Hl with + | .inl Hl => simp [*] + | .inr ⟨ Hl, Hn ⟩ => + match Classical.em (fix_fuel n f x = div) with + | .inl Hd => + simp [*] + | .inr Hd => + have Hineq : least (fix_fuel_P f x) ≤ n := by + -- Proof by contradiction + cases Classical.em (least (fix_fuel_P f x) ≤ n) <;> simp [*] + simp at * + rename_i Hineq + have Hn := Hn n Hineq + contradiction + have Hfix : ¬ (fix f x = div) := by + simp [fix] + -- By property of the least upper bound + revert Hd Hl + -- TODO: there is no conversion to select the head of a function! + have : fix_fuel_P f x (least (fix_fuel_P f x)) = fix_fuel_pred f x (least (fix_fuel_P f x)) := + by simp[fix_fuel_P] + simp [this, div?] + clear this + cases fix_fuel (least (fix_fuel_P f x)) f x <;> simp + have Hmono := fix_fuel_mono Hvalid Hineq x + simp [result_rel] at Hmono + -- TODO: there is no conversion to select the head of a function! + revert Hmono Hfix Hd + simp [fix] + -- TODO: it would be good if cases actually introduces an equation: this + -- way we wouldn't have to do all the book-keeping + cases fix_fuel (least (fix_fuel_P f x)) f x <;> cases fix_fuel n f x <;> + intros <;> simp [*] at * + +theorem fix_fuel_P_least {f : (a → Result b) → a → Result b} (Hvalid : is_valid f) : + ∀ {{x n}}, fix_fuel_P f x n → fix_fuel_P f x (least (fix_fuel_P f x)) := by sorry + +theorem fix_fixed_eq (f : (a → Result b) → a → Result b) (Hvalid : is_valid f) : + ∀ x, fix f x = f (fix f) x := by + intros x + -- conv => lhs; simp [fix] + -- Case disjunction: is there a fuel such that the execution successfully execute? + match Classical.em (∃ n, fix_fuel_P f x n) with + | .inr He => + -- No fuel: the fixed point evaluates to `div` + --simp [fix] at * + simp at * + simp [fix] + have He := He (Nat.succ (least (fix_fuel_P f x))) + simp [*, fix_fuel] at * + -- Use the monotonicity of `f` + have Hmono := fix_fuel_fix_mono Hvalid (least (fix_fuel_P f x)) x + simp [result_rel] at Hmono + simp [*] at * + -- TODO: we need a stronger validity predicate + sorry + | .inl ⟨ n, He ⟩ => + have Hl := fix_fuel_P_least Hvalid He + -- TODO: better control of simplification + have Heq : fix_fuel_P f x (least (fix_fuel_P f x)) = fix_fuel_pred f x (least (fix_fuel_P f x)) := + by simp [fix_fuel_P] + simp [Heq] at Hl; clear Heq + -- The least upper bound is > 0 + have ⟨ n, Hsucc ⟩ : ∃ n, least (fix_fuel_P f x) = Nat.succ n := by sorry + simp [Hsucc] at Hl + revert Hl + simp [*, div?, fix, fix_fuel] + -- Use the monotonicity + have Hineq : n ≤ Nat.succ n := by sorry + have Hmono := fix_fuel_fix_mono Hvalid n + have Hv := Hvalid Hmono x + -- Use functional extensionality + simp [result_rel, fix] at Hv + revert Hv + split <;> simp [*] <;> intros <;> simp [*] + + +end Fix + +end Diverge -- cgit v1.2.3