FiniteCategories-0.6.5.1: src/Math/Functors/SetValued.hs
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE MonadComprehensions #-}
{-| Module : FiniteCategories
Description : Utility functions for set-valued functors.
Copyright : Guillaume Sabbagh 2023
License : GPL-3
Maintainer : guillaumesabbagh@protonmail.com
Stability : experimental
Portability : portable
Kan extensions for set-valued functors. Inspired by DBC of M. Fleming, R. Gunther, R. Rosebrugh.
-}
module Math.Functors.SetValued
(
-- * Right Kan extension
LimitObject(..),
rightKanSetValued,
-- ** Formatting
formatLimitObject,
formatSetOfLimitObjects,
formatFunctionOfLimitObjects,
-- * Left Kan extension
ColimitObject(..),
leftKanSetValued,
-- ** Formatting
formatColimitObject,
formatSetOfColimitObjects,
formatFunctionOfColimitObjects,
)
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 Math.FiniteCategory
import Math.Categories.FinSet
import Math.Categories.FunctorCategory
import Math.Categories.CommaCategory
import Math.FiniteCategories.One
import Math.IO.PrettyPrint
import GHC.Base (maxInt)
-- | A 'LimitObject' is a map from a comma object to an element a, the maps should be seen as elements of the cartesian products indexed by comma objects.
type LimitObject o1 m2 a = Map (CommaObject One o1 m2) a
-- | Format a 'LimitObject' to be readable.
formatLimitObject :: (Eq o1, Eq m2, Eq a, PrettyPrint a) => LimitObject o1 m2 a -> String
formatLimitObject mapping = "(" ++ intercalate "," [ (pprint maxInt v) | (k,v) <- Map.mapToList mapping] ++ ")"
-- | Format a set of 'LimitObject's to be readable.
formatSetOfLimitObjects :: (Eq o1, Eq m2, Eq a, PrettyPrint a) => Set (LimitObject o1 m2 a) -> String
formatSetOfLimitObjects setOfMaps = "{" ++ intercalate "," (formatLimitObject <$> setToList setOfMaps) ++ "}"
-- | Format a 'Function' of 'LimitObject's to be readable.
formatFunctionOfLimitObjects :: (Eq o1, Eq m2, Eq a, PrettyPrint a) => Function (LimitObject o1 m2 a) -> String
formatFunctionOfLimitObjects func = intercalate "," [(formatLimitObject k) ++ " -> " ++ (formatLimitObject v) | (k,v) <- (Map.mapToList (function func))]
-- | Compute the image of the right Kan extension on an object of /B/. The image is a set of maps from the comma objects to elements a (you should see the maps as cartesian products indexed by comma objects).
rightKanSetValuedObjectwise :: (FiniteCategory c1 m1 o1, Morphism m1 o1, Eq m1, Eq o1,
FiniteCategory c2 m2 o2, Morphism m2 o2, Eq m2, Eq o2,
Eq a) =>
Diagram c1 m1 o1 c2 m2 o2 -> Diagram c1 m1 o1 (FinSet a) (Function a) (Set a) -> o2 -> Set (LimitObject o1 m2 a)
rightKanSetValuedObjectwise f x b = filteredMaps
where
commaCat = CommaCategory{leftDiagram=selectObject (tgt f) b, rightDiagram=f}
allMaps = weakMap <$> (cartesianProductOfSets.setToList $ [[(a, elemXa) | elemXa <- (x ->$ (indexTarget a))] | a <- ob commaCat])
filters = [\mapping -> ((x ->£(indexSecondArrow m)) ||!|| (mapping |!| (source m))) == (mapping |!| (target m)) | m <- setToList $ arrows commaCat]
filteredMaps = foldr Set.filter allMaps filters
-- | Compute the image of the right Kan extension on a morphism of /B/.
rightKanSetValuedObjectwiseMorphism :: (FiniteCategory c1 m1 o1, Morphism m1 o1, Eq m1, Eq o1,
FiniteCategory c2 m2 o2, Morphism m2 o2, Eq m2, Eq o2,
Eq a) =>
Diagram c1 m1 o1 c2 m2 o2 -> Diagram c1 m1 o1 (FinSet a) (Function a) (Set a) -> m2 -> Function (LimitObject o1 m2 a)
rightKanSetValuedObjectwiseMorphism f x m = result
where
precompose commaObj = unsafeCommaObject (indexSource commaObj) (indexTarget commaObj) ((selectedArrow commaObj) @ m)
imageSrc = rightKanSetValuedObjectwise f x (source m)
imageTgt = rightKanSetValuedObjectwise f x (target m)
commaCat2 = CommaCategory{leftDiagram=selectObject (tgt f) (target m), rightDiagram=f}
subsetIndexingObjects = ob commaCat2
result = Function{function=weakMapFromSet [(mapping,anElement [mapping2 | mapping2 <- imageTgt, Map.isSubmapOf (Map.mapKeys precompose mapping2) mapping]) | mapping <- imageSrc],
codomain=imageTgt}
-- | The right Kan extension of @X@ along @F@ where @X@ is a a set-valued functor.
--
-- We transform the @X@ functor so that its target becomes @(FinSet (Map (CommaObject One o1 m2) a))@, each object @A@ of @c1@ is mapped to @{Delta : {<One,id,A>} -> e | e <- X(A)}@.
rightKanSetValued :: (FiniteCategory c1 m1 o1, Morphism m1 o1, Eq c1, Eq m1, Eq o1,
FiniteCategory c2 m2 o2, Morphism m2 o2, Eq m2, Eq o2,
Eq a) =>
Diagram c1 m1 o1 c2 m2 o2 -> Diagram c1 m1 o1 (FinSet a) (Function a) (Set a) -> (Diagram c2 m2 o2 (FinSet (LimitObject o1 m2 a)) (Function (LimitObject o1 m2 a)) (Set (LimitObject o1 m2 a)), NaturalTransformation c1 m1 o1 (FinSet (LimitObject o1 m2 a)) (Function (LimitObject o1 m2 a)) (Set (LimitObject o1 m2 a)))
rightKanSetValued f x = (r,nat)
where
r = Diagram{src=tgt f,tgt=FinSet, omap=memorizeFunction (rightKanSetValuedObjectwise f x) (ob (tgt f)), mmap=memorizeFunction (rightKanSetValuedObjectwiseMorphism f x) (arrows (tgt f))}
rof = r <-@<- f
transformAIntoMap k a = weakMap [(unsafeCommaObject One k (identity (tgt f) (f ->$ k)), a)]
transformSetOfAIntoSetOfMap k v = [transformAIntoMap k e | e <- v]
transformFunctionOfAIntoFunctionOfMaps m1 func = Function{function=weakMap [(transformAIntoMap (source m1) t,transformAIntoMap (target m1) u) | (t,u) <- Map.mapToList (function func)], codomain=transformSetOfAIntoSetOfMap (target m1) (codomain func)}
newX = Diagram{src=src x, tgt=FinSet, omap=Map.mapWithKey transformSetOfAIntoSetOfMap (omap x), mmap=Map.mapWithKey transformFunctionOfAIntoFunctionOfMaps (mmap x)}
Right nat = naturalTransformation rof newX (weakMapFromSet [(o,Function{function=weakMapFromSet [(mapping,transformAIntoMap o ((snd.anElement) (Set.filter (\y -> (indexTarget.fst) y == o) (Map.mapToSet mapping))) ) | mapping <- (rof ->$ o)], codomain=newX ->$ o }) | o <- ob (src f)])
-- | A 'ColimitObject' is a set of couples (comma objects, elements a), the couples should be seen as elements of a disjoint union, the set of couples as equivalence classes of elements of a disjoint union.
type ColimitObject o1 m2 a = Set ((CommaObject o1 One m2), a)
-- | Format a 'ColimitObject' to be readable.
formatColimitObject :: (PrettyPrint a) => ColimitObject o1 m2 a -> String
formatColimitObject = (pprint maxInt).snd.anElement
-- | Format a set of 'ColimitObject's to be readable.
formatSetOfColimitObjects :: (Eq o1, Eq m2, Eq a, PrettyPrint a) => Set (ColimitObject o1 m2 a) -> String
formatSetOfColimitObjects setOfEquivClasses = "{" ++ intercalate "," (formatColimitObject <$> setToList setOfEquivClasses) ++ "}"
-- | Format a 'Function' of 'ColimitObject's to be readable.
formatFunctionOfColimitObjects :: (Eq o1, Eq m2, Eq a, PrettyPrint a) => Function (ColimitObject o1 m2 a) -> String
formatFunctionOfColimitObjects func = intercalate "," [(formatColimitObject k) ++ " -> " ++ (formatColimitObject v) | (k,v) <- (Map.mapToList (function func))]
transformGraphToEquivRelation :: (Eq a) => Set (a,a) -> Set a -> Set (Set a)
transformGraphToEquivRelation couples initialNodes = Set.foldr (\candidate clusters -> if Set.any (isIn candidate) clusters then clusters else (Set.insert (Set.insert candidate (dfs candidate [candidate] (setToList initialNodes))) clusters)) (set []) initialNodes
where
dfs node blacklist [] = set []
dfs node blacklist (candidate:nodes)
| elem candidate blacklist = (dfs node blacklist nodes)
| (node,candidate) `isIn` couples || (candidate,node) `isIn` couples = Set.insert candidate ((dfs node blacklist nodes) ||| (dfs candidate (node:blacklist) (setToList initialNodes)))
| otherwise = (dfs node blacklist nodes)
-- | Compute the image of the left Kan extension on an object of /B/. The image is a set of set of couples (comma objects, elements a) (you should see the couples as element of a disjoint union, the set of couples as equivalence classes of elements).
leftKanSetValuedObjectwise :: (FiniteCategory c1 m1 o1, Morphism m1 o1, Eq m1, Eq o1,
FiniteCategory c2 m2 o2, Morphism m2 o2, Eq m2, Eq o2,
Eq a) =>
Diagram c1 m1 o1 c2 m2 o2 -> Diagram c1 m1 o1 (FinSet a) (Function a) (Set a) -> o2 -> Set (ColimitObject o1 m2 a)
leftKanSetValuedObjectwise f x b = equivClasses
where
commaCat = CommaCategory{leftDiagram=f, rightDiagram=selectObject (tgt f) b}
allCouples = ((Set.unions).setToList $ [[(a, elemXa) | elemXa <- (x ->$ (indexSource a))] | a <- ob commaCat])
graphRelations = Set.unions [[((source m,a),(target m,b)) | (a,b) <- (Map.mapToSet (function (x ->£(indexFirstArrow m))))] | m <- (setToList (arrows commaCat)), not (isIdentity commaCat m)]
equivClasses = transformGraphToEquivRelation graphRelations allCouples
-- | Compute the image of the left Kan extension on a morphism of /B/.
leftKanSetValuedObjectwiseMorphism :: (FiniteCategory c1 m1 o1, Morphism m1 o1, Eq m1, Eq o1,
FiniteCategory c2 m2 o2, Morphism m2 o2, Eq m2, Eq o2,
Eq a) =>
Diagram c1 m1 o1 c2 m2 o2 -> Diagram c1 m1 o1 (FinSet a) (Function a) (Set a) -> m2 -> Function (ColimitObject o1 m2 a)
leftKanSetValuedObjectwiseMorphism f x m = result
where
postcompose commaObj = unsafeCommaObject (indexSource commaObj) (indexTarget commaObj) (m @ (selectedArrow commaObj))
imageSrc = leftKanSetValuedObjectwise f x (source m)
imageTgt = leftKanSetValuedObjectwise f x (target m)
result = Function{function=weakMapFromSet [(equivClass1, anElement [equivClass2 | equivClass2 <- imageTgt, Set.any (\(co, a) -> (postcompose co, a) `isIn` equivClass2) equivClass1 ]) | equivClass1 <- imageSrc],
codomain=imageTgt}
-- | The left Kan extension of @X@ along @F@ where @X@ is a a set-valued functor.
--
-- We transform the @X@ functor so that its target becomes @(FinSet (Set ((CommaObject One o1 m2), a)))@, each object @A@ of @c1@ is mapped to @{(<One,id,A>},e) | e <- X(A)}@.
leftKanSetValued :: (FiniteCategory c1 m1 o1, Morphism m1 o1, Eq c1, Eq m1, Eq o1,
FiniteCategory c2 m2 o2, Morphism m2 o2, Eq m2, Eq o2,
Eq a) =>
Diagram c1 m1 o1 c2 m2 o2 -> Diagram c1 m1 o1 (FinSet a) (Function a) (Set a) -> (Diagram c2 m2 o2 (FinSet (ColimitObject o1 m2 a)) (Function (ColimitObject o1 m2 a)) (Set (ColimitObject o1 m2 a)), NaturalTransformation c1 m1 o1 (FinSet (ColimitObject o1 m2 a)) (Function (ColimitObject o1 m2 a)) (Set (ColimitObject o1 m2 a)))
leftKanSetValued f x = (l,nat)
where
l = Diagram{src=tgt f,tgt=FinSet, omap=memorizeFunction (leftKanSetValuedObjectwise f x) (ob (tgt f)), mmap=memorizeFunction (leftKanSetValuedObjectwiseMorphism f x) (arrows (tgt f))}
lof = l <-@<- f
transformAIntoEquivClass k a = set [(unsafeCommaObject k One (identity (tgt f) (f ->$ k)), a)]
transformSetOfAIntoSetOfEquivClasses k v = [transformAIntoEquivClass k e | e <- v]
transformFunctionOfAIntoFunctionOfSetsOfEquivClasses m1 func = Function{function=weakMap [(transformAIntoEquivClass (source m1) t,transformAIntoEquivClass (target m1) u) | (t,u) <- Map.mapToList (function func)], codomain=transformSetOfAIntoSetOfEquivClasses (target m1) (codomain func)}
newX = Diagram{src=src x, tgt=FinSet, omap=Map.mapWithKey transformSetOfAIntoSetOfEquivClasses (omap x), mmap=Map.mapWithKey transformFunctionOfAIntoFunctionOfSetsOfEquivClasses (mmap x)}
Right nat = naturalTransformation newX lof (weakMapFromSet [(o,Function{function=weakMapFromSet [(equivClass,anElement [equivClass2 | equivClass2 <- (lof ->$ o), Set.any (\(co,a2) -> indexSource co == o && (snd.anElement $ equivClass) == a2) equivClass2]) | equivClass <- (newX ->$ o)], codomain=lof ->$ o }) | o <- ob (src f)])