rgl 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/ChangeLog +74 -0
  2. data/Makefile +72 -0
  3. data/README +240 -0
  4. data/Rakefile +210 -0
  5. data/TAGS +209 -0
  6. data/examples/canvas.rb +103 -0
  7. data/examples/codegraph +238 -0
  8. data/examples/example.jpg +0 -0
  9. data/examples/examples.rb +112 -0
  10. data/examples/graph.dot +54 -0
  11. data/examples/graph.png +0 -0
  12. data/examples/module_graph.jpg +0 -0
  13. data/examples/north.rb +12 -0
  14. data/examples/north/Graph.log +128 -0
  15. data/examples/north/g.10.0.graphml +28 -0
  16. data/examples/north/g.10.1.graphml +28 -0
  17. data/examples/north/g.10.11.graphml +31 -0
  18. data/examples/north/g.10.12.graphml +27 -0
  19. data/examples/north/g.10.13.graphml +27 -0
  20. data/examples/north/g.10.14.graphml +27 -0
  21. data/examples/north/g.10.15.graphml +26 -0
  22. data/examples/north/g.10.16.graphml +26 -0
  23. data/examples/north/g.10.17.graphml +26 -0
  24. data/examples/north/g.10.19.graphml +37 -0
  25. data/examples/north/g.10.2.graphml +28 -0
  26. data/examples/north/g.10.20.graphml +38 -0
  27. data/examples/north/g.10.22.graphml +43 -0
  28. data/examples/north/g.10.24.graphml +30 -0
  29. data/examples/north/g.10.25.graphml +45 -0
  30. data/examples/north/g.10.27.graphml +38 -0
  31. data/examples/north/g.10.28.graphml +30 -0
  32. data/examples/north/g.10.29.graphml +38 -0
  33. data/examples/north/g.10.3.graphml +26 -0
  34. data/examples/north/g.10.30.graphml +34 -0
  35. data/examples/north/g.10.31.graphml +42 -0
  36. data/examples/north/g.10.34.graphml +42 -0
  37. data/examples/north/g.10.37.graphml +28 -0
  38. data/examples/north/g.10.38.graphml +38 -0
  39. data/examples/north/g.10.39.graphml +36 -0
  40. data/examples/north/g.10.4.graphml +26 -0
  41. data/examples/north/g.10.40.graphml +37 -0
  42. data/examples/north/g.10.41.graphml +37 -0
  43. data/examples/north/g.10.42.graphml +26 -0
  44. data/examples/north/g.10.45.graphml +28 -0
  45. data/examples/north/g.10.46.graphml +32 -0
  46. data/examples/north/g.10.5.graphml +31 -0
  47. data/examples/north/g.10.50.graphml +30 -0
  48. data/examples/north/g.10.56.graphml +29 -0
  49. data/examples/north/g.10.57.graphml +32 -0
  50. data/examples/north/g.10.58.graphml +32 -0
  51. data/examples/north/g.10.6.graphml +26 -0
  52. data/examples/north/g.10.60.graphml +32 -0
  53. data/examples/north/g.10.61.graphml +34 -0
  54. data/examples/north/g.10.62.graphml +34 -0
  55. data/examples/north/g.10.68.graphml +30 -0
  56. data/examples/north/g.10.69.graphml +32 -0
  57. data/examples/north/g.10.7.graphml +29 -0
  58. data/examples/north/g.10.70.graphml +26 -0
  59. data/examples/north/g.10.71.graphml +27 -0
  60. data/examples/north/g.10.72.graphml +28 -0
  61. data/examples/north/g.10.74.graphml +29 -0
  62. data/examples/north/g.10.75.graphml +29 -0
  63. data/examples/north/g.10.78.graphml +27 -0
  64. data/examples/north/g.10.79.graphml +34 -0
  65. data/examples/north/g.10.8.graphml +29 -0
  66. data/examples/north/g.10.80.graphml +34 -0
  67. data/examples/north/g.10.82.graphml +35 -0
  68. data/examples/north/g.10.83.graphml +32 -0
  69. data/examples/north/g.10.85.graphml +34 -0
  70. data/examples/north/g.10.86.graphml +34 -0
  71. data/examples/north/g.10.88.graphml +37 -0
  72. data/examples/north/g.10.89.graphml +29 -0
  73. data/examples/north/g.10.9.graphml +26 -0
  74. data/examples/north/g.10.90.graphml +32 -0
  75. data/examples/north/g.10.91.graphml +31 -0
  76. data/examples/north/g.10.92.graphml +26 -0
  77. data/examples/north/g.10.93.graphml +32 -0
  78. data/examples/north/g.10.94.graphml +34 -0
  79. data/examples/north/g.12.8.graphml +40 -0
  80. data/examples/north/g.14.9.graphml +36 -0
  81. data/examples/north2.rb +21 -0
  82. data/examples/rdep-rgl.rb +395 -0
  83. data/install.rb +49 -0
  84. data/lib/rgl/adjacency.rb +151 -0
  85. data/lib/rgl/base.rb +299 -0
  86. data/lib/rgl/connected_components.rb +125 -0
  87. data/lib/rgl/dot.rb +63 -0
  88. data/lib/rgl/graphxml.rb +52 -0
  89. data/lib/rgl/implicit.rb +151 -0
  90. data/lib/rgl/mutable.rb +54 -0
  91. data/lib/rgl/rdot.rb +264 -0
  92. data/lib/rgl/topsort.rb +61 -0
  93. data/lib/rgl/transitiv_closure.rb +34 -0
  94. data/lib/rgl/traversal.rb +296 -0
  95. data/tests/TestComponents.rb +67 -0
  96. data/tests/TestDirectedGraph.rb +100 -0
  97. data/tests/TestEdge.rb +33 -0
  98. data/tests/TestGraphXML.rb +57 -0
  99. data/tests/TestImplicit.rb +52 -0
  100. data/tests/TestTransitiveClosure.rb +29 -0
  101. data/tests/TestTraversal.rb +222 -0
  102. data/tests/TestUnDirectedGraph.rb +98 -0
  103. metadata +163 -0
@@ -0,0 +1,125 @@
1
+ # This file contains the algorithms for the connected components of an
2
+ # undirected graph (each_connected_component) and strongly connected components
3
+ # for directed graphs (strongly_connected_components).
4
+ #
5
+ require 'rgl/traversal'
6
+
7
+ module RGL
8
+ 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
67
+ w = @stack.pop
68
+ @comp_map[w] = @c_index
69
+ end until w == v
70
+ @c_index += 1
71
+ 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
@@ -0,0 +1,63 @@
1
+ #
2
+ # $Id: dot.rb,v 1.4 2002/11/13 21:53:27 monora Exp $
3
+ #
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.
7
+
8
+ require 'rgl/rdot'
9
+
10
+ module RGL
11
+ 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")
52
+ src = dotfile + ".dot"
53
+ dot = dotfile + "." + fmt
54
+
55
+ File.open(src, 'w') do |f|
56
+ f << self.to_dot_graph.to_s << "\n"
57
+ end
58
+
59
+ system( "dot -T#{fmt} #{src} -o #{dot}" )
60
+ dot
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,52 @@
1
+ # 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
3
+ # have a rich set of example graphs to have some more tests.
4
+ #
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
+ #
8
+ # We use REXML::StreamListener from the REXML library
9
+ # (http://www.germane-software.com/software/rexml) to parse the grapml files.
10
+
11
+ require 'rgl/mutable'
12
+ require 'rexml/document'
13
+ require 'rexml/streamlistener'
14
+
15
+ module RGL
16
+ # Module GraphXML adds to each class including module MutableGraph a class
17
+ # method from_graphxml.
18
+ #
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.
22
+ 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
@@ -0,0 +1,151 @@
1
+ # This file contains the definition of the class RGL::ImplicitGraph which
2
+ # defines vertex and edge iterators using blocks (which again call blocks).
3
+ #
4
+ # An ImplicitGraph provides a handy way to define graphs on the fly using two
5
+ # blocks for the two iterators defining a graph. The directed cyclic graph with
6
+ # 5 vertices can be created as follows:
7
+ #
8
+ # g = RGL::ImplicitGraph.new { |g|
9
+ # g.vertex_iterator { |b| 0.upto(4,&b) }
10
+ # g.adjacent_iterator { |x, b| b.call((x+1)%5) }
11
+ # g.directed = true
12
+ # }
13
+ # g.to_s => "(0-1)(1-2)(2-3)(3-4)(4-0)"
14
+ #
15
+ # Other examples are given by the methods vertices_filtered_by and
16
+ # edges_filtered_by, which can be applied to any graph.
17
+ require 'rgl/base'
18
+
19
+ module RGL
20
+ class ImplicitGraph
21
+ include Graph
22
+
23
+ attr_writer :directed
24
+
25
+ EMPTY_VERTEX_ITERATOR = proc { |b| }
26
+ EMPTY_NEIGHBOR_ITERATOR = proc { |x, b| }
27
+
28
+ # Create a new ImplicitGraph, which is by default empty. The caller should
29
+ # configure the with vertex and neighbor iterators. If the graph is directed
30
+ # the client should set directed to true. The default value for _directed_
31
+ # is false.
32
+ def initialize
33
+ @directed = false
34
+ @vertex_iterator = EMPTY_VERTEX_ITERATOR
35
+ @adjacent_iterator = EMPTY_NEIGHBOR_ITERATOR
36
+ yield self if block_given? # let client overwrite defaults
37
+ end
38
+
39
+ # Returns the value of @directed.
40
+ def directed?; @directed; end
41
+
42
+ def each_vertex (&block) # :nodoc:
43
+ @vertex_iterator.call(block)
44
+ end
45
+
46
+ def each_adjacent (v, &block) # :nodoc:
47
+ @adjacent_iterator.call(v,block)
48
+ end
49
+
50
+ def each_edge (&block) # :nodoc:
51
+ if defined? @edge_iterator
52
+ @edge_iterator.call(block)
53
+ else
54
+ super # use default implementation
55
+ end
56
+ end
57
+
58
+ # Sets the vertex_iterator to _block_ which must be a block of one parameter
59
+ # which again is the block called by each_vertex.
60
+ def vertex_iterator (&block)
61
+ @vertex_iterator = block
62
+ end
63
+
64
+ # Sets the adjacent_iterator to _block_ which must be a block of two
65
+ # parameters:
66
+ # The first parameter is the vertex the neighbors of which are to be
67
+ # traversed. The second is the block which will be called for each neighbor
68
+ # of this vertex.
69
+ def adjacent_iterator (&block)
70
+ @adjacent_iterator = block
71
+ end
72
+
73
+ # Sets the edge_iterator to _block_ which must be a block of two parameters:
74
+ # The first parameter is the source of the edges an the second is the target
75
+ # of the edge.
76
+ def edge_iterator (&block)
77
+ @edge_iterator = block
78
+ end
79
+ end
80
+
81
+ module Graph
82
+ # ---
83
+ # === Graph adaptors
84
+ #
85
+ # Return a new ImplicitGraph which has as vertices all vertices of the
86
+ # receiver which satisfy the predicate _filter_.
87
+ #
88
+ # The methods provides similar functionaty as the BGL graph adapter
89
+ # filtered_graph (see BOOST_DOC/filtered_graph.html).
90
+ #
91
+ # ==== Example
92
+ #
93
+ # def complete (n)
94
+ # set = n.integer? ? (1..n) : n
95
+ # RGL::ImplicitGraph.new { |g|
96
+ # g.vertex_iterator { |b| set.each(&b) }
97
+ # g.adjacent_iterator { |x, b|
98
+ # set.each { |y| b.call(y) unless x == y }
99
+ # }
100
+ # }
101
+ # end
102
+ #
103
+ # complete(4).to_s => "(1=2)(1=3)(1=4)(2=3)(2=4)(3=4)"
104
+ # complete(4).vertices_filtered_by {|v| v != 4}.to_s => "(1=2)(1=3)(2=3)"
105
+ def vertices_filtered_by (&filter)
106
+ implicit_graph {|g|
107
+ g.vertex_iterator { |b|
108
+ self.each_vertex { |v| b.call(v) if filter.call(v) }
109
+ }
110
+ g.adjacent_iterator { |v, b|
111
+ self.each_adjacent(v) { |u| b.call(u) if filter.call(u) }
112
+ }
113
+ }
114
+ end
115
+
116
+ # Return a new ImplicitGraph which has as edges all edges of the receiver
117
+ # which satisfy the predicate _filter_ (a block with to parameters).
118
+ #
119
+ # ==== Example
120
+ #
121
+ # g = complete(7).edges_filtered_by {|u,v| u+v == 7}
122
+ # g.to_s => "(1=6)(2=5)(3=4)"
123
+ # g.vertices => [1, 2, 3, 4, 5, 6, 7]
124
+ def edges_filtered_by (&filter)
125
+ implicit_graph {
126
+ |g|
127
+ g.adjacent_iterator { |v, b|
128
+ self.each_adjacent(v) { |u|
129
+ b.call(u) if filter.call(v,u)
130
+ }
131
+ }
132
+ g.edge_iterator { |b|
133
+ self.each_edge {|u,v| b.call(u,v) if filter.call(u,v)}
134
+ }
135
+ }
136
+ end
137
+
138
+ # Return a new ImplicitGraph which is isomorphic (i.e. has same edges and
139
+ # vertices) to the receiver. It is a shortcut also used by
140
+ # edges_filtered_by and vertices_filtered_by.
141
+ def implicit_graph
142
+ result = ImplicitGraph.new {|g|
143
+ g.vertex_iterator { |b| self.each_vertex(&b)}
144
+ g.adjacent_iterator { |v, b| self.each_adjacent(v,&b)}
145
+ g.directed = self.directed?
146
+ }
147
+ yield result if block_given? # let client overwrite defaults
148
+ result
149
+ end
150
+ end # module Graph
151
+ end
@@ -0,0 +1,54 @@
1
+ require 'rgl/base'
2
+
3
+ module RGL
4
+ # A MutableGraph can be changed via the addition or removal of edges and
5
+ # vertices.
6
+ module MutableGraph
7
+ include Graph
8
+
9
+ # Add a new vertex _v_ to the graph. If the vertex is already in (using
10
+ # eql?) the method does nothing.
11
+ def add_vertex(v); raise NotImplementedError; end
12
+
13
+ # Inserts the edge (u,v) into the graph.
14
+ #
15
+ # Note that for undirected graphs, (u,v) is the same edge as (v,u), so after
16
+ # a call to the function add_edge(), this implies that edge (u,v) will
17
+ # appear in the out-edges of u and (u,v) (or equivalently (v,u)) will appear
18
+ # in the out-edges of
19
+ # v. Put another way, v will be adjacent to u and u will be adjacent to v.
20
+ def add_edge(u, v); raise NotImplementedError; end
21
+
22
+ # Add all objects in _a_ to the vertex set.
23
+ def add_vertices (*a)
24
+ a.each {|v| add_vertex v}
25
+ end
26
+
27
+ # Add all edges in the _edges_ array to the edge set. Element of the array
28
+ # can be both two element arrays or instances of DirectedEdge or
29
+ # UnDirectedEdge.
30
+ def add_edges (*edges)
31
+ edges.each {|edge| add_edge(edge[0],edge[1])}
32
+ end
33
+
34
+ # Remove u from the vertex set of the graph. All edges who's target is _v_
35
+ # are also removed from the edge set of the graph.
36
+ #
37
+ # Postcondition: num_vertices is one less, _v_ no longer appears in the
38
+ # vertex set of the graph and there no edge with source or target _v_.
39
+ def remove_vertex(v); raise NotImplementedError; end
40
+
41
+ # Remove the edge (u,v) from the graph. If the graph allows parallel edges
42
+ # this remove all occurrences of (u,v).
43
+ #
44
+ # Precondition: u and v are vertices in the graph.
45
+ # Postcondition: (u,v) is no longer in the edge set for g.
46
+ def remove_edge(u, v); raise NotImplementedError; end
47
+
48
+ # Remove all vertices specified by the array a from the graph calling
49
+ # remove_vertex.
50
+ def remove_vertices (*a)
51
+ a.each {|v| remove_vertex v}
52
+ end
53
+ end
54
+ end