Tom de Jong, early January 2022.

In category theory, the Ind-completion freely adds filtered colimits. For a
poset, the Ind-completion can be described as the preorder of directed families
into the poset, ordered by cofinality.

We construct the Ind-completion for a 𝓥-dcpo 𝓓 and show it is a preorder. We
define and characterize what it means for the map Ind → 𝓓 that takes suprema to
have a left adjoint. We also consider the poset reflection Ind/≈ of Ind and
define what it means for the induced map Ind/≈ → 𝓓 to have a left adjoint.

This development is used in exploring possible notions of continuous dcpo in
ContinuityDiscussion.lagda. In particular, the observation that the
Ind-completion is a preorder and not a poset is seen to be important there.

\begin{code}

{-# OPTIONS --safe --without-K #-}

open import MLTT.Spartan hiding (J)
open import UF.FunExt
open import UF.PropTrunc

module DomainTheory.BasesAndContinuity.IndCompletion
        (pt : propositional-truncations-exist)
        (fe : Fun-Ext)
        (𝓥 : Universe) -- where the index types for directed completeness live
       where

open PropositionalTruncation pt

open import UF.Equiv
open import UF.EquivalenceExamples
open import UF.Subsingletons
open import UF.Subsingletons-FunExt
open import UF.Sets

open import DomainTheory.Basics.Dcpo pt fe 𝓥
open import DomainTheory.Basics.Miscelanea pt fe 𝓥
open import DomainTheory.Basics.WayBelow pt fe 𝓥

module Ind-completion
        (𝓓 : DCPO {𝓤} {𝓣})
       where

 Ind : 𝓥   𝓤  𝓣 ̇
 Ind = Σ I  𝓥 ̇ , Σ α  (I   𝓓 ) , is-Directed 𝓓 α

 index-of-underlying-family : Ind  𝓥 ̇
 index-of-underlying-family = pr₁

 underlying-family : (α : Ind)  index-of-underlying-family α   𝓓 
 underlying-family α = pr₁ (pr₂ α)

 _≲_ : Ind  Ind  𝓥  𝓣 ̇
 (I , α , _)  (J , β , _) = (i : I)   j  J , α i ⊑⟨ 𝓓  β j

 ≲-is-prop-valued : (α β : Ind)  is-prop (α  β)
 ≲-is-prop-valued α β = Π-is-prop fe  i  ∥∥-is-prop)

 ≲-is-reflexive : (α : Ind)  α  α
 ≲-is-reflexive (I , α , δ) i =  i , reflexivity 𝓓 (α i) 

 ≲-is-transitive : (σ τ ρ : Ind)  σ  τ  τ  ρ  σ  ρ
 ≲-is-transitive (I , α , δ) (J , β , ε) (K , γ , ϕ)
  α-cofinal-in-β β-cofinal-in-γ i = ∥∥-rec ∥∥-is-prop r (α-cofinal-in-β i)
   where
    r : (Σ j  J , α i ⊑⟨ 𝓓  β j)
       ( k  K , α i ⊑⟨ 𝓓  γ k)
    r (j , u) = ∥∥-functor  (k , v)  k , (α i ⊑⟨ 𝓓 ⟩[ u ]
                                             β j ⊑⟨ 𝓓 ⟩[ v ]
                                             γ k ∎⟨ 𝓓 ))
                           (β-cofinal-in-γ j)

\end{code}

We now construct directed suprema of 𝓥-small families in Ind.

\begin{code}

 Ind-∐ : {I : 𝓥 ̇ } (𝓐 : I  Ind)
        is-directed _≲_ 𝓐
        Ind
 Ind-∐ {I} 𝓐 (I-inhabited , 𝓐-semidirected) =
  Σ J , β , K-is-inhabited , β-is-semidirected
   where
    J : I  𝓥 ̇
    J i = pr₁ (𝓐 i)
    α : (i : I)  J i   𝓓 
    α i = pr₁ (pr₂ (𝓐 i))
    δ : (i : I)  is-Directed 𝓓 (α i)
    δ i = pr₂ (pr₂ (𝓐 i))
    K : 𝓥 ̇
    K = Σ J
    β : K   𝓓 
    β (i , j) = α i j
    K-is-inhabited :  K 
    K-is-inhabited =
     ∥∥-rec ∥∥-is-prop h I-inhabited
      where
       h : I   K 
       h i = ∥∥-functor  j  (i , j)) (inhabited-if-Directed 𝓓 (α i) (δ i))
    β-is-semidirected : is-semidirected (underlying-order 𝓓) β
    β-is-semidirected (i₁ , j₁) (i₂ , j₂) =
     ∥∥-rec ∥∥-is-prop f (𝓐-semidirected i₁ i₂)
      where
       f : (Σ i  I , (𝓐 i₁  𝓐 i) × (𝓐 i₂  𝓐 i))
           k  K , (β (i₁ , j₁) ⊑⟨ 𝓓  β k) × (β (i₂ , j₂) ⊑⟨ 𝓓  β k)
       f (i , u₁ , u₂) = ∥∥-rec ∥∥-is-prop g (u₁ j₁)
        where
         g : (Σ jⁱ₁  J i , β (i₁ , j₁) ⊑⟨ 𝓓  β (i , jⁱ₁))
             k  K , (β (i₁ , j₁) ⊑⟨ 𝓓  β k) × (β (i₂ , j₂) ⊑⟨ 𝓓  β k)
         g (jⁱ₁ , v₁) = ∥∥-rec ∥∥-is-prop h (u₂ j₂)
          where
           h : (Σ jⁱ₂  J i , β (i₂ , j₂) ⊑⟨ 𝓓  β (i , jⁱ₂))
               k  K , (β (i₁ , j₁) ⊑⟨ 𝓓  β k) × (β (i₂ , j₂) ⊑⟨ 𝓓  β k)
           h (jⁱ₂ , v₂) = ∥∥-functor r
                           (semidirected-if-Directed 𝓓 (α i) (δ i) jⁱ₁ jⁱ₂)
            where
             r : (Σ j  J i , (α i jⁱ₁ ⊑⟨ 𝓓  α i j) × (α i jⁱ₂ ⊑⟨ 𝓓  α i j))
                Σ k  K , (β (i₁ , j₁) ⊑⟨ 𝓓  β k) × (β (i₂ , j₂) ⊑⟨ 𝓓  β k)
             r (j , w₁ , w₂) = (i , j) ,
                               ( (β (i₁ , j₁)  ⊑⟨ 𝓓 ⟩[ v₁ ]
                                  β (i  , jⁱ₁) ⊑⟨ 𝓓 ⟩[ w₁ ]
                                  β (i  , j)   ∎⟨ 𝓓 )
                               , (β (i₂ , j₂)  ⊑⟨ 𝓓 ⟩[ v₂ ]
                                  β (i  , jⁱ₂) ⊑⟨ 𝓓 ⟩[ w₂ ]
                                  β (i  , j)   ∎⟨ 𝓓 ))

 Ind-∐-is-directed : {I : 𝓥 ̇ } (𝓐 : I  Ind) (δ : is-directed _≲_ 𝓐)
                    is-Directed 𝓓 (underlying-family (Ind-∐ 𝓐 δ))
 Ind-∐-is-directed 𝓐 δ = pr₂ (pr₂ (Ind-∐ 𝓐 δ))

 Ind-∐-is-upperbound : {I : 𝓥 ̇ } (𝓐 : I  Ind) (δ : is-directed _≲_ 𝓐)
                      is-upperbound _≲_ (Ind-∐ 𝓐 δ) 𝓐
 Ind-∐-is-upperbound 𝓐 δ i j =
   (i , j) , reflexivity 𝓓 (pr₁ (pr₂ (𝓐 i)) j) 

 Ind-∐-is-lowerbound-of-upperbounds : {I : 𝓥 ̇ } (𝓐 : I  Ind)
                                      (δ : is-directed _≲_ 𝓐)
                                     is-lowerbound-of-upperbounds _≲_
                                       (Ind-∐ 𝓐 δ) 𝓐
 Ind-∐-is-lowerbound-of-upperbounds {A} 𝓐 _ _ ub (i , j) = ub i j

\end{code}

Taking suprema in our 𝓥-dcpo 𝓓 of directed families indexed into 𝓓 defines a
monotone map from Ind to 𝓓.

\begin{code}

 ∐-map : Ind   𝓓 
 ∐-map (I , α , δ) =  𝓓 δ

 ≲-to-⊑-of-∐ : {I J : 𝓥 ̇ } {α : I   𝓓 } {β : J   𝓓 }
               (δ : is-Directed 𝓓 α) (ε : is-Directed 𝓓 β)
              (I , α , δ)  (J , β , ε)
               𝓓 δ ⊑⟨ 𝓓   𝓓 ε
 ≲-to-⊑-of-∐ {I} {J} {α} {β} δ ε α-cofinal-in-β =
  ∐-⊑-if-cofinal 𝓓 α-cofinal-in-β δ ε

 ∐-map-is-monotone : (α β : Ind)  α  β  ∐-map α ⊑⟨ 𝓓  ∐-map β
 ∐-map-is-monotone (I , α , δ) (J , β , ε) = ≲-to-⊑-of-∐ δ ε

\end{code}

Since we can view every element of 𝓓 as a constant directed family into 𝓓, we
also have a map in the other direction which comes in useful at times.

\begin{code}

 ⌞_⌟ :  𝓓   (𝟙{𝓥}   𝓓 )
 ⌞_⌟ x = λ _  x

 ⌞⌟-is-directed : (x :  𝓓 )  is-Directed 𝓓  x 
 ⌞⌟-is-directed x =    , σ
  where
   σ : is-semidirected (underlying-order 𝓓)  _  x)
   σ i j =   , reflexivity 𝓓 x , reflexivity 𝓓 x 

 ι :  𝓓   Ind
 ι x = 𝟙 ,  x  , ⌞⌟-is-directed x

\end{code}

In our discussions on the notion of continuous dcpo we will be interested in
∐-map having a left adjoint, see ContinuityDiscussion.lagda.

We define what that means here and note that it is helpful to consider an
auxilliary relation between Ind(D) and D that we call "being left adjunct to",
because a map L : D → Ind(D) is a left adjoint to ∐-map precisely when L(x) is
left adjunct to x for every x : D.

\begin{code}

 _is-left-adjunct-to_ : Ind   𝓓   𝓥   𝓤  𝓣 ̇
 α is-left-adjunct-to x = (β : Ind)  (α  β)  (x ⊑⟨ 𝓓  ∐-map β)

 being-left-adjunct-to-is-prop : (σ : Ind) (x :  𝓓 )
                                is-prop (σ is-left-adjunct-to x)
 being-left-adjunct-to-is-prop σ x =
  Π-is-prop fe  τ  ↔-is-prop fe fe (≲-is-prop-valued σ τ)
                                      (prop-valuedness 𝓓 x (∐-map τ)))

 left-adjoint-to-∐-map : ( 𝓓   Ind)  𝓥   𝓤  𝓣 ̇
 left-adjoint-to-∐-map L = (x :  𝓓 )  L x is-left-adjunct-to x

 being-left-adjoint-to-∐-map-is-prop : (L :  𝓓   Ind)
                                      is-prop (left-adjoint-to-∐-map L)
 being-left-adjoint-to-∐-map-is-prop L =
  Π-is-prop fe  x  being-left-adjunct-to-is-prop (L x) x)

 ∐-map-has-specified-left-adjoint : 𝓥   𝓤  𝓣 ̇
 ∐-map-has-specified-left-adjoint = Σ left-adjoint-to-∐-map

\end{code}

We can equivalently describe the adjoint-condition in terms of directed suprema
and the way-below relation.

\begin{code}

 _approximates_ : Ind   𝓓   𝓥   𝓤  𝓣 ̇
 (I , α , δ) approximates x = ( 𝓓 δ  x) × ((i : I)  α i ≪⟨ 𝓓  x)

 approximates-to-∐-= : {(I , α , δ) : Ind} {x :  𝓓 }
                       (I , α , δ) approximates x
                        𝓓 δ  x
 approximates-to-∐-= = pr₁

 approximates-to-≪ : {(I , α , δ) : Ind} {x :  𝓓 }
                    (I , α , δ) approximates x
                    ((i : I)  α i ≪⟨ 𝓓  x)
 approximates-to-≪ = pr₂

 approximates-is-prop : (σ : Ind) (x :  𝓓 )  is-prop (σ approximates x)
 approximates-is-prop σ x =
  ×-is-prop (sethood 𝓓) (Π-is-prop fe  i  ≪-is-prop-valued 𝓓))

 is-approximating : ( 𝓓   Ind)  𝓥   𝓤  𝓣 ̇
 is-approximating L = (x :  𝓓 )  (L x) approximates x

 left-adjunct-to-if-approximates : (σ : Ind) (x :  𝓓 )
                                  σ approximates x  σ is-left-adjunct-to x
 left-adjunct-to-if-approximates σ@(I , α , δ) x (x-sup-of-α , α-way-below-x)
                                 τ@(J , β , ε) = ⦅⇒⦆ , ⦅⇐⦆
  where
   ⦅⇒⦆ : σ  τ  x ⊑⟨ 𝓓  ∐-map τ
   ⦅⇒⦆ α-cofinal-in-β = transport  -  - ⊑⟨ 𝓓  ∐-map τ) x-sup-of-α
                        (≲-to-⊑-of-∐ δ ε α-cofinal-in-β)
   ⦅⇐⦆ : x ⊑⟨ 𝓓  ∐-map τ  σ  τ
   ⦅⇐⦆ x-below-∐β i = α-way-below-x i J β ε x-below-∐β

 approximates-if-left-adjunct-to : (σ : Ind) (x :  𝓓 )
                                  σ is-left-adjunct-to x
                                  σ approximates x
 approximates-if-left-adjunct-to σ@(I , α , δ) x ladj =
  x-is-sup-of-α , α-way-below-x
   where
    α-way-below-x : (i : I)  α i ≪⟨ 𝓓  x
    α-way-below-x i J β ε x-below-∐β = h i
     where
      h : (I , α , δ)  (J , β , ε)
      h = rl-implication (ladj (J , β , ε)) x-below-∐β
    x-is-sup-of-α :  𝓓 δ  x
    x-is-sup-of-α = antisymmetry 𝓓 ( 𝓓 δ) x ⦅1⦆ ⦅2⦆
     where
      ⦅1⦆ :  𝓓 δ ⊑⟨ 𝓓  x
      ⦅1⦆ = ∐-is-lowerbound-of-upperbounds 𝓓 δ x
             i  ≪-to-⊑ 𝓓 (α-way-below-x i))
      ⦅2⦆ : x ⊑⟨ 𝓓   𝓓 δ
      ⦅2⦆ = lr-implication (ladj σ) (≲-is-reflexive σ)

 approximate-left-adjunct-to-≃ : (σ : Ind) (x :  𝓓 )
                                σ approximates x  σ is-left-adjunct-to x
 approximate-left-adjunct-to-≃ σ x = logically-equivalent-props-are-equivalent
                                      (approximates-is-prop σ x)
                                      (being-left-adjunct-to-is-prop σ x)
                                      (left-adjunct-to-if-approximates σ x)
                                      (approximates-if-left-adjunct-to σ x)

 left-adjoint-to-∐-map-characterization : (L :  𝓓   Ind)
                                         is-approximating L
                                         left-adjoint-to-∐-map L
 left-adjoint-to-∐-map-characterization L =
  Π-cong fe fe  x  approximate-left-adjunct-to-≃ (L x) x)

\end{code}

One may observe that the type (left-to-adjoint-to-∐-map L) does not require L to
be functorial/monotone, as would normally be required for an adjoint/Galois
connection. But this actually follows from the "hom-set" condition, as we show
now.

\begin{code}

 left-adjoint-to-∐-map-is-monotone : (L :  𝓓   Ind)
                                    left-adjoint-to-∐-map L
                                    (x y :  𝓓 )
                                    x ⊑⟨ 𝓓  y
                                    L x  L y
 left-adjoint-to-∐-map-is-monotone L L-left-adjoint x y x-below-y = γ
  where
   γ : L x  L y
   γ = rl-implication (L-left-adjoint x (L y)) x-below-∐-Ly
    where
     x-below-∐-Ly = x           ⊑⟨ 𝓓 ⟩[ x-below-y             ]
                    y           ⊑⟨ 𝓓 ⟩[ =-to-⊒ 𝓓 (pr₁ approx) ]
                    ∐-map (L y) ∎⟨ 𝓓 
      where
       approx : L y approximates y
       approx = approximates-if-left-adjunct-to (L y) y (L-left-adjoint y)

\end{code}

Because Ind is a preorder and not a poset, the type expressing that ∐-map has a
specified left adjoint is not a proposition, as the supposed left adjoint can
map elements of 𝓓 to bicofinal (but nonequal) directed families.

We could take the poset reflection Ind/≈ of Ind and ask that the map Ind/≈ → 𝓓
induced by the supremum-map Ind → 𝓓 has a left adjoint to obtain a type that is
a proposition. We describe that process here.

This is *not* the same as asking that ∐-map : Ind → 𝓓 has an unspecified left
adjoint, as we explain in ContinuityDiscussion.lagda.

\begin{code}

module Ind-completion-poset-reflection
        (pe : Prop-Ext)
        (𝓓 : DCPO {𝓤} {𝓣})
       where

 open Ind-completion 𝓓

 open import OrderedTypes.PosetReflection pt fe pe
 open poset-reflection Ind _≲_ ≲-is-prop-valued ≲-is-reflexive ≲-is-transitive public

 Ind/≈ : 𝓥   𝓣   𝓤 ̇
 Ind/≈ = poset-reflection-carrier

 Ind/≈-is-set : is-set Ind/≈
 Ind/≈-is-set = poset-reflection-is-set

 ∐-map/-specification :
   ∃!   (Ind/≈   𝓓 ) , ((σ' τ' : Ind/≈)  σ'  τ'   σ' ⊑⟨ 𝓓   τ')
                          × (  η  ∐-map)
 ∐-map/-specification =
  universal-property (underlying-order 𝓓) (sethood 𝓓) (prop-valuedness 𝓓)
                     (reflexivity 𝓓) (transitivity 𝓓) (antisymmetry 𝓓)
                     ∐-map ∐-map-is-monotone

 ∐-map/ : Ind/≈   𝓓 
 ∐-map/ = ∃!-witness ∐-map/-specification

 ∐-map/-triangle : (α : Ind)  ∐-map/ (η α)  ∐-map α
 ∐-map/-triangle = pr₂ (∃!-is-witness ∐-map/-specification)

 left-adjoint-to-∐-map/ : ( 𝓓   Ind/≈)
                         𝓥   𝓣   𝓤 ̇
 left-adjoint-to-∐-map/ L' =
  (x :  𝓓 ) (α' : Ind/≈)  (L' x  α')  (x ⊑⟨ 𝓓  ∐-map/ α')

 being-left-adjoint-to-∐-map/-is-prop : (L' :  𝓓   Ind/≈)
                                       is-prop (left-adjoint-to-∐-map/ L')
 being-left-adjoint-to-∐-map/-is-prop L' =
  Π₂-is-prop fe  x α'  ×-is-prop
                           (Π-is-prop fe  _  prop-valuedness 𝓓 x (∐-map/ α')))
                           (Π-is-prop fe  _  ≤-is-prop-valued (L' x) α')))

 ∐-map/-has-specified-left-adjoint : 𝓥   𝓣   𝓤 ̇
 ∐-map/-has-specified-left-adjoint = Σ left-adjoint-to-∐-map/

 ∐-map/-having-left-adjoint-is-prop : is-prop ∐-map/-has-specified-left-adjoint
 ∐-map/-having-left-adjoint-is-prop
  (L , L-is-left-adjoint) (L' , L'-is-left-adjoint) =
   to-subtype-= being-left-adjoint-to-∐-map/-is-prop
                (dfunext fe  x  ≤-is-antisymmetric (L x) (L' x)
                  (rl-implication (L-is-left-adjoint x (L' x))
                                  (lr-implication (L'-is-left-adjoint x (L' x))
                                    (≤-is-reflexive (L' x))))
                  (rl-implication (L'-is-left-adjoint x (L x))
                                  (lr-implication (L-is-left-adjoint x (L x))
                                    (≤-is-reflexive (L x))))))

\end{code}