rgl 0.4.0 → 0.5.0
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.
- 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
|