rgl 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|