FiniteCategories-0.6.5.1: src/Math/CocompleteCategory.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-| Module : FiniteCategories
Description : Typeclasses for 'Category' with special properties such as being cocomplete.
Copyright : Guillaume Sabbagh 2022
License : GPL-3
Maintainer : guillaumesabbagh@protonmail.com
Stability : experimental
Portability : portable
Typeclasses for 'Category' with special properties such as being cocomplete.
A 'Category' might have coproducts meaning that any 'discreteDiagram' on it has a colimit.
A 'Category' might have coequalizers meaning that any 'parallelDiagram' on it has a colimit.
If a 'Category' have both coproducts and coequalizers, it is cocomplete meaning that it has all small colimits.
To compute colimits in a custom 'FiniteCategory', see 'colimits' in Math.ConeCategory.
-}
module Math.CocompleteCategory
(
-- * Colimit type
Colimit(..),
uncoproject,
-- * Helper typeclasses to define a 'CocompleteCategory'
HasCoproducts(..),
HasCoequalizers(..),
colimitFromCoproductsAndCoequalizers,
-- * Cocomplete Category
CocompleteCategory(..),
uncoprojectBase,
)
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.Functors.DiagonalFunctor
import Math.Categories.FunctorCategory
import Math.Categories.ConeCategory
import Math.Categories.Galaxy
import Math.FiniteCategories.DiscreteCategory
import Math.FiniteCategories.Parallel
import Math.IO.PrettyPrint
import GHC.Generics
-- | For a 'Category' parametrized over a type t, the apex of the colimit of a diagram indexed by a category parametrized over a type i will contain 'Coproduct' constructions. A given distinguished element can be constructed from a 'Coprojection' at a given index.
--
-- For example, in 'FinSet', let's consider a discrete diagram from 'DiscreteTwo' to ('FinSet' Int) which selects {1,2} and {3,4}. The nadir of the colimit is obviously {('A',1),('A',2),('B',3),('B',4)}, note that it is not an object of ('Finset' Int) but an object of ('FinSet' (DiscreteTwoOb,Int)). The 'Colimit' type allows to construct type ('FinSet' ('Colimit' 'DiscreteTwo' Int)) in which we can consider the original objects with 'Coprojection' and the new distinguished elements with 'Coproduct'. Thus, the colimit will be {Coproduct 'A' 1,Coproduct 'A' 2,Coproduct 'B' 3,Coproduct 'B' 4}. We can construct coprojections in the same category, for example along 'A', which will map ('Coprojection' 1) to 'Coproduct' 'A' 1.
data Colimit i t = Coprojection t -- ^ A 'Coprojection' is the parameter type of the original category to coproject them to the colimit.
| CoproductElement i t -- ^ A 'CoproductElement' is a couple containing an indexing object and an object of type t.
deriving (Eq, Show, Generic, Simplifiable)
instance (PrettyPrint i, PrettyPrint t, Eq i) => PrettyPrint (Colimit i t) where
pprint v (Coprojection x) = pprint v x
pprint v (CoproductElement idx x) = pprint v x ++ "_" ++ pprint v idx
pprintWithIndentations cv ov indent (Coprojection x) = indentation (ov - cv) indent ++ pprint cv x ++ "\n"
pprintWithIndentations cv ov indent (CoproductElement idx x) = indentation (ov - cv) indent ++ pprint cv x ++ "_" ++ pprint cv idx ++ "\n"
-- | Remove the constructor 'Coprojection' from an original object t, throws an error if a 'CoproductElement' is given.
uncoproject :: Colimit oIndex t -> t
uncoproject (Coprojection t) = t
uncoproject (CoproductElement _ _) = error "Uncoproject a coproduct element."
-- | The typeclass of categories which have all coproducts.
class (Category c m o, Morphism m o,
Category cLim mLim oLim, Morphism mLim oLim) =>
HasCoproducts c m o cLim mLim oLim oIndex | c -> m, m -> o, cLim -> mLim, mLim -> oLim, c oIndex -> cLim where
-- | Given a 'discreteDiagram' selecting objects, return the coproduct of the selected objects as a 'Cocone'.
coproduct :: Diagram (DiscreteCategory oIndex) (DiscreteMorphism oIndex) oIndex c m o -> Cocone(DiscreteCategory oIndex) (DiscreteMorphism oIndex) oIndex cLim mLim oLim
-- | The typeclass of categories which have all coequalizers.
class (Category c m o, Morphism m o) => HasCoequalizers c m o | c -> m, m -> o where
-- | Given a 'parallelDiagram' selecting arrows, return the coequalizer of the selected arrows as a 'Cocone'.
coequalize :: Diagram Parallel ParallelAr ParallelOb c m o -> Cocone Parallel ParallelAr ParallelOb c m o
-- | The typeclass of categories which have all colimits.
class (Category c m o, Morphism m o,
Category cLim mLim oLim, Morphism mLim oLim) =>
CocompleteCategory c m o cLim mLim oLim cIndex mIndex oIndex | c -> m, m -> o, cLim -> mLim, mLim -> oLim, c cIndex mIndex oIndex -> cLim where
-- | Return the colimit of a 'Diagram'.
colimit :: (FiniteCategory cIndex mIndex oIndex, Morphism mIndex oIndex, Eq cIndex, Eq mIndex, Eq oIndex) => Diagram cIndex mIndex oIndex c m o -> Cocone cIndex mIndex oIndex cLim mLim oLim
-- | A partial 'Diagram' to coproject objects and morphisms of a base like a call of 'colimit' would do.
--
-- (coconeBase (colimit diag)) should equal ((coprojectBase diag) <-@<- diag)
coprojectBase :: Diagram cIndex mIndex oIndex c m o -> Diagram c m o cLim mLim oLim
-- | A partial 'Diagram' to uncoproject objects and morphisms of the base in a 'colimit' cocone.
--
-- uncoprojectBase and coprojectBase returned 'Diagram's are inverses.
uncoprojectBase :: (CocompleteCategory c m o cLim mLim oLim cIndex mIndex oIndex) => Diagram cIndex mIndex oIndex c m o -> Diagram cLim mLim oLim c m o
uncoprojectBase = unsafeInverseDiagram.coprojectBase
-- | Computes efficiently colimits thanks to coproducts and coequalizers. Can be used to instantiate 'CocompleteCategory'.
--
-- The first argument is a function to transform an object of the original category into a colimit object.
--
-- Most of the time, the original category takes one type parameter and the function uses 'Coprojection', when the category does not take any type parameter it is 'id'.
--
-- For example, for 'FinSet', the function has to transform a function {1 -> 2} to the function {Coprojection 1 -> Coprojection 2}.
colimitFromCoproductsAndCoequalizers ::
(Category c m o, Morphism m o,
Category cLim mLim oLim, Morphism mLim oLim, HasCoproducts c m o cLim mLim oLim oIndex,HasCoequalizers cLim mLim oLim,
FiniteCategory cIndex mIndex oIndex, Morphism mIndex oIndex, Eq oIndex, Eq mIndex, Eq cIndex) =>
(m -> mLim) -> Diagram cIndex mIndex oIndex c m o -> Cocone cIndex mIndex oIndex cLim mLim oLim
colimitFromCoproductsAndCoequalizers transformMorphismIntoColimMorphism diag = colim
where
idxCat = src diag
discreteDiag = completeDiagram Diagram{src = discreteCategory bottomObjects, tgt = tgt diag, omap = weakMapFromSet [(o,diag ->$ o) | o <- bottomObjects], mmap = weakMap []}
bottomObjects = Set.filter (\o -> Set.and [not $ Set.null $ ar idxCat (target m) o | m <- arFrom idxCat o]) (ob idxCat)
objectToAMorphismToABottomObject o
| o `Set.elem` bottomObjects = identity idxCat o
| otherwise = anElement [m | m <- arFrom idxCat o, target m `Set.elem` bottomObjects]
coprod = coproduct discreteDiag
newDiag = (completeDiagram Diagram{src = src diag, tgt = (tgt (baseCocone coprod)), omap = weakMap [], mmap = transformMorphismIntoColimMorphism <$> mmap diag})
undiscrete = unsafeCocone (nadir coprod) (unsafeNaturalTransformation newDiag (constantDiagram (src diag) (tgt (baseCocone coprod)) (nadir coprod)) (weakMapFromSet [(o,((legsCocone coprod) =>$ (target $ objectToAMorphismToABottomObject o)) @ (newDiag ->£ (objectToAMorphismToABottomObject o))) | o <- (ob.src $ diag)]))
coequalizeFromMorph fIndex currColim = unsafeCocone (nadir coeq) ((diagonal (legsCocone coeq =>$ ParallelB)) @ (legsCocone currColim))
where
f = diag ->£ fIndex
coeq = coequalize $ completeDiagram Diagram{src = Parallel, tgt = tgt (baseCocone currColim), omap = weakMap [], mmap = weakMap [(ParallelF, (legsCocone currColim) =>$ (source fIndex)),(ParallelG, ((legsCocone currColim) =>$ (target fIndex)) @ (transformMorphismIntoColimMorphism f))]}
diagonal m = unsafeNaturalTransformation (constantDiagram (src diag) (tgt (baseCocone undiscrete)) (source m)) (constantDiagram (src diag) (tgt (baseCocone undiscrete)) (target m)) (memorizeFunction (const m) (ob (src diag)))
colim = Set.foldr coequalizeFromMorph undiscrete ((set.setToList) (genArrowsWithoutId idxCat))