module transcript05 where -- Let's state and prove the following fact: -- Every function f : ℕ → ℕ has a minimal value. -- ∀f : ℕ → ℕ. ∃a : ℕ. ∀b : ℕ. f(a) ≤ f(b) open import Padova2025.ProgrammingBasics.Naturals.Base open import Padova2025.ProgrammingBasics.Naturals.Arithmetic open import Padova2025.ProvingBasics.Connectives.Existential open import Padova2025.ProvingBasics.Connectives.Disjunction open import Padova2025.ProvingBasics.Termination.Ordering -- Dickson's Lemma: For every function f : ℕ → ℕ, -- there is a number i : ℕ such that f(i) ≤ f(i+1). -- (This lemma is relevant in the subject of "well-quasi orderings" -- in computer science.) -- -- Proof. The function f has some mimimal value f(i). -- Then, trivially, f(i) ≤ f(i+1). ∎ Dickson : (ℕ → ℕ) → Set Dickson f = Σ[ i ∈ ℕ ] f i ≤ f (succ i) module Classical where data ⊥ : Set where ¬_ : Set → Set ¬ A = A → ⊥ -- "¬ A" means that A implies ↯. ⊥-elim : {A : Set} → ⊥ → A ⊥-elim () postulate oracle : (A : Set) → A ⊎ ¬ A -- "x < y" is defined as "succ x ≤ y" -- The computational interpretation is: "minimum" is -- a function which inputs a function f : ℕ → ℕ -- and outputs a pair (a , g) where a : ℕ -- and g : (b : ℕ) → f a ≤ f b. -- (For any two numbers x and y, "x ≤ y" is the type of -- witnesses that x is at most y.) {-# TERMINATING #-} go : (f : ℕ → ℕ) → ℕ → Σ[ a ∈ ℕ ] ((b : ℕ) → f a ≤ f b) go f n with oracle (Σ[ m ∈ ℕ ] f m < f n) ... | left (m , fm<fn) = go f m ... | right p = (n , h) where h : (b : ℕ) → f n ≤ f b h b with ≤-<-connex (f n) (f b) ... | left fn≤fb = fn≤fb ... | right fb<fn = ⊥-elim (p (b , fb<fn)) -- Idea: Check whether f n is minimal. -- If so (case "right"): awesome, we are done. -- If not (case "left"): then there exists some m such that f m < f n. minimum : (f : ℕ → ℕ) → Σ[ a ∈ ℕ ] ((b : ℕ) → f a ≤ f b) minimum f = go f zero lemma : (f : ℕ → ℕ) → Dickson f lemma f with minimum f ... | i , p = (i , p (succ i)) -- Obviously, classical mathematics is larger than constructive mathematics: -- We have a couple more axioms available (law of excluded middle, axiom of choice). -- With more axioms, we can prove more stuff. -- On the other hand, we can also regard constructive mathematics as being larger -- than classical mathematics: Every* result of classical mathematics can be turned -- into a result of constructive mathematics, if we just spam sufficiently many -- double negations. -- (More precisely: Every proof involving the law of excluded middle can be transformed -- into a proof not using the law of excluded middle, at the price of a couple of -- double negations; for eliminating the axiom of choice, other techniques are needed.) -- Bonus metatheorem: In some case, we can, in the end, even eliminate the double negations. -- "Let us come to the section titled 'constructive but uninformative'. Fix a type ⊥." module ConstructiveButUninformative (⊥ : Set) where ¬_ : Set → Set ¬ A = A → ⊥ -- "¬ A" means that A implies ↯. ⊥-elim : {A : Set} → ⊥ → ¬ ¬ A ⊥-elim x = λ z → x oracle : (A : Set) → ¬ ¬ (A ⊎ ¬ A) oracle A = λ z → z (right (λ z₁ → z (left z₁))) -- The "return" here does not have anything to do with the "return" keyword -- in Python or Java (which would cause an early exit out of a subroutine). return : {A : Set} → A → ¬ ¬ A return x = λ z → z x -- Note: In constructive mathematics, we do NOT have (in general) ¬ ¬ A → A. -- (Example: Let A be the assertion "there is a position where the keys to my apartment are" -- in the situation that we don't find our keys. Then we cannot constructively claim A, -- but we can constructively claim ¬ ¬ A, as it's impossible for the keys to have vanished.) escape : ¬ ¬ ⊥ → ⊥ escape m = m (λ z → z) -- Logical reading: If ¬ ¬ A, and if A implies ¬ ¬ B, then ¬ ¬ B. _⟫=_ : {A B : Set} → ¬ ¬ A → (A → ¬ ¬ B) → ¬ ¬ B m ⟫= f = λ z → m (λ z₁ → f z₁ z) -- Note: Double negation constitutes a monad (as in Haskell). {-# TERMINATING #-} go : (f : ℕ → ℕ) → ℕ → ¬ (¬ (Σ[ a ∈ ℕ ] ((b : ℕ) → ¬ ¬ (f a ≤ f b)))) go f n = oracle (Σ[ m ∈ ℕ ] f m < f n) ⟫= g where g : (Σ[ m ∈ ℕ ] f m < f n) ⊎ ¬ (Σ[ m ∈ ℕ ] f m < f n) → _ g (left (m , fm<fn)) = go f m g (right p) = return (n , h) where h : (b : ℕ) → ¬ ¬ (f n ≤ f b) h b with ≤-<-connex (f n) (f b) ... | left fn≤fb = return fn≤fb ... | right fb<fn = ⊥-elim (p (b , fb<fn)) minimum : (f : ℕ → ℕ) → ¬ (¬ (Σ[ a ∈ ℕ ] ((b : ℕ) → ¬ ¬ (f a ≤ f b)))) minimum f = go f zero lemma : (f : ℕ → ℕ) → ¬ ¬ (Dickson f) lemma f = minimum f ⟫= λ (i , p) → p (succ i) ⟫= λ fi≤fsucci → return (i , fi≤fsucci) module Constructive where theorem : (f : ℕ → ℕ) → Dickson f theorem f = escape (lemma f) where open ConstructiveButUninformative (Dickson f) -- A philosophical interpretation of this state of affairs: -- Minima of functions are (very useful) convenient fictions. -- They are not actually available in constructive mathematics (as we cannot compute them), -- but we can just pretend that they are and still obtain fully valid constructive results in the end. example-function : ℕ → ℕ example-function zero = five example-function (succ zero) = four example-function (succ (succ zero)) = two example-function (succ (succ (succ zero))) = zero example-function (succ (succ (succ (succ zero)))) = four example-function n = n ² -- This constructivization technique is variously known as: -- "Friedman's trick" -- "the nontrivial exit continuation trick" -- "Barr's theorem" -- Option 1: Terminate this session. -- Option 2: Agda exercises. -- Option 3: Numbers larger than infinity. -- Option 4: Program verification. -- (Ordinal) Numbers larger than infinity! -- 0, 1, 2, 3, ..., ω, ω+1, ω+2, ..., ω+ω=ω·2, ..., ω·3, ..., ω·ω = ω², ..., -- ω³, ..., ω^ω, ..., ω^(ω^ω), ..., ω^(ω^(ω^...)) = ε₀, ε₀+1, -- ε₀^(ε₀^(ε₀^(...))) = ε₁, ..., ε₁^(ε₁^(...)) = ε₂, ..., ε_ω, ..., ε_{ω+1}, ..., -- ε_{ε_0}, ..., ε_{ε_{ε_0}}, ..., ε_{ε_{ε_...}} = ζ₀, ... -- ω + 1 > 1 + ω = ω -- Graphical visualization of these numbers: {- 7: :-) I I I I I I I ω: :-) I I I I I I I ... +-----------------+ +---------+ ω + ω + 1: :-) |I I I I I I I ...| |I I I ...| I +-----------------+ +---------+ +----------+ 1 + ω: :-) I |I I I I...| = ω +----------+ ω · 2 > 2 · ω = ω +-----------------+ +---------+ :-) |I I I I I I I ...| |I I I ...| +-----------------+ +---------+ +---------------+ 2 · ω :-) |II II II II ...| +---------------+ -} -- Type of (representations of) ordinal numbers -- Take care, most ordinal numbers will have several representations -- (compare with the reals: 1.000... = 0.999...) data O : Set -- "x ≤ y" the type of witnesses that x is at most y data _≼_ : O → O → Set -- "x < y" the type of witnesses that x is strictly smaller than y _≺_ : O → O → Set -- "Monotonic f" is the type of witnesses that the function f -- is strictly increasing: f 0 < f 1 < f 2 < ... -- More formally, ∀n : ℕ. f n < f (succ n) Monotonic : (ℕ → O) → Set Monotonic f = (n : ℕ) → f n ≺ f (succ n) data O where zer : O suc : O → O lim : (f : ℕ → O) → Monotonic f → O -- Take care, we may NOT state a constructor "lim : (ℕ → O) → O" here. -- Such a constructor would indicate that we may take the limit of -- any sequence of ordinals, even alternating ones which cannot decide -- to which number to converge to. x ≺ y = suc x ≼ y data _≼_ where ≼-zer : {x : O} → zer ≼ x ≼-suc-mon : {x y : O} → x ≼ y → suc x ≼ suc y ≼-trans : {x y z : O} → x ≼ y → y ≼ z → x ≼ z ≼-cocone : {f : ℕ → O} {fmon : Monotonic f} {x : O} {n : ℕ} → x ≼ f n → x ≼ lim f fmon ≼-limiting : {f : ℕ → O} {fmon : Monotonic f} {x : O} → ((n : ℕ) → f n ≼ x) → lim f fmon ≼ x lim-mon : {f g : ℕ → O} {fmon : Monotonic f} {gmon : Monotonic g} → ((n : ℕ) → f n ≼ g n) → lim f fmon ≼ lim g gmon lim-mon p = ≼-limiting (λ n → ≼-cocone (p n)) ≼-refl : {x : O} → x ≼ x ≼-refl {zer} = ≼-zer ≼-refl {suc x} = ≼-suc-mon ≼-refl ≼-refl {lim f x} = lim-mon (λ n → ≼-refl {f n}) uno : O uno = suc zer fromℕ : ℕ → O fromℕ zero = zer fromℕ (succ n) = suc (fromℕ n) fromℕ-mon : Monotonic fromℕ fromℕ-mon n = ≼-refl ω : O ω = lim fromℕ fromℕ-mon -- For instance, the number ω is the limit of the sequence 0, 1, 2, ... -- For instance, the number ω is the limit of the sequence f, -- where f : ℕ → O is given by f 0 = 0, f 1 = 1, f 2 = 2, ... -- In Agda, we can then write: ω = lim f -- For instance, the number ω is the limit of the sequence 1, 2, 3, ... -- For instance, the number ω is the limit of the sequence 1, 4, 9, 16, ... -- For instance, the number ω is the limit of the sequence f, -- where f : ℕ → O is given f n = (succ n)². -- For instance, the number ω+ω is the limit of the sequence ω, ω+1, ω+2, ... -- There are sequences without a limit in the ordinal numbers, for instance -- 0, 1, 0, 1, 0, 1, ... open import Padova2025.ProvingBasics.Connectives.Conjunction _≈_ : O → O → Set x ≈ y = x ≼ y × y ≼ x infixl 6 _+'_ _+'_ : O → O → O +-mon : {x a b : O} → a ≼ b → (x +' a) ≼ (x +' b) x +' zer = x x +' suc y = suc (x +' y) x +' lim f fmon = lim (λ n → x +' f n) (λ n → +-mon {x = x} (fmon n)) -- suc (f n) ≼ f (succ n) is equivalent (by definition) to f n ≺ f (succ n) +-mon p = {!!} claim₁ : (uno +' ω) ≼ ω claim₁ = ≼-limiting (λ n → ≼-cocone (lemma n)) where lemma : (n : ℕ) → (uno +' fromℕ n) ≼ suc (fromℕ n) lemma zero = ≼-suc-mon ≼-zer lemma (succ n) = ≼-suc-mon (lemma n) claim₂ : ω ≼ (uno +' ω) claim₂ = {!!}