rgl 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/enumerable_ext.rb
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
module Enumerable
|
2
|
+
|
2
3
|
# Fixnum()
|
3
4
|
#
|
4
5
|
# Return the number of elements of the Enumerable. Same as _size_ but not all
|
5
6
|
# Enumerables implement size.
|
6
7
|
#--
|
7
8
|
# Should we call the methods _size_?
|
9
|
+
#
|
8
10
|
def length
|
9
|
-
inject(0) do |sum,v|
|
11
|
+
inject(0) do |sum, v|
|
10
12
|
sum + 1
|
11
13
|
end
|
12
14
|
end
|
15
|
+
|
13
16
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'stream'
|
2
|
+
|
3
|
+
require 'rgl/graph_wrapper'
|
4
|
+
|
5
|
+
module RGL
|
6
|
+
|
7
|
+
# A GraphIterator is the abstract basis for all Iterators on graphs.
|
8
|
+
# Each graph iterator should implement the protocol defined in module Stream.
|
9
|
+
#
|
10
|
+
module GraphIterator
|
11
|
+
include Stream
|
12
|
+
include GraphWrapper
|
13
|
+
end
|
14
|
+
|
15
|
+
end # RGL
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'rgl/graph_wrapper'
|
2
|
+
|
3
|
+
module RGL
|
4
|
+
|
5
|
+
# Module GraphVisitor defines the BGL
|
6
|
+
# BFS[http://www.boost.org/libs/graph/doc/BFSVisitor.html] Visitor Concept).
|
7
|
+
#
|
8
|
+
# Visitors provide a mechanism for extending an algorithm (i.e., for
|
9
|
+
# customizing what is done at each step of the algorithm). They allow users
|
10
|
+
# to insert their own operations at various steps within a graph algorithm.
|
11
|
+
#
|
12
|
+
# Graph algorithms typically have multiple event points where one may want to
|
13
|
+
# insert a call-back. Therefore, visitors have several methods that
|
14
|
+
# correspond to the various event points. Each algorithm has a different
|
15
|
+
# set of event points. The following are common to both DFS and BFS search.
|
16
|
+
#
|
17
|
+
# * examine_vertex
|
18
|
+
# * examine_edge
|
19
|
+
# * tree_edge
|
20
|
+
# * back_edge
|
21
|
+
# * forward_edge
|
22
|
+
# * finish_vertex
|
23
|
+
#
|
24
|
+
# These methods are all called handle_* and can be set to appropriate blocks,
|
25
|
+
# using the methods set_*_event_handler, which are defined for each event
|
26
|
+
# mentioned above.
|
27
|
+
#
|
28
|
+
# As an alternative, you can also override the handle_* methods in a
|
29
|
+
# subclass, to configure the algorithm (as an example, see TarjanSccVisitor).
|
30
|
+
#
|
31
|
+
# During a graph traversal, vertices are *colored* using the colors :GRAY
|
32
|
+
# (when waiting) and :BLACK when finished. All other vertices are :WHITE.
|
33
|
+
# The color_map is also maintained in the visitor.
|
34
|
+
#
|
35
|
+
module GraphVisitor
|
36
|
+
|
37
|
+
include GraphWrapper
|
38
|
+
|
39
|
+
attr_reader :color_map
|
40
|
+
|
41
|
+
# Create a new GraphVisitor on _graph_.
|
42
|
+
#
|
43
|
+
def initialize(graph)
|
44
|
+
super(graph)
|
45
|
+
reset
|
46
|
+
end
|
47
|
+
|
48
|
+
# Mark each vertex unvisited (i.e. :WHITE)
|
49
|
+
#
|
50
|
+
def reset
|
51
|
+
@color_map = Hash.new(:WHITE)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns true if vertex _v_ is colored :BLACK (i.e. finished).
|
55
|
+
#
|
56
|
+
def finished_vertex?(v)
|
57
|
+
@color_map[v] == :BLACK
|
58
|
+
end
|
59
|
+
|
60
|
+
# Shall we follow the edge (u,v); i.e. v has color :WHITE
|
61
|
+
#
|
62
|
+
def follow_edge?(u, v) # :nodoc:
|
63
|
+
@color_map[v] == :WHITE
|
64
|
+
end
|
65
|
+
|
66
|
+
# Attach a map to the visitor which records the distance of a visited
|
67
|
+
# vertex to the start vertex.
|
68
|
+
#
|
69
|
+
# This is similar to BGLs
|
70
|
+
# distance_recorder[http://www.boost.org/libs/graph/doc/distance_recorder.html].
|
71
|
+
#
|
72
|
+
# After the distance_map is attached, the visitor has a new method
|
73
|
+
# distance_to_root, which answers the distance to the start vertex.
|
74
|
+
#
|
75
|
+
def attach_distance_map(map = Hash.new(0))
|
76
|
+
@distance_map = map
|
77
|
+
|
78
|
+
# add distance map support to the current visitor instance
|
79
|
+
extend(DistanceMapSupport)
|
80
|
+
end
|
81
|
+
|
82
|
+
module DistanceMapSupport
|
83
|
+
|
84
|
+
def handle_tree_edge(u, v)
|
85
|
+
super
|
86
|
+
@distance_map[v] = @distance_map[u] + 1
|
87
|
+
end
|
88
|
+
|
89
|
+
# Answer the distance to the start vertex.
|
90
|
+
|
91
|
+
def distance_to_root(v)
|
92
|
+
@distance_map[v]
|
93
|
+
end
|
94
|
+
|
95
|
+
end # module DistanceMapSupport
|
96
|
+
|
97
|
+
module ClassMethods
|
98
|
+
|
99
|
+
# Defines an event handler.
|
100
|
+
#
|
101
|
+
def def_event_handlers(*events)
|
102
|
+
events.each do |event|
|
103
|
+
params = event.to_s.include?('edge') ? 'u, v' : 'u'
|
104
|
+
|
105
|
+
handler = "@#{event}_event_handler"
|
106
|
+
|
107
|
+
class_eval <<-END
|
108
|
+
def handle_#{event}(#{params})
|
109
|
+
#{handler}.call(#{params}) if defined? #{handler}
|
110
|
+
end
|
111
|
+
|
112
|
+
def set_#{event}_event_handler(&block)
|
113
|
+
#{handler} = block
|
114
|
+
end
|
115
|
+
END
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
alias def_event_handler def_event_handlers
|
120
|
+
|
121
|
+
end # module ClassMethods
|
122
|
+
|
123
|
+
extend ClassMethods # add class methods to GraphVisitor class itself
|
124
|
+
|
125
|
+
def self.included(base)
|
126
|
+
base.extend ClassMethods # when GraphVisitor is included into a class/module, add class methods as well
|
127
|
+
end
|
128
|
+
|
129
|
+
def_event_handlers :examine_vertex,
|
130
|
+
:examine_edge,
|
131
|
+
:tree_edge,
|
132
|
+
:back_edge,
|
133
|
+
:forward_edge,
|
134
|
+
:finish_vertex
|
135
|
+
|
136
|
+
end # module GraphVisitor
|
137
|
+
|
138
|
+
end # module RGL
|
data/lib/rgl/graphxml.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# graphxml.rb
|
2
2
|
#
|
3
3
|
# This file contains minimal support for creating RGL graphs from the GraphML
|
4
|
-
# format (see http://www.graphdrawing.org/graphml).
|
4
|
+
# format (see http://www.graphdrawing.org/graphml). The main purpose is to
|
5
5
|
# have a rich set of example graphs to have some more tests.
|
6
6
|
#
|
7
7
|
# See the examples directory, which contains a subdirectory _north_ with the
|
@@ -16,36 +16,46 @@ require 'rexml/document'
|
|
16
16
|
require 'rexml/streamlistener'
|
17
17
|
|
18
18
|
module RGL
|
19
|
+
|
19
20
|
module MutableGraph
|
21
|
+
|
20
22
|
# Used to parse a subset of GraphML into an RGL graph implementation.
|
23
|
+
#
|
21
24
|
class MutableGraphParser
|
25
|
+
|
22
26
|
include REXML::StreamListener
|
23
27
|
|
24
28
|
# First resets +graph+ to be empty and stores a reference for use with
|
25
29
|
# #tag_start.
|
26
|
-
|
30
|
+
#
|
31
|
+
def initialize(graph)
|
27
32
|
@graph = graph
|
28
33
|
@graph.remove_vertices(@graph.vertices)
|
29
34
|
end
|
30
35
|
|
31
36
|
# Processes incoming edge and node elements from GraphML in order to
|
32
37
|
# populate the graph given to #new.
|
33
|
-
|
38
|
+
#
|
39
|
+
def tag_start(name, attrs)
|
34
40
|
case name
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
when 'edge'
|
42
|
+
@graph.add_edge(attrs['source'], attrs['target'])
|
43
|
+
when 'node'
|
44
|
+
@graph.add_vertex(attrs['id'])
|
39
45
|
end
|
40
46
|
end
|
41
|
-
|
47
|
+
|
48
|
+
end # class MutableGraphParser
|
42
49
|
|
43
50
|
# Initializes an RGL graph from a subset of the GraphML format given in
|
44
51
|
# +source+ (see http://www.graphdrawing.org/graphml).
|
52
|
+
#
|
45
53
|
def from_graphxml(source)
|
46
54
|
listener = MutableGraphParser.new(self)
|
47
55
|
REXML::Document.parse_stream(source, listener)
|
48
56
|
self
|
49
57
|
end
|
50
|
-
|
51
|
-
end
|
58
|
+
|
59
|
+
end # module MutableGraph
|
60
|
+
|
61
|
+
end # module RGL
|
data/lib/rgl/implicit.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
|
-
# implicit.rb
|
1
|
+
# implicit.rb
|
2
2
|
#
|
3
3
|
# This file contains the definition of the class RGL::ImplicitGraph, which
|
4
4
|
# defines vertex and edge iterators using blocks (which again call blocks).
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# An ImplicitGraph provides a handy way to define graphs on the fly, using two
|
7
|
-
# blocks for the two iterators defining a graph.
|
7
|
+
# blocks for the two iterators defining a graph. A directed cyclic graph,
|
8
8
|
# with five vertices can be created as follows:
|
9
9
|
#
|
10
|
-
# g = RGL::ImplicitGraph.new
|
10
|
+
# g = RGL::ImplicitGraph.new do |g|
|
11
11
|
# g.vertex_iterator { |b| 0.upto(4,&b) }
|
12
12
|
# g.adjacent_iterator { |x, b| b.call((x+1)%5) }
|
13
13
|
# g.directed = true
|
14
|
-
#
|
14
|
+
# end
|
15
15
|
#
|
16
|
-
# g.to_s => "(0-1)(1-2)(2-3)(3-4)(4-0)"
|
16
|
+
# g.to_s # => "(0-1)(1-2)(2-3)(3-4)(4-0)"
|
17
17
|
#
|
18
18
|
# Other examples are given by the methods vertices_filtered_by and
|
19
19
|
# edges_filtered_by, which can be applied to any graph.
|
@@ -28,110 +28,109 @@ module RGL
|
|
28
28
|
|
29
29
|
attr_writer :directed
|
30
30
|
|
31
|
-
EMPTY_VERTEX_ITERATOR = proc { |b|
|
31
|
+
EMPTY_VERTEX_ITERATOR = proc { |b| }
|
32
32
|
EMPTY_NEIGHBOR_ITERATOR = proc { |x, b| }
|
33
|
-
|
34
|
-
# Create a new ImplicitGraph, which is empty by default. The caller should
|
35
|
-
# configure the graph using vertex and neighbor iterators. If the graph is
|
36
|
-
# directed, the client should set _directed_ to true. The default value
|
37
|
-
# for _directed_ is false.
|
38
33
|
|
34
|
+
# Create a new ImplicitGraph, which is empty by default. The caller should
|
35
|
+
# configure the graph using vertex and neighbor iterators. If the graph is
|
36
|
+
# directed, the client should set _directed_ to true. The default value
|
37
|
+
# for _directed_ is false.
|
38
|
+
#
|
39
39
|
def initialize
|
40
|
-
@directed
|
40
|
+
@directed = false
|
41
41
|
@vertex_iterator = EMPTY_VERTEX_ITERATOR
|
42
42
|
@adjacent_iterator = EMPTY_NEIGHBOR_ITERATOR
|
43
|
-
yield self if block_given?
|
43
|
+
yield self if block_given? # Let client overwrite defaults.
|
44
44
|
end
|
45
45
|
|
46
46
|
# Returns the value of @directed.
|
47
|
-
|
47
|
+
#
|
48
48
|
def directed?
|
49
49
|
@directed
|
50
50
|
end
|
51
|
-
|
52
|
-
def each_vertex
|
51
|
+
|
52
|
+
def each_vertex(&block) # :nodoc:
|
53
53
|
@vertex_iterator.call(block)
|
54
54
|
end
|
55
55
|
|
56
|
-
def each_adjacent
|
56
|
+
def each_adjacent(v, &block) # :nodoc:
|
57
57
|
@adjacent_iterator.call(v, block)
|
58
58
|
end
|
59
59
|
|
60
|
-
def each_edge
|
60
|
+
def each_edge(&block) # :nodoc:
|
61
61
|
if defined? @edge_iterator
|
62
62
|
@edge_iterator.call(block)
|
63
63
|
else
|
64
|
-
super
|
64
|
+
super # use default implementation
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
68
|
# Sets the vertex_iterator to _block_,
|
69
69
|
# which must be a block of one parameter
|
70
70
|
# which again is the block called by each_vertex.
|
71
|
-
|
72
|
-
def vertex_iterator
|
71
|
+
#
|
72
|
+
def vertex_iterator(&block)
|
73
73
|
@vertex_iterator = block
|
74
74
|
end
|
75
75
|
|
76
76
|
# Sets the adjacent_iterator to _block_,
|
77
|
-
# which must be a block of two parameters:
|
77
|
+
# which must be a block of two parameters:
|
78
78
|
#
|
79
79
|
# The first parameter is the vertex the neighbors of which are to be
|
80
80
|
# traversed.
|
81
81
|
#
|
82
82
|
# The second is the block which will be called for each neighbor
|
83
83
|
# of this vertex.
|
84
|
-
|
85
|
-
def adjacent_iterator
|
84
|
+
#
|
85
|
+
def adjacent_iterator(&block)
|
86
86
|
@adjacent_iterator = block
|
87
87
|
end
|
88
88
|
|
89
89
|
# Sets the edge_iterator to _block_, which must be a block of two
|
90
90
|
# parameters: The first parameter is the source of the edges; the
|
91
91
|
# second is the target of the edge.
|
92
|
-
|
93
|
-
def edge_iterator
|
92
|
+
#
|
93
|
+
def edge_iterator(&block)
|
94
94
|
@edge_iterator = block
|
95
95
|
end
|
96
96
|
|
97
|
-
end
|
98
|
-
|
97
|
+
end # class ImplicitGraph
|
99
98
|
|
100
99
|
module Graph
|
101
100
|
|
102
|
-
# ---
|
103
101
|
# === Graph adaptors
|
104
102
|
#
|
105
103
|
# Return a new ImplicitGraph which has as vertices all vertices of the
|
106
104
|
# receiver which satisfy the predicate _filter_.
|
107
105
|
#
|
108
|
-
# The methods provides similar
|
109
|
-
# filtered_graph (see BOOST_DOC/filtered_graph.html).
|
106
|
+
# The methods provides similar functionality as the BGL graph adapter
|
107
|
+
# filtered_graph (see BOOST_DOC/filtered_graph.html).
|
110
108
|
#
|
111
109
|
# ==== Example
|
112
110
|
#
|
113
111
|
# def complete (n)
|
114
112
|
# set = n.integer? ? (1..n) : n
|
115
|
-
# RGL::ImplicitGraph.new
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
113
|
+
# RGL::ImplicitGraph.new do |g|
|
114
|
+
# g.vertex_iterator { |b| set.each(&b) }
|
115
|
+
# g.adjacent_iterator do |x, b|
|
116
|
+
# set.each { |y| b.call(y) unless x == y }
|
117
|
+
# end
|
118
|
+
# end
|
121
119
|
# end
|
122
120
|
#
|
123
|
-
# complete(4).to_s =>
|
124
|
-
# complete(4).vertices_filtered_by {|v| v != 4}.to_s => "(1=2)(1=3)(2=3)"
|
125
|
-
|
126
|
-
def vertices_filtered_by
|
127
|
-
implicit_graph
|
128
|
-
g.vertex_iterator
|
121
|
+
# complete(4).to_s # => "(1=2)(1=3)(1=4)(2=3)(2=4)(3=4)"
|
122
|
+
# complete(4).vertices_filtered_by { |v| v != 4 }.to_s # => "(1=2)(1=3)(2=3)"
|
123
|
+
#
|
124
|
+
def vertices_filtered_by(&filter)
|
125
|
+
implicit_graph do |g|
|
126
|
+
g.vertex_iterator do |b|
|
129
127
|
self.each_vertex { |v| b.call(v) if filter.call(v) }
|
130
|
-
|
131
|
-
|
128
|
+
end
|
129
|
+
|
130
|
+
g.adjacent_iterator do |v, b|
|
132
131
|
self.each_adjacent(v) { |u| b.call(u) if filter.call(u) }
|
133
|
-
|
134
|
-
|
132
|
+
end
|
133
|
+
end
|
135
134
|
end
|
136
135
|
|
137
136
|
# Return a new ImplicitGraph which has as edges all edges of the receiver
|
@@ -139,36 +138,39 @@ module RGL
|
|
139
138
|
#
|
140
139
|
# ==== Example
|
141
140
|
#
|
142
|
-
# g = complete(7).edges_filtered_by {|u,v| u+v == 7}
|
141
|
+
# g = complete(7).edges_filtered_by { |u,v| u+v == 7 }
|
143
142
|
# g.to_s => "(1=6)(2=5)(3=4)"
|
144
143
|
# g.vertices => [1, 2, 3, 4, 5, 6, 7]
|
145
|
-
|
146
|
-
def edges_filtered_by
|
147
|
-
implicit_graph
|
148
|
-
g.adjacent_iterator
|
149
|
-
self.each_adjacent(v)
|
144
|
+
#
|
145
|
+
def edges_filtered_by(&filter)
|
146
|
+
implicit_graph do |g|
|
147
|
+
g.adjacent_iterator do |v, b|
|
148
|
+
self.each_adjacent(v) do |u|
|
150
149
|
b.call(u) if filter.call(v, u)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
g.edge_iterator do |b|
|
154
|
+
self.each_edge { |u, v| b.call(u, v) if filter.call(u, v) }
|
155
|
+
end
|
156
|
+
end
|
157
157
|
end
|
158
158
|
|
159
159
|
# Return a new ImplicitGraph which is isomorphic (i.e. has same edges and
|
160
|
-
# vertices) to the receiver.
|
160
|
+
# vertices) to the receiver. It is a shortcut, also used by
|
161
161
|
# edges_filtered_by and vertices_filtered_by.
|
162
|
-
|
162
|
+
#
|
163
163
|
def implicit_graph
|
164
|
-
result = ImplicitGraph.new
|
164
|
+
result = ImplicitGraph.new do |g|
|
165
165
|
g.vertex_iterator { |b| self.each_vertex(&b) }
|
166
166
|
g.adjacent_iterator { |v, b| self.each_adjacent(v, &b) }
|
167
167
|
g.directed = self.directed?
|
168
|
-
|
168
|
+
end
|
169
|
+
|
169
170
|
yield result if block_given? # let client overwrite defaults
|
170
171
|
result
|
171
172
|
end
|
172
173
|
|
173
|
-
end
|
174
|
-
|
174
|
+
end # module Graph
|
175
|
+
|
176
|
+
end # module RGL
|