0 | ||| This modules provides several functions for traversing
  1 | ||| graphs in depth-first order and accumulating results in
  2 | ||| various ways along the way.
  3 | module Data.Graph.Indexed.Query.DFS
  4 |
  5 | import Data.Either
  6 | import Data.Tree
  7 | import Data.Graph.Indexed
  8 | import Data.Graph.Indexed.Query.Util
  9 | import Data.Graph.Indexed.Query.Visited
 10 |
 11 | %default total
 12 |
 13 | parameters {k : Nat}
 14 |            (g : IGraph k e n)
 15 |
 16 | --------------------------------------------------------------------------------
 17 | -- Flat DFS traversals
 18 | --------------------------------------------------------------------------------
 19 |
 20 |   -- flat DFS implementation for large graphs
 21 |   dfsL : List (Fin k) -> (s -> Fin k -> Either s a) -> s -> MVis t k (Either s a)
 22 |   dfsL []      f st r t = Left st # t
 23 |   dfsL (x::xs) f st r t =
 24 |     let False # t := mvisited r x t | True # t => dfsL xs f st (assert_smaller r r) t
 25 |         Left st2  := f st x | Right v => Right v # t
 26 |         _ # t     := mvisit r x t
 27 |      in dfsL (neighbours g x ++ xs) f st2 (assert_smaller r r) t
 28 |
 29 |   %inline dfsL' : List (Fin k) -> (s -> Fin k -> s) -> s -> MVis t k s
 30 |   dfsL' xs acc i r t = fromLeftMVis $ dfsL xs (fleft2 acc) i r t
 31 |
 32 |   ||| Traverses the graph in depth-first order from the given
 33 |   ||| start node and accumulates the nodes encountered with the
 34 |   ||| given function.
 35 |   |||
 36 |   ||| This abborts if the function returns a `Right`, otherwise it
 37 |   ||| continues with the traversal. The result is either the
 38 |   ||| accumulated state or the final result (if any).
 39 |   |||
 40 |   ||| Unlike `dfsWith`, this takes a list of nodes that are taboo, that is
 41 |   ||| that will already be set to `visited`. This allows us exclude certain
 42 |   ||| pathways from our search.
 43 |   export
 44 |   limitedDfsWith :
 45 |        (taboo : List (Fin k))
 46 |     -> (s -> Fin k -> Either s a)
 47 |     -> (init : s)
 48 |     -> Fin k
 49 |     -> Either s a
 50 |   limitedDfsWith taboo acc init x =
 51 |     visiting k $ \r,t =>
 52 |       let _ # t := mvisitAll r taboo t
 53 |        in dfsL [x] acc init r t
 54 |
 55 |   ||| Traverses the graph in depth-first order from the given
 56 |   ||| start node and accumulates the nodes encountered with the
 57 |   ||| given function.
 58 |   |||
 59 |   ||| This abborts if the function returns a `Right`, otherwise it
 60 |   ||| continues with the traversal. The result is either the
 61 |   ||| accumulated state or the final result (if any).
 62 |   export %inline
 63 |   dfsWith : (s -> Fin k -> Either s a) -> (init : s) -> Fin k -> Either s a
 64 |   dfsWith = limitedDfsWith []
 65 |
 66 |   ||| Like `dfsWith` but accumulates the whole connected component
 67 |   ||| from the given starting node in depth-first order.
 68 |   export %inline
 69 |   limitedDfsWith' :
 70 |        (taboo : List (Fin k))
 71 |     -> (acc : s -> Fin k -> s)
 72 |     -> (init : s)
 73 |     -> Fin k
 74 |     -> s
 75 |   limitedDfsWith' taboo acc init =
 76 |     fromLeft . limitedDfsWith taboo (fleft2 acc) init
 77 |
 78 |   ||| Like `dfsWith` but accumulates the whole connected component
 79 |   ||| from the given starting node in depth-first order.
 80 |   export %inline
 81 |   dfsWith' : (acc : s -> Fin k -> s) -> (init : s) -> Fin k -> s
 82 |   dfsWith' = limitedDfsWith' []
 83 |
 84 |   ||| Traverses the graph in depth-first order for the given start nodes
 85 |   ||| returning the encountered nodes in a `SnocList`.
 86 |   export %inline
 87 |   limitedDfs : (taboo : List (Fin k)) -> Fin k -> SnocList (Fin k)
 88 |   limitedDfs taboo = limitedDfsWith' taboo (:<) [<]
 89 |
 90 |   ||| Traverses the graph in depth-first order for the given start nodes
 91 |   ||| returning the encountered nodes in a `SnocList`.
 92 |   export %inline
 93 |   dfs : Fin k -> SnocList (Fin k)
 94 |   dfs = limitedDfs []
 95 |
 96 | --------------------------------------------------------------------------------
 97 | -- Component-wise DFS traversals
 98 | --------------------------------------------------------------------------------
 99 |
100 |   -- flat component-wise DFS implementation for large graphs
101 |   cdfsL : (s -> Fin k -> s) -> s -> SnocList s -> List (Fin k) -> MVis t k (List s)
102 |   cdfsL f i ss []      r t = (ss <>> []) # t
103 |   cdfsL f i ss (x::xs) r t =
104 |     let False # t := mvisited r x t | True # t => cdfsL f i ss xs r t
105 |         y # t     := dfsL' [x] f i r t
106 |      in cdfsL f i (ss:<y) xs r t
107 |
108 |   ||| Traverses the graph in depth-first order for the given
109 |   ||| start nodes and accumulates the nodes encountered with the
110 |   ||| given function.
111 |   |||
112 |   ||| Unlike with `dfsWith`, results are accumulated component-wise,
113 |   ||| using initial state `init` for every component we encounter.
114 |   export
115 |   cdfsWith : (acc : s -> Fin k -> s) -> (init : s) -> List (Fin k) -> List s
116 |   cdfsWith acc init xs = visiting k (cdfsL acc init [<] xs)
117 |
118 |   ||| Traverses the whole graph in depth-first order
119 |   ||| accumulates the nodes encountered with the given function.
120 |   export %inline
121 |   cdfsWith' : (acc : s -> Fin k -> s) -> (init : s) -> List s
122 |   cdfsWith' acc init = cdfsWith acc init (allFinsFast k)
123 |
124 |   ||| Traverses the graph in depth-first order for the given start nodes
125 |   ||| returning the encountered nodes in a `SnocList`.
126 |   export %inline
127 |   cdfs : List (Fin k) -> List (SnocList (Fin k))
128 |   cdfs = cdfsWith (:<) [<]
129 |
130 |   ||| Traverses the whole graph in depth-first order
131 |   ||| returning the encountered nodes in a `SnocList`.
132 |   export %inline
133 |   cdfs' : List (SnocList (Fin k))
134 |   cdfs' = cdfsWith' (:<) [<]
135 |
136 | --------------------------------------------------------------------------------
137 | -- Tree-shaped DFS traversals
138 | --------------------------------------------------------------------------------
139 |
140 |   -- tree-based DFS implementation for large graphs
141 |   dffL : (Fin k -> s) -> List (Fin k) -> MVis t k (Forest s)
142 |   dffL f []      r t = [] # t
143 |   dffL f (x::xs) r t =
144 |       let False # t := mvisited r x t
145 |             | True # t => dffL f xs (assert_smaller r r) t
146 |           _  # t := mvisit r x t
147 |           ts # t := dffL f (neighbours g x) (assert_smaller r r) t
148 |           fs # t := dffL f xs (assert_smaller r r) t
149 |        in (T (f x) ts :: fs) # t
150 |
151 |   ||| Traverses the graph in depth-first order for the given
152 |   ||| start nodes and converts the nodes encountered with the
153 |   ||| given function.
154 |   |||
155 |   ||| Unlike `dfsWith`, this returns a forest of spanning trees
156 |   ||| of the connected components encountered.
157 |   export
158 |   dffWith : (Fin k -> t) -> List (Fin k) -> Forest t
159 |   dffWith f xs = visiting k (dffL f xs)
160 |
161 |   ||| Traverses the whole graph in depth-first order
162 |   ||| converts the nodes encountered with the given function.
163 |   |||
164 |   ||| Unlike `dfsWith'`, this returns a forest of spanning trees
165 |   ||| of the connected components encountered.
166 |   export %inline
167 |   dffWith' : (Fin k -> t) -> Forest t
168 |   dffWith' f = dffWith f (allFinsFast k)
169 |
170 |   ||| Traverses the graph in depth-first order for the given start nodes
171 |   ||| returning the encountered nodes in a list.
172 |   |||
173 |   ||| Unlike `dfs`, this returns a forest of spanning trees
174 |   ||| of the connected components encountered.
175 |   export %inline
176 |   dff : List (Fin k) -> Forest (Fin k)
177 |   dff = dffWith id
178 |
179 |   ||| Traverses the whole graph in depth-first order
180 |   ||| returning the encountered nodes in a list.
181 |   |||
182 |   ||| Unlike `dfs'`, this returns a forest of spanning trees
183 |   ||| of the connected components encountered.
184 |   export %inline
185 |   dff' : Forest (Fin k)
186 |   dff' = dffWith' id
187 |
188 | --------------------------------------------------------------------------------
189 | -- ALGORITHMS BASED ON DFS
190 | --------------------------------------------------------------------------------
191 |
192 |   ||| Collection of connected components
193 |   export %inline
194 |   components : List (SnocList $ Fin k)
195 |   components = cdfs'
196 |
197 |   ||| Number of connected components
198 |   export %inline
199 |   noComponents : Nat
200 |   noComponents = length components
201 |
202 |   ||| Is the graph connected?
203 |   export %inline
204 |   isConnected : Bool
205 |   isConnected = noComponents == 1
206 |