rgl 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +19 -10
- data/Gemfile +3 -0
- data/{README → README.rdoc} +70 -98
- data/Rakefile +44 -150
- data/examples/canvas.rb +63 -64
- data/examples/examples.rb +42 -42
- data/examples/graph.dot +46 -0
- data/examples/images/example.jpg +0 -0
- data/examples/images/module_graph.jpg +0 -0
- data/examples/images/rgl_modules.png +0 -0
- data/examples/{insel-der-tausend-gefahren.rb → insel_der_tausend_gefahren.rb} +18 -19
- data/examples/north.rb +2 -2
- data/examples/north2.rb +11 -11
- data/examples/rdep-rgl.rb +218 -222
- data/lib/rgl/adjacency.rb +78 -74
- data/lib/rgl/base.rb +160 -78
- data/lib/rgl/bellman_ford.rb +115 -0
- data/lib/rgl/bidirectional.rb +17 -10
- data/lib/rgl/bipartite.rb +87 -0
- data/lib/rgl/condensation.rb +13 -4
- data/lib/rgl/connected_components.rb +38 -30
- data/lib/rgl/dijkstra.rb +158 -0
- data/lib/rgl/dijkstra_visitor.rb +42 -0
- data/lib/rgl/dot.rb +40 -32
- data/lib/rgl/edge_properties_map.rb +55 -0
- data/lib/rgl/edmonds_karp.rb +136 -0
- data/lib/rgl/enumerable_ext.rb +4 -1
- data/lib/rgl/graph_iterator.rb +15 -0
- data/lib/rgl/graph_visitor.rb +138 -0
- data/lib/rgl/graph_wrapper.rb +15 -0
- data/lib/rgl/graphxml.rb +20 -10
- data/lib/rgl/implicit.rb +68 -66
- data/lib/rgl/mutable.rb +37 -31
- data/lib/rgl/path_builder.rb +40 -0
- data/lib/rgl/prim.rb +52 -0
- data/lib/rgl/rdot.rb +411 -374
- data/lib/rgl/topsort.rb +23 -16
- data/lib/rgl/transitivity.rb +29 -27
- data/lib/rgl/traversal.rb +67 -205
- data/rakelib/dep_graph.rake +4 -3
- data/test/bellman_ford_test.rb +187 -0
- data/test/bipartite_test.rb +47 -0
- data/test/components_test.rb +80 -0
- data/test/cycles_test.rb +60 -0
- data/test/dijkstra_test.rb +148 -0
- data/test/directed_graph_test.rb +118 -0
- data/test/dot_test.rb +26 -0
- data/test/edge_properties_map_test.rb +63 -0
- data/test/edge_test.rb +35 -0
- data/test/edmonds_karp_test.rb +105 -0
- data/{tests/TestGraph.rb → test/graph_test.rb} +6 -6
- data/test/graph_xml_test.rb +57 -0
- data/test/implicit_test.rb +53 -0
- data/test/prim_test.rb +98 -0
- data/{tests/TestRdot.rb → test/rdot_test.rb} +309 -308
- data/{tests → test}/test_helper.rb +4 -1
- data/{tests/TestTransitivity.rb → test/transitivity_test.rb} +43 -43
- data/test/traversal_test.rb +221 -0
- data/test/undirected_graph_test.rb +103 -0
- metadata +226 -145
- data/examples/example.jpg +0 -0
- data/examples/module_graph.jpg +0 -0
- data/install.rb +0 -49
- data/tests/TestComponents.rb +0 -65
- data/tests/TestCycles.rb +0 -61
- data/tests/TestDirectedGraph.rb +0 -125
- data/tests/TestDot.rb +0 -18
- data/tests/TestEdge.rb +0 -34
- data/tests/TestGraphXML.rb +0 -57
- data/tests/TestImplicit.rb +0 -52
- data/tests/TestTraversal.rb +0 -220
- data/tests/TestUnDirectedGraph.rb +0 -102
data/lib/rgl/topsort.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# topsort.rb
|
2
2
|
|
3
|
-
require 'rgl/
|
3
|
+
require 'rgl/graph_iterator'
|
4
4
|
|
5
5
|
module RGL
|
6
6
|
|
@@ -11,19 +11,19 @@ module RGL
|
|
11
11
|
# the ordering. The graph must be a directed acyclic graph (DAG).
|
12
12
|
#
|
13
13
|
# The iterator can also be applied to undirected graph or to a DG graph
|
14
|
-
# which contains a cycle.
|
15
|
-
# vertices.
|
16
|
-
|
14
|
+
# which contains a cycle. In this case, the Iterator does not reach all
|
15
|
+
# vertices. The implementation of acyclic? uses this fact.
|
16
|
+
#
|
17
17
|
class TopsortIterator
|
18
18
|
|
19
19
|
include GraphIterator
|
20
20
|
|
21
|
-
def initialize
|
21
|
+
def initialize(g)
|
22
22
|
super(g)
|
23
23
|
set_to_begin
|
24
24
|
end
|
25
25
|
|
26
|
-
def set_to_begin
|
26
|
+
def set_to_begin # :nodoc:
|
27
27
|
@waiting = Array.new
|
28
28
|
@inDegrees = Hash.new(0)
|
29
29
|
|
@@ -39,7 +39,7 @@ module RGL
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def basic_forward
|
42
|
+
def basic_forward # :nodoc:
|
43
43
|
u = @waiting.pop
|
44
44
|
graph.each_adjacent(u) do |v|
|
45
45
|
@inDegrees[v] -= 1
|
@@ -48,25 +48,32 @@ module RGL
|
|
48
48
|
u
|
49
49
|
end
|
50
50
|
|
51
|
-
def at_beginning
|
52
|
-
|
51
|
+
def at_beginning?
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
# :nodoc: FIXME
|
56
|
+
def at_end?
|
57
|
+
@waiting.empty?
|
58
|
+
end # :nodoc:
|
53
59
|
|
54
|
-
end
|
60
|
+
end # class TopsortIterator
|
55
61
|
|
56
62
|
module Graph
|
57
63
|
|
58
64
|
# Returns a TopsortIterator.
|
59
|
-
|
65
|
+
#
|
60
66
|
def topsort_iterator
|
61
67
|
TopsortIterator.new(self)
|
62
68
|
end
|
63
|
-
|
64
|
-
# Returns true if the graph contains no cycles. This is only meaningful
|
65
|
-
# for directed graphs. Returns false for undirected graphs.
|
66
69
|
|
70
|
+
# Returns true if the graph contains no cycles. This is only meaningful
|
71
|
+
# for directed graphs. Returns false for undirected graphs.
|
72
|
+
#
|
67
73
|
def acyclic?
|
68
74
|
topsort_iterator.length == num_vertices
|
69
75
|
end
|
70
76
|
|
71
|
-
end
|
72
|
-
|
77
|
+
end # module Graph
|
78
|
+
|
79
|
+
end # module RGL
|
data/lib/rgl/transitivity.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
|
-
require 'enumerator'
|
2
|
-
|
3
|
-
require 'rgl/adjacency'
|
4
1
|
require 'rgl/base'
|
2
|
+
require 'rgl/adjacency'
|
5
3
|
require 'rgl/condensation'
|
6
4
|
|
7
5
|
module RGL
|
6
|
+
|
8
7
|
module Graph
|
8
|
+
|
9
9
|
# Returns an RGL::DirectedAdjacencyGraph which is the transitive closure of
|
10
|
-
# this graph.
|
11
|
-
# is copied and the edge u -> v is added.
|
10
|
+
# this graph. Meaning, for each path u -> ... -> v in this graph, the path
|
11
|
+
# is copied and the edge u -> v is added. This method supports working with
|
12
12
|
# cyclic graphs by ensuring that edges are created between every pair of
|
13
13
|
# vertices in the cycle, including self-referencing edges.
|
14
14
|
#
|
@@ -16,17 +16,18 @@ module RGL
|
|
16
16
|
# of vertices and edges respectively.
|
17
17
|
#
|
18
18
|
# Raises RGL::NotDirectedError if run on an undirected graph.
|
19
|
+
#
|
19
20
|
def transitive_closure
|
20
21
|
raise NotDirectedError,
|
21
|
-
|
22
|
+
"transitive_closure only supported for directed graphs" unless directed?
|
22
23
|
|
23
24
|
# Compute a condensation graph in order to hide cycles.
|
24
25
|
cg = condensation_graph
|
25
26
|
|
26
27
|
# Use a depth first search to calculate the transitive closure over the
|
27
|
-
# condensation graph.
|
28
|
+
# condensation graph. This ensures that as we traverse up the graph we
|
28
29
|
# know the transitive closure of each subgraph rooted at each node
|
29
|
-
# starting at the leaves.
|
30
|
+
# starting at the leaves. Subsequent root nodes which consume these
|
30
31
|
# subgraphs by way of the nodes' immediate successors can then immediately
|
31
32
|
# add edges to the roots of the subgraphs and to every successor of those
|
32
33
|
# roots.
|
@@ -52,7 +53,7 @@ module RGL
|
|
52
53
|
# For each NON-trivial strongly connected component in the condensed
|
53
54
|
# graph, add each node it contains to the new graph and add edges to
|
54
55
|
# every node in the strongly connected component, including self
|
55
|
-
# referential edges.
|
56
|
+
# referential edges. Then for each edge of the original graph from any
|
56
57
|
# of the contained nodes, add edges from each of the contained nodes to
|
57
58
|
# all the edge targets.
|
58
59
|
g = DirectedAdjacencyGraph.new
|
@@ -61,7 +62,7 @@ module RGL
|
|
61
62
|
# Add edges between all members of non-trivial strongly connected
|
62
63
|
# components (size > 1) and ensure that self referential edges are
|
63
64
|
# added when necessary for trivial strongly connected components.
|
64
|
-
if scc.size > 1 || has_edge?(v, v)
|
65
|
+
if scc.size > 1 || has_edge?(v, v)
|
65
66
|
scc.each do |w|
|
66
67
|
g.add_edge(v, w)
|
67
68
|
end
|
@@ -86,8 +87,8 @@ module RGL
|
|
86
87
|
end
|
87
88
|
|
88
89
|
# Returns an RGL::DirectedAdjacencyGraph which is the transitive reduction
|
89
|
-
# of this graph.
|
90
|
-
# u -> ... -> v exists.
|
90
|
+
# of this graph. Meaning, that each edge u -> v is omitted if path
|
91
|
+
# u -> ... -> v exists. This method supports working with cyclic graphs;
|
91
92
|
# however, cycles are arbitrarily simplified which may lead to variant,
|
92
93
|
# although equally valid, results on equivalent graphs.
|
93
94
|
#
|
@@ -95,17 +96,18 @@ module RGL
|
|
95
96
|
# of vertices and edges respectively.
|
96
97
|
#
|
97
98
|
# Raises RGL::NotDirectedError if run on an undirected graph.
|
99
|
+
#
|
98
100
|
def transitive_reduction
|
99
101
|
raise NotDirectedError,
|
100
|
-
|
102
|
+
"transitive_reduction only supported for directed graphs" unless directed?
|
101
103
|
|
102
104
|
# Compute a condensation graph in order to hide cycles.
|
103
105
|
cg = condensation_graph
|
104
106
|
|
105
107
|
# Use a depth first search to compute the transitive reduction over the
|
106
|
-
# condensed graph.
|
108
|
+
# condensed graph. This is similar to the computation of the transitive
|
107
109
|
# closure over the graph in that for any node of the graph all nodes
|
108
|
-
# reachable from the node are tracked.
|
110
|
+
# reachable from the node are tracked. Using a depth first search ensures
|
109
111
|
# that all nodes reachable from a target node are known when considering
|
110
112
|
# whether or not to add an edge pointing to that target.
|
111
113
|
tr_cg = DirectedAdjacencyGraph.new
|
@@ -114,11 +116,9 @@ module RGL
|
|
114
116
|
paths_from[v] = Set.new
|
115
117
|
cg.each_adjacent(v) do |w|
|
116
118
|
# Only add the edge v -> w if there is no other edge v -> x such that
|
117
|
-
# w is reachable from x.
|
119
|
+
# w is reachable from x. Make sure to completely skip the case where
|
118
120
|
# x == w.
|
119
|
-
unless
|
120
|
-
x != w && paths_from[x].include?(w)
|
121
|
-
end then
|
121
|
+
unless cg.to_enum(:each_adjacent, v).any? { |x| x != w && paths_from[x].include?(w) }
|
122
122
|
tr_cg.add_edge(v, w)
|
123
123
|
|
124
124
|
# For each vertex v, track all nodes reachable from v by adding node
|
@@ -138,7 +138,7 @@ module RGL
|
|
138
138
|
# edge the node begins in the original graph.
|
139
139
|
# For each NON-trivial strongly connected component in the condensed
|
140
140
|
# graph, add each node it contains to the new graph and add arbitrary
|
141
|
-
# edges between the nodes to form a simple cycle.
|
141
|
+
# edges between the nodes to form a simple cycle. Then for each strongly
|
142
142
|
# connected component adjacent to the current one, find and add the first
|
143
143
|
# edge which exists in the original graph, starts in the first strongly
|
144
144
|
# connected component, and ends in the second strongly connected
|
@@ -148,7 +148,7 @@ module RGL
|
|
148
148
|
# Make a cycle of the contents of non-trivial strongly connected
|
149
149
|
# components.
|
150
150
|
scc_arr = scc.to_a
|
151
|
-
if scc.size > 1 || has_edge?(scc_arr.first, scc_arr.first)
|
151
|
+
if scc.size > 1 || has_edge?(scc_arr.first, scc_arr.first)
|
152
152
|
0.upto(scc_arr.size - 2) do |idx|
|
153
153
|
g.add_edge(scc_arr[idx], scc_arr[idx + 1])
|
154
154
|
end
|
@@ -157,12 +157,12 @@ module RGL
|
|
157
157
|
|
158
158
|
# Choose a single edge between the members of two different strongly
|
159
159
|
# connected component to add to the graph.
|
160
|
-
edges =
|
160
|
+
edges = self.to_enum(:each_edge)
|
161
161
|
tr_cg.each_adjacent(scc) do |scc2|
|
162
162
|
g.add_edge(
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
*edges.find do |v, w|
|
164
|
+
scc.member?(v) && scc2.member?(w)
|
165
|
+
end
|
166
166
|
)
|
167
167
|
end
|
168
168
|
|
@@ -175,5 +175,7 @@ module RGL
|
|
175
175
|
# Finally, the transitive reduction...
|
176
176
|
g
|
177
177
|
end
|
178
|
-
|
179
|
-
end
|
178
|
+
|
179
|
+
end # module Graph
|
180
|
+
|
181
|
+
end # module RGL
|
data/lib/rgl/traversal.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# This file defines the basic graph traversal algorithm for DFS and BFS search.
|
4
4
|
# They are implemented as an RGL::GraphIterator, which is a Stream of vertices
|
5
|
-
# of a given graph.
|
5
|
+
# of a given graph. The streams are not reversable.
|
6
6
|
#
|
7
7
|
# Beside being an iterator in the sense of the Stream mixin, RGL::BFSIterator
|
8
8
|
# and RGL::DFSIterator follow the BGL
|
@@ -10,153 +10,24 @@
|
|
10
10
|
# in a slightly modified fashion (especially for the RGL::DFSIterator).
|
11
11
|
|
12
12
|
require 'rgl/base'
|
13
|
-
require '
|
14
|
-
require '
|
13
|
+
require 'rgl/graph_visitor'
|
14
|
+
require 'rgl/graph_iterator'
|
15
15
|
|
16
16
|
module RGL
|
17
17
|
|
18
|
-
module GraphWrapper # :nodoc:
|
19
|
-
|
20
|
-
attr_accessor :graph
|
21
|
-
|
22
|
-
# Creates a new GraphWrapper on _graph_.
|
23
|
-
def initialize (graph)
|
24
|
-
@graph = graph
|
25
|
-
end
|
26
|
-
|
27
|
-
end # module GraphWrapper
|
28
|
-
|
29
|
-
# A GraphIterator is the abstract superclass of all Iterators on graphs.
|
30
|
-
# Each such iterator should implement the protocol defined in module Stream.
|
31
|
-
module GraphIterator
|
32
|
-
include Stream
|
33
|
-
include GraphWrapper
|
34
|
-
end
|
35
|
-
|
36
|
-
# Module GraphVisitor defines the BGL
|
37
|
-
# BFS[http://www.boost.org/libs/graph/doc/BFSVisitor.html] Visitor Concept).
|
38
|
-
#
|
39
|
-
# Visitors provide a mechanism for extending an algorithm (i.e., for
|
40
|
-
# customizing what is done at each step of the algorithm). They allow users
|
41
|
-
# to insert their own operations at various steps within a graph algorithm.
|
42
|
-
#
|
43
|
-
# Graph algorithms typically have multiple event points where one may want to
|
44
|
-
# insert a call-back. Therefore, visitors have several methods that
|
45
|
-
# correspond to the various event points. Each algorithm has a different
|
46
|
-
# set of event points. The following are common to both DFS and BFS search.
|
47
|
-
#
|
48
|
-
# * examine_vertex
|
49
|
-
# * finish_vertex
|
50
|
-
# * examine_edge
|
51
|
-
# * tree_edge
|
52
|
-
# * back_edge
|
53
|
-
# * forward_edge
|
54
|
-
#
|
55
|
-
# These methods are all called handle_* and can be set to appropriate blocks,
|
56
|
-
# using the methods set_*_event_handler, which are defined for each event
|
57
|
-
# mentioned above.
|
58
|
-
#
|
59
|
-
# As an alternative, you can also override the handle_* methods in a
|
60
|
-
# subclass, to configure the algorithm (as an example, see TarjanSccVisitor).
|
61
|
-
#
|
62
|
-
# During a graph traversal, vertices are *colored* using the colors :GRAY
|
63
|
-
# (when waiting) and :BLACK when finished. All other vertices are :WHITE.
|
64
|
-
# The color_map is also maintained in the visitor.
|
65
|
-
|
66
|
-
module GraphVisitor
|
67
|
-
|
68
|
-
include GraphWrapper
|
69
|
-
|
70
|
-
attr_reader :color_map
|
71
|
-
|
72
|
-
# Create a new GraphVisitor on _graph_.
|
73
|
-
|
74
|
-
def initialize (graph)
|
75
|
-
super graph
|
76
|
-
reset
|
77
|
-
end
|
78
|
-
|
79
|
-
# Mark each vertex unvisited (i.e. :WHITE)
|
80
|
-
|
81
|
-
def reset
|
82
|
-
@color_map = Hash.new(:WHITE)
|
83
|
-
end
|
84
|
-
|
85
|
-
# Returns true if vertex _v_ is colored :BLACK (i.e. finished).
|
86
|
-
|
87
|
-
def finished_vertex? (v)
|
88
|
-
@color_map[v] == :BLACK
|
89
|
-
end
|
90
|
-
|
91
|
-
# Attach a map to the visitor which records the distance of a visited
|
92
|
-
# vertex to the start vertex.
|
93
|
-
#
|
94
|
-
# This is similar to BGLs
|
95
|
-
# distance_recorder[http://www.boost.org/libs/graph/doc/distance_recorder.html].
|
96
|
-
#
|
97
|
-
# After the distance_map is attached, the visitor has a new method
|
98
|
-
# distance_to_root, which answers the distance to the start vertex.
|
99
|
-
|
100
|
-
def attach_distance_map (map = Hash.new(0))
|
101
|
-
@dist_map = map
|
102
|
-
|
103
|
-
class << self
|
104
|
-
|
105
|
-
def handle_tree_edge (u, v)
|
106
|
-
super
|
107
|
-
@dist_map[v] = @dist_map[u] + 1
|
108
|
-
end
|
109
|
-
|
110
|
-
# Answer the distance to the start vertex.
|
111
|
-
|
112
|
-
def distance_to_root (v)
|
113
|
-
@dist_map[v]
|
114
|
-
end
|
115
|
-
|
116
|
-
end # class
|
117
|
-
end
|
118
|
-
|
119
|
-
# Shall we follow the edge (u,v); i.e. v has color :WHITE
|
120
|
-
|
121
|
-
def follow_edge? (u, v) # :nodoc:
|
122
|
-
@color_map[v] == :WHITE
|
123
|
-
end
|
124
|
-
|
125
|
-
# == Visitor Event Points
|
126
|
-
|
127
|
-
def self.def_event_handler (m)
|
128
|
-
params = m =~ /edge/ ? "u,v" : "u"
|
129
|
-
self.class_eval %{
|
130
|
-
def handle_#{m} (#{params})
|
131
|
-
@#{m}_event_handler.call(#{params}) if defined? @#{m}_event_handler
|
132
|
-
end
|
133
|
-
|
134
|
-
def set_#{m}_event_handler (&b)
|
135
|
-
@#{m}_event_handler = b
|
136
|
-
end
|
137
|
-
}
|
138
|
-
end
|
139
|
-
|
140
|
-
%w[examine_vertex finish_vertex examine_edge tree_edge back_edge
|
141
|
-
forward_edge].each do |m|
|
142
|
-
def_event_handler(m)
|
143
|
-
end
|
144
|
-
|
145
|
-
end # module GraphVisitor
|
146
|
-
|
147
18
|
# A BFSIterator can be used to traverse a graph from a given start vertex in
|
148
|
-
# breath first search order.
|
19
|
+
# breath first search order. Since the Iterator also mixins the GraphVisitor,
|
149
20
|
# it provides all event points defined there.
|
150
21
|
#
|
151
22
|
# The vertices which are not yet visited are held in the queue @waiting.
|
152
23
|
# During the traversal, vertices are *colored* using the colors :GRAY
|
153
|
-
# (when waiting) and :BLACK when finished.
|
24
|
+
# (when waiting) and :BLACK when finished. All other vertices are :WHITE.
|
154
25
|
#
|
155
26
|
# For more doc see the BGL
|
156
27
|
# BFS[http://www.boost.org/libs/graph/doc/BFSVisitor.html] Visitor Concept .
|
157
28
|
#
|
158
29
|
# See the implementation of bfs_search_tree_from for an example usage.
|
159
|
-
|
30
|
+
#
|
160
31
|
class BFSIterator
|
161
32
|
|
162
33
|
include GraphIterator
|
@@ -165,96 +36,99 @@ module RGL
|
|
165
36
|
attr_accessor :start_vertex
|
166
37
|
|
167
38
|
# Create a new BFSIterator on _graph_, starting at vertex _start_.
|
168
|
-
|
169
|
-
def initialize
|
39
|
+
#
|
40
|
+
def initialize(graph, start = graph.detect { |x| true })
|
170
41
|
super(graph)
|
171
42
|
@start_vertex = start
|
172
43
|
set_to_begin
|
173
44
|
end
|
174
45
|
|
175
46
|
# Returns true if the @color_map has only one entry (for the start vertex).
|
176
|
-
|
177
|
-
def at_beginning?
|
47
|
+
#
|
48
|
+
def at_beginning? # :nodoc:
|
178
49
|
@color_map.size == 1
|
179
50
|
end
|
180
|
-
|
181
|
-
# Returns true if @waiting is empty.
|
182
51
|
|
52
|
+
# Returns true if @waiting is empty.
|
53
|
+
#
|
183
54
|
def at_end?
|
184
55
|
@waiting.empty?
|
185
56
|
end
|
186
57
|
|
187
58
|
# Reset the iterator to the initial state (i.e. at_beginning? == true).
|
188
|
-
|
59
|
+
#
|
189
60
|
def set_to_begin
|
190
61
|
color_map[@start_vertex] = :GRAY
|
191
|
-
@waiting = [@start_vertex]
|
192
|
-
handle_tree_edge(nil, @start_vertex)
|
62
|
+
@waiting = [@start_vertex] # a queue
|
63
|
+
handle_tree_edge(nil, @start_vertex) # discovers start vertex
|
193
64
|
end
|
194
65
|
|
195
|
-
def basic_forward
|
66
|
+
def basic_forward # :nodoc:
|
196
67
|
u = next_vertex
|
197
68
|
handle_examine_vertex(u)
|
198
|
-
|
69
|
+
|
70
|
+
graph.each_adjacent(u) do |v|
|
199
71
|
handle_examine_edge(u, v)
|
200
|
-
if follow_edge?(u, v)
|
201
|
-
handle_tree_edge(u, v)
|
202
|
-
color_map[v] = :GRAY
|
72
|
+
if follow_edge?(u, v) # (u,v) is a tree edge
|
73
|
+
handle_tree_edge(u, v) # also discovers v
|
74
|
+
color_map[v] = :GRAY # color of v was :WHITE
|
203
75
|
@waiting.push(v)
|
204
|
-
else
|
76
|
+
else # (u,v) is a non tree edge
|
205
77
|
if color_map[v] == :GRAY
|
206
|
-
handle_back_edge(u, v)
|
78
|
+
handle_back_edge(u, v) # (u,v) has gray target
|
207
79
|
else
|
208
|
-
handle_forward_edge(u, v)
|
80
|
+
handle_forward_edge(u, v) # (u,v) has black target
|
209
81
|
end
|
210
82
|
end
|
211
|
-
|
83
|
+
end
|
84
|
+
|
212
85
|
color_map[u] = :BLACK
|
213
|
-
handle_finish_vertex(u)
|
86
|
+
handle_finish_vertex(u) # finish vertex
|
214
87
|
u
|
215
88
|
end
|
216
89
|
|
217
90
|
protected
|
218
91
|
|
219
|
-
def next_vertex
|
92
|
+
def next_vertex # :nodoc:
|
220
93
|
# waiting is a queue
|
221
94
|
@waiting.shift
|
222
95
|
end
|
223
|
-
end # class BFSIterator
|
224
96
|
|
97
|
+
end # class BFSIterator
|
225
98
|
|
226
99
|
module Graph
|
227
100
|
|
228
101
|
# Returns a BFSIterator, starting at vertex _v_.
|
229
102
|
|
230
|
-
def bfs_iterator
|
103
|
+
def bfs_iterator(v = self.detect { |x| true })
|
231
104
|
BFSIterator.new(self, v)
|
232
105
|
end
|
233
106
|
|
234
107
|
# Returns a DirectedAdjacencyGraph, which represents a BFS search tree
|
235
|
-
# starting at _v_.
|
108
|
+
# starting at _v_. This method uses the tree_edge_event of BFSIterator
|
236
109
|
# to record all tree edges of the search tree in the result.
|
237
110
|
|
238
|
-
def bfs_search_tree_from
|
111
|
+
def bfs_search_tree_from(v)
|
239
112
|
require 'rgl/adjacency'
|
240
113
|
bfs = bfs_iterator(v)
|
241
114
|
tree = DirectedAdjacencyGraph.new
|
242
|
-
|
115
|
+
|
116
|
+
bfs.set_tree_edge_event_handler do |from, to|
|
243
117
|
tree.add_edge(from, to)
|
244
|
-
|
245
|
-
|
118
|
+
end
|
119
|
+
|
120
|
+
bfs.set_to_end # does the search
|
246
121
|
tree
|
247
122
|
end
|
248
123
|
|
249
|
-
end
|
124
|
+
end # module Graph
|
250
125
|
|
251
|
-
|
252
|
-
# Iterator for a depth first search, starting at a given vertex. The only
|
126
|
+
# Iterator for a depth first search, starting at a given vertex. The only
|
253
127
|
# difference from BFSIterator is that @waiting is a stack, instead of a queue.
|
254
128
|
#
|
255
129
|
# Note that this is different from DFSVisitor, which is used in the recursive
|
256
130
|
# version for depth first search (see depth_first_search).
|
257
|
-
|
131
|
+
#
|
258
132
|
class DFSIterator < BFSIterator
|
259
133
|
|
260
134
|
def next_vertex
|
@@ -262,11 +136,10 @@ module RGL
|
|
262
136
|
@waiting.pop
|
263
137
|
end
|
264
138
|
|
265
|
-
end
|
266
|
-
|
139
|
+
end # class DFSIterator
|
267
140
|
|
268
141
|
# A DFSVisitor is needed by the depth_first_search and depth_first_visit
|
269
|
-
# methods of a graph.
|
142
|
+
# methods of a graph. Besides the eventpoint of GraphVisitor, it provides
|
270
143
|
# an additional eventpoint start_vertex, which is called when a
|
271
144
|
# depth_first_search starts a new subtree of the depth first forest that is
|
272
145
|
# defined by the search.
|
@@ -275,29 +148,28 @@ module RGL
|
|
275
148
|
# DFSVisitor[http://www.boost.org/libs/graph/doc/DFSVisitor.html] is not
|
276
149
|
# this is also defined in the common mixin GraphVisitor of DFSVisitor,
|
277
150
|
# DFSIterator, and BFSIterator.
|
278
|
-
|
151
|
+
#
|
279
152
|
class DFSVisitor
|
280
153
|
|
281
154
|
include GraphVisitor
|
282
155
|
|
283
|
-
|
284
|
-
|
285
|
-
end # class DFSVisitor
|
156
|
+
def_event_handler 'start_vertex'
|
286
157
|
|
158
|
+
end # class DFSVisitor
|
287
159
|
|
288
160
|
module Graph
|
289
161
|
|
290
162
|
# Returns a DFSIterator staring at vertex _v_.
|
291
163
|
|
292
|
-
def dfs_iterator
|
164
|
+
def dfs_iterator(v = self.detect { |x| true })
|
293
165
|
DFSIterator.new(self, v)
|
294
166
|
end
|
295
167
|
|
296
|
-
# Do a recursive DFS search on the whole graph.
|
297
|
-
# it is called on each _finish_vertex_ event.
|
168
|
+
# Do a recursive DFS search on the whole graph. If a block is passed,
|
169
|
+
# it is called on each _finish_vertex_ event. See
|
298
170
|
# strongly_connected_components for an example usage.
|
299
|
-
|
300
|
-
def depth_first_search
|
171
|
+
#
|
172
|
+
def depth_first_search(vis = DFSVisitor.new(self), &b)
|
301
173
|
each_vertex do |u|
|
302
174
|
unless vis.finished_vertex?(u)
|
303
175
|
vis.handle_start_vertex(u)
|
@@ -306,44 +178,34 @@ module RGL
|
|
306
178
|
end
|
307
179
|
end
|
308
180
|
|
309
|
-
# Start a depth first search at vertex _u_.
|
181
|
+
# Start a depth first search at vertex _u_. The block _b_ is called on
|
310
182
|
# each finish_vertex event.
|
311
|
-
|
312
|
-
def depth_first_visit
|
183
|
+
#
|
184
|
+
def depth_first_visit(u, vis = DFSVisitor.new(self), &b)
|
313
185
|
vis.color_map[u] = :GRAY
|
314
186
|
vis.handle_examine_vertex(u)
|
315
|
-
|
187
|
+
|
188
|
+
each_adjacent(u) do |v|
|
316
189
|
vis.handle_examine_edge(u, v)
|
317
|
-
|
318
|
-
|
319
|
-
vis.
|
190
|
+
|
191
|
+
if vis.follow_edge?(u, v) # (u,v) is a tree edge
|
192
|
+
vis.handle_tree_edge(u, v) # also discovers v
|
193
|
+
vis.color_map[v] = :GRAY # color of v was :WHITE
|
320
194
|
depth_first_visit(v, vis, &b)
|
321
|
-
else
|
195
|
+
else # (u,v) is a non tree edge
|
322
196
|
if vis.color_map[v] == :GRAY
|
323
|
-
vis.handle_back_edge(u, v)
|
197
|
+
vis.handle_back_edge(u, v) # (u,v) has gray target
|
324
198
|
else
|
325
|
-
vis.handle_forward_edge(u, v)
|
199
|
+
vis.handle_forward_edge(u, v) # (u,v) is a cross or forward edge
|
326
200
|
end
|
327
201
|
end
|
328
|
-
|
202
|
+
end
|
203
|
+
|
329
204
|
vis.color_map[u] = :BLACK
|
330
|
-
vis.handle_finish_vertex(u)
|
205
|
+
vis.handle_finish_vertex(u) # finish vertex
|
331
206
|
b.call(u)
|
332
207
|
end
|
333
208
|
|
334
|
-
end
|
335
|
-
|
336
|
-
=begin
|
337
|
-
def acyclic?
|
338
|
-
has_cycle = false
|
339
|
-
dfs = DFSIterator.new(self)
|
340
|
-
dfs.set_back_edge_event {has_cycle = true}
|
341
|
-
dfs_each(dfs) do |x|
|
342
|
-
puts x,has_cycle,dfs.inspect
|
343
|
-
return false if has_cycle
|
344
|
-
end
|
345
|
-
true
|
346
|
-
end
|
347
|
-
=end
|
209
|
+
end # module Graph
|
348
210
|
|
349
|
-
end
|
211
|
+
end # module RGL
|