packages feed

FiniteCategories-0.1.0.0: src/Cat/PartialFinCat.hs

{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses  #-}

{-| Module  : FiniteCategories
Description : __PartialFinCat__ is the category of finite categories, partial functors are the morphisms of __FinCat__.
Copyright   : Guillaume Sabbagh 2021
License     : GPL-3
Maintainer  : guillaumesabbagh@protonmail.com
Stability   : experimental
Portability : portable

The __PartialFinCat__ category has as objects finite categories and as morphisms partial functors between them.

A partial functor is a functor where the object map and the morphism map can be partial functions.

It is itself a large category (therefore not a finite one),
we only construct finite full subcategories of the mathematical infinite __PartialFinCat__ category.
`PartialFinCat` is the type of full finite subcategories of __PartialFinCat__.

To instantiate it, use the `PartialFinCat` constructor on a list of categories.

To convert a `PartialFunctor` into any other kind of functor, see @Diagram.Conversion@.

For example, see @ExampleCat.ExamplePartialFinCat@.
-}

module Cat.PartialFinCat
(
    PartialFunctor(..),
    PartialFinCat(..),
    domainObjects,
    domainArrows,
    codomainObjects,
    codomainArrows,
    objectsNotMapped,
    arrowsNotMapped,
    objectsNotMappedTo,
    arrowsNotMappedTo
)
where
    import FiniteCategory.FiniteCategory
    import Cat.FinCat
    import Utils.EnumerateMaps
    import Utils.CartesianProduct
    import Utils.AssociationList
    import IO.PrettyPrint
    import IO.Show
    import Utils.SetList
    import Data.List ((\\), nub)
    
    -- | A `PartialFunctor` /F/ between two categories is a partial map between objects and a partial map between arrows of the two categories such that :
    --
    -- prop> F (srcPF f) = srcPF (F f) 
    -- prop> F (tgtPF f) = tgtPF (F f)
    -- prop> F (f @ g) = F(f) @ F(g)
    -- prop> F (identity a) = identity (F a)
    --
    -- It is meant to be a morphism between categories within `PartialFinCat`, it is homogeneous, the type of the source category must be the same as the type of the target category.
    --
    -- To convert a `PartialFunctor` into any other kind of functor, see @Diagram.Conversion@.
    data PartialFunctor c m o = PartialFunctor {srcPF :: c, tgtPF :: c, omapPF :: AssociationList o o, mmapPF :: AssociationList m m} deriving (Eq, Show)
    
    instance (Eq c, Eq m, Eq o) => Morphism (PartialFunctor c m o) c where
        (@) PartialFunctor{srcPF=s2,tgtPF=t2,omapPF=om2,mmapPF=fm2} PartialFunctor{srcPF=s1,tgtPF=t1,omapPF=om1,mmapPF=fm1}
            | t1 /= s2 = error "Illegal composition of PartialFunctors."
            | otherwise = PartialFunctor{srcPF=s1,tgtPF=t2,omapPF=om2 !-. om1,mmapPF=fm2 !-. fm1}
        source = srcPF
        target = tgtPF
            
    instance (FiniteCategory c m o, Morphism m o, PrettyPrintable c, PrettyPrintable m, PrettyPrintable o, Eq m, Eq o) =>
              PrettyPrintable (PartialFunctor c m o) where
        pprint PartialFunctor{srcPF=s,tgtPF=t,omapPF=om,mmapPF=fm} = "PartialFunctor ("++pprint s++") -> ("++pprint t++")\n\n"++pprint om++"\n\n"++pprint fm
        
    -- -- | Checks wether the properties of a functor are respected.
    checkPartialFunctoriality :: (FiniteCategory c m o, Morphism m o, Eq m, Eq o) => PartialFunctor c m o -> Bool
    checkPartialFunctoriality PartialFunctor {srcPF=s,tgtPF=t,omapPF=om,mmapPF=fm}
        | not ((keys om) `isIncludedIn` (ob s)) = False
        | not ((keys fm) `isIncludedIn` (arrows s)) = False
        | not (and imSrcExists) = False
        | not (and imTgtExists) = False
        | not (and idMapped) = False
        | not (and imIdNotId) = False
        | not (and compNotMapped) = False
        | not (and errFunct) = False
        | otherwise = True
        where
            imSrcExists = [elem (source f) (keys om) | f <- keys fm]
            imTgtExists = [elem (target f) (keys om) | f <- keys fm]
            idMapped = [elem (identity s o) (keys fm) | o <- keys om]
            imIdNotId = [fm !-! (identity s a) == identity t (om !-! a) | a <- keys om]
            compNotMapped = [elem (g @ f) (keys fm) | f <- (arrows s), g <- (arFrom s (target f)), elem f (keys fm), elem g (keys fm)]
            errFunct = [fm !-! (g @ f) == (fm !-! g) @ (fm !-! f) | f <- (arrows s), g <- (arFrom s (target f)), elem f (keys fm), elem g (keys fm)]
      
    -- | An instance of `PartialFinCat` is a list of categories of interest.
    --
    -- Listing all arrows between two objects (i.e. listing PartialFunctors between two categories) is slow (there are a lot of candidates).
    newtype PartialFinCat c m o = PartialFinCat [c]
    
    -- We are forced to use the language extension FlexibleInstances because of this instance declaration :
    -- The category 'c' could be itself a `FinCat` category therefore not respecting the uniqueness rule of instanciation.
    instance (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => FiniteCategory (PartialFinCat c m o) (PartialFunctor c m o) c where
        ob (PartialFinCat xs) = xs
        identity (PartialFinCat xs) catObj
            | elem catObj xs = PartialFunctor {srcPF=catObj,tgtPF=catObj,omapPF=mkAssocListIdentity (ob catObj),mmapPF=mkAssocListIdentity (arrows catObj)}
            | otherwise = error "Category not in PartialFinCat"
        ar (PartialFinCat xs) cat1 cat2
            | elem cat1 xs && elem cat2 xs = [PartialFunctor{srcPF=cat1,tgtPF=cat2,mmapPF=appF, omapPF=appO} | appO <- appObj, appF <- appMorph, checkPartialFunctoriality PartialFunctor{srcPF=cat1,tgtPF=cat2,mmapPF=appF, omapPF=appO}]
            | otherwise = error "Category not in PartialFinCat"
            where
                appObj = concat $ (\x -> enumAssocLists x (ob cat2)) <$> (powerList (ob cat1))
                appMorph = concat $ (\x -> enumAssocLists x (arrows cat2)) <$> (powerList (arrows cat1))
                    
    instance (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => GeneratedFiniteCategory (PartialFinCat c m o) (PartialFunctor c m o) c where
        genAr = defaultGenAr
        decompose = defaultDecompose
    
    -- | Returns the objects mapped by a partial functor.
    domainObjects :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [o]
    domainObjects funct = keys (omapPF funct)
    
    -- | Returns the objects not mapped by a partial functor.
    objectsNotMapped :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [o]
    objectsNotMapped funct = (ob (source funct))\\(domainObjects funct)
    
    -- | Returns the arrows mapped by a partial functor.
    domainArrows :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [m]
    domainArrows funct = keys (mmapPF funct)
    
    -- | Returns the arrows not mapped by a partial functor.
    arrowsNotMapped :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [m]
    arrowsNotMapped funct = (arrows (source funct))\\(domainArrows funct)

    -- | Returns the objects mapped onto by a partial functor.
    codomainObjects :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [o]
    codomainObjects funct = nub $ values (omapPF funct)

    -- | Returns the objects not mapped onto by a partial functor.
    objectsNotMappedTo :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [o]
    objectsNotMappedTo funct = (ob (target funct))\\(codomainObjects funct)
    
    -- | Returns the arrows mapped onto by a partial functor.
    codomainArrows :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [m]
    codomainArrows funct = nub $ values (mmapPF funct)

    -- | Returns the arrows not mapped onto by a partial functor.
    arrowsNotMappedTo :: (FiniteCategory c m o, Morphism m o, Eq c, Eq m, Eq o) => PartialFunctor c m o -> [m]
    arrowsNotMappedTo funct = (arrows (target funct))\\(codomainArrows funct)