FiniteCategories-0.2.0.0: src/Math/Categories/FinGrph.hs
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, MonadComprehensions #-}
{-| Module : FiniteCategories
Description : The __'FinGrph'__ category has finite multidigraphs as objects and multidigraph homomorphisms as morphisms.
Copyright : Guillaume Sabbagh 2022
License : GPL-3
Maintainer : guillaumesabbagh@protonmail.com
Stability : experimental
Portability : portable
The __'FinGrph'__ category has finite multidigraphs as objects and multidigraph homomorphisms as morphisms.
-}
module Math.Categories.FinGrph
(
-- * Graph
Arrow(..),
Graph,
-- ** Getters
nodes,
edges,
-- ** Smart constructors
graph,
unsafeGraph,
-- * Graph homomorphism
GraphHomomorphism,
-- ** Getters
nodeMap,
edgeMap,
-- ** Smart constructor
checkGraphHomomorphism,
graphHomomorphism,
unsafeGraphHomomorphism,
-- * FinGrph
FinGrph(..),
underlyingGraph,
underlyingGraphFormat,
)
where
import Math.Category
import Math.FiniteCategory
import Math.IO.PrettyPrint
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
-- | An 'Arrow' is composed of a source node, a target node and a label.
data Arrow n e = Arrow{
sourceArrow :: n,
targetArrow :: n,
labelArrow :: e
}
deriving (Eq, Show)
instance (PrettyPrint n, PrettyPrint e) => PrettyPrint (Arrow n e) where
pprint a = (pprint $ sourceArrow a)++"-"++(pprint $ labelArrow a)++"->"++(pprint $ targetArrow a)
-- | A 'Graph' is a set of nodes and a set of 'Arrow's.
--
-- 'Graph' is private, use smart constructor 'graph'.
data Graph n e = Graph {
nodes :: Set n, -- ^ The set of nodes of the graph.
edges :: Set (Arrow n e) -- ^ The set of arrows of the graph.
} deriving (Eq)
instance (Show n, Show e) => Show (Graph n e) where
show g = "(unsafeGraph "++(show $ nodes g)++" "++(show $ edges g)++")"
-- | Smart constructor of 'Graph'.
graph :: (Eq n) => Set n -> Set (Arrow n e) -> Maybe (Graph n e)
graph ns es
| (sourceArrow <$> es) `isIncludedIn` ns && (targetArrow <$> es) `isIncludedIn` ns = Just Graph{nodes=ns, edges=es}
| otherwise = Nothing
-- | Unsafe constructor of 'Graph', does not check the 'Graph' structure.
unsafeGraph :: Set n -> Set (Arrow n e) -> Graph n e
unsafeGraph n e = Graph{nodes=n, edges=e}
instance (PrettyPrint n, PrettyPrint e, Eq n, Eq e) => PrettyPrint (Graph n e) where
pprint g = "Graph ("++(pprint $ nodes g)++", "++(pprint $ edges g)++")"
-- | A 'GraphHomomorphism' is composed of a map between the nodes of the graphs, a map between the edges of the graphs, and the target 'Graph' so that we can recover it from the morphism.
--
-- It must follow axioms such that the image of an arrow is not torn appart, that is why the constructor is private. Use the smart constructor 'graphHomomorphism' instead.
data GraphHomomorphism n e = GraphHomomorphism {
nodeMap :: Map n n, -- ^ The mapping of nodes.
edgeMap :: Map (Arrow n e) (Arrow n e), -- ^ The mapping of edges.
targetGraph :: Graph n e -- ^ The target graph.
} deriving (Eq)
-- | Check wether the structure of 'GraphHomomorphism' is respected or not.
checkGraphHomomorphism :: (Eq n, Eq e) => GraphHomomorphism n e -> Bool
checkGraphHomomorphism gh = imageInTarget && Set.and noTear
where
noTear = [(nodeMap gh) |!| (sourceArrow arr) == sourceArrow ((edgeMap gh) |!| arr) && (nodeMap gh) |!| (targetArrow arr) == targetArrow ((edgeMap gh) |!| arr)| arr <- (domain.edgeMap) gh]
imageInTarget = (image.nodeMap) gh `isIncludedIn` (nodes.targetGraph) gh && (image.edgeMap) gh `isIncludedIn` (edges.targetGraph) gh
-- | The smart constructor of 'GraphHomomorphism'.
graphHomomorphism :: (Eq n, Eq e) => Map n n -> Map (Arrow n e) (Arrow n e) -> Graph n e -> Maybe (GraphHomomorphism n e)
graphHomomorphism nm em tg
| checkGraphHomomorphism gh = Just gh
| otherwise = Nothing
where
gh = GraphHomomorphism{nodeMap=nm, edgeMap=em, targetGraph=tg}
-- | Unsafe constructor of 'GraphHomomorphism' which does not check the structure of the 'GraphHomomorphism'.
unsafeGraphHomomorphism :: Map n n -> Map (Arrow n e) (Arrow n e) -> Graph n e -> GraphHomomorphism n e
unsafeGraphHomomorphism nm em tg = GraphHomomorphism{nodeMap=nm, edgeMap=em, targetGraph=tg}
instance (Show n, Show e) => Show (GraphHomomorphism n e) where
show gh = "(unsafeGraphHomomorphism "++(show $ nodeMap gh)++" "++(show $ edgeMap gh)++ " " ++ (show $ targetGraph gh) ++")"
instance (PrettyPrint n, PrettyPrint e, Eq n, Eq e) => PrettyPrint (GraphHomomorphism n e) where
pprint gh = "("++(pprint $ nodeMap gh)++", "++(pprint $ edgeMap gh)++")"
instance (Eq n, Eq e) => Morphism (GraphHomomorphism n e) (Graph n e) where
source gh = Graph {nodes = (domain.nodeMap) gh, edges = (domain.edgeMap) gh}
target = targetGraph
(@?) gh2 gh1
| target gh1 == source gh2 = Just $ GraphHomomorphism {nodeMap = (nodeMap gh2) |.| (nodeMap gh1), edgeMap = (edgeMap gh2) |.| (edgeMap gh1), targetGraph = target gh2}
| otherwise = Nothing
-- | The category of finite graphs.
data FinGrph n e = FinGrph deriving (Eq, Show)
instance (PrettyPrint n, PrettyPrint e, Eq n, Eq e) => PrettyPrint (FinGrph n e) where
pprint = show
instance (Eq n, Eq e, Show n ,Show e) => Category (FinGrph n e) (GraphHomomorphism n e) (Graph n e) where
identity _ g = GraphHomomorphism {nodeMap = (idFromSet.nodes) g, edgeMap = (idFromSet.edges) g, targetGraph = g}
ar _ s t = [GraphHomomorphism
{
nodeMap = appO, edgeMap = appF, targetGraph = t
} | appO <- appObj, appF <- ((fmap $ (Map.unions)).cartesianProductOfSets $ [twoObjToEdgeMaps x y appO | x <- (setToList $ nodes s), y <- (setToList $ nodes s)])]
where
appObj = Map.enumerateMaps (nodes s) (nodes t)
twoObjToEdgeMaps n1 n2 nMap = Map.enumerateMaps (Set.filter (\a -> sourceArrow a == n1 && targetArrow a == n2) (edges s)) (Set.filter (\a -> sourceArrow a == nMap |!| n1 && targetArrow a == nMap |!| n2) (edges t))
-- | Return the underlying graph of a 'FiniteCategory'.
underlyingGraph :: (FiniteCategory c m o, Morphism m o) => c -> Graph o m
underlyingGraph c = Graph{
nodes = ob c,
edges = (\m -> Arrow{sourceArrow=source m, targetArrow=target m, labelArrow=m}) <$> arrows c
}
-- | Return the underlying graph of a 'FiniteCategory' and apply formatting functions on objects and arrows.
underlyingGraphFormat :: (FiniteCategory c m o, Morphism m o) => (o -> a) -> (m -> b) -> c -> Graph a b
underlyingGraphFormat formatObj formatAr c = Graph{
nodes = formatObj <$> ob c,
edges = (\m -> Arrow{sourceArrow=formatObj.source $ m, targetArrow=formatObj.target $ m, labelArrow=formatAr m}) <$> arrows c
}