{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {- | Bayesian Network Library. It is a very preliminary version. It has only been tested on very simple examples where it worked. It should be considered as experimental and not used in any production work. * Look at the "Bayes.Examples" and "Bayes.Examples.Tutorial" in this package to see how to use the library. * In "Bayes.Examples.Influence" you'll find additional examples about influence diagrams. * In "Bayes.Examples.Sampling" there are some explanations about the samplers for discrete networks. * "Bayes.Examples.EMTest" is explaining learning with expectation / maximization. * "Bayes.Examples.ContinuousSampling" is showing an example of sampling with a continuous network. -} module Bayes( -- * Graph -- ** Graph classes Graph(..) , UndirectedGraph(..) , DirectedGraph(..) , FoldableWithVertex(..) , FunctorWithVertex(..) , NamedGraph(..) -- ** Graph Monad , GraphMonad , GMState(..) , graphNode , runGraph , execGraph , evalGraph , emptyAuxiliaryState , getNewEmptyVariable , isRoot , rootNode , parentNodes , childrenNodes , markovBlanket -- ** Support functions for Graph constructions , Vertex , Edge , edge , newEdge , getVertex , edgeEndPoints , connectedGraph , dag , printGraphValues -- * SimpleGraph implementation -- ** The SimpleGraph type , DirectedSG , UndirectedSG , SBN(..) , varMap , displaySimpleGraph -- ** Bayesian network , BayesianNetwork(..) -- * Testing , testEdgeRemoval_prop , testVertexRemoval_prop ) where import qualified Data.IntMap as IM import qualified Data.Map as M import Control.Monad.State.Strict import Control.Monad.Writer.Strict import Control.Applicative((<$>)) import Bayes.Factor hiding(isEmpty) import Bayes.Factor.CPT(CPT(..)) import Bayes.Factor.MaxCPT(MAXCPT(..)) import Data.Maybe import qualified Data.Map as Map import qualified Data.Foldable as F import qualified Data.Traversable as T import Control.Applicative import qualified Data.Set as Set import Test.QuickCheck hiding ((.&.),Testable) import Test.QuickCheck.Arbitrary import Data.List(sort,intercalate,nub,foldl') import Bayes.PrivateTypes hiding(isEmpty) import GHC.Float(float2Double) --import Debug.Trace --debug a = trace (show a) a -- | An implementation of the BayesianNetwork using the simple graph and no value for the edges type SBN f = DirectedSG () f instance Arbitrary (DirectedSG String String) where arbitrary = do let createVertex g i = do name <- arbitrary :: Gen String return $ addVertex (Vertex i) name g createEdge g (va,vb) = do name <- arbitrary :: Gen String return $ addEdge (edge va vb) name g nbVertex <- choose (1,8) :: Gen Int g <- foldM createVertex emptyGraph [1..nbVertex] let allPairs = [(Vertex x,Vertex y) | x <- [1..nbVertex], y <- [1..nbVertex], x /= y] anEdge (x,y) = arbitrary :: Gen Bool edges <- filterM anEdge allPairs foldM createEdge g edges instance Arbitrary (DirectedSG () String) where arbitrary = do let createVertex g i = do name <- arbitrary :: Gen String return $ addVertex (Vertex i) name g createEdge g (va,vb) = do return $ addEdge (edge va vb) () g nbVertex <- choose (1,8) :: Gen Int g <- foldM createVertex emptyGraph [1..nbVertex] let allPairs = [(Vertex x,Vertex y) | x <- [1..nbVertex], y <- [1..nbVertex], x /= y] anEdge (x,y) = arbitrary :: Gen Bool edges <- filterM anEdge allPairs foldM createEdge g edges -- | Warning : the generated graph is not at all a bayesian network -- The variables in the CPT have no reason to correspond to the edges -- connected to that CPT. -- Only the main variable (first variable) is linked to the right vertex instance Factor f => Arbitrary (DirectedSG () f) where arbitrary = do let createVertex g i = do let value = fromJust $ factorWithVariables [DV (Vertex i) 2] [0.1,0.9] return $ addVertex (Vertex i) value g createEdge g (va,vb) = do return $ addEdge (edge va vb) () g nbVertex <- choose (1,8) :: Gen Int g <- foldM createVertex emptyGraph [1..nbVertex] let allPairs = [(Vertex x,Vertex y) | x <- [1..nbVertex], y <- [1..nbVertex], x /= y] anEdge (x,y) = arbitrary :: Gen Bool edges <- filterM anEdge allPairs foldM createEdge g edges testEdgeRemoval_prop :: DirectedSG String String -> Property testEdgeRemoval_prop g = (not . hasNoEdges) g ==> let Just e = someEdge g Just (vs,ve) = edgeVertices g e Just bi = ingoing g ve Just bo = outgoing g vs g' = removeEdge e g Just bi' = ingoing g' ve Just bo' = outgoing g' vs in (map (sort . (:) e ) [bi', bo'] == map sort [bi,bo]) && (sort (allEdges g) == sort (e:allEdges g')) testVertexRemoval_prop :: DirectedSG String String -> Property testVertexRemoval_prop g = (not . hasNoVertices) g ==> let Just v = someVertex g Just bi = ingoing g v Just bo = outgoing g v g' = removeVertex v g srcVertices = mapMaybe (startVertex g') bi dstVertices = mapMaybe (endVertex g') bo isNotDstVertex = not . (v `elem`) . mapMaybe (endVertex g') . fromJust . outgoing g' isNotStartVertex = not . (v `elem`) . mapMaybe (startVertex g') . fromJust . ingoing g' in (sort (allVertices g) == sort (v:allVertices g')) && (all isNotDstVertex srcVertices) && (all isNotStartVertex dstVertices) -- | Graph class used for graph processing algorithms. -- A graph processing algorithm does not have to know how the graph is implemented nor if it is -- directed or undirected class Graph g where -- | Add a new vertex addVertex :: Vertex -> b -> g a b -> g a b -- | Remove a vertex removeVertex :: Vertex -> g a b -> g a b -- | Get the vertex value if the vertex is found in the graph vertexValue :: g a b -> Vertex -> Maybe b -- | Change the vertex value if the vertex is found in the graph changeVertexValue :: Vertex -> b -> g a b -> Maybe (g a b) -- | Generate a \"random\" vertex someVertex :: g a b -> Maybe Vertex -- | Check is the graph has no vertrex hasNoVertices :: g a b -> Bool -- | Generate all vertices allVertices :: g a b -> [Vertex] -- | Get all the values allVertexValues :: g a b -> [b] -- | Get all nodes allNodes :: g a b -> [(Vertex,b)] -- | Check if two vertices are linked by a vertex isLinkedWithAnEdge :: g a b -> Vertex -> Vertex -> Bool -- | Add an edge addEdge :: Edge -> a -> g a b -> g a b -- | Remove an dedge removeEdge :: Edge -> g a b -> g a b -- | Vertices for an edge edgeVertices :: g a b -> Edge -> Maybe (Vertex,Vertex) -- | Edge value if the edge is found in the graph edgeValue :: g a b -> Edge -> Maybe a -- | Return a \"random\" edge someEdge :: g a b -> Maybe Edge -- | Check if the graph has no edges hasNoEdges :: g a b -> Bool -- | One extremity of the edge (which is the end only for directed edge) endVertex :: g a b -> Edge -> Maybe Vertex endVertex g e = do (_,ve) <- edgeVertices g e return ve -- | One extremity of the edge (which is the start only for directed edge) startVertex :: g a b -> Edge -> Maybe Vertex startVertex g e = do (vs,_) <- edgeVertices g e return vs -- | All edges of the graph allEdges :: g a b -> [Edge] -- | All values of the graph allEdgeValues :: g a b -> [a] -- | Returns an empty graph emptyGraph :: g a b -- | Check if the graph is empty isEmpty :: g a b -> Bool isEmpty g = hasNoVertices g && hasNoEdges g -- | Check if the graph is oriented oriented :: g a b -> Bool -- | All the neighbors of a vertex neighbors :: g a b -> Vertex -> Maybe [Vertex] -- | A named graph is a graph where the vertices have a name. -- This name is not a vertex value. Putting this name in the vertex value -- would make algorithm less readable. -- A vertex name is only useful to display the graph. -- Labeled graph has a different meaning in graph theory. class Graph g => NamedGraph g where -- | Add a vertex with a vertex name in addition to the value addLabeledVertex :: String -> Vertex -> b -> g a b -> g a b -- | Returns the vertex label vertexLabel :: g a b -> Vertex -> Maybe String -- | Undirected graph class Graph g => UndirectedGraph g where edges :: g a b -> Vertex -> Maybe [Edge] -- | Directed graph class Graph g => DirectedGraph g where ingoing :: g a b -> Vertex -> Maybe [Edge] outgoing :: g a b -> Vertex -> Maybe [Edge] -- | Return the Markov blanket of a node markovBlanket :: DirectedGraph g => g a b -> Vertex -> [Vertex] markovBlanket g c = let p = parentNodes g c children = childrenNodes g c parentOfChildrens = concatMap (parentNodes g) children in nub (c:p ++ children ++ parentOfChildrens) -- | Return the parents of a node parentNodes :: DirectedGraph g => g a b -> Vertex -> [Vertex] parentNodes g v = maybe [] id $ do ie <- ingoing g v p <- mapM (startVertex g) ie return p -- | Return the children of a node childrenNodes :: DirectedGraph g => g a b -> Vertex -> [Vertex] childrenNodes g v = maybe [] id $ do ie <- outgoing g v p <- mapM (endVertex g) ie return p isRoot :: DirectedGraph g => g a b -> Vertex -> Bool {-# INLINE isRoot #-} isRoot g v = case ingoing g v of Just [] -> True _ -> False -- | Get the root node for the graph rootNode :: DirectedGraph g => g a b -> Maybe Vertex rootNode g = let someRoots = filter (isRoot g) . allVertices $ g in case someRoots of (h:l) -> Just h _ -> Nothing -- | Check if the graph is a directed Acyclic graph dag :: DirectedGraph g => g a b -> Bool dag g = case rootNode g of Nothing -> isEmpty g Just r -> dag (removeVertex r g) -- | Check if the graph is connected connectedGraph :: Graph g => g a b -> Bool connectedGraph g = let visited = visitVertex g (Set.empty) ([fromJust $ someVertex g]) vertices = Set.fromList $ allVertices g equalSets a b = Set.isSubsetOf a b && Set.isSubsetOf b a in equalSets visited vertices where visitVertex _ visited [] = visited visitVertex theGraph visited (current:n) = if Set.member current visited then visitVertex theGraph visited n else let n' = fromJust $ neighbors theGraph current in visitVertex theGraph (Set.insert current visited) (n ++ n') -- | Create an edge description edge :: Vertex -> Vertex -> Edge {-# INLINE edge #-} edge a b = Edge a b -- | Endpoints of an edge edgeEndPoints :: Edge -> (Vertex,Vertex) edgeEndPoints (Edge va vb) = (va,vb) -- | Class used to share as much code as possible between -- directed and undirected graphs without -- implementing an undirected graph as a graph where -- we have a directed edge in both directions class NeighborhoodStructure n where -- | Return an empty neighborhood emptyNeighborhood :: n -- | Ingoing edges ingoingNeighbors :: n -> [Edge] -- | Outgoing edge outgoingNeighbors :: n -> [Edge] -- | Remove an edge removeNeighborsEdge :: Edge -> n -> n -- | Add an outgoing edge addOutgoingEdge :: Edge -> n -> n -- Add in ingoing edge addIngoingEdge :: Edge -> n -> n -- | Directed neighborhood structure for a vertex instance NeighborhoodStructure DE where emptyNeighborhood = DE [] [] ingoingNeighbors (DE i _) = i outgoingNeighbors (DE _ o) = o removeNeighborsEdge e (DE i o) = let i' = filter (/= e) i o' = filter (/= e) o in DE i' o' addOutgoingEdge e (DE i o) = DE i (e:o) addIngoingEdge e (DE i o) = DE (e:i) o -- | Undirected neighborhood structure for a vertex instance NeighborhoodStructure UE where emptyNeighborhood = UE [] ingoingNeighbors (UE e) = e outgoingNeighbors (UE e) = e removeNeighborsEdge e (UE l) = let l' = filter (/= e) l in UE l' addOutgoingEdge e (UE l) = UE (e:l) addIngoingEdge e (UE l) = UE (e:l) -- | Directed simple graph type DirectedSG = SimpleGraph DE -- | Undirected simple graph type UndirectedSG = SimpleGraph UE -- | Get the variable name mapping varMap :: SimpleGraph n e v -> M.Map String Vertex varMap (SP _ _ n) = M.fromList . map (\(i,s) -> (s, Vertex i)) . IM.toList $ n instance (Eq a, Eq b) => Eq (SimpleGraph DE a b) where (==) (SP a b _) (SP a' b' _) = a == a' && b == b' -- | An empty simple graph emptySimpleGraph = SP M.empty IM.empty IM.empty -- | Used to prevent adding duplicates to a graph noRedundancy new old = old instance FactorContainer (SimpleGraph local edge) where changeFactor = changeFactorInFunctor instance Functor (SimpleGraph local edge) where fmap f (SP em vm nm) = SP em (IM.map (\(l,d) -> (l, f d)) vm) nm class FunctorWithVertex g where fmapWithVertex :: (Vertex -> a -> b) -> g c a -> g c b fmapWithVertexM :: Monad m => (Vertex -> a -> m b) -> g c a -> m (g c b) instance FunctorWithVertex (SimpleGraph local) where fmapWithVertex f (SP em vm nm) = SP em (IM.mapWithKey (\k (l,d) -> (l, f (Vertex k) d)) vm) nm fmapWithVertexM f (SP em vm nm) = do let l = IM.toList vm g f (k,(l,d)) = do r <- f (Vertex k) d return (k,(l,r)) rl <- mapM (g f) $ l let vm' = IM.fromList rl return $ SP em vm' nm instance F.Foldable (SimpleGraph local edge) where foldr f c (SP _ vm _) = IM.foldr (\(_,d) s -> f d s) c vm instance T.Traversable (SimpleGraph local edge) where traverse f (SP em vm nm) = let l = IM.toList vm -- [(IM.Key, (DE, String))] onTriple f (k,(l,v)) = (\z -> (k,(l,z))) <$> f v l' = T.traverse (onTriple f) l -- f [(k,(l,z))] result y = (\x -> SP em (IM.fromList x) nm) <$> y in result l' -- | The foldable class is limited. For a graph g we may need the vertex in addition to the value class FoldableWithVertex g where -- | Fold with vertex foldrWithVertex :: (Vertex -> a -> b -> b) -> b -> g c a -> b foldlWithVertex' :: (b -> Vertex -> a -> b) -> b -> g c a -> b instance FoldableWithVertex (SimpleGraph local) where foldrWithVertex f s (SP _ vm _) = IM.foldrWithKey (\k (_,v) y -> f (Vertex k) v y) s vm foldlWithVertex' f s (SP _ vm _) = IM.foldlWithKey' (\y k (_,v) -> f y (Vertex k) v) s vm _addLabeledVertex vertexName vert@(Vertex v) value (SP em vm name) = let vm' = IM.insertWith' noRedundancy v (emptyNeighborhood,value) vm name' = IM.insert v vertexName name in SP em vm' name' _vertexLabel (SP _ _ name) (Vertex v) = IM.lookup v name instance NamedGraph DirectedSG where addLabeledVertex = _addLabeledVertex vertexLabel = _vertexLabel instance NamedGraph UndirectedSG where addLabeledVertex = _addLabeledVertex vertexLabel = _vertexLabel -- | SimpleGraph is an instance of Graph. instance Graph DirectedSG where addVertex = _addVertex removeVertex = _removeVertex vertexValue = _vertexValue changeVertexValue = _changeVertexValue someVertex = _someVertex hasNoVertices = _hasNoVertices allVertices = _allVertices allVertexValues = _allVertexValues allNodes = _allNodes isLinkedWithAnEdge = _isLinkedWithAnEdge addEdge = _addEdge removeEdge = _removeEdge edgeVertices = _edgeVertices edgeValue = _edgeValue someEdge = _someEdge hasNoEdges = _hasNoEdges allEdges = _allEdges allEdgeValues = _allEdgeValues emptyGraph = _emptyGraph oriented _ = True neighbors g v = nub <$> liftA2 (++) (map (\(Edge _ e) -> e) <$> (outgoing g v)) (map (\(Edge s _) -> s) <$> (ingoing g v)) -- | Reverse edge direction reverseEdge :: Edge -> Edge reverseEdge (Edge va vb) = edge vb va -- | SimpleGraph is an instance of Graph. instance Graph UndirectedSG where addVertex = _addVertex removeVertex = _removeVertex vertexValue = _vertexValue changeVertexValue = _changeVertexValue someVertex = _someVertex hasNoVertices = _hasNoVertices allVertices = _allVertices allVertexValues = _allVertexValues allNodes = _allNodes isLinkedWithAnEdge = _isLinkedWithAnEdge addEdge = _addEdge removeEdge e g = _removeEdge (reverseEdge e) (_removeEdge e g) edgeVertices = _edgeVertices edgeValue g e = case _edgeValue g e of Nothing -> _edgeValue g (reverseEdge e) r@(Just _) -> r someEdge = _someEdge hasNoEdges = _hasNoEdges allEdges = _allEdges allEdgeValues = _allEdgeValues emptyGraph = _emptyGraph oriented _ = False -- in undirected graphs the edge direction does not count so we need to get both -- ends to be sure we don not forget a vertex. In addition to that, an end may be the current vertex which -- is not part of the neighbors. So it has to be filtered out. Obviously, a better solution will -- have to be designed. neighbors g v = filter (/= v) <$> nub <$> liftA2 (++) (map (\(Edge _ e) -> e) <$> (edges g v)) (map (\(Edge s _) -> s) <$> (edges g v)) _emptyGraph = emptySimpleGraph _hasNoVertices (SP _ vm _) = IM.null vm _hasNoEdges (SP em _ _) = M.null em _allVertices (SP _ vm _) = map Vertex . IM.keys $ vm _allEdges (SP em _ _) = M.keys $ em _allNodes (SP _ vm _) = map (\(k,(_,v)) -> (Vertex k,v)) . IM.assocs $ vm _allVertexValues (SP _ vm _) = map snd (IM.elems vm) _allEdgeValues (SP em _ _) = M.elems em _isLinkedWithAnEdge :: SimpleGraph n e v -> Vertex -> Vertex -> Bool {-# INLINE _isLinkedWithAnEdge #-} _isLinkedWithAnEdge (SP em _ _) va vb = M.member (edge va vb) em || M.member (edge vb va) em _someVertex (SP _ vm _) = if IM.null vm then Nothing else Just . Vertex . head . IM.keys $ vm _someEdge (SP em _ _) = if M.null em then Nothing else Just . head . M.keys $ em _addVertex vert@(Vertex v) value (SP em vm nm) = SP em (IM.insertWith' noRedundancy v (emptyNeighborhood,value) vm) nm _removeVertex v@(Vertex vertex) g@(SP _ vm _) = maybe g removeVertexWithValue $! (IM.lookup vertex vm) where removeVertexWithValue (n,_) = let g' = foldr _removeEdge g (ingoingNeighbors n) SP em vm' nm' = foldr _removeEdge g' (outgoingNeighbors n) in SP em (IM.delete vertex vm') nm' _vertexValue g@(SP _ vm _) (Vertex i) = maybe Nothing (Just . extractValue) $! (IM.lookup i vm) where extractValue (_,d) = d _changeVertexValue v@(Vertex vi) newValue g@(SP e vm nm) = let newVertexMap = do (n,_) <- IM.lookup vi vm return $ IM.insert vi (n,newValue) vm in case newVertexMap of Nothing -> Just g Just nvm -> Just $ SP e nvm nm _removeEdge e@(Edge (Vertex vs) (Vertex ve)) g@(SP em vm nm) = let r = do _ <- M.lookup e em -- Check e is member of the graph (ns,vsdata) <- IM.lookup vs vm (ne,vedata) <- IM.lookup ve vm return ((vs,(removeNeighborsEdge e ns,vsdata)),(ve,(removeNeighborsEdge e ne,vedata))) updateGraph ((vs,vsdata),(ve,vedata)) = let vm' = IM.insert ve vedata . IM.insert vs vsdata $ vm em' = M.delete e em in SP em' vm' nm in maybe g updateGraph r _edgeVertices (SP em _ _) e@(Edge vs ve) = if M.member e em then Just (vs,ve) else Nothing _edgeValue :: SimpleGraph n e v -> Edge -> Maybe e {-# INLINE _edgeValue #-} _edgeValue (SP em _ _) e = do v <- M.lookup e em return v addEdgeReference :: NeighborhoodStructure local => Edge -> IM.IntMap (local, vertexdata) -> Vertex -> Vertex -> IM.IntMap (local, vertexdata) {-# INLINE addEdgeReference #-} addEdgeReference newEdge vm (Vertex vsi) (Vertex vei) = id $! IM.adjust addi vei $! (IM.adjust addo vsi vm) where addi (n,v) = (addIngoingEdge newEdge n,v) addo (n,v) = (addOutgoingEdge newEdge n,v) _addEdge :: (NeighborhoodStructure n,Graph (SimpleGraph n)) => Edge -> e -> SimpleGraph n e v -> SimpleGraph n e v {-# INLINE _addEdge #-} _addEdge newEdge@(Edge vs ve) value g@(SP em vm nm) = if testEdgeExistence g em vs ve then g else SP (M.insert newEdge value em) (addEdgeReference newEdge vm vs ve) nm where testEdgeExistence g em va vb = if (oriented g) then M.member (Edge va vb) em else M.member (Edge va vb) em || M.member (Edge vb va) em instance UndirectedGraph UndirectedSG where edges g@(SP _ vm _) v@(Vertex vi) = do (n,_) <- IM.lookup vi vm return (ingoingNeighbors n) instance DirectedGraph DirectedSG where ingoing g@(SP _ vm _) v@(Vertex vi) = do (n,_) <- IM.lookup vi vm return (ingoingNeighbors n) outgoing g@(SP _ vm _) v@(Vertex vi) = do (n,_) <- IM.lookup vi vm return (outgoingNeighbors n) {- Following code is used to display a graph in a form adapted to humans. -} bracketS :: String -> String bracketS [] = [] bracketS s = " [" ++ s ++ "];" createNodeStyle :: (MonadWriter String m) => (Vertex -> n -> Maybe String) -> (Vertex -> n -> Maybe String) -> Maybe String -> Vertex -> n -> m () createNodeStyle nodeShape nodeColor maybeLabel v n = let apply f = f v n label _ _ = case maybeLabel of Nothing -> Nothing Just s -> Just $ "label=\"" ++ s ++ "\"" in tell $ bracketS . intercalate "," . mapMaybe apply $ [nodeShape,nodeColor, label] createEdgeStyle :: (MonadWriter String m) => (Edge -> e -> Maybe String) -> (Edge -> e -> Maybe String) -> Edge -> e -> m () createEdgeStyle edgeShape edgeColor e n = let apply f = f e n in tell $ bracketS . intercalate "," . mapMaybe apply $ [edgeShape,edgeColor] printNode nm (Vertex k,v) = do tell "\n" let r = IM.lookup k nm when (isJust r) $ do tell $ "Node " ++ fromJust r tell "\n" tell $ show v tell "\n" addVertexToGraphviz nodeShape nodeColor nm (k,(_,v)) = do tell $ show k let r = IM.lookup k $ nm createNodeStyle nodeShape nodeColor r (Vertex k) v tell "\n" addVertexToUndirectedGraphviz nm (k,(_,v)) = do tell $ show k tell "\n" -- | Print the values of the graph vertices printGraphValues :: (Graph (SimpleGraph n), Show b) => SimpleGraph n e b -> IO () printGraphValues g@(SP _ _ nm) = putStrLn . execWriter $ mapM_ (printNode nm) (allNodes g) displaySimpleGraph :: (Vertex -> n -> Maybe String) -> (Vertex -> n -> Maybe String) -> (Edge -> e -> Maybe String) -> (Edge -> e -> Maybe String) -> SimpleGraph local e n -> String displaySimpleGraph nodeShape nodeColor edgeShape edgeColor g@(SP em vm nm) = execWriter $ do tell "digraph dot {\n" mapM_ (addVertexToGraphviz nodeShape nodeColor nm) $ IM.toList vm tell "\n" mapM_ (addEdgeToGraphviz edgeShape edgeColor ) $ M.toList em tell "}\n" where addEdgeToGraphviz es ec (e@(Edge (Vertex vs) (Vertex ve)),l) = do tell $ show vs tell " -> " tell $ show ve createEdgeStyle es ec e l tell "\n" noNodeStyle _ _ = Nothing noEdgeStyle _ _ = Nothing instance Show (DirectedSG () CPT) where show g = displaySimpleGraph noNodeStyle noNodeStyle noEdgeStyle noEdgeStyle g instance Show (DirectedSG () MAXCPT) where show g = displaySimpleGraph noNodeStyle noNodeStyle noEdgeStyle noEdgeStyle g instance Show (DirectedSG String String) where show g = displaySimpleGraph noNodeStyle noNodeStyle noEdgeStyle noEdgeStyle g instance (Show b, Show e) => Show (UndirectedSG e b)where show g@(SP em vm nm) = execWriter $ do tell "graph dot {\n" mapM_ (addVertexToUndirectedGraphviz nm) $ IM.toList vm tell "\n" mapM_ (addEdgeToGraphviz) $ M.toList em tell "}\n" where addEdgeToGraphviz (e@(Edge (Vertex vs) (Vertex ve)),l) = do tell $ show vs tell " -- " tell $ show ve tell "\n" displayFactors :: (NeighborhoodStructure n, Show f, Factor f, Graph (SimpleGraph n)) => SimpleGraph n a f -> String displayFactors g@(SP _ _ nm) = let nodes = allNodes g displayFactor (Vertex i,f) = let s = fromJust . IM.lookup i $ nm in s ++ "\n" ++ show f in intercalate "\n" $ map displayFactor nodes {- Graph Monad -} -- | State used for the construction of the graph in the monad and containing -- auxiliary informations like vertex name to vertex id and vertex count type AuxiliaryState = (M.Map String Int, Int) emptyAuxiliaryState = (M.empty,0) -- | The state of the graph monad : the graph and auxiliary data -- useful during the construction type GMState g e f = (AuxiliaryState,g e f) -- | Graph monad. -- The monad used to simplify the description of a new graph -- g is the graph type. e the edge type. f the node type (generally a 'Factor') newtype GraphMonad g e f a = GM {runGraphMonad :: State (GMState g e f) a} deriving(Functor,Applicative,Monad, MonadState (GMState g e f)) -- | Get a named vertex from the graph monad getVertex :: Graph g => String -> GraphMonad g e f (Maybe Vertex) getVertex a = do (namemap,_) <- gets fst return $ do i <- M.lookup a namemap return (Vertex i) -- | Add a new labeled edge to the graph newEdge :: Graph g => Vertex -> Vertex -> e -> GraphMonad g e f () newEdge va vb e = do (aux,g) <- get let g1 = addEdge (edge va vb) e g put $! (aux,g1) return () -- | Add a node in the graph using the graph monad graphNode :: NamedGraph g => String -> f -> GraphMonad g e f Vertex graphNode vertexName initValue = do ((namemap,_),_) <- get maybe (getNewEmptyVariable (Just vertexName) initValue) returnVertex $! (M.lookup vertexName namemap) where returnVertex i = return (Vertex i) -- | Generate a new unique unamed empty variable getNewEmptyVariable :: NamedGraph g => Maybe String -> f -> GraphMonad g e f Vertex getNewEmptyVariable name initValue = do ((namemap,count),g) <- get let vertexName = maybe ("unamed" ++ show count) id name g1 = addLabeledVertex vertexName (Vertex count) initValue g namemap1 = M.insert vertexName count namemap put $! ((namemap1,count+1),g1) return (Vertex count) runGraph :: Graph g => GraphMonad g e f a -> (a,g e f) runGraph = removeAuxiliaryState . flip runState (emptyAuxiliaryState,emptyGraph) . runGraphMonad where removeAuxiliaryState (r,(_,g)) = (r,g) evalGraph :: Graph g => GraphMonad g e f a -> a evalGraph = flip evalState (emptyAuxiliaryState,emptyGraph) . runGraphMonad execGraph :: Graph g => GraphMonad g e f a -> g e f execGraph = snd . flip execState (emptyAuxiliaryState,emptyGraph) . runGraphMonad