packages feed

graph-core 0.2.0.1 → 0.2.1.0

raw patch · 12 files changed

+391/−398 lines, 12 filesdep −safecopydep ~HTFdep ~QuickCheckdep ~containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: safecopy

Dependency ranges changed: HTF, QuickCheck, containers, deepseq, hashable, mtl, safe, unordered-containers, vector

API changes (from Hackage documentation)

- Data.Graph: Edge :: !Node -> !Node -> Edge
- Data.Graph: addEdge :: Node -> Node -> Graph -> Graph
- Data.Graph: addEdges :: [Edge] -> Graph -> Graph
- Data.Graph: addNode :: Node -> Graph -> Graph
- Data.Graph: children :: Graph -> Node -> Vector Node
- Data.Graph: data Edge
- Data.Graph: data Graph
- Data.Graph: edgeCount :: Graph -> Int
- Data.Graph: edges :: Graph -> [Edge]
- Data.Graph: edgesAdj :: AdjList -> [Edge]
- Data.Graph: empty :: Graph
- Data.Graph: fromAdj :: [(Node, [Node])] -> Graph
- Data.Graph: fromEdges :: [Edge] -> Graph
- Data.Graph: hasEdge :: Node -> Node -> Graph -> Bool
- Data.Graph: hull :: Graph -> Node -> NodeSet
- Data.Graph: hullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b
- Data.Graph: hullFoldM :: Monad m => Graph -> (b -> Node -> m b) -> b -> Node -> m b
- Data.Graph: isConsistent :: Graph -> Bool
- Data.Graph: nodes :: Graph -> [Node]
- Data.Graph: parents :: Graph -> Node -> Vector Node
- Data.Graph: removeEdge :: Node -> Node -> Graph -> Graph
- Data.Graph: removeEdges :: [Edge] -> Graph -> Graph
- Data.Graph: removeNode :: Node -> Graph -> Graph
- Data.Graph: rhull :: Graph -> Node -> NodeSet
- Data.Graph: rhullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b
- Data.Graph: solitaireNodes :: Graph -> [Node]
- Data.Graph: src :: Edge -> !Node
- Data.Graph: tgt :: Edge -> !Node
- Data.Graph: type Node = Int
- Data.Graph: type NodeSet = IntSet
- Data.Graph.NodeManager: data NodeManager k
- Data.Graph.NodeManager: emptyNode :: Node
- Data.Graph.NodeManager: emptyNodeManager :: NodeManager k
- Data.Graph.NodeManager: getExistingNodeHandle :: (Hashable k, Eq k) => k -> NodeManager k -> Maybe Node
- Data.Graph.NodeManager: getNewNodesSince :: Node -> NodeManager k -> NodeMap k
- Data.Graph.NodeManager: getNodeHandle :: (Hashable k, Eq k, MonadState (NodeManager k) m) => k -> m Node
- Data.Graph.NodeManager: getNodeMap :: (Hashable k, Eq k) => NodeManager k -> NodeMap k
- Data.Graph.NodeManager: hasKey :: (Eq k, Hashable k) => k -> NodeManager k -> Bool
- Data.Graph.NodeManager: initNodeManager :: (Hashable k, Eq k) => NodeMap k -> NodeManager k
- Data.Graph.NodeManager: instance Arbitrary v => Arbitrary (IntMap v)
- Data.Graph.NodeManager: instance Eq k => Eq (NodeManager k)
- Data.Graph.NodeManager: instance Show k => Show (NodeManager k)
- Data.Graph.NodeManager: isConsistent :: Ord k => NodeManager k -> Bool
- Data.Graph.NodeManager: keys :: NodeManager k -> [k]
- Data.Graph.NodeManager: lookupNode :: Node -> NodeManager k -> Maybe k
- Data.Graph.NodeManager: nodes :: NodeManager k -> [Node]
- Data.Graph.NodeManager: removeNodeHandle :: (Hashable k, Eq k) => Node -> NodeManager k -> NodeManager k
- Data.Graph.NodeManager: toList :: NodeManager k -> [(k, Node)]
- Data.Graph.NodeManager: type Node = Int
- Data.Graph.NodeManager: type NodeMap v = IntMap v
- Data.Graph.NodeManager: type NodeSet = IntSet
- Data.Graph.NodeManager: unsafeLookupNode :: Node -> NodeManager k -> k
- Data.Graph.Persistence: data PersistentGraph k
- Data.Graph.Persistence: instance Eq k => Eq (PersistentGraph k)
- Data.Graph.Persistence: instance SafeCopy Edge
- Data.Graph.Persistence: instance SafeCopy k0 => SafeCopy (PersistentGraph k0)
- Data.Graph.Persistence: instance Show k => Show (PersistentGraph k)
- Data.Graph.Persistence: loadGraph :: (Eq k, Hashable k) => PersistentGraph k -> (NodeManager k, Graph)
- Data.Graph.Persistence: persistGraph :: (Eq k, Hashable k) => NodeManager k -> Graph -> PersistentGraph k
+ Data.Core.Graph: Edge :: !Node -> !Node -> Edge
+ Data.Core.Graph: addEdge :: Node -> Node -> Graph -> Graph
+ Data.Core.Graph: addEdges :: [Edge] -> Graph -> Graph
+ Data.Core.Graph: addNode :: Node -> Graph -> Graph
+ Data.Core.Graph: children :: Graph -> Node -> Vector Node
+ Data.Core.Graph: data Edge
+ Data.Core.Graph: data Graph
+ Data.Core.Graph: edgeCount :: Graph -> Int
+ Data.Core.Graph: edges :: Graph -> [Edge]
+ Data.Core.Graph: edgesAdj :: AdjList -> [Edge]
+ Data.Core.Graph: empty :: Graph
+ Data.Core.Graph: fromAdj :: [(Node, [Node])] -> Graph
+ Data.Core.Graph: fromEdges :: [Edge] -> Graph
+ Data.Core.Graph: hasEdge :: Node -> Node -> Graph -> Bool
+ Data.Core.Graph: hull :: Graph -> Node -> NodeSet
+ Data.Core.Graph: hullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b
+ Data.Core.Graph: hullFoldM :: Monad m => Graph -> (b -> Node -> m b) -> b -> Node -> m b
+ Data.Core.Graph: isConsistent :: Graph -> Bool
+ Data.Core.Graph: nodes :: Graph -> [Node]
+ Data.Core.Graph: parents :: Graph -> Node -> Vector Node
+ Data.Core.Graph: removeEdge :: Node -> Node -> Graph -> Graph
+ Data.Core.Graph: removeEdges :: [Edge] -> Graph -> Graph
+ Data.Core.Graph: removeNode :: Node -> Graph -> Graph
+ Data.Core.Graph: rhull :: Graph -> Node -> NodeSet
+ Data.Core.Graph: rhullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b
+ Data.Core.Graph: solitaireNodes :: Graph -> [Node]
+ Data.Core.Graph: src :: Edge -> !Node
+ Data.Core.Graph: tgt :: Edge -> !Node
+ Data.Core.Graph: type Node = Int
+ Data.Core.Graph: type NodeSet = IntSet
+ Data.Core.Graph.NodeManager: data NodeManager k
+ Data.Core.Graph.NodeManager: emptyNode :: Node
+ Data.Core.Graph.NodeManager: emptyNodeManager :: NodeManager k
+ Data.Core.Graph.NodeManager: getExistingNodeHandle :: (Hashable k, Eq k) => k -> NodeManager k -> Maybe Node
+ Data.Core.Graph.NodeManager: getNewNodesSince :: Node -> NodeManager k -> NodeMap k
+ Data.Core.Graph.NodeManager: getNodeHandle :: (Hashable k, Eq k, MonadState (NodeManager k) m) => k -> m Node
+ Data.Core.Graph.NodeManager: getNodeMap :: (Hashable k, Eq k) => NodeManager k -> NodeMap k
+ Data.Core.Graph.NodeManager: hasKey :: (Eq k, Hashable k) => k -> NodeManager k -> Bool
+ Data.Core.Graph.NodeManager: initNodeManager :: (Hashable k, Eq k) => NodeMap k -> NodeManager k
+ Data.Core.Graph.NodeManager: instance Arbitrary v => Arbitrary (IntMap v)
+ Data.Core.Graph.NodeManager: instance Eq k => Eq (NodeManager k)
+ Data.Core.Graph.NodeManager: instance Show k => Show (NodeManager k)
+ Data.Core.Graph.NodeManager: isConsistent :: Ord k => NodeManager k -> Bool
+ Data.Core.Graph.NodeManager: keys :: NodeManager k -> [k]
+ Data.Core.Graph.NodeManager: lookupNode :: Node -> NodeManager k -> Maybe k
+ Data.Core.Graph.NodeManager: nodes :: NodeManager k -> [Node]
+ Data.Core.Graph.NodeManager: removeNodeHandle :: (Hashable k, Eq k) => Node -> NodeManager k -> NodeManager k
+ Data.Core.Graph.NodeManager: toList :: NodeManager k -> [(k, Node)]
+ Data.Core.Graph.NodeManager: type Node = Int
+ Data.Core.Graph.NodeManager: type NodeMap v = IntMap v
+ Data.Core.Graph.NodeManager: type NodeSet = IntSet
+ Data.Core.Graph.NodeManager: unsafeLookupNode :: Node -> NodeManager k -> k
+ Data.Core.Graph.Persistence: data PersistentGraph k
+ Data.Core.Graph.Persistence: instance Eq k => Eq (PersistentGraph k)
+ Data.Core.Graph.Persistence: instance Show k => Show (PersistentGraph k)
+ Data.Core.Graph.Persistence: loadGraph :: (Eq k, Hashable k) => PersistentGraph k -> (NodeManager k, Graph)
+ Data.Core.Graph.Persistence: persistGraph :: (Eq k, Hashable k) => NodeManager k -> Graph -> PersistentGraph k

Files

graph-core.cabal view
@@ -1,5 +1,5 @@ name:                graph-core-version:             0.2.0.1+version:             0.2.1.0 synopsis:            Fast, memory efficient and persistent graph implementation description:         A small package providing a powerful and easy to use Haskell graph implementation. homepage:            https://github.com/factisresearch/graph-core@@ -13,18 +13,17 @@ cabal-version:       >=1.8  library-  exposed-modules:     Data.Graph, Data.Graph.NodeManager, Data.Graph.Persistence-  other-modules:       Data.Graph.PureCore+  exposed-modules:     Data.Core.Graph, Data.Core.Graph.NodeManager, Data.Core.Graph.Persistence+  other-modules:       Data.Core.Graph.PureCore   build-depends:       base >=4.6 && <4.8,-                       hashable >=1.2 && <1.3,-                       unordered-containers >=0.2 && <0.3,-                       containers >=0.5 && <0.6,-                       safe >=0.3 && <0.4,-                       deepseq >=1.3 && <1.4,-                       vector >=0.10 && <0.11,-                       QuickCheck >=2.6 && <2.7,-                       mtl >=2.1 && <2.2,-                       safecopy >=0.8 && <0.9+                       hashable >=1.2,+                       unordered-containers >=0.2,+                       containers >=0.5,+                       safe >=0.3,+                       deepseq >=1.3,+                       vector >=0.10,+                       QuickCheck >=2.6,+                       mtl >=2.1   hs-source-dirs:      src   ghc-options: -Wall -fno-warn-orphans @@ -34,16 +33,15 @@   main-is:             Tests.hs   other-modules:       Test.NodeManager, Test.Core, Test.Persistence   build-depends:       base >=4.6 && <4.8,-                       hashable >=1.2 && <1.3,-                       unordered-containers >=0.2 && <0.3,-                       containers >=0.5 && <0.6,-                       safe >=0.3 && <0.4,-                       deepseq >=1.3 && <1.4,-                       vector >=0.10 && <0.11,-                       QuickCheck >=2.6 && <2.7,-                       mtl >=2.1 && <2.2,-                       safecopy >=0.8 && <0.9,-                       HTF >=0.11 && <0.12+                       hashable >=1.2,+                       unordered-containers >=0.2,+                       containers >=0.5,+                       safe >=0.3,+                       deepseq >=1.3,+                       vector >=0.10,+                       QuickCheck >=2.6,+                       mtl >=2.1,+                       HTF >=0.11   ghc-options: -Wall -fno-warn-orphans  source-repository head
+ src/Data/Core/Graph.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE RecordWildCards #-}+module Data.Core.Graph+  ( Graph, Node, NodeSet, Edge(..)+  , empty, fromEdges, fromAdj, isConsistent+  , nodes, edges, children, parents, hasEdge+  , edgeCount+  , hull, rhull, hullFold, hullFoldM, rhullFold+  , addEdge, addEdges, removeEdge, removeEdges+  , addNode, removeNode, solitaireNodes+  , edgesAdj+  )+where++import Data.Core.Graph.NodeManager hiding (isConsistent, nodes)+import Data.Core.Graph.PureCore
+ src/Data/Core/Graph/NodeManager.hs view
@@ -0,0 +1,115 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE CPP #-}+module Data.Core.Graph.NodeManager+    ( NodeManager, Node, NodeMap, NodeSet+    , emptyNode+    , initNodeManager, emptyNodeManager, getNodeMap+    , getNodeHandle, getExistingNodeHandle, lookupNode, unsafeLookupNode+    , removeNodeHandle+    , getNewNodesSince, keys, hasKey, nodes, toList+    , isConsistent+    )+where++import Control.Monad.State.Strict+import Data.Hashable+import Data.Maybe+import Test.QuickCheck (NonNegative(..), Arbitrary(..))+import qualified Data.HashMap.Strict as HM+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import qualified Data.List as L++type Node = Int+type NodeMap v = IM.IntMap v+type NodeSet = IS.IntSet++emptyNode :: Node+emptyNode = -1++data NodeManager k+    = NodeManager+    { nm_nodeToKey :: !(NodeMap k)+    , nm_keyToNode :: !(HM.HashMap k Node)+    , nm_nextNode :: !Node+    } deriving (Show, Eq)++swap :: forall a b. (a, b) -> (b, a)+swap (x,y) = (y,x)++isConsistent :: (Ord k) => NodeManager k -> Bool+isConsistent (NodeManager{..}) =+    IM.size nm_nodeToKey == HM.size nm_keyToNode+    && (IM.null nm_nodeToKey || (nm_nextNode > fst (IM.findMax nm_nodeToKey)+                              && emptyNode < fst (IM.findMin nm_nodeToKey)))+    && L.sort (HM.toList nm_keyToNode) == L.sort (map swap (IM.toList nm_nodeToKey))++-- map must contain only non-negative keys!+initNodeManager :: (Hashable k, Eq k) => NodeMap k -> NodeManager k+initNodeManager nm =+    case IM.minViewWithKey nm of+       Just ((n, _), _) | n <= emptyNode -> error $ "Invalid node ID: " ++ show n+       _ -> NodeManager nm (invert nm) nextNode+    where nextNode+            | IM.null nm = 0+            | otherwise = 1 + fst (IM.findMax nm)+          invert im = HM.fromList . map swap $ IM.toList im++getNodeMap :: (Hashable k, Eq k) => NodeManager k -> NodeMap k+getNodeMap = nm_nodeToKey++keys :: NodeManager k -> [k]+keys nm =+    HM.keys (nm_keyToNode nm)++hasKey :: (Eq k, Hashable k) => k -> NodeManager k -> Bool+hasKey k nm =+    isJust $ HM.lookup k (nm_keyToNode nm)++toList :: NodeManager k -> [(k, Node)]+toList nm = HM.toList (nm_keyToNode nm)++nodes :: NodeManager k -> [Node]+nodes nm = IM.keys (nm_nodeToKey nm)++getNewNodesSince :: Node -> NodeManager k -> NodeMap k+getNewNodesSince n (NodeManager{..}) = snd $ IM.split n nm_nodeToKey++emptyNodeManager :: forall k. NodeManager k+emptyNodeManager = NodeManager IM.empty HM.empty 0++getNodeHandle :: (Hashable k, Eq k, MonadState (NodeManager k) m) => k -> m Node+getNodeHandle k =+    do NodeManager{..} <- get+       case HM.lookup k nm_keyToNode of+          Just i -> return i+          Nothing ->+            do let i = nm_nextNode+               put $! NodeManager { nm_nodeToKey = IM.insert i k nm_nodeToKey+                                  , nm_keyToNode = HM.insert k i nm_keyToNode+                                  , nm_nextNode = i + 1+                                  }+               return i++removeNodeHandle :: (Hashable k, Eq k) => Node -> NodeManager k -> NodeManager k+removeNodeHandle i nm@(NodeManager{..}) =+    case IM.lookup i nm_nodeToKey of+      Just k ->+          nm { nm_nodeToKey = IM.delete i nm_nodeToKey+             , nm_keyToNode = HM.delete k nm_keyToNode+             }+      Nothing -> nm++getExistingNodeHandle :: (Hashable k, Eq k) => k -> NodeManager k -> Maybe Node+getExistingNodeHandle k (NodeManager{..}) = HM.lookup k nm_keyToNode++lookupNode :: Node -> NodeManager k -> Maybe k+lookupNode i (NodeManager{..}) = IM.lookup i nm_nodeToKey++unsafeLookupNode :: Node -> NodeManager k -> k+unsafeLookupNode i nm = fromJust $ lookupNode i nm++instance Arbitrary v => Arbitrary (IM.IntMap v) where+    arbitrary = fmap (IM.fromList . map (\(NonNegative i, x) -> (i, x))) arbitrary
+ src/Data/Core/Graph/Persistence.hs view
@@ -0,0 +1,27 @@+module Data.Core.Graph.Persistence+  ( PersistentGraph, persistGraph, loadGraph )+where++import Data.Core.Graph.PureCore+import Data.Core.Graph.NodeManager++import Data.Hashable+import qualified Data.IntMap.Strict as IM+import qualified Data.Vector.Unboxed as VU++data PersistentGraph k+   = PersistentGraph+   { pg_nodeData :: NodeMap k+   , pg_graphData :: [(Node, [Node])]+   } deriving (Show, Eq)++persistGraph :: (Eq k, Hashable k) => NodeManager k -> Graph -> PersistentGraph k+persistGraph nodeManager graph =+    PersistentGraph+    { pg_nodeData = getNodeMap nodeManager+    , pg_graphData = map (\(k, vals) -> (k, VU.toList vals)) (IM.toList $ g_adj graph)+    }++loadGraph :: (Eq k, Hashable k) => PersistentGraph k -> (NodeManager k, Graph)+loadGraph (PersistentGraph nodeData graphData) =+    (initNodeManager nodeData, fromAdj graphData)
+ src/Data/Core/Graph/PureCore.hs view
@@ -0,0 +1,203 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE RecordWildCards #-}+module Data.Core.Graph.PureCore where++import Data.Core.Graph.NodeManager hiding (nodes, isConsistent)++import Control.Applicative hiding (empty)+import Control.Arrow+import Control.DeepSeq+import Control.Monad+import Control.Monad.Identity+import Control.Monad.ST+import Data.Function (on)+import Data.Hashable+import Data.Maybe+import Data.STRef+import Test.QuickCheck+import qualified Data.Foldable as F+import qualified Data.HashSet as HS+import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS+import qualified Data.List as L+import qualified Data.Vector.Unboxed as VU++type AdjList = NodeMap (VU.Vector Node)+data Edge = Edge { src :: !Node, tgt :: !Node } deriving (Show, Eq, Ord)++instance Hashable Edge where+    hashWithSalt s (Edge x y) = s `hashWithSalt` x `hashWithSalt` y++data Graph+    = Graph+    { g_adj :: !AdjList+    , g_radj :: !AdjList+    }++empty :: Graph+empty = Graph IM.empty IM.empty++invert :: Edge -> Edge+invert (Edge x y) = Edge y x++instance Show Graph where+    show g = "< " ++ L.intercalate ",\n  " (map showNode (nodes g)) ++ " >"+        where showNode x = show x+                        ++ " -> ["+                        ++ L.intercalate "," (map show (VU.toList (children g x)))+                        ++ "]"++instance Eq Graph where+    a == b = sameItems (nodes a) (nodes b)+          && all (\x -> sameItems (VU.toList (children a x)) (VU.toList (children b x)))+                  (nodes a)+      where sameItems x y = IS.fromList x == IS.fromList y++instance NFData Graph where+    rnf (Graph a b) = rnf a `seq` rnf b++adjToEdges :: [(Node, [Node])] -> [Edge]+adjToEdges = concatMap (\(x, ys) -> map (Edge x) ys)++edgesAdj :: AdjList -> [Edge]+edgesAdj adj = adjToEdges . map (second VU.toList) $ IM.toList adj++isConsistent :: Graph -> Bool+isConsistent (Graph{..}) = L.sort forwardEdges == L.sort (map invert (edgesAdj g_radj))+                        && HS.size (HS.fromList forwardEdges) == length forwardEdges+    where forwardEdges = edgesAdj g_adj++fromEdges :: [Edge] -> Graph+fromEdges edgeList =+    Graph { g_adj = mkAdj edgeList+          , g_radj = mkAdj $ map invert edgeList+          }+    where+      mkAdj e = IM.fromList $ map (src . head &&& VU.fromList . map tgt)+                        . L.groupBy ((==) `on` src)+                        . L.sortBy (compare `on` src) $ e++fromAdj :: [(Node, [Node])] -> Graph+fromAdj l =+    let g1 = fromEdges (adjToEdges l)+        solitaires = map fst $ filter (\(_, xs) -> null xs) l+    in L.foldl' (\g n -> g { g_adj = IM.insert n VU.empty (g_adj g) }) g1 solitaires++nodes :: Graph -> [Node]+nodes g = IM.keys (IM.union (g_adj g) (g_radj g))++edges :: Graph -> [Edge]+edges = edgesAdj . g_adj++solitaireNodes :: Graph -> [Node]+solitaireNodes g = IM.keys (IM.filter VU.null (IM.union (g_adj g) (g_radj g)))++edgeCount :: Graph -> Int+edgeCount = F.foldl' (\old (_,adj) -> old + VU.length adj) 0+          . IM.toList . g_adj++children :: Graph -> Node -> VU.Vector Node+children g x = neighbors g (g_adj g) x++parents :: Graph -> Node -> VU.Vector Node+parents g x = neighbors g (g_radj g) x++neighbors :: Graph -> AdjList -> Node -> VU.Vector Node+neighbors (Graph{..}) adj x = IM.findWithDefault VU.empty x adj++hasEdge :: Node -> Node -> Graph -> Bool+hasEdge x y (Graph{..}) = y `VU.elem` IM.findWithDefault VU.empty x g_adj++addNode :: Node -> Graph -> Graph+addNode x g =+    g { g_adj = IM.insertWith (\_new old -> old) x VU.empty (g_adj g) }++removeNode :: Node -> Graph -> Graph+removeNode x g =+    let rmInAdj adj localF =+            foldl (\adjList child ->+                       IM.adjust (VU.filter (/=x)) child adjList+                  ) (IM.delete x adj) $ VU.toList (localF g x)++        newAdj = rmInAdj (g_adj g) parents+        newRAdj = rmInAdj (g_radj g) children+    in g { g_adj = newAdj+         , g_radj = newRAdj+         }++addEdge :: Node -> Node -> Graph -> Graph+addEdge x y g@(Graph{..}) =+   if hasEdge x y g+       then g+       else Graph { g_adj = alterDef VU.empty (flip VU.snoc y) x g_adj+                  , g_radj = alterDef VU.empty (flip VU.snoc x) y g_radj+                  }+   where alterDef def f = IM.alter (Just . f . fromMaybe def)++addEdges :: [Edge] -> Graph -> Graph+addEdges edgeList g = L.foldl' (flip (\(Edge x y) -> addEdge x y)) g edgeList++removeEdge :: Node -> Node -> Graph -> Graph+removeEdge x y (Graph{..}) =+    Graph { g_adj = IM.adjust (VU.filter (/=y)) x g_adj+          , g_radj = IM.adjust (VU.filter (/=x)) y g_radj+          }++removeEdges :: [Edge] -> Graph -> Graph+removeEdges edgeList g = L.foldl' (flip (\(Edge x y) -> removeEdge x y)) g edgeList++hull :: Graph -> Node -> NodeSet+hull g = hullImpl g (g_adj g)++rhull :: Graph -> Node -> NodeSet+rhull g = hullImpl g (g_radj g)++hullImpl :: Graph -> AdjList -> Node -> NodeSet+hullImpl (Graph{..}) adj root =+    runST $+       do vis <- newSTRef IS.empty+          let go x =+               (IS.member x <$> readSTRef vis) >>=+                  (flip unless $+                     do modifySTRef' vis (IS.insert x)+                        VU.forM_ (IM.findWithDefault VU.empty x adj) go)+          go root+          readSTRef vis++rhullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b+rhullFold g f initial node =+    runIdentity $ hullFoldImpl (g_radj g) (\x y -> return (f x y)) initial node++-- FIXME: benchmark against old hullFold implementation+hullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b+hullFold g f initial node =+    runIdentity $ hullFoldImpl (g_adj g) (\x y -> return (f x y)) initial node++hullFoldM :: Monad m => Graph -> (b -> Node -> m b) -> b -> Node -> m b+hullFoldM g = hullFoldImpl (g_adj g)++hullFoldImpl :: Monad m => AdjList -> (b -> Node -> m b) -> b -> Node -> m b+hullFoldImpl adj f initial root =+    go IS.empty initial [root]+    where+      go _ acc [] = return acc+      go !visited !acc (x:xs) =+          if (IS.member x visited)+          then go visited acc xs+          else do newAcc <- f acc x+                  let succs = IM.findWithDefault VU.empty x adj+                  go (IS.insert x visited) newAcc (xs ++ VU.toList succs)++instance Arbitrary Graph where+    arbitrary = frequency [(1, return empty), (20, denseGraph)]+        where denseGraph =+                do n <- choose (0, 30::Int)+                   let nodeList = [1..n]+                   adj <- forM nodeList $ \i ->+                            do bits <- vectorOf n arbitrary+                               return (i, [ x | (x,b) <- zip nodeList bits, b ])+                   return $ fromAdj adj
− src/Data/Graph.hs
@@ -1,20 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE RecordWildCards #-}-module Data.Graph-  ( Graph, Node, NodeSet, Edge(..)-  , empty, fromEdges, fromAdj, isConsistent-  , nodes, edges, children, parents, hasEdge-  , edgeCount-  , hull, rhull, hullFold, hullFoldM, rhullFold-  , addEdge, addEdges, removeEdge, removeEdges-  , addNode, removeNode, solitaireNodes-  , edgesAdj-  )-where--import Data.Graph.NodeManager hiding (isConsistent, nodes)-import Data.Graph.PureCore
− src/Data/Graph/NodeManager.hs
@@ -1,115 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE CPP #-}-module Data.Graph.NodeManager-    ( NodeManager, Node, NodeMap, NodeSet-    , emptyNode-    , initNodeManager, emptyNodeManager, getNodeMap-    , getNodeHandle, getExistingNodeHandle, lookupNode, unsafeLookupNode-    , removeNodeHandle-    , getNewNodesSince, keys, hasKey, nodes, toList-    , isConsistent-    )-where--import Control.Monad.State.Strict-import Data.Hashable-import Data.Maybe-import Test.QuickCheck (NonNegative(..), Arbitrary(..))-import qualified Data.HashMap.Strict as HM-import qualified Data.IntMap.Strict as IM-import qualified Data.IntSet as IS-import qualified Data.List as L--type Node = Int-type NodeMap v = IM.IntMap v-type NodeSet = IS.IntSet--emptyNode :: Node-emptyNode = -1--data NodeManager k-    = NodeManager-    { nm_nodeToKey :: !(NodeMap k)-    , nm_keyToNode :: !(HM.HashMap k Node)-    , nm_nextNode :: !Node-    } deriving (Show, Eq)--swap :: forall a b. (a, b) -> (b, a)-swap (x,y) = (y,x)--isConsistent :: (Ord k) => NodeManager k -> Bool-isConsistent (NodeManager{..}) =-    IM.size nm_nodeToKey == HM.size nm_keyToNode-    && (IM.null nm_nodeToKey || (nm_nextNode > fst (IM.findMax nm_nodeToKey)-                              && emptyNode < fst (IM.findMin nm_nodeToKey)))-    && L.sort (HM.toList nm_keyToNode) == L.sort (map swap (IM.toList nm_nodeToKey))---- map must contain only non-negative keys!-initNodeManager :: (Hashable k, Eq k) => NodeMap k -> NodeManager k-initNodeManager nm =-    case IM.minViewWithKey nm of-       Just ((n, _), _) | n <= emptyNode -> error $ "Invalid node ID: " ++ show n-       _ -> NodeManager nm (invert nm) nextNode-    where nextNode-            | IM.null nm = 0-            | otherwise = 1 + fst (IM.findMax nm)-          invert im = HM.fromList . map swap $ IM.toList im--getNodeMap :: (Hashable k, Eq k) => NodeManager k -> NodeMap k-getNodeMap = nm_nodeToKey--keys :: NodeManager k -> [k]-keys nm =-    HM.keys (nm_keyToNode nm)--hasKey :: (Eq k, Hashable k) => k -> NodeManager k -> Bool-hasKey k nm =-    isJust $ HM.lookup k (nm_keyToNode nm)--toList :: NodeManager k -> [(k, Node)]-toList nm = HM.toList (nm_keyToNode nm)--nodes :: NodeManager k -> [Node]-nodes nm = IM.keys (nm_nodeToKey nm)--getNewNodesSince :: Node -> NodeManager k -> NodeMap k-getNewNodesSince n (NodeManager{..}) = snd $ IM.split n nm_nodeToKey--emptyNodeManager :: forall k. NodeManager k-emptyNodeManager = NodeManager IM.empty HM.empty 0--getNodeHandle :: (Hashable k, Eq k, MonadState (NodeManager k) m) => k -> m Node-getNodeHandle k =-    do NodeManager{..} <- get-       case HM.lookup k nm_keyToNode of-          Just i -> return i-          Nothing ->-            do let i = nm_nextNode-               put $! NodeManager { nm_nodeToKey = IM.insert i k nm_nodeToKey-                                  , nm_keyToNode = HM.insert k i nm_keyToNode-                                  , nm_nextNode = i + 1-                                  }-               return i--removeNodeHandle :: (Hashable k, Eq k) => Node -> NodeManager k -> NodeManager k-removeNodeHandle i nm@(NodeManager{..}) =-    case IM.lookup i nm_nodeToKey of-      Just k ->-          nm { nm_nodeToKey = IM.delete i nm_nodeToKey-             , nm_keyToNode = HM.delete k nm_keyToNode-             }-      Nothing -> nm--getExistingNodeHandle :: (Hashable k, Eq k) => k -> NodeManager k -> Maybe Node-getExistingNodeHandle k (NodeManager{..}) = HM.lookup k nm_keyToNode--lookupNode :: Node -> NodeManager k -> Maybe k-lookupNode i (NodeManager{..}) = IM.lookup i nm_nodeToKey--unsafeLookupNode :: Node -> NodeManager k -> k-unsafeLookupNode i nm = fromJust $ lookupNode i nm--instance Arbitrary v => Arbitrary (IM.IntMap v) where-    arbitrary = fmap (IM.fromList . map (\(NonNegative i, x) -> (i, x))) arbitrary
− src/Data/Graph/Persistence.hs
@@ -1,32 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-module Data.Graph.Persistence-  ( PersistentGraph, persistGraph, loadGraph )-where--import Data.Graph.PureCore-import Data.Graph.NodeManager--import Data.Hashable-import Data.SafeCopy-import qualified Data.IntMap.Strict as IM-import qualified Data.Vector.Unboxed as VU--data PersistentGraph k-   = PersistentGraph-   { pg_nodeData :: NodeMap k-   , pg_graphData :: [(Node, [Node])]-   } deriving (Show, Eq)--persistGraph :: (Eq k, Hashable k) => NodeManager k -> Graph -> PersistentGraph k-persistGraph nodeManager graph =-    PersistentGraph-    { pg_nodeData = getNodeMap nodeManager-    , pg_graphData = map (\(k, vals) -> (k, VU.toList vals)) (IM.toList $ g_adj graph)-    }--loadGraph :: (Eq k, Hashable k) => PersistentGraph k -> (NodeManager k, Graph)-loadGraph (PersistentGraph nodeData graphData) =-    (initNodeManager nodeData, fromAdj graphData)--$(deriveSafeCopy 1 'base ''PersistentGraph)-$(deriveSafeCopy 1 'base ''Edge)
− src/Data/Graph/PureCore.hs
@@ -1,203 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE RecordWildCards #-}-module Data.Graph.PureCore where--import Data.Graph.NodeManager hiding (nodes, isConsistent)--import Control.Applicative hiding (empty)-import Control.Arrow-import Control.DeepSeq-import Control.Monad-import Control.Monad.Identity-import Control.Monad.ST-import Data.Function (on)-import Data.Hashable-import Data.Maybe-import Data.STRef-import Test.QuickCheck-import qualified Data.Foldable as F-import qualified Data.HashSet as HS-import qualified Data.IntMap.Strict as IM-import qualified Data.IntSet as IS-import qualified Data.List as L-import qualified Data.Vector.Unboxed as VU--type AdjList = NodeMap (VU.Vector Node)-data Edge = Edge { src :: !Node, tgt :: !Node } deriving (Show, Eq, Ord)--instance Hashable Edge where-    hashWithSalt s (Edge x y) = s `hashWithSalt` x `hashWithSalt` y--data Graph-    = Graph-    { g_adj :: !AdjList-    , g_radj :: !AdjList-    }--empty :: Graph-empty = Graph IM.empty IM.empty--invert :: Edge -> Edge-invert (Edge x y) = Edge y x--instance Show Graph where-    show g = "< " ++ L.intercalate ",\n  " (map showNode (nodes g)) ++ " >"-        where showNode x = show x-                        ++ " -> ["-                        ++ L.intercalate "," (map show (VU.toList (children g x)))-                        ++ "]"--instance Eq Graph where-    a == b = sameItems (nodes a) (nodes b)-          && all (\x -> sameItems (VU.toList (children a x)) (VU.toList (children b x)))-                  (nodes a)-      where sameItems x y = IS.fromList x == IS.fromList y--instance NFData Graph where-    rnf (Graph a b) = rnf a `seq` rnf b--adjToEdges :: [(Node, [Node])] -> [Edge]-adjToEdges = concatMap (\(x, ys) -> map (Edge x) ys)--edgesAdj :: AdjList -> [Edge]-edgesAdj adj = adjToEdges . map (second VU.toList) $ IM.toList adj--isConsistent :: Graph -> Bool-isConsistent (Graph{..}) = L.sort forwardEdges == L.sort (map invert (edgesAdj g_radj))-                        && HS.size (HS.fromList forwardEdges) == length forwardEdges-    where forwardEdges = edgesAdj g_adj--fromEdges :: [Edge] -> Graph-fromEdges edgeList =-    Graph { g_adj = mkAdj edgeList-          , g_radj = mkAdj $ map invert edgeList-          }-    where-      mkAdj e = IM.fromList $ map (src . head &&& VU.fromList . map tgt)-                        . L.groupBy ((==) `on` src)-                        . L.sortBy (compare `on` src) $ e--fromAdj :: [(Node, [Node])] -> Graph-fromAdj l =-    let g1 = fromEdges (adjToEdges l)-        solitaires = map fst $ filter (\(_, xs) -> null xs) l-    in L.foldl' (\g n -> g { g_adj = IM.insert n VU.empty (g_adj g) }) g1 solitaires--nodes :: Graph -> [Node]-nodes g = IM.keys (IM.union (g_adj g) (g_radj g))--edges :: Graph -> [Edge]-edges = edgesAdj . g_adj--solitaireNodes :: Graph -> [Node]-solitaireNodes g = IM.keys (IM.filter VU.null (IM.union (g_adj g) (g_radj g)))--edgeCount :: Graph -> Int-edgeCount = F.foldl' (\old (_,adj) -> old + VU.length adj) 0-          . IM.toList . g_adj--children :: Graph -> Node -> VU.Vector Node-children g x = neighbors g (g_adj g) x--parents :: Graph -> Node -> VU.Vector Node-parents g x = neighbors g (g_radj g) x--neighbors :: Graph -> AdjList -> Node -> VU.Vector Node-neighbors (Graph{..}) adj x = IM.findWithDefault VU.empty x adj--hasEdge :: Node -> Node -> Graph -> Bool-hasEdge x y (Graph{..}) = y `VU.elem` IM.findWithDefault VU.empty x g_adj--addNode :: Node -> Graph -> Graph-addNode x g =-    g { g_adj = IM.insertWith (\_new old -> old) x VU.empty (g_adj g) }--removeNode :: Node -> Graph -> Graph-removeNode x g =-    let rmInAdj adj localF =-            foldl (\adjList child ->-                       IM.adjust (VU.filter (/=x)) child adjList-                  ) (IM.delete x adj) $ VU.toList (localF g x)--        newAdj = rmInAdj (g_adj g) parents-        newRAdj = rmInAdj (g_radj g) children-    in g { g_adj = newAdj-         , g_radj = newRAdj-         }--addEdge :: Node -> Node -> Graph -> Graph-addEdge x y g@(Graph{..}) =-   if hasEdge x y g-       then g-       else Graph { g_adj = alterDef VU.empty (flip VU.snoc y) x g_adj-                  , g_radj = alterDef VU.empty (flip VU.snoc x) y g_radj-                  }-   where alterDef def f = IM.alter (Just . f . fromMaybe def)--addEdges :: [Edge] -> Graph -> Graph-addEdges edgeList g = L.foldl' (flip (\(Edge x y) -> addEdge x y)) g edgeList--removeEdge :: Node -> Node -> Graph -> Graph-removeEdge x y (Graph{..}) =-    Graph { g_adj = IM.adjust (VU.filter (/=y)) x g_adj-          , g_radj = IM.adjust (VU.filter (/=x)) y g_radj-          }--removeEdges :: [Edge] -> Graph -> Graph-removeEdges edgeList g = L.foldl' (flip (\(Edge x y) -> removeEdge x y)) g edgeList--hull :: Graph -> Node -> NodeSet-hull g = hullImpl g (g_adj g)--rhull :: Graph -> Node -> NodeSet-rhull g = hullImpl g (g_radj g)--hullImpl :: Graph -> AdjList -> Node -> NodeSet-hullImpl (Graph{..}) adj root =-    runST $-       do vis <- newSTRef IS.empty-          let go x =-               (IS.member x <$> readSTRef vis) >>=-                  (flip unless $-                     do modifySTRef' vis (IS.insert x)-                        VU.forM_ (IM.findWithDefault VU.empty x adj) go)-          go root-          readSTRef vis--rhullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b-rhullFold g f initial node =-    runIdentity $ hullFoldImpl (g_radj g) (\x y -> return (f x y)) initial node---- FIXME: benchmark against old hullFold implementation-hullFold :: Graph -> (b -> Node -> b) -> b -> Node -> b-hullFold g f initial node =-    runIdentity $ hullFoldImpl (g_adj g) (\x y -> return (f x y)) initial node--hullFoldM :: Monad m => Graph -> (b -> Node -> m b) -> b -> Node -> m b-hullFoldM g = hullFoldImpl (g_adj g)--hullFoldImpl :: Monad m => AdjList -> (b -> Node -> m b) -> b -> Node -> m b-hullFoldImpl adj f initial root =-    go IS.empty initial [root]-    where-      go _ acc [] = return acc-      go !visited !acc (x:xs) =-          if (IS.member x visited)-          then go visited acc xs-          else do newAcc <- f acc x-                  let succs = IM.findWithDefault VU.empty x adj-                  go (IS.insert x visited) newAcc (xs ++ VU.toList succs)--instance Arbitrary Graph where-    arbitrary = frequency [(1, return empty), (20, denseGraph)]-        where denseGraph =-                do n <- choose (0, 30::Int)-                   let nodeList = [1..n]-                   adj <- forM nodeList $ \i ->-                            do bits <- vectorOf n arbitrary-                               return (i, [ x | (x,b) <- zip nodeList bits, b ])-                   return $ fromAdj adj
src/Test/Core.hs view
@@ -1,8 +1,8 @@ {-# OPTIONS_GHC -F -pgmF htfpp #-} module Test.Core where -import Data.Graph.PureCore-import Data.Graph.NodeManager (Node)+import Data.Core.Graph.PureCore+import Data.Core.Graph.NodeManager (Node)  import Control.Monad import Test.Framework
src/Test/NodeManager.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -F -pgmF htfpp #-} module Test.NodeManager where -import Data.Graph.NodeManager+import Data.Core.Graph.NodeManager  import Test.Framework import Control.Monad.State.Strict
src/Test/Persistence.hs view
@@ -1,9 +1,9 @@ {-# OPTIONS_GHC -F -pgmF htfpp #-} module Test.Persistence where -import Data.Graph-import Data.Graph.NodeManager-import Data.Graph.Persistence+import Data.Core.Graph+import Data.Core.Graph.NodeManager+import Data.Core.Graph.Persistence  import Test.Framework