Martin Escardo, 20th June 2019 and 28th May 2021. Search over uniformly continuous decidable predicates on the Cantor type. This is loosely based on my LICS'2007 paper "Infinite sets that admit fast exhaustive search" and my LMCS'2008 paper "Exhaustible sets in higher-type computation". Removed assumption of function extensionality 11th July 2024 by using the observation that 𝟚-valued uniformly continuous functions on the Cantor type are extensional in the sense that they map pointwise equal sequences to equal booleans. \begin{code} {-# OPTIONS --safe --without-K #-} open import MLTT.Spartan open import MLTT.Two-Properties open import TypeTopology.Cantor open import UF.Base module TypeTopology.CantorSearch where \end{code} We first consider search over the type 𝟚 of binary digits ₀ and ₁. To check that for all n : 𝟚 we have p n = ₁, it is enough to check that p (p ₀) = ₁. \begin{code} private motivating-fact : (p : 𝟚 → 𝟚) → p (p ₀) = ₁ → (n : 𝟚) → p n = ₁ motivating-fact p r = f (p ₀) refl r where f : (n₀ : 𝟚) → p ₀ = n₀ → p n₀ = ₁ → (n : 𝟚) → p n = ₁ f ₀ s r ₀ = r f ₀ s r ₁ = 𝟘-elim (zero-is-not-one (s ⁻¹ ∙ r)) f ₁ s r ₀ = s f ₁ s r ₁ = r ε𝟚 : (𝟚 → 𝟚) → 𝟚 ε𝟚 p = p ₀ A𝟚 : (𝟚 → 𝟚) → 𝟚 A𝟚 p = p (ε𝟚 p) \end{code} The function A𝟚 is the characteristic function of universal quantification: \begin{code} A𝟚-property→ : (p : 𝟚 → 𝟚) → A𝟚 p = ₁ → (n : 𝟚) → p n = ₁ A𝟚-property→ = motivating-fact A𝟚-property← : (p : 𝟚 → 𝟚) → ((n : 𝟚) → p n = ₁) → A𝟚 p = ₁ A𝟚-property← p ϕ = ϕ (ε𝟚 p) 𝟚-searchable : (p : 𝟚 → 𝟚) → Σ n₀ ꞉ 𝟚 , (p n₀ = ₁ → (n : 𝟚) → p n = ₁) 𝟚-searchable p = ε𝟚 p , A𝟚-property→ p \end{code} The function p has a root (that is, there is n with p n = ₀) if and only if ε𝟚 p is a root. This follows from A𝟚-property→. So ε𝟚 chooses a root if there is some root, and we can check whether there is a root by checking whether or not p (ε𝟚 p) = ₀. This is what A𝟚 does. \begin{code} ε𝟚-property→ : (p : 𝟚 → 𝟚) → (Σ n ꞉ 𝟚 , p n = ₀) → p (ε𝟚 p) = ₀ ε𝟚-property→ p = IV where I : (Σ n ꞉ 𝟚 , p n = ₀) → ¬ ((n : 𝟚) → p n = ₁) I (n , e) ϕ = equal-₀-different-from-₁ e (ϕ n) II : ¬ ((n : 𝟚) → p n = ₁) → ¬ (A𝟚 p = ₁) II = contrapositive (A𝟚-property→ p) III : ¬ (A𝟚 p = ₁) → p (ε𝟚 p) = ₀ III = different-from-₁-equal-₀ IV : (Σ n ꞉ 𝟚 , p n = ₀) → p (ε𝟚 p) = ₀ IV = III ∘ II ∘ I ε𝟚-property← : (p : 𝟚 → 𝟚) → p (ε𝟚 p) = ₀ → (Σ n ꞉ 𝟚 , p n = ₀) ε𝟚-property← p e = ε𝟚 p , e \end{code} We use this to search over the Cantor type. Notice that a function has modulus of continuity zero if and only if it is constant, and that if a function has modulus of continuity n then it has modulus of continuity k for any k > n. \begin{code} modulus-zero-iff-constant : (p : Cantor → 𝟚) → 0 is-a-modulus-of-uniform-continuity-of p ↔ ((α β : Cantor) → p α = p β) modulus-zero-iff-constant p = I , II where I : 0 is-a-modulus-of-uniform-continuity-of p → ((α β : Cantor) → p α = p β) I u α β = u α β ⋆ II : ((α β : Cantor) → p α = p β) → 0 is-a-modulus-of-uniform-continuity-of p II κ α β ⋆ = κ α β \end{code} The crucial lemma for Cantor search is this: \begin{code} cons-decreases-modulus : (p : Cantor → 𝟚) (n : ℕ) (b : 𝟚) → (succ n) is-a-modulus-of-uniform-continuity-of p → n is-a-modulus-of-uniform-continuity-of (p ∘ cons b) cons-decreases-modulus p n b u α β = III where I : α =⟦ n ⟧ β → cons b α =⟦ succ n ⟧ cons b β I e = refl , e II : cons b α =⟦ succ n ⟧ cons b β → p (cons b α) = p (cons b β) II = u (cons b α) (cons b β) III : α =⟦ n ⟧ β → p (cons b α) = p (cons b β) III = II ∘ I \end{code} Added 11th July 2024. Uniformly continuous functions are extensional in the following sense. This allows us to remove the assumption of function extensionality from previous versions of this file. \begin{code} uniform-continuity-gives-extensionality : (p : Cantor → 𝟚) → uniformly-continuous p → (α β : Cantor) → α ∼ β → p α = p β uniform-continuity-gives-extensionality p (n , u) = II where I : (n : ℕ) (α β : Cantor) → α ∼ β → α =⟦ n ⟧ β I 0 α β h = ⋆ I (succ n) α β h = h 0 , I n (α ∘ succ) (β ∘ succ) (h ∘ succ) II : (α β : Cantor) → α ∼ β → p α = p β II α β h = u α β (I n α β h) \end{code} We now define search over the Cantor space. The functions A and ε are mutually recursively defined. But of course we can consider only ε expanding the definition of A in that of ε, because the definition of A doesn't use induction. The following point c₀ of the Cantor type is arbitrary, and what we do works with any choice of c₀. So we make it abstract. \begin{code} abstract c₀ : Cantor c₀ = λ i → ₀ A : ℕ → (Cantor → 𝟚) → 𝟚 ε : ℕ → (Cantor → 𝟚) → Cantor A n p = p (ε n p) ε 0 p = c₀ ε (succ n) p = case ε𝟚 (λ b → A n (p ∘ cons b)) of (λ (b₀ : 𝟚) → cons b₀ (ε n (p ∘ cons b₀))) \end{code} The function A is designed to satisfy the specification A n p = ₁ ↔ ((α : Cantor) → p α = ₁) for any decidable predicate p with modulus of uniform continuity n. So A is the characteristic function of universal quantification over uniformly continuous decidable predicates. One direction is direct and doesn't require uniform continuity, but we still need to supply a number: \begin{code} A-property← : (p : Cantor → 𝟚) (n : ℕ) → ((α : Cantor) → p α = ₁) → A n p = ₁ A-property← p n ϕ = ϕ (ε n p) \end{code} The other direction is proved by induction on ℕ. \begin{code} A-property→ : (p : Cantor → 𝟚) (n : ℕ) → n is-a-modulus-of-uniform-continuity-of p → A n p = ₁ → (α : Cantor) → p α = ₁ A-property→ p 0 u r α = p α =⟨ u α c₀ ⋆ ⟩ p c₀ =⟨ r ⟩ ₁ ∎ A-property→ p (succ n) u r α = V where IH : (b : 𝟚) → A n (p ∘ cons b) = ₁ → (β : Cantor) → p (cons b β) = ₁ IH b = A-property→ (p ∘ cons b) n (cons-decreases-modulus p n b u) b₀ : 𝟚 b₀ = ε𝟚 (λ b → A n (p ∘ cons b)) I : A n (p ∘ cons b₀) = ₁ → (b : 𝟚) → A n (p ∘ cons b) = ₁ I = A𝟚-property→ (λ b → A n (p ∘ cons b)) observation₀ : A (succ n) p = ₁ observation₀ = r observation₁ : A (succ n) p = A n (p ∘ cons b₀) observation₁ = refl II : (b : 𝟚) (β : Cantor) → p (cons b β) = ₁ II b = IH b (I r b) III : p (cons (head α) (tail α)) = ₁ III = II (head α) (tail α) IV : p α = p (cons (head α) (tail α)) IV = uniform-continuity-gives-extensionality p (succ n , u) α (cons (head α) (tail α)) (cons-head-tail α) V : p α = ₁ V = p α =⟨ IV ⟩ p (cons (head α) (tail α)) =⟨ III ⟩ ₁ ∎ \end{code} The desired construction is the following: \begin{code} Cantor-uniformly-searchable : (p : Cantor → 𝟚) → uniformly-continuous p → Σ α₀ ꞉ Cantor , (p α₀ = ₁ → (α : Cantor) → p α = ₁) Cantor-uniformly-searchable p (n , u) = ε n p , A-property→ p n u having-root-is-decidable : (p : Cantor → 𝟚) → uniformly-continuous p → is-decidable (Σ α ꞉ Cantor , p α = ₀) having-root-is-decidable p (n , u) = γ (p α) refl where α : Cantor α = ε n p γ : (k : 𝟚) → p α = k → is-decidable (Σ α ꞉ Cantor , p α = ₀) γ ₀ r = inl (α , r) γ ₁ r = inr (λ (β , s) → zero-is-not-one (₀ =⟨ s ⁻¹ ⟩ p β =⟨ A-property→ p n u r β ⟩ ₁ ∎)) being-constantly-₁-is-decidable : (p : Cantor → 𝟚) → uniformly-continuous p → is-decidable ((α : Cantor) → p α = ₁) being-constantly-₁-is-decidable p u = γ (having-root-is-decidable p u) where γ : is-decidable (Σ α ꞉ Cantor , p α = ₀) → is-decidable ((α : Cantor) → p α = ₁) γ (inl (α , r)) = inr (λ ϕ → zero-is-not-one (r ⁻¹ ∙ ϕ α)) γ (inr ν) = inl (λ α → different-from-₀-equal-₁ (λ r → ν (α , r))) \end{code} Examples that show that A can be fast (in this case linear time) even if the supplied modulus of uniform continuity is large. \begin{code} module examples where prc : ℕ → Cantor → 𝟚 prc n α = α n sprc-lemma : (n : ℕ) → (succ n) is-a-modulus-of-uniform-continuity-of (prc n) sprc-lemma 0 α β (r , _) = r sprc-lemma (succ n) α β (_ , s) = sprc-lemma n (tail α) (tail β) s sprc : (n : ℕ) → uniformly-continuous (prc n) sprc n = succ n , sprc-lemma n prc-example : ℕ → 𝟚 prc-example n = A (succ n) (prc n) large-prc-example : prc-example 10000 = ₀ large-prc-example = refl \end{code} In the worst case, however, A n p runs in time 2ⁿ. \begin{code} xor : ℕ → Cantor → 𝟚 xor 0 α = ₀ xor (succ n) α = head α ⊕ xor n (tail α) xor-uc : (n : ℕ) → n is-a-modulus-of-uniform-continuity-of (xor n) xor-uc 0 α β ⋆ = refl xor-uc (succ n) α β (p , q) = γ where IH : xor n (tail α) = xor n (tail β) IH = xor-uc n (tail α) (tail β) q γ : head α ⊕ xor n (tail α) = head β ⊕ xor n (tail β) γ = ap₂ _⊕_ p IH xor-example : ℕ → 𝟚 xor-example n = A n (xor n) \end{code} If we set the following to `true` then the type checking of this module increases by 17s in a MacBook Air M1, where the total time to check this file with `false` is less than 2s. \begin{code} open import MLTT.Bool check-large-example : Bool check-large-example = false large-xor-example : if check-large-example then (xor-example 17 = ₀) else (₀ = ₀) large-xor-example = refl \end{code} The time is 2^n for this example. Another fast example (linear): \begin{code} κ₁ : ℕ → Cantor → 𝟚 κ₁ n α = complement (α n ⊕ α n) sκ₁-lemma : (n : ℕ) → (succ n) is-a-modulus-of-uniform-continuity-of (κ₁ n) sκ₁-lemma 0 α β (r , _) = ap (λ - → complement (- ⊕ -)) r sκ₁-lemma (succ n) α β (_ , s) = sκ₁-lemma n (tail α) (tail β) s sκ₁ : (n : ℕ) → uniformly-continuous (κ₁ n) sκ₁ n = succ n , sκ₁-lemma n κ₁-example : ℕ → 𝟚 κ₁-example n = A (succ n) (κ₁ n) large-κ₁-example : κ₁-example 100000 = ₁ large-κ₁-example = refl \end{code}