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/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
|