|
1 | 1 | -- | A data structure and functions for graphs
|
2 | 2 |
|
3 | 3 | module Data.Graph
|
4 |
| - ( Edge(..) |
5 |
| - , Graph(..) |
6 |
| - , SCC(..) |
| 4 | + ( Graph |
| 5 | + , unfoldGraph |
| 6 | + , fromMap |
7 | 7 | , vertices
|
8 |
| - , scc |
9 |
| - , scc' |
10 |
| - , topSort |
11 |
| - , topSort' |
| 8 | + , lookup |
| 9 | + , outEdges |
| 10 | + , topologicalSort |
12 | 11 | ) where
|
13 | 12 |
|
14 |
| -import Prelude hiding |
15 |
| - |
16 |
| -import Control.Monad.Eff (runPure) |
17 |
| -import Control.Monad.ST (writeSTRef, modifySTRef, readSTRef, newSTRef, runST) |
18 |
| - |
19 |
| -import Data.Foldable (any, for_, elem) |
20 |
| -import Data.List (List(..), concatMap, reverse, singleton) |
| 13 | +import Prelude |
| 14 | +import Data.Bifunctor (lmap) |
| 15 | +import Data.CatList (CatList) |
| 16 | +import Data.CatList as CL |
| 17 | +import Data.Foldable (class Foldable) |
| 18 | +import Data.List (List(..)) |
| 19 | +import Data.List as L |
| 20 | +import Data.Map (Map) |
21 | 21 | import Data.Map as M
|
22 |
| -import Data.Maybe (Maybe(..), isNothing) |
23 |
| -import Data.Traversable (for) |
24 |
| - |
25 |
| --- | An directed edge between vertices labelled with keys of type `k`. |
26 |
| -data Edge k = Edge k k |
| 22 | +import Data.Maybe (Maybe(..), maybe) |
| 23 | +import Data.Monoid (mempty) |
| 24 | +import Data.Tuple (Tuple(..), fst, snd) |
27 | 25 |
|
28 | 26 | -- | A graph with vertices of type `v`.
|
29 | 27 | -- |
|
30 | 28 | -- | Edges refer to vertices using keys of type `k`.
|
31 |
| -data Graph k v = Graph (List v) (List (Edge k)) |
32 |
| - |
33 |
| --- | A strongly-connected component of a graph. |
| 29 | +newtype Graph k v = Graph (Map k (Tuple v (List k))) |
| 30 | + |
| 31 | +instance functorGraph :: Functor (Graph k) where |
| 32 | + map f (Graph m) = Graph (map (lmap f) m) |
| 33 | + |
| 34 | +-- | Unfold a `Graph` from a collection of keys and functions which label keys |
| 35 | +-- | and specify out-edges. |
| 36 | +unfoldGraph |
| 37 | + :: forall f k v out |
| 38 | + . (Ord k, Functor f, Foldable f, Foldable out) |
| 39 | + => f k |
| 40 | + -> (k -> v) |
| 41 | + -> (k -> out k) |
| 42 | + -> Graph k v |
| 43 | +unfoldGraph ks label edges = |
| 44 | + Graph (M.fromFoldable (map (\k -> |
| 45 | + Tuple k (Tuple (label k) (L.fromFoldable (edges k)))) ks)) |
| 46 | + |
| 47 | +-- | Create a `Graph` from a `Map` which maps vertices to their labels and |
| 48 | +-- | outgoing edges. |
| 49 | +fromMap :: forall k v. Map k (Tuple v (List k)) -> Graph k v |
| 50 | +fromMap = Graph |
| 51 | + |
| 52 | +-- | List all vertices in a graph. |
| 53 | +vertices :: forall k v. Graph k v -> List v |
| 54 | +vertices (Graph g) = map fst (M.values g) |
| 55 | + |
| 56 | +-- | Lookup a vertex by its key. |
| 57 | +lookup :: forall k v. Ord k => k -> Graph k v -> Maybe v |
| 58 | +lookup k (Graph g) = map fst (M.lookup k g) |
| 59 | + |
| 60 | +-- | Get the keys which are directly accessible from the given key. |
| 61 | +outEdges :: forall k v. Ord k => k -> Graph k v -> Maybe (List k) |
| 62 | +outEdges k (Graph g) = map snd (M.lookup k g) |
| 63 | + |
| 64 | +type SortState k v = |
| 65 | + { unvisited :: Map k (Tuple v (List k)) |
| 66 | + , result :: List k |
| 67 | + } |
| 68 | + |
| 69 | +-- To defunctionalize the `topologicalSort` function and make it tail-recursive, |
| 70 | +-- we introduce this data type which captures what we intend to do at each stage |
| 71 | +-- of the recursion. |
| 72 | +data SortStep a = Emit a | Visit a |
| 73 | + |
| 74 | +-- | Topologically sort the vertices of a graph. |
34 | 75 | -- |
|
35 |
| --- | - `AcyclicSCC` identifies strongly-connected components consisting of a single vertex. |
36 |
| --- | - `CyclicSCC` identifies strongly-connected components with one or more vertices with |
37 |
| --- | cycles. |
38 |
| -data SCC v = AcyclicSCC v | CyclicSCC (List v) |
39 |
| - |
40 |
| -instance showSCC :: (Show v) => Show (SCC v) where |
41 |
| - show (AcyclicSCC v) = "(AcyclicSCC " <> show v <> ")" |
42 |
| - show (CyclicSCC vs) = "(CyclicSCC " <> show vs <> ")" |
43 |
| - |
44 |
| -instance eqSCC :: (Eq v) => Eq (SCC v) where |
45 |
| - eq (AcyclicSCC v1) (AcyclicSCC v2) = v1 == v2 |
46 |
| - eq (CyclicSCC vs1) (CyclicSCC vs2) = vs1 == vs2 |
47 |
| - eq _ _ = false |
48 |
| - |
49 |
| --- | Returns the vertices contained in a strongly-connected component. |
50 |
| -vertices :: forall v. SCC v -> List v |
51 |
| -vertices (AcyclicSCC v) = singleton v |
52 |
| -vertices (CyclicSCC vs) = vs |
53 |
| - |
54 |
| --- | Compute the strongly connected components of a graph. |
55 |
| -scc :: forall v. Ord v => Graph v v -> List (SCC v) |
56 |
| -scc = scc' id id |
57 |
| - |
58 |
| --- | Compute the strongly connected components of a graph. |
59 |
| --- | |
60 |
| --- | This function is a slight generalization of `scc` which allows key and value types |
61 |
| --- | to differ. |
62 |
| -scc' :: forall k v. Ord k => (v -> k) -> (k -> v) -> Graph k v -> List (SCC v) |
63 |
| -scc' makeKey makeVert (Graph vs es) = runPure (runST (do |
64 |
| - index <- newSTRef zero |
65 |
| - path <- newSTRef Nil |
66 |
| - indexMap <- newSTRef M.empty |
67 |
| - lowlinkMap <- newSTRef M.empty |
68 |
| - components <- newSTRef Nil |
69 |
| - |
70 |
| - (let |
71 |
| - indexOf v = indexOfKey (makeKey v) |
72 |
| - |
73 |
| - indexOfKey k = do |
74 |
| - m <- readSTRef indexMap |
75 |
| - pure $ M.lookup k m |
76 |
| - |
77 |
| - lowlinkOf v = lowlinkOfKey (makeKey v) |
78 |
| - |
79 |
| - lowlinkOfKey k = do |
80 |
| - m <- readSTRef lowlinkMap |
81 |
| - pure $ M.lookup k m |
82 |
| - |
83 |
| - go Nil = readSTRef components |
84 |
| - go (Cons v vs) = do |
85 |
| - currentIndex <- indexOf v |
86 |
| - when (isNothing currentIndex) $ strongConnect (makeKey v) |
87 |
| - go vs |
88 |
| - |
89 |
| - strongConnect k = do |
90 |
| - let v = makeVert k |
91 |
| - |
92 |
| - i <- readSTRef index |
93 |
| - |
94 |
| - modifySTRef indexMap $ M.insert k i |
95 |
| - modifySTRef lowlinkMap $ M.insert k i |
96 |
| - |
97 |
| - writeSTRef index $ i + one |
98 |
| - modifySTRef path $ Cons v |
99 |
| - |
100 |
| - for es $ \(Edge k' l) -> when (k == k') $ do |
101 |
| - wIndex <- indexOfKey l |
102 |
| - currentPath <- readSTRef path |
103 |
| - |
104 |
| - case wIndex of |
105 |
| - Nothing -> do |
106 |
| - let w = makeVert l |
107 |
| - strongConnect l |
108 |
| - wLowlink <- lowlinkOfKey l |
109 |
| - for_ wLowlink $ \lowlink -> |
110 |
| - modifySTRef lowlinkMap $ M.alter (maybeMin lowlink) k |
111 |
| - _ -> when (l `elem` map makeKey currentPath) $ do |
112 |
| - wIndex' <- indexOfKey l |
113 |
| - for_ wIndex' $ \index' -> |
114 |
| - modifySTRef lowlinkMap $ M.alter (maybeMin index') k |
115 |
| - |
116 |
| - vIndex <- indexOfKey k |
117 |
| - vLowlink <- lowlinkOfKey k |
118 |
| - |
119 |
| - when (vIndex == vLowlink) $ do |
120 |
| - currentPath <- readSTRef path |
121 |
| - let newPath = popUntil makeKey v currentPath Nil |
122 |
| - modifySTRef components $ flip (<>) (singleton (makeComponent newPath.component)) |
123 |
| - writeSTRef path newPath.path |
124 |
| - pure unit |
125 |
| - |
126 |
| - makeComponent (Cons v Nil) | not (isCycle (makeKey v)) = AcyclicSCC v |
127 |
| - makeComponent vs = CyclicSCC vs |
128 |
| - |
129 |
| - isCycle k = any (\(Edge k1 k2) -> k1 == k && k2 == k) es |
130 |
| - in go vs))) |
131 |
| - |
132 |
| -popUntil :: forall k v. Eq k => (v -> k) -> v -> List v -> List v -> { path :: List v, component :: List v } |
133 |
| -popUntil _ _ Nil popped = { path: Nil, component: popped } |
134 |
| -popUntil makeKey v (Cons w path) popped | makeKey v == makeKey w = { path: path, component: Cons w popped } |
135 |
| -popUntil makeKey v (Cons w ws) popped = popUntil makeKey v ws (Cons w popped) |
136 |
| - |
137 |
| -maybeMin :: Int -> Maybe Int -> Maybe Int |
138 |
| -maybeMin i Nothing = Just i |
139 |
| -maybeMin i (Just j) = Just $ min i j |
| 76 | +-- | If the graph contains cycles, then the behavior is undefined. |
| 77 | +topologicalSort :: forall k v. Ord k => Graph k v -> List k |
| 78 | +topologicalSort (Graph g) = |
| 79 | + go initialState |
140 | 80 | where
|
141 |
| - min x y = if x < y then x else y |
142 |
| - |
143 |
| --- | Topologically sort the vertices of a graph |
144 |
| -topSort :: forall v. Ord v => Graph v v -> List v |
145 |
| -topSort = topSort' id id |
146 |
| - |
147 |
| --- | Topologically sort the vertices of a graph |
148 |
| --- | |
149 |
| --- | This function is a slight generalization of `scc` which allows key and value types |
150 |
| --- | to differ. |
151 |
| -topSort' :: forall k v. Ord k => (v -> k) -> (k -> v) -> Graph k v -> List v |
152 |
| -topSort' makeKey makeVert = reverse <<< concatMap vertices <<< scc' makeKey makeVert |
| 81 | + go :: SortState k v -> List k |
| 82 | + go state@{ unvisited, result } = |
| 83 | + case M.findMin unvisited of |
| 84 | + Just { key } -> go (visit state (CL.fromFoldable [Visit key])) |
| 85 | + Nothing -> result |
| 86 | + |
| 87 | + visit :: SortState k v -> CatList (SortStep k) -> SortState k v |
| 88 | + visit state stack = |
| 89 | + case CL.uncons stack of |
| 90 | + Nothing -> state |
| 91 | + Just (Tuple (Emit k) ks) -> |
| 92 | + let state' = { result: Cons k state.result |
| 93 | + , unvisited: state.unvisited |
| 94 | + } |
| 95 | + in visit state' ks |
| 96 | + Just (Tuple (Visit k) ks) |
| 97 | + | k `M.member` state.unvisited -> |
| 98 | + let start :: SortState k v |
| 99 | + start = |
| 100 | + { result: state.result |
| 101 | + , unvisited: M.delete k state.unvisited |
| 102 | + } |
| 103 | + |
| 104 | + next :: List k |
| 105 | + next = maybe mempty snd (M.lookup k g) |
| 106 | + in visit start (CL.fromFoldable (map Visit next) <> CL.cons (Emit k) ks) |
| 107 | + | otherwise -> visit state ks |
| 108 | + |
| 109 | + initialState :: SortState k v |
| 110 | + initialState = { unvisited: g |
| 111 | + , result: Nil |
| 112 | + } |
0 commit comments