1 /**
2  * Implements depth-first search over graphs.
3  */
4 module anansi.algorithms.dfs;
5 
6 import std.exception;
7 
8 import anansi.types,
9        anansi.traits,
10        anansi.container.stack;
11 
12 /**
13  * Compile time test to check if a given type can be considered a DFS visitor.
14  */
15 template isDfsVisitor (V) {
16     enum bool isDfsVisitor = is(typeof(
17     (inout int = 0) {
18 
19     }));
20 }
21 
22 
23 /**
24  * A default implementation of the depth-first-search visitor concept. More 
25  * specialised visitors can delegate the bits that they don't care about
26  * to an instance of NullVisitor without having to re-implement them. 
27  *
28  * Also servers as a handy point for documenting the visitor interface.
29  */
30 struct NullVisitor(GraphT) if (isGraph!GraphT) {
31     /// A vertex in a GraphT
32     alias Vertex = GraphT.VertexDescriptor;
33 
34     /// An edge in a GraphT
35     alias Edge = GraphT.EdgeDescriptor;
36 
37     /// Called when a vertex is set to its initial state, before the search.
38     void initVertex(ref const(GraphT) g, Vertex v) {};
39 
40     /// Called when a vertex is identified as the root of a depth-first spanning 
41     /// tree
42     void startVertex(ref const(GraphT) g, Vertex v) {};
43 
44     /// Called when a vertex is first encountered during the search.
45     void discoverVertex(ref const(GraphT) g, Vertex v) {};
46 
47     /// Called when an edge is being expanded.
48     void examineEdge(ref const(GraphT) g, Edge e) {};
49 
50     /// Called when an edge has been identified as part of the current 
51     /// spanning tree.
52     void treeEdge(ref const(GraphT) g, Edge e) {};
53 
54     /// Called when an edge has been identified as part of a cycle
55     void backEdge(ref const(GraphT) g, Edge e) {};
56 
57     /// Called when an edge crosses to a pre-existing spanning tree
58     void forwardOrCrossEdge(ref const(GraphT) g, Edge e) {};
59 
60     /// Called whan all of the adjacent nodes of a vertex have been examined.
61     void finishVertex(ref const(GraphT) g, Vertex e) {};
62 }
63 
64 /**
65  * Performs a depth-first traversal of the graph, which can be customised
66  * using a visitor object. Note that disconnected graphs will still be 
67  * entirely traversed - this function will walk the spanning tree of each  
68  * disconnected component (in random order). 
69  *
70  * Params:
71  *    GraphT = The type of the graph object to traverse. Must model the 
72  *             incidence graph concept.
73  *    VertexDescriptorT = The descriptor type for vertices in a GraphT.
74  *    VisitorT = The visitor type.
75  *    ColourMapT = The type of the property map that will be used to control 
76  *                 the graph traversal. Must model a property map that stores 
77  *                 Colours keyed by a VertexDescriptorT. 
78  */
79 template depthFirstSearch(GraphT, 
80                           VertexDescriptorT,
81                           VisitorT = NullVisitor!GraphT,
82                           ColourMapT = Colour[VertexDescriptorT]) {
83 
84     static assert (isIncidenceGraph!GraphT);
85     static assert (is(VertexDescriptorT == GraphT.VertexDescriptor));
86     static assert (isPropertyMap!(ColourMapT, GraphT.VertexDescriptor, Colour));
87     static assert (isDfsVisitor!VisitorT);
88 
89     /**
90      * Params:
91      *   graph = The graph object to traverse.
92      *   root = The vertex to serve as the starting point.
93      *   colourMap = The colour map used to control the expansion of edges
94      *               and verices in the graph. This will be totally re-
95      *               initialised before the traversal begins. 
96      *   visitor = A visitor object that will be notified of various events 
97      *             during the traversal. 
98      */
99     void depthFirstSearch(ref const(GraphT) graph,
100                           VertexDescriptorT root,
101                           ref ColourMapT colourMap,
102                           VisitorT visitor = VisitorT.init) {
103         foreach(v; graph.vertices()) {
104             colourMap[v] = Colour.White;
105             visitor.initVertex(graph, v);
106         }
107 
108         visitor.startVertex(graph, root);
109         depthFirstVisit(graph, root, colourMap, visitor);
110 
111         foreach(v; graph.vertices()) {
112             if (colourMap[v] == Colour.White) {
113                 visitor.startVertex(graph, root);
114                 depthFirstVisit(graph, v, colourMap, visitor);
115             }
116         }
117     }
118 }
119 
120 /**
121  * Performs a depth-first traversal of the graph, which can be customised
122  * using a visitor object. The main difference between this function and 
123  * depthFirstSearch is that depthFirstSearch will initialise the 
124  * supplied colour map, but depthFirstVisit will use it as-is. 
125  *
126  * Use this function if you need to efficiently make multiple passes over 
127  * non-overlappng parts of the same graph, or depthFirstSearch if you just
128  * want to walk over the whole thing. 
129  *
130  * Params:
131  *    GraphT = The type of the graph object to traverse. Must model the 
132  *             incidence graph concept.
133  *    VertexDescriptorT = The descriptor type for vertices in a GraphT.
134  *    VisitorT = The visitor type.
135  *    ColourMapT = The type of the property map that will be used to control 
136  *                 the graph traversal. Must model a property map that stores 
137  *                 Colours keyed by a VertexDescriptorT. 
138  */
139 template depthFirstVisit(GraphT,
140                          VertexDescriptorT,
141                          VisitorT = NullVisitor!GraphT,
142                          ColourMapT = Colour[VertexDescriptorT]) {
143 
144     static assert (isIncidenceGraph!GraphT);
145     static assert (is(VertexDescriptorT == GraphT.VertexDescriptor));
146     static assert (isPropertyMap!(ColourMapT, GraphT.VertexDescriptor, Colour));
147     static assert (isDfsVisitor!VisitorT);
148 
149     /**
150      * Params:
151      *   graph = The graph object to traverse.
152      *   root = The vertex to serve as the starting point.
153      *   colourMap = The colour map used to control the expansion of edges
154      *               and verices in the graph.
155      *   visitor = A visitor object that will be notified of various events 
156      *             during the traversal. 
157      */
158     void depthFirstVisit(ref const(GraphT) graph,
159                          VertexDescriptorT root,
160                          ref ColourMapT colourMap,
161                          VisitorT visitor = VisitorT.init) {
162         static struct VertexInfo {
163             alias EdgeRange = typeof(graph.outEdges(VertexDescriptorT.init));
164             VertexDescriptorT vertex;
165             EdgeRange edges;
166         }
167 
168         Stack!VertexInfo stack;
169 
170         // set up the initial point of departure
171         stack.push(VertexInfo(root, graph.outEdges(root)));
172         colourMap[root] = Colour.Grey; 
173         visitor.discoverVertex(graph, root);
174 
175         while (!stack.empty) {
176             auto u = stack.front.vertex;
177             auto edges = stack.front.edges;
178             stack.pop();
179 
180             // not using foreach because we'll need access to the modified range 
181             // later on...
182             while (!edges.empty) {
183                 auto e = edges.front; 
184                 edges.popFront();
185 
186                 visitor.examineEdge(graph, e);
187                 auto v = graph.target(e);
188 
189                 switch (colourMap[v]) {
190                     case Colour.White:
191                         visitor.treeEdge(graph, e);
192                         colourMap[v] = Colour.Grey;
193                         visitor.discoverVertex(graph, v);
194                         stack.push(VertexInfo(u, edges));
195 
196                         edges = graph.outEdges(v);
197                         u = v;
198                         break;
199 
200                     case Colour.Grey:
201                         visitor.backEdge(graph, e);
202                         break;
203 
204                     case Colour.Black:
205                         visitor.forwardOrCrossEdge(graph, e);
206                         break;
207 
208                     default:
209                         enforce(false, "Unexpected vertex colour");
210                 }
211             }
212 
213             colourMap[u] = Colour.Black;
214             visitor.finishVertex(graph, u);
215         }
216     }
217 }
218 
219 // ----------------------------------------------------------------------------
220 //
221 // ----------------------------------------------------------------------------
222 
223 version (unittest) {
224     import std.array, std.algorithm, std.conv, std.stdio;
225     import anansi.adjacencylist, anansi.traits, anansi.container.set;
226 
227     auto indexOf(ValueT)(ValueT[] haystack, ValueT needle) {
228         foreach(n, v; haystack) {
229             if (v == needle) return n; 
230         }
231         return -1;
232     }
233 
234     bool all(RangeT, DelegateT)(RangeT range, DelegateT d = DelegateT.init) {
235         foreach (x; range) {
236             if (!d(x)) return false;
237         }
238         return true;
239     }
240 
241     private struct TestGraph(GraphT) {
242         GraphT graph;
243         GraphT.VertexDescriptor[char] vertices;
244     };
245 
246     private TestGraph!GraphT MakeTestGraph(GraphT)() {
247         GraphT graph;
248         auto a = graph.addVertex('a');
249         auto b = graph.addVertex('b');
250         auto c = graph.addVertex('c');
251         auto d = graph.addVertex('d');
252         auto e = graph.addVertex('e');
253         auto f = graph.addVertex('f');
254         auto g = graph.addVertex('g');
255 
256         // *----------------*
257         // |                |
258         // |     /-> b ->\ /
259         // *--> a         e -> f  g
260         //       \-> c ->/
261         //            \-> d
262 
263         graph.addEdge(a, b, "a -> b"); graph.addEdge(b, e, "b -> e");
264         graph.addEdge(a, c, "a -> c"); graph.addEdge(c, e, "c -> e");
265         graph.addEdge(e, f, "e -> f");
266         graph.addEdge(e, a, "e -> a");
267         graph.addEdge(c, d, "c -> d");
268 
269         GraphT.VertexDescriptor[char] vertices;
270         vertices = reduce!((acc, v) { acc[graph[v]] = v; return acc; })(
271             vertices, [a, b, c, d, e, f, g]);
272 
273         return TestGraph!GraphT(graph, vertices);
274     }
275 
276     private alias G = AdjacencyList!(VecS, VecS, DirectedS, char, string); 
277     private alias Vertex = G.VertexDescriptor;
278     private alias Edge = G.EdgeDescriptor;
279 }
280 
281 unittest {
282     writeln("DFS: Vertices are discovered exactly once, and siblings discovered " ~
283             "after children.");
284 
285     static struct Visitor {
286         this(ref G graph, ref char[] discoveryOrder) {
287             _graph = &graph;
288             _discoveryOrder = &discoveryOrder;
289         }
290 
291         void discoverVertex(ref const(G) g, Vertex v) {
292             (*_discoveryOrder) ~= (*_graph)[v];
293         }
294 
295         NullVisitor!G impl;
296         alias impl this;
297 
298         G* _graph;
299         char[]* _discoveryOrder;
300     }
301     char[] discoveryOrder;
302     Colour[Vertex] colourMap;
303 
304     auto testGraph = MakeTestGraph!G(); 
305 
306     depthFirstSearch(testGraph.graph,
307                      testGraph.vertices['a'], 
308                      colourMap, 
309                      Visitor(testGraph.graph, discoveryOrder));
310 
311     // Assert that each vertex is examined, and examined exactly once
312     assert (discoveryOrder.length == 7,
313         "Expected 7 entries in discovery order array, got " ~
314         to!string(discoveryOrder.length));
315 
316     auto keyExists = delegate(char v) { return indexOf(discoveryOrder, v) >= 0; };
317     
318     assert (all(testGraph.vertices.keys, keyExists),
319         "Expected all vertices to appear in the discovery order list: " ~
320         discoveryOrder);
321 
322     // Assert that the source vertex is examined
323     assert (indexOf(discoveryOrder, 'a') == 0,
324         "Expected Vertex A to be the first vertex discovered.");
325 
326     // Assert that the vertices are enumerated depth first
327     assert (indexOf(discoveryOrder, 'f') < indexOf(discoveryOrder, 'c'), 
328         "Expected vertex F to appear before vertex C.");
329  
330     assert (indexOf(discoveryOrder, 'f') < indexOf(discoveryOrder, 'd'), 
331         "Expected vertex F to appear before vertex D.");
332 }
333 
334 unittest {
335     writeln("DFS: Edges in a directed graph should be examined exactly once.");
336 
337     static struct Visitor {
338         this(ref int[Edge] counts) {
339             _counts = &counts;
340         }
341 
342         void examineEdge(ref const(G) g, Edge e) {
343             (*_counts)[e]++;
344         }
345 
346         NullVisitor!G impl;
347         alias impl this;
348 
349         int[Edge]* _counts;
350     }
351 
352     int[Edge] counts;
353     Colour[Vertex] colourMap;
354 
355     auto testGraph = MakeTestGraph!G(); 
356 
357     depthFirstSearch(testGraph.graph,
358                      testGraph.vertices['a'], 
359                      colourMap, 
360                      Visitor(counts));
361 
362     auto edges = Set!Edge();
363     foreach (v; testGraph.graph.vertices)
364         edges.insert(testGraph.graph.outEdges(v));
365 
366     // Assert that each edge is discovered, and discovered exactly once
367     assert (counts.length == edges.length,
368         "Expected " ~ to!string(edges.length) ~ 
369         " entries in discovery count array, got " ~ to!string(counts.length));
370 
371     auto pred = (Edge e) { return (e in counts) !is null; };
372     assert (all(edges, pred),
373         "Every edge must appear in the discovery count array");
374 
375     assert (std.algorithm.all!("a == 1")(counts.values));
376 }
377 
378 unittest {
379     writeln("DFS: Edges in an undirected graph should be examined at least once.");
380     alias UndirectedGraph = AdjacencyList!(VecS, VecS, UndirectedS, char, string); 
381     alias UEdge = UndirectedGraph.EdgeDescriptor;
382 
383     static struct Visitor {
384         this(ref int[UEdge] counts) {
385             _counts = &counts;
386         }
387 
388         void examineEdge(ref const(UndirectedGraph) g, UEdge e) {
389             (*_counts)[e]++;
390         }
391 
392         NullVisitor!UndirectedGraph impl;
393         alias impl this;
394 
395         int[UEdge]* _counts;
396     }
397 
398     auto testGraph = MakeTestGraph!UndirectedGraph(); 
399 
400     int[UEdge] counts;
401     Colour[Vertex] colourMap;
402 
403     depthFirstSearch(testGraph.graph,
404                      testGraph.vertices['a'], 
405                      colourMap, 
406                      Visitor(counts));
407 
408     auto edges = Set!UEdge();
409     foreach (v; testGraph.graph.vertices)
410         edges.insert(testGraph.graph.outEdges(v));
411 
412     // Assert that each edge is discovered at least once
413     assert (counts.length == edges.length,
414         "Expected " ~ to!string(edges.length) ~ 
415         " entries in discovery count array, got " ~ to!string(counts.length));
416 
417     auto pred = (UEdge e) { return (e in counts) !is null; };
418     assert (all(edges, pred),
419         "Every edge must appear in the examination count array");
420 
421     assert (std.algorithm.all!("a > 0")(counts.values));
422 }
423 
424 unittest {
425     writeln("DFS: Tree & non-tree vertices should be identified.");
426 
427     static struct Visitor {
428         this(ref Edge[] treeEdges, 
429              ref Edge[] backEdges,
430              ref Edge[] forwardEdges) {
431             _treeEdges = &treeEdges;
432             _backEdges = &backEdges;
433             _forwardEdges = &forwardEdges;
434         }
435 
436         /// Called when an edge has been identified as part of the current 
437         /// spanning tree.
438         void treeEdge(ref const(G) g, Edge e) {
439             (*_treeEdges) ~= e;        
440         };
441 
442         /// Called when an edge has been identified as part of a cycle
443         void backEdge(ref const(G) g, Edge e) {
444             (*_backEdges) ~= e;
445         };
446 
447         void forwardOrCrossEdge(ref const(G) g, Edge e) {
448             (*_forwardEdges) ~= e;
449         };
450 
451         NullVisitor!G impl;
452         alias impl this;
453 
454         private Edge[]* _treeEdges;
455         private Edge[]* _backEdges;
456         private Edge[]* _forwardEdges;
457     }
458 
459     Edge[] treeEdges, backEdges, fwdEdges;
460     Colour[Vertex] colourMap;
461 
462     auto testGraph = MakeTestGraph!G(); 
463     auto g = &testGraph.graph;
464 
465     depthFirstSearch(testGraph.graph,
466                      testGraph.vertices['a'], 
467                      colourMap, 
468                      Visitor(treeEdges, backEdges, fwdEdges));
469 
470     assert (treeEdges.length == 5, 
471         "Expected 5 tree edges, got " ~ to!string(treeEdges.length));
472 
473     assert (backEdges.length == 1, 
474         "Expected 1 back edge, got " ~ to!string(backEdges.length));
475 
476     auto backEdge = backEdges[0];
477     assert (g.source(backEdge) == testGraph.vertices['e'],
478         "Expected the source of the fwd edge to be vertex e, instead was " ~
479         (*g)[g.source(backEdge)]);
480 
481     assert (g.target(backEdge) == testGraph.vertices['a'],
482         "Expected the target of the fwd edge to be vertex a, instead was " ~
483         (*g)[g.target(backEdge)]);
484 
485     assert (fwdEdges.length == 1, 
486         "Expected 1 forward/cross edge, got " ~ to!string(fwdEdges.length));
487 
488     auto fwdEdge = fwdEdges[0];
489     assert (g.source(fwdEdge) == testGraph.vertices['c'],
490         "Expected the source of the fwd edge to be vertex c, instead was " ~
491         (*g)[g.source(fwdEdge)]);
492 
493     assert (g.target(fwdEdge) == testGraph.vertices['e'],
494         "Expected the target of the fwd edge to be vertex c, instead was " ~
495         (*g)[g.target(fwdEdge)]);
496 }
497 
498 unittest {
499     writeln("DFS: Vertices should be finished exactly once.");
500 
501     static struct Visitor {
502         this(ref int[Vertex] finishCounts) {
503             _counts = &finishCounts;
504         }
505 
506         void finishVertex(ref const(G) g, Vertex v) {
507             (*_counts)[v]++;
508         }
509 
510         NullVisitor!G impl;
511         alias impl this;
512 
513         G* _graph;
514         int[Vertex]* _counts;
515     }
516 
517     int[Vertex] counts;
518     Colour[Vertex] colourMap;
519 
520     auto testGraph = MakeTestGraph!G(); 
521 
522     depthFirstSearch(testGraph.graph,
523                      testGraph.vertices['a'], 
524                      colourMap, 
525                      Visitor(counts));
526 
527     // Assert that each vertex is discovered, and discovered exactly once
528     auto vertices = array(testGraph.graph.vertices);
529     assert (counts.length == vertices.length,
530         "Expected " ~ to!string(vertices.length) ~ 
531         " entries in edge finished array, got " ~ to!string(counts.length));
532 
533     auto pred = (Vertex v) { return (v in counts) !is null; };
534     assert (all(testGraph.vertices.values, pred),
535         "Every vertex must appear in the finish count array");
536 
537     assert (std.algorithm.all!("a == 1")(counts.values),
538         "Vertices must be finished exactly once.");
539 }