{-# LANGUAGE MonadComprehensions #-} {-| Module : FiniteCategories Description : Visualize finite categories with GraphViz. Copyright : Guillaume Sabbagh 2022 License : GPL-3 Maintainer : guillaumesabbagh@protonmail.com Stability : experimental Portability : portable This module is a way of exporting finite categories with GraphViz. Every function assumes that the 'FiniteCategory' is a 'FiniteCategory', if you want to export a category without implementing an instantiation of 'FiniteCategory', you can instantiate 'FiniteCategory' with the default functions 'defaultGenAr' and 'defaultDecompose'. -} module Math.IO.FiniteCategories.ExportGraphViz ( -- * Visualize categories categoryToGraph, catToDot, catToPdf, genToDot, genToPdf, categoryToGraphFormat, catToDotFormat, catToPdfFormat, -- * Visualize diagrams diagToDotCluster, diagToPdfCluster, diagToDot, diagToPdf, diagToDot2, diagToPdf2, diagToDot2Format, diagToPdf2Format, -- * Visualize natural transformations natToDot, natToPdf, natToDotFormat, natToPdfFormat, ) where import Math.FiniteCategory import Math.FiniteCategories import Math.IO.PrettyPrint import qualified Data.Text.Lazy as L (pack, Text) import qualified Data.Text.Lazy.IO as IO (putStrLn, writeFile) import Data.Graph.Inductive.Graph (mkGraph, Node, Edge, LNode, LEdge) import Data.Graph.Inductive.PatriciaTree (Gr) import Data.GraphViz (graphToDot, nonClusteredParams, fmtNode, fmtEdge, GraphvizParams(..), NodeCluster(..), blankParams,GraphID( Num ), Number(..)) import Data.GraphViz.Attributes.Complete (Label(StrLabel), Attribute(Label)) import Data.GraphViz.Attributes (X11Color(..), color) import Data.GraphViz.Printing (renderDot, toDot) import Data.Maybe (fromJust) import Data.List (elemIndex,intercalate) import Data.WeakSet.Safe import qualified Data.WeakSet as Set import Data.WeakMap.Safe import System.Process (callCommand) import System.Directory (createDirectoryIfMissing) import System.FilePath.Posix (takeDirectory) -- | Write lazy text to a file specified by a path, if the path leads to non existing directories, it creates the directories. Credits to wisn : https://stackoverflow.com/a/58685979 createAndWriteFile :: FilePath -> L.Text -> IO () createAndWriteFile path content = do createDirectoryIfMissing True $ takeDirectory path IO.writeFile path content -- | Transform an object of a category into a pure node. objToNode :: (Eq o, FiniteCategory c m o) => c -> o -> Node objToNode c o | index == Nothing = error("Call objToNod on an object not in the category.") | otherwise = i where Just i = index index = elemIndex o (setToList.ob $ c) -- | Transform an object of a category into a labeled node. objToLNode :: (Eq o, PrettyPrint o, FiniteCategory c m o) => c -> o -> LNode String objToLNode c o = (objToNode c o, pprint o) -- | Transform an object of a category into a labeled node, using a custom function. objToLNodeFormat :: (Eq o, FiniteCategory c m o) => c -> (o -> String) -> o -> LNode String objToLNodeFormat c formatObj o = (objToNode c o, formatObj o) -- | Transform a morphism of a category into a pure edge. arToEdge :: (Eq o, Morphism m o, FiniteCategory c m o) => c -> m -> Edge arToEdge c m = ((objToNode c). source $ m, (objToNode c). target $ m) -- | Transform a morphism of a category into a labeled edge. arToLEdge :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> m -> LEdge String arToLEdge c m = ((objToNode c). source $ m, (objToNode c). target $ m, pprint m) -- | Transform a morphism of a category into a labeled edge using a custom function. arToLEdgeFormat :: (Eq o, Morphism m o, FiniteCategory c m o) => c -> (m -> String) -> m -> LEdge String arToLEdgeFormat c formatMorph m = ((objToNode c). source $ m, (objToNode c). target $ m, formatMorph m) -- | Transform a category into an underlying inductive graph. categoryToGraph :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> Gr String String categoryToGraph c = mkGraph (setToList (objToLNode c <$> (ob c))) (setToList (arToLEdge c <$> (arrows c))) -- | Transform a category into an underlying inductive graph using formatting functions. categoryToGraphFormat :: (Eq o, Morphism m o, FiniteCategory c m o) => c -> (o -> String) -> (m -> String) -> Gr String String categoryToGraphFormat c formatObj formatMorph = mkGraph (setToList (objToLNodeFormat c formatObj <$> (ob c))) (setToList (arToLEdgeFormat c formatMorph <$> (arrows c))) -- | Transform a dot representation of a graph into a pdf file. dotToPdf :: IO () -> String -> IO () dotToPdf dot path = dot >> callCommand ("dot "++path++" -o "++path++".pdf -T pdf") -- | Export a category with GraphViz. If the category is too large, use `genToDot` instead. -- -- The black arrows are generating arrows, grey one are generated arrows. catToDot :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> String -> IO () catToDot c path = createAndWriteFile path $ renderDot $ toDot dot_file where dot_file = graphToDot nonClusteredParams { fmtNode= \(n,label)-> [Label (StrLabel (L.pack label))], fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label)), if label `isIn` generatorsLabels then color Black else color Gray80]} (categoryToGraph c) generators = genArrows c generatorsLabels = pprint <$> generators -- | Export a category with GraphViz, format the objects and the morphisms. If the category is too large, use `genToDot` instead. -- -- The black arrows are generating arrows, grey one are generated arrows. catToDotFormat :: (Eq o, Morphism m o, FiniteCategory c m o) => c -> (o -> String) -> (m -> String) -> String -> IO () catToDotFormat c formatObj formatMorph path = createAndWriteFile path $ renderDot $ toDot dot_file where dot_file = graphToDot nonClusteredParams { fmtNode= \(n,label)-> [Label (StrLabel (L.pack label))], fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label)), if label `isIn` generatorsLabels then color Black else color Gray80]} (categoryToGraphFormat c formatObj formatMorph) generators = genArrows c generatorsLabels = formatMorph <$> generators -- | Export a category with GraphViz. If the category is too large, use `genToPdf` instead. -- -- The black arrows are generating arrows, grey one are generated arrows. catToPdf :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> String -> IO () catToPdf c path = dotToPdf (catToDot c path) path -- | Export a category with GraphViz and format objects and arrows. If the category is too large, use `genToPdf` instead. -- -- The black arrows are generating arrows, grey one are generated arrows. catToPdfFormat :: (Eq o, Morphism m o, FiniteCategory c m o) => c -> (o -> String) -> (m -> String) -> String -> IO () catToPdfFormat c formatObj formatMorph path = dotToPdf (catToDotFormat c formatObj formatMorph path) path -- | Transforms a category into an inductive graph. categoryToGeneratorGraph :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> Gr String String categoryToGeneratorGraph c = mkGraph (setToList (objToLNode c <$> (ob c))) (setToList (arToLEdge c <$> (genArrows c))) -- | Export the generator of a category with GraphViz. Use this when the category is too large to be fully exported. genToDot :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> String -> IO () genToDot c path = createAndWriteFile path $ renderDot $ toDot dot_file where dot_file = graphToDot nonClusteredParams { fmtNode= \(n,label)-> [Label (StrLabel (L.pack label))], fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label))]} (categoryToGeneratorGraph c) -- | Export the generator of a category with GraphViz. Use this when the category is to large to be fully exported. genToPdf :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> String -> IO () genToPdf c path = dotToPdf (genToDot c path) path -- __________________________________ -- __________________________________ -- -- Diagram representation with cluster of objects mapped together -- __________________________________ -- __________________________________ -- | If the node is pair, then it is part of the source category, else it is part of the target category. diagObjToNodeCluster :: (Eq o, FiniteCategory c m o) => c -> Bool -> o -> Node diagObjToNodeCluster c b o | index == Nothing = error("Call diagObjToNod on an object not in the category.") | otherwise = if b then 2*i else 2*i+1 where Just i = index index = elemIndex o (setToList (ob c)) diagObjToLNodeCluster :: (Eq o, PrettyPrint o, FiniteCategory c m o) => c -> Bool -> o -> LNode String diagObjToLNodeCluster c b o = (diagObjToNodeCluster c b o, pprint o) diagArToEdgeCluster :: (Eq o, Morphism m o, FiniteCategory c m o) => c -> Bool -> m -> Edge diagArToEdgeCluster c b m = ((diagObjToNodeCluster c b). source $ m, (diagObjToNodeCluster c b). target $ m) diagArToLEdgeCluster :: (Eq o, PrettyPrint o, PrettyPrint m, Morphism m o, FiniteCategory c m o) => c -> Bool -> m -> LEdge String diagArToLEdgeCluster c b m = ((diagObjToNodeCluster c b). source $ m, (diagObjToNodeCluster c b). target $ m, pprint m) diagToGraphCluster :: (Eq c1, Eq o1, PrettyPrint o1, PrettyPrint m1, Morphism m1 o1, FiniteCategory c1 m1 o1, Eq c2, Eq o2, PrettyPrint o2, PrettyPrint m2, Morphism m2 o2, FiniteCategory c2 m2 o2) => Diagram c1 m1 o1 c2 m2 o2 -> Gr String String diagToGraphCluster f = mkGraph (setToList ((diagObjToLNodeCluster (src f) True <$> (ob (src f))))++(setToList (diagObjToLNodeCluster (tgt f) False <$> (ob (tgt f))))) (setToList ((diagArToLEdgeCluster (src f) True <$> (genArrows (src f))))++(setToList (diagArToLEdgeCluster (tgt f) False <$> (genArrows (tgt f))))) -- | Export a functor with GraphViz such that the source category is in green, the target in blue, the objects mapped together are in the same cluster. diagToDotCluster :: (Eq c1, Eq o1, PrettyPrint o1, PrettyPrint m1, Morphism m1 o1, FiniteCategory c1 m1 o1, Eq c2, Eq o2, PrettyPrint o2, PrettyPrint m2, Morphism m2 o2, FiniteCategory c2 m2 o2) => Diagram c1 m1 o1 c2 m2 o2 -> String -> IO () diagToDotCluster f@Diagram{src=s,tgt=t,omap=om,mmap=fm} path = createAndWriteFile path $ renderDot $ toDot dot_file where dot_file = graphToDot Params { isDirected = True ,globalAttributes = [] ,clusterBy = (\(n,nl) -> if (n `mod` 2) == 0 then (C ((fromJust (elemIndex (om |!| ((setToList (ob s)) !! (n `div` 2))) (setToList (ob t))))) $ N (n,nl)) else (C (fromJust (elemIndex ((setToList (ob t)) !! (n `div` 2)) (setToList (ob t)))) $ N (n,nl))) ,isDotCluster = const True ,clusterID = Num . Int ,fmtCluster = const [] ,fmtNode = \(n,label)-> [Label (StrLabel (L.pack label)), if (n `mod` 2) == 0 then color Green else color Blue] ,fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label))] } (diagToGraphCluster f) -- | Export a functor as a pdf with GraphViz such that the source category is in green, the target in blue, the objects mapped together are in the same cluster. diagToPdfCluster :: (Eq c1, Eq o1, PrettyPrint o1, PrettyPrint m1, Morphism m1 o1, FiniteCategory c1 m1 o1, Eq c2, Eq o2, PrettyPrint o2, PrettyPrint m2, Morphism m2 o2, FiniteCategory c2 m2 o2) => Diagram c1 m1 o1 c2 m2 o2 -> String -> IO () diagToPdfCluster f path = dotToPdf (diagToDotCluster f path) path -- __________________________________ -- __________________________________ -- -- Diagram representation with arrows between arrows -- __________________________________ -- __________________________________ indexAr :: (Morphism m o, FiniteCategory c m o, Eq o, Eq m) => c -> m -> Int indexAr c m | isIn m (arrows c) = fromJust $ elemIndex m (setToList (arrows c)) | otherwise = error "indexAr of arrow not in category" indexOb :: (FiniteCategory c m o, Eq o) => c -> o -> Int indexOb c o | isIn o (ob c) = fromJust $ elemIndex o (setToList (ob c)) | otherwise = error "indexOb of object not in category" -- | If the node%4 == 0, then it is part of the source category, else if node%4 == 1 it is part of the target category. diagObjToNode :: (Eq o, FiniteCategory c m o) => c -> Bool -> o -> Node diagObjToNode c b o | index == Nothing = error("Call diagObjToNode on an object not in the category.") | otherwise = if b then 4*i else 4*i+1 where Just i = index index = elemIndex o (setToList (ob c)) diagObjToLNode :: (Eq o, PrettyPrint o, FiniteCategory c m o) => c -> Bool -> o -> LNode String diagObjToLNode c b o = (diagObjToNode c b o, pprint o) -- Creates the invisible node associated to an arrow of the source category if the boolean is True, of the target category if the boolean is False. invisNodeSrc :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2) => (Diagram c1 m1 o1 c2 m2 o2) -> m1 -> LNode String invisNodeSrc f@Diagram{src=s,tgt=t,mmap=_,omap=_} m = (4*(indexAr s m)+2, pprint m) invisNodeTgt :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2) => (Diagram c1 m1 o1 c2 m2 o2) -> m2 -> LNode String invisNodeTgt f@Diagram{src=s,tgt=t,mmap=_,omap=_} m = (4*(indexAr t m)+3, pprint m) diagArToLEdges :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2) => (Diagram c1 m1 o1 c2 m2 o2) -> Either m1 m2 -> [LEdge String] diagArToLEdges f@Diagram{src=s,tgt=t,omap=_,mmap=_} (Left m) = [((diagObjToNode s True). source $ m, fst.(invisNodeSrc f) $ m, ""),(fst.(invisNodeSrc f) $ m,(diagObjToNode s True). target $ m, "")] diagArToLEdges f@Diagram{src=s,tgt=t,omap=_,mmap=_} (Right m) = [((diagObjToNode t False). source $ m, fst.(invisNodeTgt f) $ m, ""),(fst.(invisNodeTgt f) $ m,(diagObjToNode t False). target $ m, "")] linkArrows :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2) => (Diagram c1 m1 o1 c2 m2 o2) -> [LEdge String] linkArrows f@Diagram{src=s,tgt=t,omap=_,mmap=fm} = (\m->(fst(invisNodeSrc f m),fst(invisNodeTgt f (fm |!| m)),"")) <$> (setToList (arrows s)) linkObjects :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2) => (Diagram c1 m1 o1 c2 m2 o2) -> [LEdge String] linkObjects f@Diagram{src=s,tgt=t,omap=om,mmap=_} = (\o->(diagObjToNode s True o,diagObjToNode t False (om |!| o),"")) <$> (setToList (ob s)) diagToGraph :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2, PrettyPrint o2) => (Diagram c1 m1 o1 c2 m2 o2) -> Gr String String diagToGraph f = mkGraph ((diagObjToLNode (src f) True <$> (setToList (ob (src f))))++(diagObjToLNode (tgt f) False <$> (setToList (ob (tgt f))))++((invisNodeSrc f) <$> (setToList (arrows (src f))))++((invisNodeTgt f) <$> (setToList (arrows (tgt f))))) ((Prelude.concat ((diagArToLEdges f <$> (Left <$> (setToList (arrows (src f)))))++(diagArToLEdges f <$> (Right <$> (setToList (arrows (tgt f)))))))++(linkArrows f)++(linkObjects f)) -- | Export a diagram with GraphViz such that the source category is in green, the target in blue, each morphism is represented by a node. diagToDot :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2, PrettyPrint o2) => (Diagram c1 m1 o1 c2 m2 o2) -> String -> IO () diagToDot f@Diagram{src=s,tgt=t,omap=om,mmap=fm} path = createAndWriteFile path $ renderDot $ toDot dot_file where dot_file = graphToDot Params { isDirected = True ,globalAttributes = [] ,clusterBy = (\(n,nl) -> case () of _ | (n `mod` 2) == 0 -> (C 0 $ N (n,nl)) | (n `mod` 2) == 1 -> (C 1 $ N (n,nl))) ,isDotCluster = const True ,clusterID = Num . Int ,fmtCluster = const [] ,fmtNode = \(n,label)-> [Label (StrLabel (L.pack label)), fmtColorN n] ,fmtEdge= \e@(n1,n2,label)-> [Label (StrLabel (L.pack label)), fmtColorE e] } (diagToGraph f) where fmtColorN n | n `mod` 4 == 0 = color Green | n `mod` 4 == 1 = color Blue | n `mod` 4 == 2 = color Red | n `mod` 4 == 3 = color Pink fmtColorE (s,t,_) | s `mod ` 4 == 0 = if t `mod` 2 == 1 then color Red else color Green | t `mod ` 4 == 0 = color Green | s `mod ` 4 == 1 = color Blue | t `mod ` 4 == 1 = color Blue | otherwise = color Black -- | Export a diagram as a pdf with GraphViz such that the source category is in green, the target in blue, each morphism is represented by a node. diagToPdf :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2, PrettyPrint o2) => (Diagram c1 m1 o1 c2 m2 o2) -> String -> IO () diagToPdf f path = dotToPdf (diagToDot f path) path -- __________________________________ -- __________________________________ -- -- Diagram representation as a selection of the target category -- __________________________________ -- __________________________________ -- | Export a diagram with GraphViz such that a node or an arrow is in orange if it is the target of the functor. diagToDot2 :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2, PrettyPrint o2) => (Diagram c1 m1 o1 c2 m2 o2) -> String -> IO () diagToDot2 f@Diagram{src=s,tgt=t,omap=om,mmap=fm} path = createAndWriteFile path $ renderDot $ toDot dot_file where dot_file = graphToDot nonClusteredParams { fmtNode= \(n,label)-> [Label (StrLabel (L.pack label)), colorNode n], fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label)), colorEdge label]} (categoryToGraph t) where colorNode n = case () of _ | countPredN == 0 -> color Black | countPredN == 1 -> color Orange | countPredN == 2 -> color Orange1 | countPredN == 3 -> color Orange2 | countPredN == 4 -> color Orange3 | countPredN == 5 -> color Orange4 | otherwise -> color OrangeRed4 where countPredN = Prelude.length [1 | o <- (setToList (ob s)), (objToNode t (om |!| o)) == n] colorEdge e = case () of _ | countPredE == 0 -> color Black | countPredE == 1 -> color Orange | countPredE == 2 -> color Orange1 | countPredE == 3 -> color Orange2 | countPredE == 4 -> color Orange3 | countPredE == 5 -> color Orange4 | otherwise -> color OrangeRed4 where countPredE = Prelude.length [1 | m <- (setToList (arrows s)), (pprint (fm |!| m)) == e] -- | Export a diagram as a pdf with GraphViz such that a node or an arrow is in orange if it is the target of the functor. diagToPdf2 :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2, PrettyPrint o2) => (Diagram c1 m1 o1 c2 m2 o2) -> String -> IO () diagToPdf2 f path = dotToPdf (diagToDot2 f path) path -- | Export a diagram with GraphViz such that a node or an arrow is in orange if it is the target of the functor. -- -- Allows to format the name of the objects and of the morphisms. diagToDot2Format :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2, PrettyPrint o2) => (Diagram c1 m1 o1 c2 m2 o2) -> (o2 -> String) -> (m2 -> String) -> String -> IO () diagToDot2Format f@Diagram{src=s,tgt=t,omap=om,mmap=fm} formatObj formatMorph path = createAndWriteFile path $ renderDot $ toDot dot_file where dot_file = graphToDot nonClusteredParams { fmtNode= \(n,label)-> [Label (StrLabel (L.pack label)), colorNode n], fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label)), colorEdge label]} (categoryToGraphFormat t formatObj formatMorph) where colorNode n = case () of _ | countPredN == 0 -> color Black | countPredN == 1 -> color Orange | countPredN == 2 -> color Orange1 | countPredN == 3 -> color Orange2 | countPredN == 4 -> color Orange3 | countPredN == 5 -> color Orange4 | otherwise -> color OrangeRed4 where countPredN = Prelude.length [1 | o <- (setToList (ob s)), (objToNode t (om |!| o)) == n] colorEdge e = case () of _ | countPredE == 0 -> color Black | countPredE == 1 -> color Orange | countPredE == 2 -> color Orange1 | countPredE == 3 -> color Orange2 | countPredE == 4 -> color Orange3 | countPredE == 5 -> color Orange4 | otherwise -> color OrangeRed4 where countPredE = Prelude.length [1 | m <- (setToList (arrows s)), (pprint (fm |!| m)) == e] -- | Export a diagram as a pdf with GraphViz such that a node or an arrow is in orange if it is the target of the functor. -- -- Allows to format the name of the objects and of the morphisms. diagToPdf2Format :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, PrettyPrint m2, PrettyPrint o2) => (Diagram c1 m1 o1 c2 m2 o2) -> (o2 -> String) -> (m2 -> String) -> String -> IO () diagToPdf2Format f formatObj formatMorph path = dotToPdf (diagToDot2Format f formatObj formatMorph path) path -- __________________________________ -- __________________________________ -- -- Natural transformation representation as a translation in the target category. -- __________________________________ -- __________________________________ -- | Export a natural transformation with GraphViz such that the source diagram is in green, the target diagram is in blue and the translation is in yellow. natToDot :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, Eq c1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, Eq c2, PrettyPrint m2, PrettyPrint o2) => (NaturalTransformation c1 m1 o1 c2 m2 o2) -> String -> IO () natToDot nt path = createAndWriteFile path $ renderDot $ toDot dot_file where s = source nt t = target nt c = components nt dot_file = graphToDot nonClusteredParams { fmtNode= \(n,label)-> [Label (StrLabel (L.pack label)), colorNode n], fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label)), colorEdge label]} (categoryToGraph (tgt s)) where colorNode n = case () of _ | predNSrc && predNTgt -> color Turquoise | predNSrc -> color Green | predNTgt -> color Blue | otherwise -> color Black where predNSrc = Set.or [(objToNode (tgt s) ((omap s) |!| o)) == n | o <- (ob (src s))] predNTgt = Set.or [(objToNode (tgt t) ((omap t) |!| o)) == n | o <- (ob (src t))] colorEdge e = case () of _ | predESrc && predETgt && predENat -> color Beige | predESrc && predETgt -> color Turquoise | predESrc && predENat -> color Orange | predETgt && predENat -> color LightBlue | predESrc -> color Green | predETgt -> color Blue | predENat -> color Yellow | otherwise -> color Black where predESrc = Set.foldr (||) False [(pprint ((mmap s) |!| m)) == e | m <- (arrows (src s))] predETgt = Set.foldr (||) False [(pprint ((mmap t) |!| m)) == e | m <- (arrows (src t))] predENat = Set.foldr (||) False [(pprint (c |!| o)) == e | o <- (ob (src s))] -- | Export a natural transformation as pdf with GraphViz such that the source diagram is in green, the target diagram is in blue and the translation is in yellow. natToPdf :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, Eq c1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, Eq c2, PrettyPrint m2, PrettyPrint o2) => (NaturalTransformation c1 m1 o1 c2 m2 o2) -> String -> IO () natToPdf nt path = dotToPdf (natToDot nt path) path -- | Export a natural transformation with GraphViz such that the source diagram is in green, the target diagram is in blue and the translation is in yellow. -- -- Allows to format the name of the objects and of the morphisms. natToDotFormat :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, Eq c1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, Eq c2, PrettyPrint m2, PrettyPrint o2) => (NaturalTransformation c1 m1 o1 c2 m2 o2) -> (o2 -> String) -> (m2 -> String) -> String -> IO () natToDotFormat nt formatObj formatMorph path = createAndWriteFile path $ renderDot $ toDot dot_file where s = source nt t = target nt c = components nt dot_file = graphToDot nonClusteredParams { fmtNode= \(n,label)-> [Label (StrLabel (L.pack label)), colorNode n], fmtEdge= \(n1,n2,label)-> [Label (StrLabel (L.pack label)), colorEdge label]} (categoryToGraphFormat (tgt s) formatObj formatMorph) where colorNode n = case () of _ | predNSrc && predNTgt -> color Turquoise | predNSrc -> color Green | predNTgt -> color Blue | otherwise -> color Black where predNSrc = Set.or [(objToNode (tgt s) ((omap s) |!| o)) == n | o <- (ob (src s))] predNTgt = Set.or [(objToNode (tgt t) ((omap t) |!| o)) == n | o <- (ob (src t))] colorEdge e = case () of _ | predESrc && predETgt && predENat -> color Beige | predESrc && predETgt -> color Turquoise | predESrc && predENat -> color Orange | predETgt && predENat -> color LightBlue | predESrc -> color Green | predETgt -> color Blue | predENat -> color Yellow | otherwise -> color Black where predESrc = Set.foldr (||) False [(pprint ((mmap s) |!| m)) == e | m <- (arrows (src s))] predETgt = Set.foldr (||) False [(pprint ((mmap t) |!| m)) == e | m <- (arrows (src t))] predENat = Set.foldr (||) False [(pprint (c |!| o)) == e | o <- (ob (src s))] -- | Export a natural transformation as pdf with GraphViz such that the source diagram is in green, the target diagram is in blue and the translation is in yellow. -- -- Allows to format the name of the objects and of the morphisms. natToPdfFormat :: (Morphism m1 o1, FiniteCategory c1 m1 o1, Eq o1, Eq m1, Eq c1, PrettyPrint m1, PrettyPrint o1, Morphism m2 o2, FiniteCategory c2 m2 o2, Eq o2, Eq m2, Eq c2, PrettyPrint m2, PrettyPrint o2) => (NaturalTransformation c1 m1 o1 c2 m2 o2) -> (o2 -> String) -> (m2 -> String) -> String -> IO () natToPdfFormat nt formatObj formatMorph path = dotToPdf (natToDotFormat nt formatObj formatMorph path) path