rgl 0.2.2 → 0.2.3

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