0 | ||| Core and utility functionality for graphs
  1 | module Data.Graph.Util
  2 |
  3 | import Data.AssocList
  4 | import Data.BitMap
  5 | import Data.Graph.Types
  6 | import Data.List
  7 | import Data.Maybe
  8 | import Data.Prim.Bits32
  9 | import Data.String
 10 |
 11 | %default total
 12 |
 13 | --------------------------------------------------------------------------------
 14 | --          Internal utilities
 15 | --------------------------------------------------------------------------------
 16 |
 17 | delNeighbour : Node -> Adj e n -> Adj e n
 18 | delNeighbour n (MkAdj lbl ns) = MkAdj lbl (delete n ns)
 19 |
 20 | delEdgeTo : Node -> GraphRep e n -> (Node,e) -> GraphRep e n
 21 | delEdgeTo n m (n2,_) = update n2 (delNeighbour n) m
 22 |
 23 | delNeighbours : Node -> GraphRep e n -> AssocList e -> GraphRep e n
 24 | delNeighbours n g = foldlKV (delEdgeTo n) g
 25 |
 26 | addEdge : Node -> e -> Adj e n -> Adj e n
 27 | addEdge k lbl (MkAdj l ns) = MkAdj l $ insert k lbl ns
 28 |
 29 | addEdgeTo : Node -> GraphRep e n -> (Node,e) -> GraphRep e n
 30 | addEdgeTo k m (n2,lbl) = update n2 (addEdge k lbl) m
 31 |
 32 | addEdgesTo : Node -> GraphRep e n -> AssocList e -> GraphRep e n
 33 | addEdgesTo n g = foldlKV (addEdgeTo n) g
 34 |
 35 | toContext : (Node,Adj e n) -> Context e n
 36 | toContext (k,MkAdj l es) = MkContext k l es
 37 |
 38 | toLNode : (Node,Adj e n) -> LNode n
 39 | toLNode (k,MkAdj l _) = MkLNode k l
 40 |
 41 | -- we return only edges to nodes greater than the node in the
 42 | -- context to avoid returning every edge twice in `labEdges`.
 43 | ctxtEdges : Context e n -> List (LEdge e)
 44 | ctxtEdges (MkContext k _ ns) = go Nil (pairs ns)
 45 |
 46 |   where
 47 |     go : List (LEdge e) -> List (Node,e) -> List (LEdge e)
 48 |     go es Nil            = reverse es
 49 |     go es ((j,lbl) :: t) = case comp j k of
 50 |       GT _ _ p => go (MkLEdge (MkEdge k j p) lbl :: es) t
 51 |       _        => go es t
 52 |
 53 | --------------------------------------------------------------------------------
 54 | --          Decomposing Graphs
 55 | --------------------------------------------------------------------------------
 56 |
 57 | ||| Decompose a `Graph` into the `Context` found
 58 | ||| for the given node and the remaining `Graph`.
 59 | |||
 60 | ||| All edges leading to `node` will be removed from the
 61 | ||| resulting `Graph`.
 62 | export
 63 | match : (node : Node) -> Graph e n -> Decomp e n
 64 | match node (MkGraph g) = case lookup node g of
 65 |   Nothing              => Empty
 66 |   Just (MkAdj lbl ns)  =>
 67 |     let g1   := MkGraph $ delNeighbours node (delete node g) ns
 68 |         ctxt := MkContext node lbl ns
 69 |      in Split ctxt g1
 70 |
 71 | ||| Decompose a graph into the `Context` for the largest `Node`
 72 | ||| and the remaining `Graph`.
 73 | export
 74 | matchAny : Graph e n -> Decomp e n
 75 | matchAny (MkGraph g) = case decomp g of
 76 |   NoMatch                => Empty
 77 |   Match k (MkAdj lbl ns) m =>
 78 |     Split (MkContext k lbl ns) (MkGraph $ delNeighbours k m ns)
 79 |
 80 | --------------------------------------------------------------------------------
 81 | --          Inspecting Graphs
 82 | --------------------------------------------------------------------------------
 83 |
 84 | ||| True, if the given graph is empty
 85 | export
 86 | isEmpty : Graph e n -> Bool
 87 | isEmpty = isEmpty . graph
 88 |
 89 | ||| A list of contexts of a graph
 90 | export
 91 | contexts : Graph e n -> List (Context e n)
 92 | contexts = map toContext . pairs . graph
 93 |
 94 | ||| A list of all labeled nodes of a `Graph`
 95 | export
 96 | labNodes  : Graph e n -> List (LNode n)
 97 | labNodes = map toLNode . pairs . graph
 98 |
 99 | ||| Find the label for a `Node`.
100 | export
101 | lab : Graph e n -> Node -> Maybe n
102 | lab (MkGraph m) v = label <$> lookup v m
103 |
104 | ||| Find the label for an `Edge`.
105 | export
106 | elab : Graph e n -> Node -> Node -> Maybe e
107 | elab (MkGraph g) k j = lookup k g >>= \(MkAdj _ ns) => lookup j ns
108 |
109 | ||| List all 'Node's in the 'Graph'.
110 | export
111 | nodes : Graph e n -> List Node
112 | nodes = map fst . pairs . graph
113 |
114 | ||| `True` if the `Node` is present in the `Graph`.
115 | export
116 | gelem : Node -> Graph e n -> Bool
117 | gelem v = isJust . lookup v . graph
118 |
119 | ||| The number of `Node`s in a `Graph`.
120 | export
121 | order : Graph e n -> Nat
122 | order = length . labNodes
123 |
124 | ||| A list of all `LEdge`s in the `Graph` (in lexicographic order).
125 | export
126 | labEdges  : Graph e n -> List (LEdge e)
127 | labEdges = foldMap ctxtEdges . contexts
128 |
129 | ||| List all 'Edge's in the 'Graph'.
130 | export
131 | edges : Graph e n -> List Edge
132 | edges = map edge . labEdges
133 |
134 | ||| The number of edges in the graph.
135 | export
136 | size : Graph e n -> Nat
137 | size = length . labEdges
138 |
139 | ||| Find the labelled links to a `Node`.
140 | export
141 | lneighbours : Graph e n -> Node -> List (Node,e)
142 | lneighbours (MkGraph g) k = case lookup k g of
143 |   Just (MkAdj _ ns) => pairs ns
144 |   Nothing           => Nil
145 |
146 | ||| Return the labels of all neighbours of a node paired
147 | ||| with the label of the edges leading to those neighbours.
148 | export
149 | neighbourLabels : Graph e n -> Node -> List (n,e)
150 | neighbourLabels g = mapMaybe (\(k,l) => (,l) <$> lab g k) . lneighbours g
151 |
152 | ||| Find the neighbours for a 'Node'.
153 | export
154 | neighbours : Graph e n -> Node -> List Node
155 | neighbours g = map fst . lneighbours g
156 |
157 | ||| The degree of the `Node`.
158 | export
159 | deg : Graph e n -> Node -> Nat
160 | deg g = length . lneighbours g
161 |
162 | ||| Checks if there is an undirected edge between two nodes.
163 | export
164 | hasNeighbour : Graph e n -> Node -> Node -> Bool
165 | hasNeighbour g k = isJust . elab g k
166 |
167 | ||| Checks if there is an edge between two nodes.
168 | export
169 | hasEdge : Graph e n -> Edge -> Bool
170 | hasEdge g (MkEdge k j _) = hasNeighbour g k j
171 |
172 | ||| Checks if there is a labelled edge between two nodes.
173 | export
174 | hasLEdge : Eq e => Graph e n -> LEdge e -> Bool
175 | hasLEdge g (MkLEdge (MkEdge k j _) le) = maybe False (le ==) $ elab g k j
176 |
177 | --------------------------------------------------------------------------------
178 | --          Modifying Graphs
179 | --------------------------------------------------------------------------------
180 |
181 | ||| Merge the `Context` into the `DynGraph`.
182 | |||
183 | ||| Context adjacencies should only refer to either a Node already
184 | ||| in a graph.
185 | |||
186 | ||| Behaviour is undefined if the specified 'Node' already exists
187 | ||| in the graph.
188 | export
189 | add : Context e n -> Graph e n -> Graph e n
190 | add (MkContext k lbl ns) (MkGraph m) =
191 |   let m1 := insert k (MkAdj lbl ns) m
192 |    in MkGraph $ addEdgesTo k m1 ns
193 |
194 | ||| Fold a function over the graph by recursively calling 'match'.
195 | export
196 | ufold : (Context e n -> c -> c) -> c -> Graph e n -> c
197 | ufold f acc g = case matchAny g of
198 |   Split ctxt gr => f ctxt (ufold f acc $ assert_smaller g gr)
199 |   Empty         => acc
200 |
201 | ||| Map a function over the graph by recursively calling `match`.
202 | export
203 | gmap : (Context e1 n1 -> Context e2 n2) -> Graph e1 n1 -> Graph e2 n2
204 | gmap f = ufold (\c => add (f c)) (MkGraph empty)
205 |
206 | ||| Insert a labeled node into the `Graph`.
207 | ||| The behavior is undefined if the node is already
208 | ||| in the graph.
209 | export
210 | insNode : Node -> (lbl : n) -> Graph e n -> Graph e n
211 | insNode v l (MkGraph m) = MkGraph $ insert v (MkAdj l $ MkAL []) m
212 |
213 | ||| Insert a `LEdge` into the 'Graph'.
214 | ||| Behavior is undefined if the edge does not
215 | ||| connect two nodes already in the graph.
216 | export
217 | insEdge : LEdge e -> Graph e n -> Graph e n
218 | insEdge (MkLEdge (MkEdge n1 n2 _) lbl) (MkGraph g) =
219 |   let g1 := update n1 (addEdge n2 lbl) g
220 |    in MkGraph $ update n2 (addEdge n1 lbl) g1
221 |
222 | ||| Insert multiple `LNode`s into the `Graph`.
223 | export
224 | insNodes : List (LNode n) -> Graph e n -> Graph e n
225 | insNodes vs g = foldl (\g2,(MkLNode k l) => insNode k l g2) g vs
226 |
227 | ||| Insert multiple `LEdge`s into the `Graph`.
228 | export
229 | insEdges : List (LEdge e) -> Graph e n -> Graph e n
230 | insEdges es g = foldl (flip insEdge) g es
231 |
232 | ||| Remove a 'Node' from the 'Graph'.
233 | export
234 | delNode : Node -> Graph e n -> Graph e n
235 | delNode v g = case match v g of
236 |   Split _ gr => gr
237 |   Empty      => g
238 |
239 | ||| Remove multiple 'Node's from the 'Graph'.
240 | export
241 | delNodes : List Node -> Graph e n -> Graph e n
242 | delNodes vs g = foldl (flip delNode) g vs
243 |
244 | ||| Remove an 'Edge' from the 'Graph'.
245 | export
246 | delEdge : Edge -> Graph e n -> Graph e n
247 | delEdge (MkEdge i j _) g = case match i g of
248 |   Split (MkContext n l ns) gr => add (MkContext n l (delete j ns)) gr
249 |   Empty                       => g
250 |
251 | ||| Remove multiple 'Edge's from the 'Graph'.
252 | export
253 | delEdges : List Edge -> Graph e n -> Graph e n
254 | delEdges es g = foldl (flip delEdge) g es
255 |
256 | ||| Returns the subgraph only containing the labelled nodes which
257 | ||| satisfy the given predicate.
258 | export
259 | labnfilter : (LNode n -> Bool) -> Graph e n -> Graph e n
260 | labnfilter p gr = delNodes (map node . filter (not . p) $ labNodes gr) gr
261 |
262 | ||| Returns the subgraph only containing the nodes which satisfy the
263 | ||| given predicate.
264 | export
265 | nfilter : (Node -> Bool) -> Graph e n -> Graph e n
266 | nfilter f = labnfilter (f . node)
267 |
268 | ||| Returns the subgraph only containing the nodes whose labels
269 | ||| satisfy the given predicate.
270 | export
271 | labfilter : (n -> Bool) -> Graph e n -> Graph e n
272 | labfilter f = labnfilter (f . label)
273 |
274 | ||| Retruns the same graph additionaly containing list of connecting
275 | ||| edges and labels to each node.
276 | export
277 | pairWithNeighbours : Graph e n -> Graph e (n, List (n,e))
278 | pairWithNeighbours g =
279 |   MkGraph $ mapWithKey (\k => map (,neighbourLabels g k)) (graph g)
280 |
281 | ||| Returns the same graph additionaly containing list of connecting
282 | ||| labels to each node.
283 | export
284 | pairWithNeighbours' : Graph e n -> Graph e (n, List n)
285 | pairWithNeighbours' g =
286 |   MkGraph $ mapWithKey (\k => map (,map fst $ neighbourLabels g k)) (graph g)
287 |
288 | --------------------------------------------------------------------------------
289 | --          Creating Graphs
290 | --------------------------------------------------------------------------------
291 |
292 | ||| An empty `Graph`
293 | export
294 | empty : Graph e n
295 | empty = MkGraph empty
296 |
297 | ||| Create a `Graph` from the list of labeled nodes and
298 | ||| edges.
299 | |||
300 | ||| TODO: Can we easily enforce that the edges only point
301 | |||       To the nodes in the list?
302 | export
303 | mkGraph : List (LNode n) -> List (LEdge e) -> Graph e n
304 | mkGraph ns es = insEdges es (insNodes ns empty)
305 |
306 | -- ||| Returns the subgraph induced by the supplied nodes.
307 | -- export
308 | -- subgraph : List Node -> Graph e n -> Graph e n
309 | -- subgraph vs = let vs' = IntSet.fromList vs
310 | --                in nfilter (`IntSet.member` vs')
311 |
312 | --------------------------------------------------------------------------------
313 | --          Displaying Graphs
314 | --------------------------------------------------------------------------------
315 |
316 | export
317 | Show e => Show n => Show (Graph e n) where
318 |   showPrec p g =
319 |     showCon p "mkGraph" $
320 |     showArg (labNodes g) ++ showArg (labEdges g)
321 |
322 | pl : Nat -> Node -> String
323 | pl n = padLeft n ' ' . show
324 |
325 |
326 | export
327 | pretty : (e -> String) -> (n -> String) -> Graph e n -> String
328 | pretty de dn g = case matchAny g of
329 |   Empty                     => "empty graph"
330 |   Split (MkContext n _ _) _ =>
331 |     let ns     := sortBy (comparing node) $ labNodes g
332 |         es     := sortBy (comparing edge) $ labEdges g
333 |         maxLen := length $ show n
334 |      in unlines $
335 |           "Nodes:"   :: map (dispNode maxLen) ns ++
336 |           "\nEdges:" :: map (dispEdge maxLen) es
337 |
338 |   where
339 |     dispNode : Nat -> LNode n -> String
340 |     dispNode k (MkLNode n1 l) =
341 |       #"  \#{pl k n1} :> \#{dn l}"#
342 |
343 |     dispEdge : Nat -> LEdge e -> String
344 |     dispEdge k (MkLEdge (MkEdge n1 n2 _) l) =
345 |       #"  \#{pl k n1} <> \#{pl k n2} :> \#{de l}"#
346 |