rgl 0.5.1 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/ChangeLog +72 -16
- data/Gemfile +4 -3
- data/README.md +267 -0
- data/Rakefile +15 -30
- data/examples/examples.rb +11 -9
- data/examples/unix.dot +53 -0
- data/lib/rgl/adjacency.rb +2 -2
- data/lib/rgl/base.rb +4 -3
- data/lib/rgl/bellman_ford.rb +1 -2
- data/lib/rgl/bipartite.rb +2 -2
- data/lib/rgl/dijkstra.rb +3 -29
- data/lib/rgl/dot.rb +33 -14
- data/lib/rgl/edmonds_karp.rb +4 -4
- data/lib/rgl/graph_iterator.rb +4 -0
- data/lib/rgl/graphxml.rb +2 -2
- data/lib/rgl/path.rb +17 -0
- data/lib/rgl/rdot.rb +1 -1
- data/lib/rgl/traversal.rb +8 -1
- data/test/components_test.rb +2 -2
- data/test/dijkstra_issue24_test.rb +23 -0
- data/test/dot_test.rb +54 -14
- data/test/graph_test.rb +5 -0
- data/test/graph_xml_test.rb +1 -7
- data/test/path_test.rb +52 -0
- data/test/test_helper.rb +3 -0
- data/test/traversal_test.rb +26 -0
- metadata +131 -150
- data/README.rdoc +0 -248
- data/examples/images/example.jpg +0 -0
- data/examples/images/module_graph.jpg +0 -0
- data/examples/images/rgl_modules.png +0 -0
- data/lib/rgl/enumerable_ext.rb +0 -16
data/examples/examples.rb
CHANGED
@@ -77,7 +77,7 @@ def bfs_example(g = cycle(5), start = g.detect { |x| true })
|
|
77
77
|
end
|
78
78
|
|
79
79
|
# Would like to have GraphXML here
|
80
|
-
def graph_from_dotfile
|
80
|
+
def graph_from_dotfile(file)
|
81
81
|
g = RGL::AdjacencyGraph.new
|
82
82
|
pattern = /\s*([^\"]+)[\"\s]*--[\"\s]*([^\"\[\;]+)/ # ugly but works
|
83
83
|
IO.foreach(file) { |line|
|
@@ -94,21 +94,23 @@ def graph_from_dotfile (file)
|
|
94
94
|
g
|
95
95
|
end
|
96
96
|
|
97
|
-
# ruby -Ilib
|
98
|
-
|
97
|
+
# ruby -Ilib examples/examples.rb
|
99
98
|
if $0 == __FILE__
|
100
99
|
require 'rgl/dot'
|
101
100
|
|
102
101
|
dg = RGL::DirectedAdjacencyGraph[1,2, 2,3, 2,4, 4,5, 6,4, 1,6]
|
103
|
-
dg.dotty
|
104
102
|
dg.write_to_graphic_file
|
105
|
-
bfs_example(dg, 1).dotty
|
106
|
-
bfs_example(graph_from_dotfile('dot/unix.dot'), 'Interdata').dotty({ 'label' => 'Interdata Nachfolger', 'fontsize' => 12 })
|
107
103
|
|
104
|
+
# BFS tree from 1 of dg:
|
105
|
+
bfs_example(dg, 1).write_to_graphic_file('png', 'bfs_example')
|
106
|
+
|
107
|
+
# Unix history as a graph:
|
108
|
+
g = bfs_example(graph_from_dotfile('examples/unix.dot'), 'Interdata')
|
109
|
+
g.write_to_graphic_file('png', 'unix', { 'label' => 'Interdata Nachfolger', 'fontsize' => 12 })
|
110
|
+
|
111
|
+
# Modules included by AdjacencyGraph:
|
108
112
|
g = module_graph
|
109
113
|
tree = bfs_example(module_graph, RGL::AdjacencyGraph)
|
110
114
|
g = g.vertices_filtered_by { |v| tree.has_vertex? v }
|
111
|
-
g.write_to_graphic_file
|
112
|
-
g.dotty
|
115
|
+
g.write_to_graphic_file('png', 'module_graph')
|
113
116
|
end
|
114
|
-
|
data/examples/unix.dot
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
/* courtesy Ian Darwin and Geoff Collyer, Softquad Inc. */
|
2
|
+
digraph unix {
|
3
|
+
size="6,6";
|
4
|
+
"5th Edition" -> "6th Edition";
|
5
|
+
"5th Edition" -> "PWB 1.0";
|
6
|
+
"6th Edition" -> "LSX";
|
7
|
+
"6th Edition" -> "1 BSD";
|
8
|
+
"6th Edition" -> "Mini Unix";
|
9
|
+
"6th Edition" -> "Wollongong";
|
10
|
+
"6th Edition" -> "Interdata";
|
11
|
+
"Interdata" -> "Unix/TS 3.0";
|
12
|
+
"Interdata" -> "PWB 2.0";
|
13
|
+
"Interdata" -> "7th Edition";
|
14
|
+
"7th Edition" -> "8th Edition";
|
15
|
+
"7th Edition" -> "32V";
|
16
|
+
"7th Edition" -> "V7M";
|
17
|
+
"7th Edition" -> "Ultrix-11";
|
18
|
+
"7th Edition" -> "Xenix";
|
19
|
+
"7th Edition" -> "UniPlus+";
|
20
|
+
"V7M" -> "Ultrix-11";
|
21
|
+
"8th Edition" -> "9th Edition";
|
22
|
+
"1 BSD" -> "2 BSD";
|
23
|
+
"2 BSD" -> "2.8 BSD";
|
24
|
+
"2.8 BSD" -> "Ultrix-11";
|
25
|
+
"2.8 BSD" -> "2.9 BSD";
|
26
|
+
"32V" -> "3 BSD";
|
27
|
+
"3 BSD" -> "4 BSD";
|
28
|
+
"4 BSD" -> "4.1 BSD";
|
29
|
+
"4.1 BSD" -> "4.2 BSD";
|
30
|
+
"4.1 BSD" -> "2.8 BSD";
|
31
|
+
"4.1 BSD" -> "8th Edition";
|
32
|
+
"4.2 BSD" -> "4.3 BSD";
|
33
|
+
"4.2 BSD" -> "Ultrix-32";
|
34
|
+
"PWB 1.0" -> "PWB 1.2";
|
35
|
+
"PWB 1.0" -> "USG 1.0";
|
36
|
+
"PWB 1.2" -> "PWB 2.0";
|
37
|
+
"USG 1.0" -> "CB Unix 1";
|
38
|
+
"USG 1.0" -> "USG 2.0";
|
39
|
+
"CB Unix 1" -> "CB Unix 2";
|
40
|
+
"CB Unix 2" -> "CB Unix 3";
|
41
|
+
"CB Unix 3" -> "Unix/TS++";
|
42
|
+
"CB Unix 3" -> "PDP-11 Sys V";
|
43
|
+
"USG 2.0" -> "USG 3.0";
|
44
|
+
"USG 3.0" -> "Unix/TS 3.0";
|
45
|
+
"PWB 2.0" -> "Unix/TS 3.0";
|
46
|
+
"Unix/TS 1.0" -> "Unix/TS 3.0";
|
47
|
+
"Unix/TS 3.0" -> "TS 4.0";
|
48
|
+
"Unix/TS++" -> "TS 4.0";
|
49
|
+
"CB Unix 3" -> "TS 4.0";
|
50
|
+
"TS 4.0" -> "System V.0";
|
51
|
+
"System V.0" -> "System V.2";
|
52
|
+
"System V.2" -> "System V.3";
|
53
|
+
}
|
data/lib/rgl/adjacency.rb
CHANGED
@@ -77,7 +77,7 @@ module RGL
|
|
77
77
|
# Complexity is O(1), because the vertices are kept in a Hash containing
|
78
78
|
# as values the lists of adjacent vertices of _v_.
|
79
79
|
#
|
80
|
-
def has_vertex?
|
80
|
+
def has_vertex?(v)
|
81
81
|
@vertices_dict.has_key?(v)
|
82
82
|
end
|
83
83
|
|
@@ -87,7 +87,7 @@ module RGL
|
|
87
87
|
# ---
|
88
88
|
# MutableGraph interface.
|
89
89
|
#
|
90
|
-
def has_edge?
|
90
|
+
def has_edge?(u, v)
|
91
91
|
has_vertex?(u) && @vertices_dict[u].include?(v)
|
92
92
|
end
|
93
93
|
|
data/lib/rgl/base.rb
CHANGED
@@ -4,9 +4,7 @@
|
|
4
4
|
# library. The main module is RGL::Graph which defines the abstract behavior of
|
5
5
|
# all graphs in the library.
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
RGL_VERSION = "0.5.1"
|
7
|
+
RGL_VERSION = "0.5.7"
|
10
8
|
|
11
9
|
module RGL
|
12
10
|
class NotDirectedError < RuntimeError; end
|
@@ -77,6 +75,9 @@ module RGL
|
|
77
75
|
"(#{source}-#{target})"
|
78
76
|
end
|
79
77
|
|
78
|
+
# Since Ruby 2.0 #inspect no longer calls #to_s. So we alias it to to_s (fixes #22)
|
79
|
+
alias inspect to_s
|
80
|
+
|
80
81
|
# Returns the array [source,target].
|
81
82
|
#
|
82
83
|
def to_a
|
data/lib/rgl/bellman_ford.rb
CHANGED
data/lib/rgl/bipartite.rb
CHANGED
@@ -22,7 +22,7 @@ module RGL
|
|
22
22
|
next if bfs.finished_vertex?(u)
|
23
23
|
|
24
24
|
bfs.reset_start(u)
|
25
|
-
bfs.move_forward_until {
|
25
|
+
bfs.move_forward_until { bfs.found_odd_cycle }
|
26
26
|
|
27
27
|
return if bfs.found_odd_cycle
|
28
28
|
end
|
@@ -84,4 +84,4 @@ module RGL
|
|
84
84
|
|
85
85
|
end # class BipartiteBFSIterator
|
86
86
|
|
87
|
-
end # module RGL
|
87
|
+
end # module RGL
|
data/lib/rgl/dijkstra.rb
CHANGED
@@ -2,8 +2,7 @@ require 'rgl/dijkstra_visitor'
|
|
2
2
|
require 'rgl/edge_properties_map'
|
3
3
|
require 'rgl/path_builder'
|
4
4
|
|
5
|
-
require '
|
6
|
-
require 'algorithms'
|
5
|
+
require 'lazy_priority_queue'
|
7
6
|
|
8
7
|
module RGL
|
9
8
|
|
@@ -55,7 +54,7 @@ module RGL
|
|
55
54
|
def init(source)
|
56
55
|
@visitor.set_source(source)
|
57
56
|
|
58
|
-
@queue =
|
57
|
+
@queue = MinPriorityQueue.new
|
59
58
|
@queue.push(source, 0)
|
60
59
|
end
|
61
60
|
|
@@ -82,8 +81,6 @@ module RGL
|
|
82
81
|
new_v_distance = @distance_combinator.call(@visitor.distance_map[u], @edge_weights_map.edge_property(u, v))
|
83
82
|
|
84
83
|
if new_v_distance < @visitor.distance_map[v]
|
85
|
-
old_v_distance = @visitor.distance_map[v]
|
86
|
-
|
87
84
|
@visitor.distance_map[v] = new_v_distance
|
88
85
|
@visitor.parents_map[v] = u
|
89
86
|
|
@@ -91,7 +88,7 @@ module RGL
|
|
91
88
|
@visitor.color_map[v] = :GRAY
|
92
89
|
@queue.push(v, new_v_distance)
|
93
90
|
elsif @visitor.color_map[v] == :GRAY
|
94
|
-
@queue.decrease_key(v,
|
91
|
+
@queue.decrease_key(v, new_v_distance)
|
95
92
|
end
|
96
93
|
|
97
94
|
@visitor.handle_edge_relaxed(u, v)
|
@@ -104,29 +101,6 @@ module RGL
|
|
104
101
|
edge_weights_map.is_a?(EdgePropertiesMap) ? edge_weights_map : NonNegativeEdgePropertiesMap.new(edge_weights_map, @graph.directed?)
|
105
102
|
end
|
106
103
|
|
107
|
-
class Queue < SimpleDelegator # :nodoc:
|
108
|
-
|
109
|
-
def initialize
|
110
|
-
@heap = Containers::Heap.new { |a, b| a.distance < b.distance }
|
111
|
-
super(@heap)
|
112
|
-
end
|
113
|
-
|
114
|
-
def push(vertex, distance)
|
115
|
-
@heap.push(vertex_key(vertex, distance), vertex)
|
116
|
-
end
|
117
|
-
|
118
|
-
def decrease_key(vertex, old_distance, new_distance)
|
119
|
-
@heap.change_key(vertex_key(vertex, old_distance), vertex_key(vertex, new_distance))
|
120
|
-
end
|
121
|
-
|
122
|
-
def vertex_key(vertex, distance)
|
123
|
-
VertexKey.new(vertex, distance)
|
124
|
-
end
|
125
|
-
|
126
|
-
VertexKey = Struct.new(:vertex, :distance)
|
127
|
-
|
128
|
-
end
|
129
|
-
|
130
104
|
end # class DijkstraAlgorithm
|
131
105
|
|
132
106
|
module Graph
|
data/lib/rgl/dot.rb
CHANGED
@@ -5,6 +5,9 @@
|
|
5
5
|
# Minimal Dot support, based on Dave Thomas's dot module (included in rdoc).
|
6
6
|
# rdot.rb is a modified version which also contains support for undirected
|
7
7
|
# graphs.
|
8
|
+
#
|
9
|
+
# You need to have [GraphViz](http://www.graphviz.org) installed, because the
|
10
|
+
# functions in this modul execute the GraphViz executables _dot_ or _dotty_.
|
8
11
|
|
9
12
|
require 'rgl/rdot'
|
10
13
|
|
@@ -17,6 +20,10 @@ module RGL
|
|
17
20
|
v.to_s
|
18
21
|
end
|
19
22
|
|
23
|
+
def vertex_id(v)
|
24
|
+
v
|
25
|
+
end
|
26
|
+
|
20
27
|
# Return a RGL::DOT::Digraph for directed graphs or a DOT::Graph for an
|
21
28
|
# undirected Graph. _params_ can contain any graph property specified in
|
22
29
|
# rdot.rb.
|
@@ -26,21 +33,29 @@ module RGL
|
|
26
33
|
fontsize = params['fontsize'] ? params['fontsize'] : '8'
|
27
34
|
graph = (directed? ? DOT::Digraph : DOT::Graph).new(params)
|
28
35
|
edge_class = directed? ? DOT::DirectedEdge : DOT::Edge
|
36
|
+
vertex_options = params['vertex'] || {}
|
37
|
+
edge_options = params['edge'] || {}
|
29
38
|
|
30
39
|
each_vertex do |v|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
40
|
+
default_vertex_options = {
|
41
|
+
'name' => vertex_id(v),
|
42
|
+
'fontsize' => fontsize,
|
43
|
+
'label' => vertex_label(v)
|
44
|
+
}
|
45
|
+
each_vertex_options = default_vertex_options.merge(vertex_options)
|
46
|
+
vertex_options.each{|option, val| each_vertex_options[option] = val.call(v) if val.is_a?(Proc)}
|
47
|
+
graph << DOT::Node.new(each_vertex_options)
|
36
48
|
end
|
37
49
|
|
38
50
|
each_edge do |u, v|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
51
|
+
default_edge_options = {
|
52
|
+
'from' => vertex_id(u),
|
53
|
+
'to' => vertex_id(v),
|
54
|
+
'fontsize' => fontsize
|
55
|
+
}
|
56
|
+
each_edge_options = default_edge_options.merge(edge_options)
|
57
|
+
edge_options.each{|option, val| each_edge_options[option] = val.call(u, v) if val.is_a?(Proc)}
|
58
|
+
graph << edge_class.new(each_edge_options)
|
44
59
|
end
|
45
60
|
|
46
61
|
graph
|
@@ -60,21 +75,25 @@ module RGL
|
|
60
75
|
File.open(dotfile, "w") do |f|
|
61
76
|
print_dotted_on(params, f)
|
62
77
|
end
|
63
|
-
system("dotty", dotfile)
|
78
|
+
unless system("dotty", dotfile)
|
79
|
+
raise "Error executing dotty. Did you install GraphViz?"
|
80
|
+
end
|
64
81
|
end
|
65
82
|
|
66
83
|
# Use dot[http://www.graphviz.org] to create a graphical representation of
|
67
84
|
# the graph. Returns the filename of the graphics file.
|
68
85
|
#
|
69
|
-
def write_to_graphic_file(fmt='png', dotfile="graph")
|
86
|
+
def write_to_graphic_file(fmt='png', dotfile="graph", options={})
|
70
87
|
src = dotfile + ".dot"
|
71
88
|
dot = dotfile + "." + fmt
|
72
89
|
|
73
90
|
File.open(src, 'w') do |f|
|
74
|
-
f << self.to_dot_graph.to_s << "\n"
|
91
|
+
f << self.to_dot_graph(options).to_s << "\n"
|
75
92
|
end
|
76
93
|
|
77
|
-
system("dot -T#{fmt} #{src} -o #{dot}")
|
94
|
+
unless system("dot -T#{fmt} #{src} -o #{dot}")
|
95
|
+
raise "Error executing dot. Did you install GraphViz?"
|
96
|
+
end
|
78
97
|
dot
|
79
98
|
end
|
80
99
|
|
data/lib/rgl/edmonds_karp.rb
CHANGED
@@ -46,9 +46,9 @@ module RGL
|
|
46
46
|
min_residual_capacity = [min_residual_capacity, @residual_capacity_map[u, v]].min
|
47
47
|
end
|
48
48
|
|
49
|
-
augmenting_path.each_cons(2) do |(
|
50
|
-
@flow_map[[
|
51
|
-
@flow_map[[
|
49
|
+
augmenting_path.each_cons(2) do |(uu, vv)|
|
50
|
+
@flow_map[[uu, vv]] += min_residual_capacity
|
51
|
+
@flow_map[[vv, uu]] -= min_residual_capacity
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -133,4 +133,4 @@ module RGL
|
|
133
133
|
|
134
134
|
end # module Graph
|
135
135
|
|
136
|
-
end
|
136
|
+
end
|
data/lib/rgl/graph_iterator.rb
CHANGED
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://
|
4
|
+
# format (see http://graphml.graphdrawing.org/). 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
|
@@ -48,7 +48,7 @@ module RGL
|
|
48
48
|
end # class MutableGraphParser
|
49
49
|
|
50
50
|
# Initializes an RGL graph from a subset of the GraphML format given in
|
51
|
-
# +source+ (see http://
|
51
|
+
# +source+ (see http://graphml.graphdrawing.org/).
|
52
52
|
#
|
53
53
|
def from_graphxml(source)
|
54
54
|
listener = MutableGraphParser.new(self)
|
data/lib/rgl/path.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rgl/traversal'
|
4
|
+
|
5
|
+
module RGL
|
6
|
+
module Graph
|
7
|
+
# Checks whether a path exists between _source_ and _target_ vertices
|
8
|
+
# in the graph.
|
9
|
+
#
|
10
|
+
def path?(source, target)
|
11
|
+
return false unless has_vertex?(source)
|
12
|
+
|
13
|
+
bfs_iterator = bfs_iterator(source)
|
14
|
+
bfs_iterator.include?(target)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/rgl/rdot.rb
CHANGED
data/lib/rgl/traversal.rb
CHANGED
@@ -58,9 +58,12 @@ module RGL
|
|
58
58
|
# Reset the iterator to the initial state (i.e. at_beginning? == true).
|
59
59
|
#
|
60
60
|
def set_to_begin
|
61
|
+
# Reset color_map
|
62
|
+
@color_map = Hash.new(:WHITE)
|
61
63
|
color_map[@start_vertex] = :GRAY
|
62
|
-
@waiting = [@start_vertex]
|
64
|
+
@waiting = [@start_vertex] # a queue
|
63
65
|
handle_tree_edge(nil, @start_vertex) # discovers start vertex
|
66
|
+
self
|
64
67
|
end
|
65
68
|
|
66
69
|
def basic_forward # :nodoc:
|
@@ -169,6 +172,10 @@ module RGL
|
|
169
172
|
# it is called on each _finish_vertex_ event. See
|
170
173
|
# strongly_connected_components for an example usage.
|
171
174
|
#
|
175
|
+
# Note that this traversal does not garantee, that roots are at the top of
|
176
|
+
# each spanning subtree induced by the DFS search on a directed graph (see
|
177
|
+
# also the discussion in issue #20[https://github.com/monora/rgl/issues/20]).
|
178
|
+
#
|
172
179
|
def depth_first_search(vis = DFSVisitor.new(self), &b)
|
173
180
|
each_vertex do |u|
|
174
181
|
unless vis.finished_vertex?(u)
|
data/test/components_test.rb
CHANGED
@@ -61,7 +61,7 @@ i -> h j e c
|
|
61
61
|
|
62
62
|
assert_equal(4, vis.num_comp)
|
63
63
|
|
64
|
-
|
64
|
+
result = vis.comp_map.to_a.sort.reduce({}) { |res, a|
|
65
65
|
if res.key?(a[1])
|
66
66
|
res[a[1]] << a[0]
|
67
67
|
else
|
@@ -70,7 +70,7 @@ i -> h j e c
|
|
70
70
|
res
|
71
71
|
}
|
72
72
|
|
73
|
-
std_res =
|
73
|
+
std_res = result.to_a.map {
|
74
74
|
|a|
|
75
75
|
[a[1][0], a[1]]
|
76
76
|
}.sort
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'rgl/dijkstra'
|
4
|
+
require 'rgl/adjacency'
|
5
|
+
|
6
|
+
include RGL
|
7
|
+
|
8
|
+
class TestDijkstraIssue24 < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@graph = RGL::AdjacencyGraph[2,53, 2,3, 3,8, 3,28, 3,39, 29,58, 8,35, 12,39, 10,29, 62,15, 15,32, 32,58, 58,44, 44,53]
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_shortest_path_search
|
16
|
+
assert_equal([53, 44, 58, 32, 15, 62], shortest_path(53, 62))
|
17
|
+
end
|
18
|
+
|
19
|
+
def shortest_path(v,w)
|
20
|
+
@graph.dijkstra_shortest_path(Hash.new(1), v, w)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|