graph-wrapper 0.1 → 0.1.1
raw patch · 2 files changed
+67/−8 lines, 2 filesdep ~containersPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: containers
API changes (from Hackage documentation)
- Data.Graph.Wrapper: instance (Show i) => Show (SCC i)
+ Data.Graph.Wrapper: depthNumbering :: Ord i => Graph i v -> [i] -> Graph i (v, Maybe Int)
+ Data.Graph.Wrapper: instance Show i => Show (SCC i)
- Data.Graph.Wrapper: fromList :: (Ord i) => [(i, v, [i])] -> Graph i v
+ Data.Graph.Wrapper: fromList :: Ord i => [(i, v, [i])] -> Graph i v
- Data.Graph.Wrapper: fromListBy :: (Ord i) => (v -> i) -> [(v, [i])] -> Graph i v
+ Data.Graph.Wrapper: fromListBy :: Ord i => (v -> i) -> [(v, [i])] -> Graph i v
- Data.Graph.Wrapper: fromListSimple :: (Ord v) => [(v, [v])] -> Graph v v
+ Data.Graph.Wrapper: fromListSimple :: Ord v => [(v, [v])] -> Graph v v
- Data.Graph.Wrapper: fromVerticesEdges :: (Ord i) => [(i, v)] -> [Edge i] -> Graph i v
+ Data.Graph.Wrapper: fromVerticesEdges :: Ord i => [(i, v)] -> [Edge i] -> Graph i v
- Data.Graph.Wrapper: hasPath :: (Ord i) => Graph i v -> Edge i -> Bool
+ Data.Graph.Wrapper: hasPath :: Ord i => Graph i v -> i -> i -> Bool
- Data.Graph.Wrapper: indegree :: (Ord i) => Graph i v -> i -> Int
+ Data.Graph.Wrapper: indegree :: Ord i => Graph i v -> i -> Int
- Data.Graph.Wrapper: outdegree :: (Ord i) => Graph i v -> i -> Int
+ Data.Graph.Wrapper: outdegree :: Ord i => Graph i v -> i -> Int
- Data.Graph.Wrapper: reachableVertices :: (Ord i) => Graph i v -> i -> [i]
+ Data.Graph.Wrapper: reachableVertices :: Ord i => Graph i v -> i -> [i]
- Data.Graph.Wrapper: sccGraph :: (Ord i) => Graph i v -> Graph (Set i) (Map i v)
+ Data.Graph.Wrapper: sccGraph :: Ord i => Graph i v -> Graph (Set i) (Map i v)
- Data.Graph.Wrapper: successors :: (Ord i) => Graph i v -> i -> [i]
+ Data.Graph.Wrapper: successors :: Ord i => Graph i v -> i -> [i]
- Data.Graph.Wrapper: vertex :: (Ord i) => Graph i v -> i -> v
+ Data.Graph.Wrapper: vertex :: Ord i => Graph i v -> i -> v
Files
- Data/Graph/Wrapper.hs +65/−6
- graph-wrapper.cabal +2/−2
Data/Graph/Wrapper.hs view
@@ -24,15 +24,19 @@ reachableVertices, hasPath, - topologicalSort,+ topologicalSort, depthNumbering, SCC(..), stronglyConnectedComponents, sccGraph ) where import Control.Arrow (second)+import Control.Monad+import Control.Monad.ST import Data.Array+import Data.Array.ST import qualified Data.Graph as G+import qualified Data.IntSet as IS import Data.List (sortBy, mapAccumL) import Data.Maybe (fromMaybe, fromJust) import qualified Data.Map as M@@ -53,6 +57,15 @@ thd3 (_, _, c) = c +-- amapWithKey :: Ix i => (i -> v -> v') -> Array i v -> Array i v'+-- -- More efficient, but not portable (uses GHC.Arr exports):+-- --amapWithKey f arr = unsafeArray' (bounds arr) (numElements arr) [(i, f i (unsafeAt arr i)) | i <- [0 .. n - 1]]+-- amapWithKey f arr = array (bounds arr) [(i, f i v) | (i, v) <- assocs arr]++amapWithKeyM :: (Monad m, Ix i) => (i -> v -> m v') -> Array i v -> m (Array i v')+amapWithKeyM f arr = liftM (array (bounds arr)) $ mapM (\(i, v) -> liftM (\v' -> (i, v')) $ f i v) (assocs arr)++ -- | An edge from the first vertex to the second type Edge i = (i, i) @@ -76,9 +89,14 @@ traverse f g = fmap (\gvva -> g { gVertexVertexArray = gvva }) (Traversable.traverse f (gVertexVertexArray g)) +{-# RULES "indexGVertex/gVertexIndex" forall g i. gVertexIndex g (indexGVertex g i) = i #-}+{-# RULES "gVertexIndex/indexGVertex" forall g v. indexGVertex g (gVertexIndex g v) = v #-}++{-# NOINLINE [0] indexGVertex #-} indexGVertex :: Ord i => Graph i v -> i -> G.Vertex indexGVertex g i = indexGVertex' (indexGVertexArray g) i +{-# NOINLINE [0] gVertexIndex #-} gVertexIndex :: Graph i v -> G.Vertex -> i gVertexIndex g gv = indexGVertexArray g ! gv @@ -175,7 +193,7 @@ -- | Topological sort of of the graph (<http://en.wikipedia.org/wiki/Topological_sort>). If the graph is acyclic, -- vertices will only appear in the list once all of those vertices with arrows to them have already appeared. ----- Vertex i precedes j in the output whenever j is reachable from i but not vice versa.+-- Vertex /i/ precedes /j/ in the output whenever /j/ is reachable from /i/ but not vice versa. topologicalSort :: Graph i v -> [i] topologicalSort g = map (gVertexIndex g) $ G.topSort (graph g) @@ -184,10 +202,51 @@ reachableVertices g = map (gVertexIndex g) . G.reachable (graph g) . indexGVertex g -- | Is the second vertex reachable by following edges from the first vertex?-hasPath :: Ord i => Graph i v -> Edge i -> Bool-hasPath g (i1, i2) = G.path (graph g) (indexGVertex g i1) (indexGVertex g i2)+--+-- It is worth sharing a partial application of 'hasPath' to the first vertex if you are testing for several+-- vertices being reachable from it.+hasPath :: Ord i => Graph i v -> i -> i -> Bool+hasPath g i1 = (`elem` reachableVertices g i1) +-- | Number the vertices in the graph by how far away they are from the given roots. The roots themselves have depth 0,+-- and every subsequent link we traverse adds 1 to the depth. If a vertex is not reachable it will have a depth of 'Nothing'.+depthNumbering :: Ord i => Graph i v -> [i] -> Graph i (v, Maybe Int)+depthNumbering g is = runST $ do+ -- This array records the minimum known depth for the node at the moment+ depth_array <- newArray (bounds (graph g)) Nothing :: ST s (STArray s G.Vertex (Maybe Int))+ let -- Lets us adjust the known depth given a new observation+ atDepth gv depth = do+ mb_old_depth <- readArray depth_array gv+ let depth' = maybe depth (`min` depth) mb_old_depth+ depth' `seq` writeArray depth_array gv (Just depth') + -- Do an depth-first search on the graph (checking for cycles to prevent non-termination),+ -- recording the depth at which any node was seen in that array.+ let gos seen depth gvs = mapM_ (go seen depth) gvs++ go seen depth gv + | depth `seq` False = error "depthNumbering: unreachable"+ | gv `IS.member` seen = return ()+ | otherwise = do+ gv `atDepth` depth+ gos (IS.insert gv seen) (depth + 1) (graph g ! gv)+ gos IS.empty 0 (map (indexGVertex g) is)+ + -- let go _ _ [] = return ()+ -- go seen depth gvs = do+ -- let go_one (seen, next_gvs) gv+ -- | gv `IS.member` seen = return (seen, next_gvs)+ -- | otherwise = do gv `atDepth` depth+ -- return (IS.insert gv seen, next_gvs ++ (graph g ! gv))+ -- (seen, next_gvs) <- foldM go_one (seen, []) gvs+ -- go seen (depth + 1) next_gvs+ -- + -- go IS.empty 0 (map (indexGVertex g) is)+ + gvva <- amapWithKeyM (\gv v -> liftM (\mb_depth -> (v, mb_depth)) $ readArray depth_array gv) (gVertexVertexArray g)+ return $ g { gVertexVertexArray = gvva }++ data SCC i = AcyclicSCC i | CyclicSCC [i] deriving (Show)@@ -212,8 +271,8 @@ -- 1) Within the SCC itself (in the case of a 'CyclicSCC' only) -- 2) Or from a node in a SCC later on in the list ----- Vertex i strictly precedes j in the output whenever i is reachable from j but not vice versa.--- Vertex i occurs in the same SCC as j whenever both i is reachable from j and j is reachable from i.+-- Vertex /i/ strictly precedes /j/ in the output whenever /i/ is reachable from /j/ but not vice versa.+-- Vertex /i/ occurs in the same SCC as /j/ whenever both /i/ is reachable from /j/ and /j/ is reachable from /i/. stronglyConnectedComponents :: Graph i v -> [SCC i] stronglyConnectedComponents g = map decode forest where
graph-wrapper.cabal view
@@ -1,7 +1,7 @@ Cabal-Version: >= 1.2 Build-Type: Simple Name: graph-wrapper-Version: 0.1+Version: 0.1.1 Maintainer: Max Bolingbroke <batterseapower@hotmail.com> Homepage: http://www.github.com/batterseapower/graph-wrapper License: BSD3@@ -15,4 +15,4 @@ Build-Depends: base >= 3.0 && < 5.0, array >= 0.3 && < 0.4,- containers >= 0.3 && < 0.4+ containers >= 0.3 && < 0.5