QIITOrd.Order.Code.Base

The order-code, via the external eliminator

This module defines the order-code _≤ᵖ_ : Ord → Ord → hProp — a recursive, computing characterisation of _≤_ — as a single application of the external eliminator of QIITOrd.Eliminator.NonDependent. This is the construction that eliminates {-# TERMINATING #-}: rather than defining _≤ᵖ_ by direct double recursion (which Agda cannot see terminating, because its two-argument recursion re-enters itself at cross positions), we recurse on the first ordinal through an eliminator that was already proved terminating in QIITOrd.Eliminator. Since α ≤ᵖ β = fst (elim α) β is then non-recursive, there is no recursion for the termination checker to reject.

The full story — why the naive definition fails, and why routing through an external recursor dissolves it — is in docs/termination.md; this module is its concrete realisation, faithful to the “work in progress” the reference paper left open.

{-# OPTIONS --cubical --safe #-}
{-# OPTIONS --lossy-unification #-}
module QIITOrd.Order.Code.Base where

open import QIITOrd.Base

open import Cubical.Foundations.Prelude
open import Cubical.Foundations.Function
open import Cubical.Foundations.HLevels
open import Cubical.Foundations.Path
open import Cubical.Foundations.Structure
open import Cubical.Foundations.Univalence
open import Cubical.Data.Empty using (; isProp⊥) renaming (rec to ⊥-rec)
open import Cubical.Data.Unit
open import Cubical.Data.Nat using ()
open import Cubical.Data.Sigma
open import Cubical.HITs.PropositionalTruncation using (∣_∣₁; squash₁; rec)

The carrier of the eliminator

We eliminate Ord into the type A of monotone hProp-valued predicates: each P : Ord → hProp paired with a proof that typ ∘ P respects _≤_. The monotonicity is essential — the second-argument coherence lim f ≤ᵖ l≡l … needs it and the bare function Ord → hProp does not carry it. The order _≦_ on A is up-set inclusion: x ≦ y iff everything above y is above x. It is written with projections (not pattern matching) so that it reduces on variables.

-- A monotone hProp-valued predicate on ordinals.
Mono : (Ord  hProp ℓ-zero)  Type
Mono P =  {x y}  x  y  typ (P x)  typ (P y)

isPropMono :  P  isProp (Mono P)
isPropMono P = isPropImplicitΠ2 λ x y  isPropΠ λ _  isPropΠ λ _  str (P y)

A : Type₁
A = Σ[ P  (Ord  hProp ℓ-zero) ] Mono P

-- "up-set inclusion": x ≦ y iff y's up-set ⊆ x's up-set.  Defined with
-- projections (not pattern matching) so it reduces on variables, e.g. `ι ≦ x`.
_≦_ : A  A  Type
x  y =  γ  typ (fst y γ)  typ (fst x γ)

isProp≦ :  {x y}  isProp (x  y)
isProp≦ {x} = isPropΠ λ γ  isPropΠ λ _  str (fst x γ)

isSetA : isSet A
isSetA = isSetΣ (isSetΠ λ _  isSetHProp) λ P  isProp→isSet (isPropMono P)

We instantiate the non-dependent eliminator at this A/_≦_. The zero case is the always-true predicate ι (Unit everywhere), which is trivially -above everything; -transitivity is composition.

import QIITOrd.Eliminator (ℓ-suc ℓ-zero) ℓ-zero as SD
open import QIITOrd.Eliminator.NonDependent (ℓ-suc ℓ-zero) ℓ-zero
open Targeting A _≦_

ι : A
ι =  _  Unit , isPropUnit) ,  _ _  tt)

≦-trans :  {x y z}  x  y  y  z  x  z
≦-trans h₁ h₂ γ = h₁ γ  h₂ γ

The successor method

succ sends α ≤ᵖ_ to suc α ≤ᵖ_. Its function part is an inner SD.elim recursion on the second ordinal β (so even this nested recursion lives inside an external eliminator), and its monotonicity is that inner eliminator’s own ≤-elim. The inner relation ≦ᵇ X Y = typ X → typ Y is exactly what makes ≤-elim deliver the monotonicity the bundle needs, and what the inner supPath (β’s l≡l coherence) supports.

-- `succ (α ≤ᵖ_ , monoP) = (suc α ≤ᵖ_ , its monotonicity)`.
succ : A  A
succ (P , monoP) = elimᵇ , ≤-elimᵇ
  where
  module IT = SD.Targeting  (_ : Ord)  hProp ℓ-zero)  {_ _} _ Px Py  typ Px  typ Py)

  ιᵇ : IT.Initial
  ιᵇ =  , isProp⊥

  succᵇ : IT.Successor
  succᵇ {β'} _ = P β'             -- suc α ≤ᵖ suc β' = α ≤ᵖ β'

  module IA = IT.Assigning ιᵇ succᵇ
     Fᵇ  (∃[ n   ] typ (fst Fᵇ n)) , squash₁)       -- suc α ≤ᵖ lim f
     { f≈g F G (F≾G , G≾F)  Σ≡Prop  _  isPropIsProp)
        (hPropExt squash₁ squash₁
          (rec squash₁  { (n , H)  rec squash₁  { (m , _ , impl)   m , impl H ∣₁ }) (F≾G n) }))
          (rec squash₁  { (n , H)  rec squash₁  { (m , _ , impl)   m , impl H ∣₁ }) (G≾F n) }))) })

  module IS = IA.Satisfying
     _  isSetHProp)
     {_} {_} _ {_} {Py}  isPropΠ λ _  str Py)
     {_} {_}  ⊥-rec)
     {_} {_} {x≤y} _  monoP x≤y)
     { {_} {_} {n} _ _ h H   n , h H ∣₁ })
     { _  hs  rec (str )  { (n , H)  hs n H }) })
     h₁ h₂  h₂  h₁)

  elimᵇ : Ord  hProp ℓ-zero
  elimᵇ = IS.elim
  ≤-elimᵇ : Mono elimᵇ
  ≤-elimᵇ = IS.≤-elim

The supremum method

sup sends a -increasing sequence F of predicates α'ₙ ≤ᵖ_ to lim α' ≤ᵖ_. Its function part P is built directly from the outer sequence (it does not self-recurse on β, except isSetOrd, structurally). The subtle half is its monotonicity mono: pushing a limit element α'ⱼ below lim w would look circular, but is obtained without transitivity by using the sequence’s strict monotonicity Fmono : suc α'ⱼ ≤ α'ⱼ₊₁ to bump to the existential form suc α'ⱼ ≤ᶜ lim w, then stripping the suc back off with the per-element monotonicity snd (F j) (the helper s≤ᶜ→). Inversion (inv) and the supremum bound (bound) are each a single elimProp on the ordinal.

-- the type of `≦`-increasing sequences of `A`-values (= `Mapped`).
module MS = MonotonicSequence succ _≦_
module BS = Bisimulation _≦_

module _ (FF : MS.MonoSeq) where
  private
    F     = fst FF
    Fmono = snd FF
    -- strip a `suc` off the j-th predicate, using its monotonicity `snd (F j)`.
    s≤ᶜ→ :  j w  typ (fst (succ (F j)) w)  typ (fst (F j) w)
    s≤ᶜ→ j = elimProp {P = λ w  typ (fst (succ (F j)) w)  typ (fst (F j) w)}
       w  isPropΠ λ _  str (fst (F j) w))
       H  ⊥-rec H)
       _ H  snd (F j) ≤s H)
       {v} ih  rec (str (fst (F j) (lim v)))  { (k , H)  snd (F j) f≤l (ih k H) }))

  P : Ord  hProp ℓ-zero
  P zero =  , isProp⊥
  P (suc z) = (∀ j  typ (fst (F j) (suc z))) , isPropΠ  j  str (fst (F j) (suc z)))
  P (lim f) = (∀ j  ∃[ m   ] typ (fst (F j) (fst f m))) , isPropΠ  _  squash₁)
  P (l≡l {f} {g} (f≼g , g≼f) i) = Σ≡Prop  _  isPropIsProp)
    {u = (∀ j  ∃[ m   ] typ (fst (F j) (fst f m))) , isPropΠ  _  squash₁)}
    {v = (∀ j  ∃[ m   ] typ (fst (F j) (fst g m))) , isPropΠ  _  squash₁)}
    (hPropExt (isPropΠ λ _  squash₁) (isPropΠ λ _  squash₁)
       H j  rec squash₁  { (m , Hm)  rec squash₁  { (k , fm≤gk)   k , snd (F j) fm≤gk Hm ∣₁ }) (f≼g m) }) (H j))
       H j  rec squash₁  { (m , Hm)  rec squash₁  { (k , gm≤fk)   k , snd (F j) gm≤fk Hm ∣₁ }) (g≼f m) }) (H j)))
    i
  P (isSetOrd x y p q i j) = isSet→SquareP  _ _  isSetHProp)
     i  P (p i))  i  P (q i)) refl refl i j

  -- `lim α' ≤ᶜ w → ∀ j. α'ⱼ ≤ᶜ w`   (l≤-inversion for the supremum)
  inv :  w  typ (P w)   j  typ (fst (F j) w)
  inv = elimProp {P = λ w  typ (P w)   j  typ (fst (F j) w)}
     w  isPropΠ λ _  isPropΠ λ j  str (fst (F j) w))
     H  ⊥-rec H)
     _ H  H)
     {v} _ H j  rec (str (fst (F j) (lim v)))  { (k , Hk)  snd (F j) f≤l Hk }) (H j))

  -- `(∀ j. α'ⱼ ≤ᶜ w) → lim α' ≤ᶜ w`  (the supremum property; uses `Fmono`)
  bound :  w  (∀ j  typ (fst (F j) w))  typ (P w)
  bound = elimProp {P = λ w  (∀ j  typ (fst (F j) w))  typ (P w)}
     w  isPropΠ λ _  str (P w))
     H  Fmono 0 zero (H 1))
     _ H  H)
     {v} _ H j  rec squash₁  { (m , Hm)   m , s≤ᶜ→ j (fst v m) Hm ∣₁ }) (Fmono j (lim v) (H (ℕ.suc j))))

  mono : Mono P
  mono {x} {y} x≤y H = bound y  j  snd (F j) x≤y (inv x H j))

  sup-A : A
  sup-A = P , mono

The two l≡l coherences and the order

supPath is the first-argument l≡l coherence: bisimilar α'-sequences give the same predicate lim α' ≤ᵖ_. It is a path of functions Ord → hProp, given pointwise by elimProp on γ (a proposition, since hProp is a set). With ι, succ, sup-A, supPath in hand we open Assigning, prove the three outer order methods (s≦s, ≦l, l≦ — each an elimProp on γ, or inv/bound), and open Satisfying to obtain elim.

-- first-argument `l≡l` coherence: bisimilar α'-sequences give equal `lim α' ≤ᵖ_`.
supPath :  (FF GG : MS.MonoSeq)  BS._≈_ (fst FF) (fst GG)  sup-A FF  sup-A GG
supPath FF GG (F≼G , G≼F) = Σ≡Prop isPropMono (funExt pw)
  where
  Pf = fst FF
  Pg = fst GG
  pw :  γ  P FF γ  P GG γ
  pw = elimProp {P = λ γ  P FF γ  P GG γ}
     γ  isSetHProp (P FF γ) (P GG γ))
    refl
     {z} _  Σ≡Prop  _  isPropIsProp) (hPropExt (snd (P FF (suc z))) (snd (P GG (suc z)))
       H m  rec (str (fst (Pg m) (suc z)))  { (n , incl)  incl (suc z) (H n) }) (G≼F m))
       H n  rec (str (fst (Pf n) (suc z)))  { (m , incl)  incl (suc z) (H m) }) (F≼G n))))
     {w} _  Σ≡Prop  _  isPropIsProp) (hPropExt (snd (P FF (lim w))) (snd (P GG (lim w)))
       H m  rec squash₁  { (n , incl)  rec squash₁  { (k , Hk)   k , incl (fst w k) Hk ∣₁ }) (H n) }) (G≼F m))
       H n  rec squash₁  { (m , incl)  rec squash₁  { (k , Hk)   k , incl (fst w k) Hk ∣₁ }) (H m) }) (F≼G n))))

open Assigning ι succ sup-A supPath

-- monotonicity of `succ` in `≦`, by `elimProp` on γ (prop-valued).
s≦s :  {x y}  x  y  succ x  succ y
s≦s {x} {y} h = elimProp {P = λ γ  typ (fst (succ y) γ)  typ (fst (succ x) γ)}
   γ  isPropΠ λ _  str (fst (succ x) γ))
  ⊥-rec
   {z} _  h z)
   {f} ih  rec squash₁  { (n , H)   n , ih n H ∣₁ }))

-- cocone: `xx ≦ (α'ₙ ≤ᵖ_)` lifts to `xx ≦ (lim α' ≤ᵖ_)`, via the inversion `inv`.
≦l :  {n} F x  (x  fst F n)  x  sup-A F
≦l {n} F x h γ H = h γ (inv F γ H n)

-- supremum: if every `α'ₙ ≤ᵖ_` is `≦ xx`, then `lim α' ≤ᵖ_` is, via `bound`.
l≦ :  F x  (∀ n  fst F n  x)  sup-A F  x
l≦ F x hs γ H = bound F γ  n  hs n γ H)

-- (`ι≦` inlined: zero's predicate `Unit` is above everything, trivially.)
open Satisfying isSetA isProp≦  {_} _ _  tt) s≦s ≦l l≦ ≦-trans

Finally the order-code: α ≤ᵖ β is the predicate elim α evaluated at β. It is non-recursivefst ∘ elim — so it passes the termination checker with no pragma.

-- the reconstructed order: NON-RECURSIVE (delegates to the external `elim`).
_≤ᵖ_ : Ord  Ord  hProp ℓ-zero
α ≤ᵖ β = fst (elim α) β