packages feed

FiniteCategories-0.6.0.0: src/Math/CartesianClosedCategory.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances #-}

{-| Module  : FiniteCategories
Description : Typeclasses for 'Category' which are cartesian complete.
Copyright   : Guillaume Sabbagh 2022
License     : GPL-3
Maintainer  : guillaumesabbagh@protonmail.com
Stability   : experimental
Portability : portable

Typeclasses for 'Category' which are cartesian complete.

A 'TwoBase' specifies an internal domain and an internal codomain.

A 'Tripod' is the data necessary to specify an exponential object.

An exponential object is a terminal object in the category of candidate exponential objects.
-}

module Math.CartesianClosedCategory
(
    -- * 2-base
    TwoBase(..),
    -- * Tripod
    Tripod,
    -- ** Getters
    twoCone,
    evalMap,
    internalDomain,
    internalCodomain,
    powerObject,
    universeCategoryTripod,
    -- ** Smart constructors
    tripod,
    unsafeTripod,
    -- * Cartesian closed category
    CartesianClosedCategory(..),
    TwoProduct(..),
    Exponential(..),
    unexproject,
    Cartesian(..),
    -- * Exponential object
    -- ** Candidate exponential object
    isCandidateExponentialObject,
    CandidateExponentialObject(..),
    -- ** Candidate exponential object morphism
    CandidateExponentialObjectMorphism,
    -- *** Getters
    transpose,
    -- *** Smart constructors
    candidateExponentialObjectMorphism,
    unsafeCandidateExponentialObjectMorphism,
    -- ** Candidate exponential object category,
    CandidateExponentialObjectCategory(..),
    -- ** Exponential object function
    exponentialObjects,
)
where
    import              Data.WeakSet             (Set)
    import qualified    Data.WeakSet           as Set
    import              Data.WeakSet.Safe
    import              Data.WeakMap             (Map)
    import qualified    Data.WeakMap           as Map
    import              Data.WeakMap.Safe
    import              Data.List               (intercalate)
    import              Data.Simplifiable
    
    import              Math.Category
    import              Math.FiniteCategory
    import              Math.CompleteCategory
    import              Math.FiniteCategories.DiscreteTwo
    import              Math.FiniteCategories.ConeCategory
    import              Math.FiniteCategories.FunctorCategory
    import              Math.IO.PrettyPrint
    
    import              GHC.Generics
    
    -- | A 'TwoBase' is the specification of an internal domain and an internal codomain.
    --
    -- The object 'A' selects the internal domain, the object 'B' selects the internal codomain.
    --
    -- Don't confuse the 2-base of an exponential object with the base of its 2-cone. The 2-base selects the internal domain and internal codomain of the exponential object. The base of the 2-cone selects the power object and the internal domain.
    type TwoBase c m o = Diagram DiscreteTwo DiscreteTwoAr DiscreteTwoOb c m o
    
    -- | A 'Tripod' is a 2-cone on the power object and the internal domain, and an evaluation map from the apex of the 2-cone to the internal codomain.
    --
    -- 'Tripod' is private, use smart constructor 'tripod' which checks the structure of the 'Tripod' or use 'unsafeTripod' if you know that the 'evalMap' has the apex of the 'twoCone' as a source.
    data Tripod c m o = Tripod {
        twoCone :: (Cone DiscreteTwo DiscreteTwoAr DiscreteTwoOb c m o), -- ^ In the base of the cone, the power object should be the image of 'A' and the internal domain should be the image of 'B'.
        evalMap :: m -- ^ The evaluation map should go from the apex of the 2-cone to the internal codomain
        } deriving (Eq, Show, Generic, Simplifiable, PrettyPrint)
        
    -- | Smart constructor of 'Tripod', checks that the apex of the 'twoCone' is the source of the 'evalMap'.
    tripod :: (Morphism m o, Eq o) => Cone DiscreteTwo DiscreteTwoAr DiscreteTwoOb c m o -> m -> Maybe (Tripod c m o)
    tripod cone em
        | source em /= apex cone = Nothing
        | otherwise = Just Tripod{twoCone = cone, evalMap = em}
        
    -- | Construct a 'Tripod' without checking the sructure of the 'Tripod', use with caution.
    unsafeTripod :: Cone DiscreteTwo DiscreteTwoAr DiscreteTwoOb c m o -> m -> Tripod c m o
    unsafeTripod cone em = Tripod{twoCone = cone, evalMap = em}    
    
    -- | Return the internal domain of a 'Tripod'.
    internalDomain :: (Morphism m o) => Tripod c m o -> o
    internalDomain t = target $ (legsCone (twoCone t)) =>$ B
    
    -- | Return the internal codomain of a 'Tripod'.
    internalCodomain :: (Morphism m o) => Tripod c m o -> o
    internalCodomain t = target $ evalMap t
    
    -- | Return the power object of a 'Tripod'.
    powerObject :: (Morphism m o) => Tripod c m o -> o
    powerObject t = target $ (legsCone (twoCone t)) =>$ A
    
    -- | Return the category in which a 'Tripod' lives.
    universeCategoryTripod :: (Category c m o, Morphism m o, Eq c, Eq m, Eq o) => 
        Tripod c m o -> c
    universeCategoryTripod = universeCategoryCone.twoCone
    
    -- | For a 'Category' parametrized over a type t, the power object of an exponential object of a 2-base will contain 'ExponentialElement' constructions (an exponential element is a mapping). A given mapping has as domain and codomain values 'Exprojection's.
    --
    -- For example, in 'FinSet', let's consider the 2-base selecting {1,2} and {3,4} as internal domain and codomain. The power object of the 2-base is {{1->3,2->3},{1->3,2->4},{1->4,2->3},{1->4,2->4}}, note that it is not an object of ('Finset' Int) but an object of ('FinSet' (Set (Map Int))). The 'Exponential' type allows to construct type ('FinSet' ('Exponential' Int)) in which we can consider the original objects with 'Exprojection' and the new mappings with 'ExponentialElement'. Here, instead of mappings, we can construct the type ('FinSet' (Set (Exponential Int))), a mapping is then ('ExponentialElement' (weakMap [(1,3),(2,3)])). We can construct exprojections in the same category, for example along 'A' which will map ('ExponentialElement' (weakMap [(1,3),(2,3)])) to (set ['Exprojection' 1, 'Exprojection' 2]).
    data Exponential t = Exprojection t -- ^A 'ExponentialElement' can be applied to an 'Exprojection' yielding another 'Exprojection'.  
                       | ExponentialElement (Map t t) -- ^ An 'ExponentialElement' is an element in an exponential object, it is a mapping between objects of type t.
        deriving (Eq, Show, Generic, Simplifiable, PrettyPrint)
        
    -- | Remove the constructor 'Exprojection' from an original object t, throws an error if an 'ExponentialElement' is given.
    unexproject :: Exponential t -> t
    unexproject (Exprojection t) = t
    unexproject (ExponentialElement _) = error "Unexproject an exponential element."
        
    -- | For a category to be cartesian closed, we need to construct 2-products.
    type TwoProduct t = Limit DiscreteTwoOb t
    
    -- | For a category to be cartesian closed, we need to construct 2-products of exponential objects with exprojected objects.
    type Cartesian t = TwoProduct (Exponential t)
    
    
    class CartesianClosedCategory c m o cLim mLim oLim cLimExp mLimExp oLimExp | 
        c -> m, m -> o, cLim -> mLim, mLim -> oLim, cLimExp -> mLimExp, mLimExp -> oLimExp, c m o -> cLimExp where
        
        internalHom :: (CompleteCategory c m o cLim mLim oLim DiscreteTwo DiscreteTwoAr DiscreteTwoOb) => TwoBase c m o -> Tripod cLimExp mLimExp oLimExp

    -- | Return wether a 'Tripod' is a candidate exponential object or not. A 'Tripod' is a candidate exponential object if its 'twoCone' is a limit. Tests if the 2-cone is a 2-product by computing the limits in the universe category with the function 'limits' : it is slow.
    isCandidateExponentialObject :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => Tripod c m o -> Bool
    isCandidateExponentialObject t = (twoCone t) `Set.elem` (limits.baseCone.twoCone $ t)
    
    -- | A 'CandidateExponentialObject' is a 'Tripod' such that its 'twoCone' is a limit.
    type CandidateExponentialObject c m o = Tripod c m o
    
    -- | A 'CandidateExponentialObjectMorphism' is a morphism between two 'CandidateExponentialObject's. It is private, use smart constructors 'candidateExponentialObjectMorphism' and 'unsafeCandidateExponentialObjectMorphism' to instantiate.
    data CandidateExponentialObjectMorphism c m o = CandidateExponentialObjectMorphism {
                                                        sourceCandidateExponentialObject :: CandidateExponentialObject c m o,
                                                        targetCandidateExponentialObject :: CandidateExponentialObject c m o,
                                                        transpose :: m
                                                    }
                                                    deriving (Eq, Show, Generic, Simplifiable, PrettyPrint)
    
    
    -- | Smart constructor for 'CandidateExponentialObjectMorphism'. Checks wether the transpose has valid domain and valid codomain.
    candidateExponentialObjectMorphism :: (Morphism m o, Eq m, Eq o) => CandidateExponentialObject c m o -> CandidateExponentialObject c m o -> m -> Maybe (CandidateExponentialObjectMorphism c m o)
    candidateExponentialObjectMorphism s t transp
        | source transp == target ((legsCone $ twoCone s) =>$ A) && target transp == target ((legsCone $ twoCone t) =>$ A) = Just CandidateExponentialObjectMorphism {sourceCandidateExponentialObject = s, targetCandidateExponentialObject = t, transpose = transp}
        | otherwise = Nothing
        
    -- | Unsafe version of 'candidateExponentialObjectMorphism', use with caution as it does not check wether the transpose has valid domain and valid codomain.
    unsafeCandidateExponentialObjectMorphism :: CandidateExponentialObject c m o -> CandidateExponentialObject c m o -> m -> CandidateExponentialObjectMorphism c m o
    unsafeCandidateExponentialObjectMorphism s t transp = CandidateExponentialObjectMorphism {sourceCandidateExponentialObject = s, targetCandidateExponentialObject = t, transpose = transp}
    
    instance (Morphism m o) => Morphism (CandidateExponentialObjectMorphism c m o) (CandidateExponentialObject c m o) where
        source = sourceCandidateExponentialObject
        target = targetCandidateExponentialObject
        ceom2 @ ceom1 = CandidateExponentialObjectMorphism {sourceCandidateExponentialObject = sourceCandidateExponentialObject ceom1, targetCandidateExponentialObject = targetCandidateExponentialObject ceom2, transpose = (transpose ceom2) @ (transpose ceom1)}
    
    data CandidateExponentialObjectCategory c m o = CandidateExponentialObjectCategory (TwoBase c m o)
        deriving (Eq, Show, Generic, Simplifiable, PrettyPrint)
        
    instance (Category c m o, Morphism m o, Eq c, Eq m ,Eq o) => Category (CandidateExponentialObjectCategory c m o) (CandidateExponentialObjectMorphism c m o) (CandidateExponentialObject c m o) where
        
        identity (CandidateExponentialObjectCategory base) tripod
            | differentInternalDomain = error "identity in a CandidateExponentialObjectCategory of a tripod with a different internal domain."
            | differentInternalCodomain = error "identity in a CandidateExponentialObjectCategory of a tripod with a different internal codomain."
            | differentUniverseCategory = error "identity in a CandidateExponentialObjectCategory of a tripod with a different universe category."
            | otherwise = CandidateExponentialObjectMorphism {sourceCandidateExponentialObject = tripod, targetCandidateExponentialObject = tripod, transpose = identity (universeCategoryTripod tripod) (powerObject tripod)}
            where
                differentInternalDomain = internalDomain tripod /= base ->$ A
                differentInternalCodomain = internalCodomain tripod /= base ->$ B
                differentUniverseCategory = universeCategoryTripod tripod /= tgt base
            
        
        ar (CandidateExponentialObjectCategory base) tripod1 tripod2
            | differentInternalDomain1 = error "ar in a CandidateExponentialObjectCategory where the source has different internal domain."
            | differentInternalCodomain1 = error "ar in a CandidateExponentialObjectCategory where the source has different internal codomain."
            | differentUniverseCategory1 = error "ar in a CandidateExponentialObjectCategory where the source has different universe Category."
            | differentInternalDomain2 = error "ar in a CandidateExponentialObjectCategory where the target has different internal domain."
            | differentInternalCodomain2 = error "ar in a CandidateExponentialObjectCategory where the target has different internal codomain."
            | differentUniverseCategory2 = error "ar in a CandidateExponentialObjectCategory where the target has different universe Category."
            | otherwise = (\m -> CandidateExponentialObjectMorphism{sourceCandidateExponentialObject = tripod1, targetCandidateExponentialObject = tripod2, transpose = m}) <$> ar (universeCategoryTripod tripod1) (powerObject tripod1) (powerObject tripod2)
            where
                differentInternalDomain1 = internalDomain tripod1 /= base ->$ A
                differentInternalCodomain1 = internalCodomain tripod1 /= base ->$ B
                differentUniverseCategory1 = universeCategoryTripod tripod1 /= tgt base
                differentInternalDomain2 = internalDomain tripod2 /= base ->$ A
                differentInternalCodomain2 = internalCodomain tripod2 /= base ->$ B
                differentUniverseCategory2 = universeCategoryTripod tripod2 /= tgt base
    
    instance (FiniteCategory c m o, Morphism m o, Eq c, Eq m ,Eq o) => FiniteCategory (CandidateExponentialObjectCategory c m o) (CandidateExponentialObjectMorphism c m o) (CandidateExponentialObject c m o) where
        
        ob (CandidateExponentialObjectCategory base) = [Tripod{twoCone = c, evalMap = m} | powerObj <- ob (tgt base), c <- limits (twoDiagram (tgt base) powerObj (base ->$ A)), m <- ar (tgt base) (apex c) (base ->$ B), isCandidateExponentialObject Tripod{twoCone = c, evalMap = m}]
        
    -- | Return the exponential objects of a given 2-base.
    exponentialObjects :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m ,Eq o) => TwoBase c m o -> Set (CandidateExponentialObject c m o)
    exponentialObjects base = terminalObjects (CandidateExponentialObjectCategory base)