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/rakelib/dep_graph.rake
CHANGED
@@ -13,9 +13,10 @@ end
|
|
13
13
|
desc "Generate dependency graph of rake tasks"
|
14
14
|
task :dep_graph do |task|
|
15
15
|
this_task = task.name
|
16
|
-
|
16
|
+
|
17
|
+
dep = RGL::ImplicitGraph.new do |g|
|
17
18
|
# vertices of the graph are all defined tasks without this task
|
18
|
-
|
19
|
+
g.vertex_iterator do |b|
|
19
20
|
Rake::Task.tasks.each do |t|
|
20
21
|
b.call(t) unless t.name == this_task
|
21
22
|
end
|
@@ -23,7 +24,7 @@ task :dep_graph do |task|
|
|
23
24
|
# neighbors of task t are its prerequisites
|
24
25
|
g.adjacent_iterator { |t, b| t.prerequisites.each(&b) }
|
25
26
|
g.directed = true
|
26
|
-
|
27
|
+
end
|
27
28
|
|
28
29
|
dep.write_to_graphic_file('png', this_task)
|
29
30
|
puts "Wrote dependency graph to #{this_task}.png."
|
@@ -0,0 +1,187 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'rgl/bellman_ford'
|
4
|
+
require 'rgl/adjacency'
|
5
|
+
|
6
|
+
include RGL
|
7
|
+
|
8
|
+
class TestBellmanFord < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@graph = AdjacencyGraph[1,2, 1,3, 2,3, 2,4, 3,4]
|
12
|
+
|
13
|
+
@edge_weights = {
|
14
|
+
[1, 2] => 10,
|
15
|
+
[1, 3] => 1,
|
16
|
+
[2, 3] => 1,
|
17
|
+
[2, 4] => 1,
|
18
|
+
[3, 4] => 10
|
19
|
+
}
|
20
|
+
|
21
|
+
@edge_weights_lambda = lambda { |edge| @edge_weights[edge] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_shortest_paths_search
|
25
|
+
assert_equal(
|
26
|
+
{
|
27
|
+
1 => [1],
|
28
|
+
2 => [1, 3, 2],
|
29
|
+
3 => [1, 3],
|
30
|
+
4 => [1, 3, 2, 4]
|
31
|
+
},
|
32
|
+
shortest_paths(1)
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_shortest_paths_search_with_lambda
|
37
|
+
assert_equal(
|
38
|
+
{
|
39
|
+
1 => [1],
|
40
|
+
2 => [1, 3, 2],
|
41
|
+
3 => [1, 3],
|
42
|
+
4 => [1, 3, 2, 4]
|
43
|
+
},
|
44
|
+
shortest_paths(1, @edge_weights_lambda)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_shortest_paths_search_with_unreachable_vertex
|
49
|
+
@graph.add_vertex(5)
|
50
|
+
|
51
|
+
assert_equal(
|
52
|
+
{
|
53
|
+
1 => [1],
|
54
|
+
2 => [1, 3, 2],
|
55
|
+
3 => [1, 3],
|
56
|
+
4 => [1, 3, 2, 4],
|
57
|
+
5 => nil
|
58
|
+
},
|
59
|
+
shortest_paths(1)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_shortest_paths_with_negative_weights
|
64
|
+
# can't use an undirected graph with a negative weighted edge here, because a negative weighted undirected edge is
|
65
|
+
# already a negative weighted cycle and therefore Bellman-Ford can't be applied for such graph
|
66
|
+
@graph = DirectedAdjacencyGraph[1,2, 1,3, 2,3, 2,4, 3,2, 3,4]
|
67
|
+
@edge_weights[[3, 2]] = 1
|
68
|
+
@edge_weights[[3, 4]] = -1
|
69
|
+
|
70
|
+
assert_equal(
|
71
|
+
{
|
72
|
+
1 => [1],
|
73
|
+
2 => [1, 3, 2],
|
74
|
+
3 => [1, 3],
|
75
|
+
4 => [1, 3, 4]
|
76
|
+
},
|
77
|
+
shortest_paths(1)
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_missing_edge_weight
|
82
|
+
@edge_weights.delete([2, 3])
|
83
|
+
assert_raises(ArgumentError, 'weight of edge (2, 3) is not defined') { shortest_paths(1) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_negative_cycles
|
87
|
+
@graph = DirectedAdjacencyGraph[1,2, 1,3, 2,3, 2,4, 3,4, 4,2]
|
88
|
+
@edge_weights[[4, 2]] = 1
|
89
|
+
@edge_weights[[3, 4]] = -3 # cycle 2-3-4-2 has negative weight
|
90
|
+
|
91
|
+
assert_raises(ArgumentError, 'there is a negative-weight cycle including edge (3, 4)') { shortest_paths(1) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_visitor
|
95
|
+
visitor = BellmanFordVisitor.new(@graph)
|
96
|
+
|
97
|
+
events = []
|
98
|
+
|
99
|
+
%w[examine_edge edge_relaxed edge_not_relaxed edge_minimized edge_not_minimized].each do |event|
|
100
|
+
visitor.send("set_#{event}_event_handler") { |*args| events << { event.to_sym => args } }
|
101
|
+
end
|
102
|
+
|
103
|
+
@graph.bellman_ford_shortest_paths(@edge_weights, 1, visitor)
|
104
|
+
|
105
|
+
assert_equal(
|
106
|
+
[
|
107
|
+
# first iteration
|
108
|
+
{ :examine_edge => [1, 2] },
|
109
|
+
{ :edge_relaxed => [1, 2] },
|
110
|
+
{ :examine_edge => [2, 1] },
|
111
|
+
{ :edge_not_relaxed => [2, 1] },
|
112
|
+
{ :examine_edge => [1, 3] },
|
113
|
+
{ :edge_relaxed => [1, 3] },
|
114
|
+
{ :examine_edge => [3, 1] },
|
115
|
+
{ :edge_not_relaxed => [3, 1] },
|
116
|
+
{ :examine_edge => [2, 3] },
|
117
|
+
{ :edge_not_relaxed => [2, 3] },
|
118
|
+
{ :examine_edge => [3, 2] },
|
119
|
+
{ :edge_relaxed => [3, 2] },
|
120
|
+
{ :examine_edge => [2, 4] },
|
121
|
+
{ :edge_relaxed => [2, 4] },
|
122
|
+
{ :examine_edge => [4, 2] },
|
123
|
+
{ :edge_not_relaxed => [4, 2] },
|
124
|
+
{ :examine_edge => [3, 4] },
|
125
|
+
{ :edge_not_relaxed => [3, 4] },
|
126
|
+
{ :examine_edge => [4, 3] },
|
127
|
+
{ :edge_not_relaxed => [4, 3] },
|
128
|
+
# second iteration
|
129
|
+
{ :examine_edge => [1, 2] },
|
130
|
+
{ :edge_not_relaxed => [1, 2] },
|
131
|
+
{ :examine_edge => [2, 1] },
|
132
|
+
{ :edge_not_relaxed => [2, 1] },
|
133
|
+
{ :examine_edge => [1, 3] },
|
134
|
+
{ :edge_not_relaxed => [1, 3] },
|
135
|
+
{ :examine_edge => [3, 1] },
|
136
|
+
{ :edge_not_relaxed => [3, 1] },
|
137
|
+
{ :examine_edge => [2, 3] },
|
138
|
+
{ :edge_not_relaxed => [2, 3] },
|
139
|
+
{ :examine_edge => [3, 2] },
|
140
|
+
{ :edge_not_relaxed => [3, 2] },
|
141
|
+
{ :examine_edge => [2, 4] },
|
142
|
+
{ :edge_not_relaxed => [2, 4] },
|
143
|
+
{ :examine_edge => [4, 2] },
|
144
|
+
{ :edge_not_relaxed => [4, 2] },
|
145
|
+
{ :examine_edge => [3, 4] },
|
146
|
+
{ :edge_not_relaxed => [3, 4] },
|
147
|
+
{ :examine_edge => [4, 3] },
|
148
|
+
{ :edge_not_relaxed => [4, 3] },
|
149
|
+
# thirds iteration
|
150
|
+
{ :examine_edge => [1, 2] },
|
151
|
+
{ :edge_not_relaxed => [1, 2] },
|
152
|
+
{ :examine_edge => [2, 1] },
|
153
|
+
{ :edge_not_relaxed => [2, 1] },
|
154
|
+
{ :examine_edge => [1, 3] },
|
155
|
+
{ :edge_not_relaxed => [1, 3] },
|
156
|
+
{ :examine_edge => [3, 1] },
|
157
|
+
{ :edge_not_relaxed => [3, 1] },
|
158
|
+
{ :examine_edge => [2, 3] },
|
159
|
+
{ :edge_not_relaxed => [2, 3] },
|
160
|
+
{ :examine_edge => [3, 2] },
|
161
|
+
{ :edge_not_relaxed => [3, 2] },
|
162
|
+
{ :examine_edge => [2, 4] },
|
163
|
+
{ :edge_not_relaxed => [2, 4] },
|
164
|
+
{ :examine_edge => [4, 2] },
|
165
|
+
{ :edge_not_relaxed => [4, 2] },
|
166
|
+
{ :examine_edge => [3, 4] },
|
167
|
+
{ :edge_not_relaxed => [3, 4] },
|
168
|
+
{ :examine_edge => [4, 3] },
|
169
|
+
{ :edge_not_relaxed => [4, 3] },
|
170
|
+
# post-iteration check
|
171
|
+
{ :edge_minimized => [1, 2] },
|
172
|
+
{ :edge_minimized => [1, 3] },
|
173
|
+
{ :edge_minimized => [2, 3] },
|
174
|
+
{ :edge_minimized => [2, 4] },
|
175
|
+
{ :edge_minimized => [3, 4] }
|
176
|
+
],
|
177
|
+
events
|
178
|
+
)
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def shortest_paths(source, edge_weights = @edge_weights)
|
184
|
+
@graph.bellman_ford_shortest_paths(edge_weights, source)
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'rgl/bipartite'
|
4
|
+
require 'rgl/adjacency'
|
5
|
+
|
6
|
+
include RGL
|
7
|
+
|
8
|
+
class TestBipartite < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def test_bipartite_sets
|
11
|
+
assert_equal([[1, 2, 3], [4, 5, 6]], bipartite_sets(AdjacencyGraph[1,5, 1,6, 2,4, 2,5, 3,4, 3,5, 3,6]))
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_bipartite_sets_for_non_bipartite_graph
|
15
|
+
assert_equal(nil, bipartite_sets(AdjacencyGraph[1,4, 1,5, 2,3, 2,4, 2,5, 3,5]))
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_bipartite_sets_for_directed_graph
|
19
|
+
assert_raise(NotUndirectedError, 'bipartite sets can only be found for an undirected graph') do
|
20
|
+
DirectedAdjacencyGraph.new.bipartite_sets
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_bipartite_sets_for_bipartite_disconnected_graph
|
25
|
+
assert_equal([[1, 3], [2, 4]], bipartite_sets(AdjacencyGraph[1,2, 3,4]))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_bipartite_sets_for_non_bipartite_disconnected_graph
|
29
|
+
assert_equal(nil, bipartite_sets(AdjacencyGraph[1,2, 3,4, 4,5, 5,3]))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_bipartite
|
33
|
+
assert(AdjacencyGraph[1,2, 2,3].bipartite?)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_not_bipartite
|
37
|
+
assert(!AdjacencyGraph[1,2, 2,3, 3,1].bipartite?)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def bipartite_sets(graph)
|
43
|
+
sets = graph.bipartite_sets
|
44
|
+
sets && sets.map { |set| set.sort }.sort
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'rgl/connected_components'
|
4
|
+
require 'rgl/adjacency'
|
5
|
+
|
6
|
+
include RGL
|
7
|
+
|
8
|
+
def graph_from_string(s)
|
9
|
+
g = DirectedAdjacencyGraph.new(Array)
|
10
|
+
s.split(/\n/).collect { |x| x.split(/->/) }.each do |a|
|
11
|
+
from = a[0].strip
|
12
|
+
a[1].split.each do |to|
|
13
|
+
g.add_edge from, to
|
14
|
+
end
|
15
|
+
end
|
16
|
+
g
|
17
|
+
end
|
18
|
+
|
19
|
+
class TestComponents < Test::Unit::TestCase
|
20
|
+
|
21
|
+
def setup
|
22
|
+
@dg = DirectedAdjacencyGraph.new(Array)
|
23
|
+
edges = [[1, 2], [2, 3], [2, 4], [4, 5], [1, 6], [6, 4]]
|
24
|
+
edges.each do |(src, target)|
|
25
|
+
@dg.add_edge(src, target)
|
26
|
+
end
|
27
|
+
@bfs = @dg.bfs_iterator(1)
|
28
|
+
@dfs = @dg.dfs_iterator(1)
|
29
|
+
|
30
|
+
@ug = AdjacencyGraph.new(Array)
|
31
|
+
@ug.add_edges(*edges)
|
32
|
+
|
33
|
+
@dg2 = graph_from_string(<<-END
|
34
|
+
a -> b f h
|
35
|
+
b -> c a
|
36
|
+
c -> d b
|
37
|
+
d -> e
|
38
|
+
e -> d
|
39
|
+
f -> g
|
40
|
+
g -> f d
|
41
|
+
h -> i
|
42
|
+
i -> h j e c
|
43
|
+
END
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_connected_components
|
48
|
+
ccs = []
|
49
|
+
@ug.each_connected_component { |c| ccs << c }
|
50
|
+
assert_equal(1, ccs.size)
|
51
|
+
|
52
|
+
ccs = []
|
53
|
+
@ug.add_edge 10, 11
|
54
|
+
@ug.add_edge 33, 44
|
55
|
+
@ug.each_connected_component { |c| ccs << c.sort }
|
56
|
+
assert_equal([[10, 11], [1, 2, 3, 4, 5, 6], [33, 44]].sort, ccs.sort)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_strong_components
|
60
|
+
vis = @dg2.strongly_connected_components
|
61
|
+
|
62
|
+
assert_equal(4, vis.num_comp)
|
63
|
+
|
64
|
+
res = vis.comp_map.to_a.sort.reduce({}) { |res, a|
|
65
|
+
if res.key?(a[1])
|
66
|
+
res[a[1]] << a[0]
|
67
|
+
else
|
68
|
+
res[a[1]] = [a[0]]
|
69
|
+
end
|
70
|
+
res
|
71
|
+
}
|
72
|
+
|
73
|
+
std_res = res.to_a.map {
|
74
|
+
|a|
|
75
|
+
[a[1][0], a[1]]
|
76
|
+
}.sort
|
77
|
+
|
78
|
+
assert_equal([["a", ["a", "b", "c", "h", "i"]], ["d", ["d", "e"]], ["f", ["f", "g"]], ["j", ["j"]]], std_res)
|
79
|
+
end
|
80
|
+
end
|
data/test/cycles_test.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'rgl/adjacency'
|
4
|
+
|
5
|
+
include RGL
|
6
|
+
|
7
|
+
class TestCycles < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@dg = DirectedAdjacencyGraph.new(Array)
|
11
|
+
edges = [[1, 2], [2, 2], [2, 3], [3, 4], [4, 5], [5, 1], [6, 4], [6, 6], [1, 4], [7, 7], [7, 7]]
|
12
|
+
edges.each do |(src, target)|
|
13
|
+
@dg.add_edge(src, target)
|
14
|
+
end
|
15
|
+
|
16
|
+
@ug = AdjacencyGraph.new(Array)
|
17
|
+
@ug.add_edges(*edges)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Helper for testing for different permutations of a cycle
|
21
|
+
def contains_cycle?(cycles, cycle)
|
22
|
+
cycle.size.times do |i|
|
23
|
+
return true if cycles.include?(cycle)
|
24
|
+
cycle = cycle[1..-1] + [cycle[0]]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_cycles
|
29
|
+
d_cycles = @dg.cycles
|
30
|
+
assert_equal 6, d_cycles.size
|
31
|
+
assert d_cycles.include?([6])
|
32
|
+
assert d_cycles.include?([7])
|
33
|
+
assert d_cycles.include?([2])
|
34
|
+
assert contains_cycle?(d_cycles, [1, 4, 5])
|
35
|
+
assert contains_cycle?(d_cycles, [1, 2, 3, 4, 5])
|
36
|
+
|
37
|
+
assert_equal 5, DirectedAdjacencyGraph.new(Set, @dg).cycles.size
|
38
|
+
|
39
|
+
u_cycles = AdjacencyGraph.new(Set, @dg).cycles.sort
|
40
|
+
|
41
|
+
assert u_cycles.include?([2])
|
42
|
+
assert u_cycles.include?([6])
|
43
|
+
assert u_cycles.include?([7])
|
44
|
+
assert contains_cycle?(u_cycles, [1, 2, 3, 4, 5])
|
45
|
+
assert contains_cycle?(u_cycles, [1, 5, 4, 3, 2])
|
46
|
+
assert contains_cycle?(u_cycles, [1, 4, 3, 2])
|
47
|
+
assert contains_cycle?(u_cycles, [1, 4, 5])
|
48
|
+
assert contains_cycle?(u_cycles, [1, 5, 4])
|
49
|
+
assert contains_cycle?(u_cycles, [1, 5])
|
50
|
+
assert contains_cycle?(u_cycles, [1, 2])
|
51
|
+
assert contains_cycle?(u_cycles, [1, 2, 3, 4])
|
52
|
+
assert contains_cycle?(u_cycles, [2, 3])
|
53
|
+
assert contains_cycle?(u_cycles, [1, 4])
|
54
|
+
assert contains_cycle?(u_cycles, [3, 4])
|
55
|
+
assert contains_cycle?(u_cycles, [4, 5])
|
56
|
+
assert contains_cycle?(u_cycles, [4, 6])
|
57
|
+
assert_equal 16, u_cycles.size
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'rgl/dijkstra'
|
4
|
+
require 'rgl/adjacency'
|
5
|
+
|
6
|
+
include RGL
|
7
|
+
|
8
|
+
class TestDijkstra < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@graph = AdjacencyGraph[1,2, 1,3, 2,3, 2,4, 3,4]
|
12
|
+
|
13
|
+
@edge_weights = {
|
14
|
+
[1, 2] => 10,
|
15
|
+
[1, 3] => 1,
|
16
|
+
[2, 3] => 1,
|
17
|
+
[2, 4] => 1,
|
18
|
+
[3, 4] => 10
|
19
|
+
}
|
20
|
+
|
21
|
+
@edge_weights_lambda = lambda { |edge| @edge_weights[edge] }
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_shortest_path_search
|
25
|
+
assert_equal([1, 3, 2, 4], shortest_path(1, 4))
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_shortest_path_search_with_lambda
|
29
|
+
assert_equal([1, 3, 2, 4], shortest_path(1, 4, @edge_weights_lambda))
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_shortest_path_to_the_source
|
33
|
+
assert_equal([1], shortest_path(1, 1))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_path_for_unreachable_vertex
|
37
|
+
@graph.add_vertex(5)
|
38
|
+
assert_equal(nil, shortest_path(1, 5))
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_shortest_paths_search
|
42
|
+
assert_equal(
|
43
|
+
{
|
44
|
+
1 => [1],
|
45
|
+
2 => [1, 3, 2],
|
46
|
+
3 => [1, 3],
|
47
|
+
4 => [1, 3, 2, 4]
|
48
|
+
},
|
49
|
+
shortest_paths(1)
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_shortest_paths_search_with_lambda
|
54
|
+
assert_equal(
|
55
|
+
{
|
56
|
+
1 => [1],
|
57
|
+
2 => [1, 3, 2],
|
58
|
+
3 => [1, 3],
|
59
|
+
4 => [1, 3, 2, 4]
|
60
|
+
},
|
61
|
+
shortest_paths(1, @edge_weights_lambda)
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_shortest_paths_search_with_unreachable_vertex
|
66
|
+
@graph.add_vertex(5)
|
67
|
+
|
68
|
+
assert_equal(
|
69
|
+
{
|
70
|
+
1 => [1],
|
71
|
+
2 => [1, 3, 2],
|
72
|
+
3 => [1, 3],
|
73
|
+
4 => [1, 3, 2, 4],
|
74
|
+
5 => nil
|
75
|
+
},
|
76
|
+
shortest_paths(1)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_visitor
|
81
|
+
visitor = DijkstraVisitor.new(@graph)
|
82
|
+
|
83
|
+
events = []
|
84
|
+
|
85
|
+
%w[examine_vertex examine_edge edge_relaxed edge_not_relaxed finish_vertex].each do |event|
|
86
|
+
visitor.send("set_#{event}_event_handler") { |*args| events << { event.to_sym => args } }
|
87
|
+
end
|
88
|
+
|
89
|
+
@graph.dijkstra_shortest_paths(@edge_weights, 1, visitor)
|
90
|
+
|
91
|
+
assert_equal(
|
92
|
+
[
|
93
|
+
{ :examine_vertex => [1] },
|
94
|
+
{ :examine_edge => [1, 2] },
|
95
|
+
{ :edge_relaxed => [1, 2] },
|
96
|
+
{ :examine_edge => [1, 3] },
|
97
|
+
{ :edge_relaxed => [1, 3] },
|
98
|
+
{ :finish_vertex => [1] },
|
99
|
+
{ :examine_vertex => [3] },
|
100
|
+
{ :examine_edge => [3, 2] },
|
101
|
+
{ :edge_relaxed => [3, 2] },
|
102
|
+
{ :examine_edge => [3, 4] },
|
103
|
+
{ :edge_relaxed => [3, 4] },
|
104
|
+
{ :finish_vertex => [3] },
|
105
|
+
{ :examine_vertex => [2] },
|
106
|
+
{ :examine_edge => [2, 4] },
|
107
|
+
{ :edge_relaxed => [2, 4] },
|
108
|
+
{ :finish_vertex => [2] },
|
109
|
+
{ :examine_vertex => [4] },
|
110
|
+
{ :finish_vertex => [4] },
|
111
|
+
],
|
112
|
+
events
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_negative_edge_weight
|
117
|
+
@edge_weights[[2, 3]] = -7
|
118
|
+
assert_raises(ArgumentError, 'weight of edge (2, 3) is negative') { shortest_path(1, 5) }
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_negative_edge_weight_with_lambda
|
122
|
+
@edge_weights[[2, 3]] = -7
|
123
|
+
assert_raises(ArgumentError, 'weight of edge (2, 3) is negative') { shortest_path(1, 5, @edge_weights_lambda) }
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_missing_edge_weight
|
127
|
+
@edge_weights.delete([2, 3])
|
128
|
+
assert_raises(ArgumentError, 'weight of edge (2, 3) is not defined') { shortest_path(1, 5) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_edge_weights_map_object_in_argument
|
132
|
+
weights_map = EdgePropertiesMap.new(@edge_weights, @graph.directed?)
|
133
|
+
dijkstra = DijkstraAlgorithm.new(@graph, weights_map, DijkstraVisitor.new(@graph))
|
134
|
+
|
135
|
+
assert_equal([1, 3, 2, 4], dijkstra.shortest_path(1, 4))
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def shortest_path(source, target, edge_weights = @edge_weights)
|
141
|
+
@graph.dijkstra_shortest_path(edge_weights, source, target)
|
142
|
+
end
|
143
|
+
|
144
|
+
def shortest_paths(source, edge_weights = @edge_weights)
|
145
|
+
@graph.dijkstra_shortest_paths(edge_weights, source)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|