rgl 0.5.1 → 0.5.7
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.
- 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
|