rgl 0.2.2

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.
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