rgl 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +75 -2
- data/README +52 -28
- data/Rakefile +3 -3
- data/TAGS +242 -198
- data/examples/debgraph.rb +118 -0
- data/examples/examples.rb +5 -3
- data/examples/graph.dot +731 -17
- data/examples/insel.rb +141 -0
- data/lib/rgl/adjacency.rb +172 -139
- data/lib/rgl/base.rb +247 -251
- data/lib/rgl/connected_components.rb +125 -112
- data/lib/rgl/dot.rb +54 -46
- data/lib/rgl/graphxml.rb +48 -37
- data/lib/rgl/implicit.rb +159 -136
- data/lib/rgl/mutable.rb +69 -48
- data/lib/rgl/rdot.rb +268 -205
- data/lib/rgl/topsort.rb +63 -52
- data/lib/rgl/transitiv_closure.rb +40 -28
- data/lib/rgl/traversal.rb +300 -247
- data/tests/TestDirectedGraph.rb +22 -2
- data/tests/TestUnDirectedGraph.rb +4 -0
- metadata +7 -7
- data/Makefile +0 -72
- data/examples/graph.png +0 -0
@@ -1,3 +1,5 @@
|
|
1
|
+
# connected_components.rb
|
2
|
+
#
|
1
3
|
# This file contains the algorithms for the connected components of an
|
2
4
|
# undirected graph (each_connected_component) and strongly connected components
|
3
5
|
# for directed graphs (strongly_connected_components).
|
@@ -5,121 +7,132 @@
|
|
5
7
|
require 'rgl/traversal'
|
6
8
|
|
7
9
|
module RGL
|
10
|
+
|
8
11
|
module Graph
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
12
|
+
|
13
|
+
# Compute the connected components of an undirected graph, using a
|
14
|
+
# DFS (Depth-first search)-based approach. A _connected component_ of
|
15
|
+
# an undirected graph is a set of vertices that are all reachable
|
16
|
+
# from each other.
|
17
|
+
#
|
18
|
+
# The function is implemented as an iterator which calls the client
|
19
|
+
# with an array of vertices for each component.
|
20
|
+
#
|
21
|
+
# It raises an exception if the graph is directed.
|
22
|
+
|
23
|
+
def each_connected_component
|
24
|
+
raise NotUndirectedError,
|
25
|
+
"each_connected_component only works " +
|
26
|
+
"for undirected graphs." if directed?
|
27
|
+
comp = []
|
28
|
+
vis = DFSVisitor.new(self)
|
29
|
+
vis.set_finish_vertex_event_handler { |v| comp << v }
|
30
|
+
vis.set_start_vertex_event_handler { |v|
|
31
|
+
yield comp unless comp.empty?
|
32
|
+
comp = []
|
33
|
+
}
|
34
|
+
depth_first_search(vis) { |v| }
|
35
|
+
yield comp unless comp.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
# This GraphVisitor is used by strongly_connected_components to compute
|
39
|
+
# the strongly connected components of a directed graph.
|
40
|
+
|
41
|
+
class TarjanSccVisitor < DFSVisitor
|
42
|
+
|
43
|
+
attr_reader :comp_map
|
44
|
+
|
45
|
+
# Creates a new TarjanSccVisitor for graph _g_, which should be directed.
|
46
|
+
|
47
|
+
def initialize (g)
|
48
|
+
super g
|
49
|
+
@root_map = {}
|
50
|
+
@comp_map = {}
|
51
|
+
@discover_time_map = {}
|
52
|
+
@dfs_time = 0
|
53
|
+
@c_index = 0
|
54
|
+
@stack = []
|
55
|
+
end
|
56
|
+
|
57
|
+
def handle_examine_vertex (v)
|
58
|
+
@root_map[v] = v
|
59
|
+
@comp_map[v] = -1
|
60
|
+
@dfs_time += 1
|
61
|
+
@discover_time_map[v] = @dfs_time
|
62
|
+
@stack.push(v)
|
63
|
+
end
|
64
|
+
|
65
|
+
def handle_finish_vertex (v)
|
66
|
+
# Search adjacent vertex w with earliest discover time
|
67
|
+
root_v = @root_map[v]
|
68
|
+
graph.each_adjacent(v) do |w|
|
69
|
+
if @comp_map[w] == -1
|
70
|
+
root_v = min_discover_time(root_v, @root_map[w])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@root_map[v] = root_v
|
74
|
+
if root_v == v # v is topmost vertex of a SCC
|
75
|
+
begin # pop off all vertices until v
|
67
76
|
w = @stack.pop
|
68
77
|
@comp_map[w] = @c_index
|
69
78
|
end until w == v
|
70
79
|
@c_index += 1
|
71
80
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return the number of components found so far.
|
84
|
+
|
85
|
+
def num_comp
|
86
|
+
@c_index
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def min_discover_time (u, v)
|
92
|
+
@discover_time_map[u] < @discover_time_map[v] ? u : v
|
93
|
+
end
|
94
|
+
|
95
|
+
end # class TarjanSccVisitor
|
96
|
+
|
97
|
+
# This is Tarjan's algorithm for strongly connected components, from his
|
98
|
+
# paper "Depth first search and linear graph algorithms". It calculates
|
99
|
+
# the components in a single application of DFS. We implement the
|
100
|
+
# algorithm with the help of the DFSVisitor TarjanSccVisitor.
|
101
|
+
#
|
102
|
+
# === Definition
|
103
|
+
#
|
104
|
+
# A _strongly connected component_ of a directed graph G=(V,E) is a
|
105
|
+
# maximal set of vertices U which is in V, such that for every pair of
|
106
|
+
# vertices u and v in U, we have both a path from u to v and a path
|
107
|
+
# from v to u. That is to say, u and v are reachable from each other.
|
108
|
+
#
|
109
|
+
# @Article{Tarjan:1972:DFS,
|
110
|
+
# author = "R. E. Tarjan",
|
111
|
+
# key = "Tarjan",
|
112
|
+
# title = "Depth First Search and Linear Graph Algorithms",
|
113
|
+
# journal = "SIAM Journal on Computing",
|
114
|
+
# volume = "1",
|
115
|
+
# number = "2",
|
116
|
+
# pages = "146--160",
|
117
|
+
# month = jun,
|
118
|
+
# year = "1972",
|
119
|
+
# CODEN = "SMJCAT",
|
120
|
+
# ISSN = "0097-5397 (print), 1095-7111 (electronic)",
|
121
|
+
# bibdate = "Thu Jan 23 09:56:44 1997",
|
122
|
+
# bibsource = "Parallel/Multi.bib, Misc/Reverse.eng.bib",
|
123
|
+
# }
|
124
|
+
#
|
125
|
+
# The output of the algorithm is recorded in a TarjanSccVisitor _vis_.
|
126
|
+
# vis.comp_map will contain numbers giving the component ID assigned to
|
127
|
+
# each vertex. The number of components is vis.num_comp.
|
128
|
+
|
129
|
+
def strongly_connected_components
|
130
|
+
raise NotDirectedError,
|
131
|
+
"strong_components only works for directed graphs." unless directed?
|
132
|
+
vis = TarjanSccVisitor.new(self)
|
133
|
+
depth_first_search(vis) { |v| }
|
134
|
+
vis
|
135
|
+
end
|
136
|
+
|
137
|
+
end # module Graph
|
138
|
+
end # module RGL
|
data/lib/rgl/dot.rb
CHANGED
@@ -1,54 +1,61 @@
|
|
1
|
+
# dot.rb
|
1
2
|
#
|
2
|
-
# $Id: dot.rb,v 1.
|
3
|
+
# $Id: dot.rb,v 1.5 2005/02/04 22:41:46 monora Exp $
|
3
4
|
#
|
4
|
-
# Minimal Dot support based on Dave Thomas dot module (included in
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# Minimal Dot support, based on Dave Thomas's dot module (included in rdoc).
|
6
|
+
# rdot.rb is a modified version which also contains support for undirected
|
7
|
+
# graphs.
|
7
8
|
|
8
9
|
require 'rgl/rdot'
|
9
10
|
|
10
11
|
module RGL
|
12
|
+
|
11
13
|
module Graph
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
14
|
+
|
15
|
+
# Return a DOT::DOTDigraph for directed graphs or a DOT::DOTSubgraph for an
|
16
|
+
# undirected Graph. _params_ can contain any graph property specified in
|
17
|
+
# rdot.rb.
|
18
|
+
|
19
|
+
def to_dot_graph (params = {})
|
20
|
+
params['name'] ||= self.class.name.gsub(/:/,'_')
|
21
|
+
fontsize = params['fontsize'] ? params['fontsize'] : '8'
|
22
|
+
graph = (directed? ? DOT::DOTDigraph : DOT::DOTSubgraph).new(params)
|
23
|
+
edge_class = directed? ? DOT::DOTDirectedEdge : DOT::DOTEdge
|
24
|
+
each_vertex do |v|
|
25
|
+
name = v.to_s
|
26
|
+
graph << DOT::DOTNode.new('name' => '"' + name + '"',
|
27
|
+
'fontsize' => fontsize,
|
28
|
+
'label' => name)
|
29
|
+
end
|
30
|
+
each_edge do |u,v|
|
31
|
+
graph << edge_class.new('from' => '"'+ u.to_s + '"',
|
32
|
+
'to' => '"'+ v.to_s + '"',
|
33
|
+
'fontsize' => fontsize)
|
34
|
+
end
|
35
|
+
graph
|
36
|
+
end
|
37
|
+
|
38
|
+
# Output the DOT-graph to stream _s_.
|
39
|
+
|
40
|
+
def print_dotted_on (params = {}, s = $stdout)
|
41
|
+
s << to_dot_graph(params).to_s << "\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Call +dotty+ for the graph which is written to the file 'graph.dot'
|
45
|
+
# in the # current directory.
|
46
|
+
|
47
|
+
def dotty (params = {})
|
48
|
+
dotfile = "graph.dot"
|
49
|
+
File.open(dotfile, "w") {|f|
|
50
|
+
print_dotted_on(params, f)
|
51
|
+
}
|
52
|
+
system("dotty", dotfile)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Use +do+ to create a graphical representation of the graph. Returns the
|
56
|
+
# filename of the graphics file.
|
57
|
+
|
58
|
+
def write_to_graphic_file (fmt='png', dotfile="graph")
|
52
59
|
src = dotfile + ".dot"
|
53
60
|
dot = dotfile + "." + fmt
|
54
61
|
|
@@ -59,5 +66,6 @@ module RGL
|
|
59
66
|
system( "dot -T#{fmt} #{src} -o #{dot}" )
|
60
67
|
dot
|
61
68
|
end
|
62
|
-
|
63
|
-
end
|
69
|
+
|
70
|
+
end # module Graph
|
71
|
+
end # module RGL
|
data/lib/rgl/graphxml.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# graphxml.rb
|
2
|
+
#
|
1
3
|
# This file contains minimal support for creating RGL graphs from the GraphML
|
2
|
-
# format (see http://www.graphdrawing.org/graphml).
|
4
|
+
# format (see http://www.graphdrawing.org/graphml). The main purpose is to
|
3
5
|
# have a rich set of example graphs to have some more tests.
|
4
6
|
#
|
5
|
-
# See the examples directory which contains a subdirectory _north_ with the
|
6
|
-
# Graph catalog GraphViz (see
|
7
|
+
# See the examples directory, which contains a subdirectory _north_ with the
|
8
|
+
# Graph catalog GraphViz (see
|
9
|
+
# http://www.research.att.com/sw/tools/graphviz/refs.html).
|
7
10
|
#
|
8
11
|
# We use REXML::StreamListener from the REXML library
|
9
12
|
# (http://www.germane-software.com/software/rexml) to parse the grapml files.
|
@@ -13,40 +16,48 @@ require 'rexml/document'
|
|
13
16
|
require 'rexml/streamlistener'
|
14
17
|
|
15
18
|
module RGL
|
16
|
-
|
19
|
+
|
20
|
+
# Module GraphXML adds to each class, including module MutableGraph, a class
|
17
21
|
# method from_graphxml.
|
18
22
|
#
|
19
|
-
# Attention: Because append_features is used to provide the
|
20
|
-
#
|
21
|
-
#
|
23
|
+
# Attention: Because append_features is used to provide the functionality,
|
24
|
+
# GraphXML must be loaded before the concrete class including MutableGraph
|
25
|
+
# is loaded.
|
26
|
+
|
22
27
|
module GraphXML
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
28
|
+
|
29
|
+
class MutableGraphParser
|
30
|
+
|
31
|
+
include REXML::StreamListener
|
32
|
+
|
33
|
+
attr_reader :graph
|
34
|
+
|
35
|
+
def initialize (graph)
|
36
|
+
@graph = graph
|
37
|
+
end
|
38
|
+
|
39
|
+
def tag_start (name, attrs)
|
40
|
+
case name
|
41
|
+
when 'edge'
|
42
|
+
@graph.add_edge(attrs['source'], attrs['target'])
|
43
|
+
when 'node'
|
44
|
+
@graph.add_vertex(attrs['id'])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end # class MutableGraphParser
|
49
|
+
|
50
|
+
def MutableGraph.append_features (includingClass)
|
51
|
+
super
|
52
|
+
|
53
|
+
# Create a new MutableGraph from the XML-Source _source_.
|
54
|
+
|
55
|
+
def includingClass.from_graphxml (source)
|
56
|
+
listener = MutableGraphParser.new(self.new)
|
57
|
+
REXML::Document.parse_stream(source, listener)
|
58
|
+
listener.graph
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end # module GraphXML
|
63
|
+
end # module RGL
|