This module is the foundation of the library. It defines the type
Ord of ordinals — Brouwer trees — as a quotient
inductive-inductive type (QIIT), mutually with its
order _≤_, following Kraus, Nordvall Forsberg and Xu, Type-Theoretic Approaches to
Ordinals. It also derives the two eliminators
(elimProp, ≤-elimProp) that every later module
is built from, and the basic order lemmas.
{-# OPTIONS --cubical --safe #-}
module QIITOrd.Base where
open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Path
open import Cubical.Data.Nat as ℕ using (ℕ)
open import Cubical.Data.Sigma
open import Cubical.Relation.Nullary using (¬_)
open import Cubical.Relation.Binary.Base using (Rel; module BinaryRelation)
open BinaryRelation using (isRefl'; isTrans')
We reuse the cubical library’s binary-relation vocabulary:
Rel A B ℓ is the type of ℓ-valued relations,
and BinaryRelation’s isRefl' /
isTrans' are the (implicit-argument) reflexivity and
transitivity predicates. We only ever need homogeneous relations
Rel A A ℓ.
A limit ordinal is built from a sequence ℕ → A. Two such
sequences should give the same limit exactly when they are
mutually cofinal — they bisimulate. f ≼ g
says every f n is dominated by some g m (the
existence is a propositional truncation ∃), and
f ≈ g is bisimilarity: f ≼ g and
g ≼ f. This is stated generally for any relation
_≤_, so the same definitions serve both the order on
ordinals and, later, the order on order-codes.
module Bisimulation {ℓ ℓ'} {A : Type ℓ} (_≤_ : Rel A A ℓ') where
private Seq = ℕ → A
_≼⟨_⟩_ : Seq → ℕ → Seq → Type _
f ≼⟨ n ⟩ g = ∃[ m ∈ ℕ ] f n ≤ g m
_≼_ : Rel Seq Seq _
f ≼ g = ∀ n → f ≼⟨ n ⟩ g
_≈_ : Rel Seq Seq _
f ≈ g = f ≼ g × g ≼ f
A sequence is monotone when
suc (f n) ≤ f (suc n), i.e. it strictly increases. A
MonoSeq bundles a sequence with that proof, and
f [ n ] projects out its n-th term. Limits
will be taken only of monotone sequences.
module MonotonicSequence {ℓ ℓ'} {A : Type ℓ} (suc : A → A) (_≤_ : Rel A A ℓ') where
private Seq = ℕ → A
isMonoSeq : Seq → Type _
isMonoSeq f = ∀ n → suc (f n) ≤ f (ℕ.suc n)
MonoSeq : Type _
MonoSeq = Σ[ f ∈ Seq ] isMonoSeq f
infix 30 _[_]
_[_] : MonoSeq → ℕ → A
f [ n ] = fst f n
Ord and _≤_ are declared mutually:
_≤_ already appears in the type of lim
(through MonoSeq), and Ord appears in the type
of _≤_. We first forward-declare both, plus a private copy
pre-suc of the successor (needed to instantiate
MonotonicSequence before suc itself is in
scope), then open the two helper modules at _≤_ so that
MonoSeq, _≼_, ≈,
_[_] are available.
data Ord : Type data _≤_ : Rel Ord Ord ℓ-zero private pre-suc : Ord → Ord open Bisimulation _≤_ public open MonotonicSequence pre-suc _≤_ public variable α β γ : Ord f g h : MonoSeq n : ℕ
Ord has the three ordinal formers — zero,
suc, and lim of a monotone sequence — and two
higher constructors that make it a QIIT: l≡l
quotients bisimilar limits to be equal (the path constructor), and
isSetOrd truncates Ord to a set, so all of its
path structure is determined by these.
data Ord where zero : Ord suc : Ord → Ord lim : MonoSeq → Ord l≡l : fst f ≈ fst g → lim f ≡ lim g isSetOrd : isSet Ord
The order _≤_ is generated by: zero is
least (z≤); suc is monotone
(s≤s); lim f is an upper bound of its
sequence and the least one (≤l and
l≤); together with transitivity and the proof that
_≤_ is propositional. The last two are constructors because
_≤_ is defined simultaneously with Ord, so we
cannot derive them after the fact.
data _≤_ where z≤ : zero ≤ α s≤s : α ≤ β → suc α ≤ suc β ≤l : α ≤ f [ n ] → α ≤ lim f l≤ : (∀ n → f [ n ] ≤ α) → lim f ≤ α ≤-trans : isTrans' _≤_ isProp≤ : isProp (α ≤ β) pre-suc = suc
This completes the QIIT.
The work-horse recursor elimProp is structural recursion
on Ord into a proposition-valued family
P. Because each P α is a proposition, the two
higher constructors are discharged automatically: the l≡l
case by isProp→PathP and the isSetOrd case by
isProp→SquareP. The user only supplies the
zero/suc/lim methods — and in the
lim method the inductive hypothesis arrives already
unpacked as ∀ n → P (f [ n ]). This unpacking is
what later lets transitivity of the order-code be defined
structurally.
elimProp : ∀ {ℓ} {P : Ord → Type ℓ} →
(∀ α → isProp (P α)) →
P zero →
(∀ {α} → P α → P (suc α)) →
(∀ {f} → (∀ n → P (f [ n ])) → P (lim f)) →
(α : Ord) → P α
elimProp Pprop Pz Ps Pl zero = Pz
elimProp Pprop Pz Ps Pl (suc α) = Ps (elimProp Pprop Pz Ps Pl α)
elimProp Pprop Pz Ps Pl (lim f) = Pl λ n → elimProp Pprop Pz Ps Pl (fst f n)
elimProp Pprop Pz Ps Pl (l≡l {f} {g} f≈g i) = isProp→PathP
(λ j → Pprop (l≡l f≈g j))
(Pl λ n → elimProp Pprop Pz Ps Pl (fst f n))
(Pl λ n → elimProp Pprop Pz Ps Pl (fst g n)) i
elimProp Pprop Pz Ps Pl (isSetOrd _ _ p q i j) = isProp→SquareP
(λ i j → Pprop $ isSetOrd _ _ _ _ i j) refl refl
(λ i → elimProp Pprop Pz Ps Pl (p i))
(λ i → elimProp Pprop Pz Ps Pl (q i)) i j
Dually, ≤-elimProp is structural recursion on a
derivation of α ≤ β into a
proposition-valued family. Its l≤ method is the subtle one:
it supplies, in addition to the induction hypotheses on the proofs
f [ n ] ≤ α, the sequence’s monotonicity
hypotheses ∀ n → P (snd f n). That extra hypothesis is
exactly what encode≤ needs later, and is why a plain
“induction on the derivation” is not enough.
≤-elimProp : ∀ {ℓ} {P : ∀ {α β} → α ≤ β → Type ℓ} →
(∀ {α β} (α≤β : α ≤ β) → isProp (P α≤β)) →
(∀ {α} → P (z≤ {α})) →
(∀ {α β} (α≤β : α ≤ β) → P α≤β → P (s≤s α≤β)) →
(∀ {α f n} (α≤f : α ≤ f [ n ]) → P α≤f → P (≤l {f = f} α≤f)) →
(∀ {f α} (f≤α : ∀ n → f [ n ] ≤ α) → (∀ n → P (f≤α n)) → (∀ n → P (snd f n)) → P (l≤ {f} f≤α)) →
(∀ {α β γ} {α≤β : α ≤ β} {β≤γ : β ≤ γ} → P α≤β → P β≤γ → P (≤-trans α≤β β≤γ)) →
(α≤β : α ≤ β) → P α≤β
≤-elimProp Pprop Pz Ps Pu Pl Pt z≤ = Pz
≤-elimProp Pprop Pz Ps Pu Pl Pt (s≤s α≤β) = Ps α≤β $ ≤-elimProp Pprop Pz Ps Pu Pl Pt α≤β
≤-elimProp Pprop Pz Ps Pu Pl Pt (≤l α≤f) = Pu α≤f $ ≤-elimProp Pprop Pz Ps Pu Pl Pt α≤f
≤-elimProp Pprop Pz Ps Pu Pl Pt (l≤ {f} f≤) = Pl f≤
(λ n → ≤-elimProp Pprop Pz Ps Pu Pl Pt (f≤ n))
(λ n → ≤-elimProp Pprop Pz Ps Pu Pl Pt (snd f n))
≤-elimProp Pprop Pz Ps Pu Pl Pt (≤-trans α≤β β≤γ) = Pt
(≤-elimProp Pprop Pz Ps Pu Pl Pt α≤β)
(≤-elimProp Pprop Pz Ps Pu Pl Pt β≤γ)
≤-elimProp Pprop Pz Ps Pu Pl Pt (isProp≤ α≤β β≤γ i) = isProp→PathP
(λ i → Pprop (isProp≤ α≤β β≤γ i))
(≤-elimProp Pprop Pz Ps Pu Pl Pt α≤β)
(≤-elimProp Pprop Pz Ps Pu Pl Pt β≤γ) i
The strict order is α < β := suc α ≤ β, and we set up
the usual negated and flipped notations, plus a couple of small
numerals.
Seq = ℕ → Ord one = suc zero two = suc one _≢_ : Rel Ord Ord _ α ≢ β = ¬ α ≡ β _≥_ : Rel Ord Ord _ α ≥ β = β ≤ α _≰_ : Rel Ord Ord _ α ≰ β = ¬ α ≤ β _≱_ : Rel Ord Ord _ α ≱ β = ¬ β ≤ α _<_ : Rel Ord Ord _ α < β = suc α ≤ β _>_ : Rel Ord Ord _ α > β = β < α _≮_ : Rel Ord Ord _ α ≮ β = ¬ α < β _≯_ : Rel Ord Ord _ α ≯ β = ¬ β < α
Reflexivity is the first real use of elimProp: it
follows the constructors, with the limit case combining l≤
and ≤l. From it we get that equal ordinals are related,
that every sequence element is ≤ its limit
(f≤l, the upper-bound property as a closed term), and the
one inversion that needs no encode-decode: from
lim f ≤ α we read back f [ n ] ≤ α just by
transitivity with f≤l.
≤-refl : isRefl' _≤_
≤-refl = elimProp (λ α → isProp≤ {α} {α}) z≤ s≤s (λ ih → l≤ λ n → ≤l (ih n)) _
≡⇒≤ : α ≡ β → α ≤ β
≡⇒≤ α≡β = subst (_≤ _) (sym α≡β) ≤-refl
f≤l : f [ n ] ≤ lim f
f≤l = ≤l ≤-refl
l≤-inv : lim f ≤ α → ∀ n → f [ n ] ≤ α
l≤-inv l≤α n = ≤-trans f≤l l≤α
≤-reasoningEquational-style reasoning combinators for chaining ≤,
<, and ≡ steps; these make the limit proofs
below (and throughout the library) readable.
infixr 0 _≤⟨_⟩_ _<⟨_⟩_ _<⟨_⟩suc_ _=⟨_⟩_ _=˘⟨_⟩_ infix 1 _≤∎ _≤⟨_⟩_ : (α : Ord) → α ≤ β → β ≤ γ → α ≤ γ _≤⟨_⟩_ _ = ≤-trans _<⟨_⟩_ : (α : Ord) → α < β → β ≤ γ → α < γ _<⟨_⟩_ _ = ≤-trans _<⟨_⟩suc_ : (α : Ord) → α < β → β ≤ γ → α < γ _<⟨_⟩suc_ = _<⟨_⟩_ _=⟨_⟩_ : (α : Ord) → α ≡ β → β ≤ γ → α ≤ γ α =⟨ α≡β ⟩ β≤γ = subst (_≤ _) (sym α≡β) β≤γ _=˘⟨_⟩_ : (α : Ord) → β ≡ α → β ≤ γ → α ≤ γ α =˘⟨ β≡α ⟩ β≤γ = subst (_≤ _) β≡α β≤γ _≤∎ : ∀ α → α ≤ α _ ≤∎ = ≤-refl
Finally, α ≤ suc α (again by elimProp, the
limit case using the reasoning combinators), and the two evident
relations between α ≤ β and the successor.
≤s : α ≤ suc α
≤s = elimProp (λ α → isProp≤ {α} {suc α}) z≤ s≤s
(λ {f} f≤sf → l≤ λ n →
f [ n ] ≤⟨ f≤sf n ⟩
suc (f [ n ]) ≤⟨ s≤s f≤l ⟩
suc (lim f) ≤∎) _
s≤→≤ : suc α ≤ β → α ≤ β
s≤→≤ = ≤-trans ≤s
≤→≤s : α ≤ β → α ≤ suc β
≤→≤s α≤β = ≤-trans α≤β ≤s