packages feed

FiniteCategories-0.6.5.1: src/Math/Categories/FinSet.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE MultiParamTypeClasses #-}

{-| Module  : FiniteCategories
Description : The __'FinSet'__ category has finite sets as objects and functions as morphisms.
Copyright   : Guillaume Sabbagh 2022
License     : GPL-3
Maintainer  : guillaumesabbagh@protonmail.com
Stability   : experimental
Portability : portable

The __'FinSet'__ category has finite sets as objects and functions as morphisms.

Finite sets are represented by weak sets from Data.WeakSet and functions by enriched weak maps from Data.WeakMap.

These structures are homogeneous, meaning you can only have sets containing one type of objects in a given 'FinSet' category.

See the category __'PureSet'__ for the category of sets which can be arbitrarily nested.
-}

module Math.Categories.FinSet
(
    -- * Function
    Function(..),
    (||!||),
    -- * __FinSet__
    FinSet(..),
)
where
    import              Math.Category
    import              Math.FiniteCategory
    import              Math.Categories.ConeCategory
    import              Math.Categories.FunctorCategory
    import              Math.FiniteCategories.DiscreteCategory
    import              Math.FiniteCategories.DiscreteTwo
    import              Math.FiniteCategories.Parallel
    import              Math.IO.PrettyPrint
    
    import              Math.CompleteCategory
    import              Math.CocompleteCategory
    import              Math.CartesianClosedCategory
    
    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           (nub)
    import              Data.Maybe          (fromJust)
    import              Data.Simplifiable
    
    import              GHC.Generics

    
    -- | A 'Function' (finite function) is a weak map enriched with a codomain.
    --
    -- We have to store the codomain to retrieve the target set of a morphism in __'FinSet'__.
    data Function a = Function 
                            {
                                function :: Map a a,
                                codomain :: Set a
                            }
                        deriving
                            (Eq, Show, Generic, Simplifiable)
    
    instance (PrettyPrint a, Eq a) => PrettyPrint (Function a) where
        pprint v a = pprint v (function a)
        
        -- pprintWithIndentations cv ov indent a = pprintWithIndentations cv ov indent (function a)
        
    instance (Eq a) => Morphism (Function a) (Set a) where
        source = domain.function
        target = codomain
        (@) f2 f1 = Function{function = (function f2) |.| (function f1), codomain = codomain f2}
            
    -- | A function to apply a 'Function' to an object in the domain of the 'Function'.
    (||!||) :: (Eq a) => Function a -> a -> a
    (||!||) f x = (function f) |!| x
    
    -- | __'FinSet'__ is the category of finite sets.
    data FinSet a = FinSet deriving (Eq, Show, Generic, PrettyPrint, Simplifiable)
    
    instance (Eq a) => Category (FinSet a) (Function a) (Set a) where
        identity _ s = Function {function = idFromSet s, codomain = s}
        
        ar _ s t
            | Set.null s = set [Function{function = weakMap [], codomain = t}]
            | Set.null t = set []
            | otherwise = (\x -> Function{function = x, codomain = t}) <$> functions where
                domain = setToList s
                images = (t |^| (length domain))
                functions = weakMap <$> zip domain <$> images
                
    instance (Eq a, Eq oIndex) => HasProducts (FinSet a) (Function a) (Set a) (FinSet (Limit oIndex a)) (Function (Limit oIndex a)) (Set (Limit oIndex a)) oIndex where
        product discreteDiag = result
            where
                indexingCat = src discreteDiag
                prod = (ProductElement).weakMap <$> cartesianProductOfSets (setToList [(\x -> (i,x)) <$> (discreteDiag ->$ i) | i <- ob indexingCat])
                mapping i = memorizeFunction (\(ProductElement tuple) -> Projection (tuple |!| i)) prod
                constDiag = constantDiagram indexingCat FinSet prod
                transformObject x = [Projection e | e <- x]
                transformFunction Function{function = m, codomain = cod} = Function{function = weakMapFromSet [(Projection k, Projection v) | (k,v) <- Map.mapToSet m], codomain = transformObject cod}
                newDiag = Diagram{src = src discreteDiag, tgt = FinSet, omap = transformObject <$> (omap discreteDiag), mmap = transformFunction <$> (mmap discreteDiag)}
                result = unsafeCone prod (unsafeNaturalTransformation constDiag newDiag (weakMapFromSet [(i,Function {function=mapping i, codomain = newDiag ->$ i}) | i <- ob indexingCat]))
                
    instance (Eq a) => HasEqualizers (FinSet a) (Function a) (Set a) where
        equalize parallelDiag = result
            where
                equalizer = [e | e <- parallelDiag ->$ ParallelA, (parallelDiag ->£ ParallelF) ||!|| e == (parallelDiag ->£ ParallelG) ||!|| e]
                mapping i = memorizeFunction id equalizer
                constDiag = constantDiagram Parallel FinSet equalizer
                result = unsafeCone equalizer (unsafeNaturalTransformation constDiag parallelDiag (weakMap [(ParallelA,legA), (ParallelB, (parallelDiag ->£ ParallelF) @ legA) ]))
                legA = Function {function=mapping ParallelA, codomain = parallelDiag ->$ ParallelA}
    
    _projectMorphism m = Function{function = weakMapFromSet [(Projection k, Projection v) | (k,v) <- (Map.mapToSet.function) m], codomain = Projection <$> codomain m}
    
    instance (Eq a, Eq mIndex, Eq oIndex) => CompleteCategory (FinSet a) (Function a) (Set a) (FinSet (Limit oIndex a)) (Function (Limit oIndex a)) (Set (Limit oIndex a)) cIndex mIndex oIndex where
        limit = limitFromProductsAndEqualizers _projectMorphism
        
        projectBase diag = Diagram{src = FinSet, tgt = FinSet, omap = memorizeFunction (fmap Projection) (Map.values (omap diag)), mmap = memorizeFunction _projectMorphism (Map.values (mmap diag))}
        
    
    instance (Eq a, Eq oIndex) => HasCoproducts (FinSet a) (Function a) (Set a) (FinSet (Colimit oIndex a)) (Function (Colimit oIndex a)) (Set (Colimit oIndex a)) oIndex where
        coproduct discreteDiag = result
            where
                indexingCat = src discreteDiag
                coprod = Set.unions (setToList [CoproductElement i <$> (discreteDiag ->$ i) | i <- ob indexingCat])
                constDiag = constantDiagram indexingCat FinSet coprod
                transformObject x = [Coprojection e | e <- x]
                transformFunction Function{function = m, codomain = cod} = Function{function = weakMapFromSet [(Coprojection k, Coprojection v) | (k,v) <- Map.mapToSet m], codomain = transformObject cod}
                newDiag = Diagram{src = indexingCat, tgt = FinSet, omap = transformObject <$> (omap discreteDiag), mmap = transformFunction <$> (mmap discreteDiag)}
                mapping i = memorizeFunction (\(Coprojection x) -> CoproductElement i x) (newDiag ->$ i)
                result = unsafeCocone coprod (unsafeNaturalTransformation newDiag constDiag (weakMapFromSet [(i,Function {function=mapping i, codomain = coprod}) | i <- ob indexingCat]))
                
    instance (Eq a) => HasCoequalizers (FinSet a) (Function a) (Set a) where
        coequalize parallelDiag = result
            where
                glue x (s,mapping)
                    | (parallelDiag ->£ ParallelF) ||!|| x == (parallelDiag ->£ ParallelG) ||!|| x = (s,mapping)
                    | otherwise = (Set.delete ((parallelDiag ->£ ParallelF) ||!|| x) s, Map.adjust (const $ (parallelDiag ->£ ParallelG) ||!|| x) ((parallelDiag ->£ ParallelF) ||!|| x) mapping)
                (coequalizer,mapping) = Set.foldr glue ((parallelDiag ->$ ParallelB),memorizeFunction id (parallelDiag ->$ ParallelB)) (parallelDiag ->$ ParallelA) 
                constDiag = constantDiagram Parallel FinSet coequalizer
                result = unsafeCocone coequalizer (unsafeNaturalTransformation parallelDiag constDiag (weakMap [(ParallelA,Function {function=mapping, codomain = coequalizer} @ (parallelDiag ->£ ParallelF)), (ParallelB, Function {function=mapping, codomain = coequalizer}) ]))
                
    
    instance (Eq a, Eq mIndex, Eq oIndex) => CocompleteCategory (FinSet a) (Function a) (Set a) (FinSet (Colimit oIndex a)) (Function (Colimit oIndex a)) (Set (Colimit oIndex a)) cIndex mIndex oIndex where
        colimit = colimitFromCoproductsAndCoequalizers transformMorphismIntoColimMorphism
            where   
                transformMorphismIntoColimMorphism m = Function{function = weakMapFromSet [(Coprojection k, Coprojection v) | (k,v) <- (Map.mapToSet.function) m], codomain = Coprojection <$> codomain m}
                
        coprojectBase diag = Diagram{src = FinSet, tgt = FinSet, omap = memorizeFunction (fmap Coprojection) (Map.values (omap diag)), mmap = memorizeFunction transformMorphismIntoColimMorphism (Map.values (mmap diag))}
            where
                transformMorphismIntoColimMorphism m = Function{function = weakMapFromSet [(Coprojection k, Coprojection v) | (k,v) <- (Map.mapToSet.function) m], codomain = Coprojection <$> codomain m}
                
    instance (Eq a) => CartesianClosedCategory (FinSet a) (Function a) (Set a) (FinSet (TwoProduct a)) (Function (TwoProduct a)) (Set (TwoProduct a)) (FinSet (Cartesian a)) (Function (Cartesian a)) (Set (Cartesian a))  where
        internalHom twoBase = unsafeTripod twoLimit evalMap_
            where
                powerObject = [ExponentialElement (function m) | m <- ar FinSet (twoBase ->$ A) (twoBase ->$ B)]
                newInternalDomain = Exprojection <$> twoBase ->$ A
                baseTwoCone = completeDiagram Diagram{src = DiscreteTwo, tgt = FinSet, omap = weakMap [(A,powerObject),(B,newInternalDomain)], mmap = weakMap []}
                twoLimit = limit baseTwoCone
                eval tuple = (Projection (Exprojection $ m |!| x))
                    where
                        (ExponentialElement m) = tuple |!| A
                        (Exprojection x)  = tuple |!| B
                finalInternalCodomain = Projection <$> (Exprojection <$> twoBase ->$ A)
                evalMap_ = Function{function = Map.weakMapFromSet [(ProductElement tuple, eval tuple) | (ProductElement tuple) <- apex twoLimit], codomain = finalInternalCodomain}