graph_matching 0.0.1

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.
Files changed (94) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.rubocop.yml +112 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +9 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +205 -0
  9. data/Rakefile +9 -0
  10. data/benchmark/mcm_bipartite/complete_bigraphs/benchmark.rb +33 -0
  11. data/benchmark/mcm_bipartite/complete_bigraphs/compare.gnuplot +19 -0
  12. data/benchmark/mcm_bipartite/complete_bigraphs/edges_times_vertexes.data +500 -0
  13. data/benchmark/mcm_bipartite/complete_bigraphs/plot.gnuplot +21 -0
  14. data/benchmark/mcm_bipartite/complete_bigraphs/plot.png +0 -0
  15. data/benchmark/mcm_bipartite/complete_bigraphs/time.data +499 -0
  16. data/benchmark/mcm_general/complete_graphs/benchmark.rb +30 -0
  17. data/benchmark/mcm_general/complete_graphs/plot.gnuplot +19 -0
  18. data/benchmark/mcm_general/complete_graphs/plot.png +0 -0
  19. data/benchmark/mcm_general/complete_graphs/time.data +499 -0
  20. data/benchmark/mcm_general/complete_graphs/v_cubed.data +500 -0
  21. data/benchmark/mwm_bipartite/complete_bigraphs/benchmark.rb +43 -0
  22. data/benchmark/mwm_bipartite/complete_bigraphs/nmN.data +499 -0
  23. data/benchmark/mwm_bipartite/complete_bigraphs/nmN.xlsx +0 -0
  24. data/benchmark/mwm_bipartite/complete_bigraphs/plot.gnuplot +22 -0
  25. data/benchmark/mwm_bipartite/complete_bigraphs/plot.png +0 -0
  26. data/benchmark/mwm_bipartite/complete_bigraphs/time.data +299 -0
  27. data/benchmark/mwm_bipartite/misc/calc_d2/benchmark.rb +29 -0
  28. data/benchmark/mwm_general/complete_graphs/benchmark.rb +32 -0
  29. data/benchmark/mwm_general/complete_graphs/compare.gnuplot +19 -0
  30. data/benchmark/mwm_general/complete_graphs/mn_log_n.data +299 -0
  31. data/benchmark/mwm_general/complete_graphs/mn_log_n.xlsx +0 -0
  32. data/benchmark/mwm_general/complete_graphs/plot.gnuplot +22 -0
  33. data/benchmark/mwm_general/complete_graphs/plot.png +0 -0
  34. data/benchmark/mwm_general/complete_graphs/time.data +299 -0
  35. data/benchmark/mwm_general/incomplete_graphs/benchmark.rb +39 -0
  36. data/benchmark/mwm_general/incomplete_graphs/plot.gnuplot +22 -0
  37. data/benchmark/mwm_general/incomplete_graphs/plot.png +0 -0
  38. data/benchmark/mwm_general/incomplete_graphs/time_10_pct.data +299 -0
  39. data/benchmark/mwm_general/incomplete_graphs/time_20_pct.data +299 -0
  40. data/benchmark/mwm_general/incomplete_graphs/time_30_pct.data +299 -0
  41. data/graph_matching.gemspec +35 -0
  42. data/lib/graph_matching.rb +15 -0
  43. data/lib/graph_matching/algorithm/matching_algorithm.rb +23 -0
  44. data/lib/graph_matching/algorithm/mcm_bipartite.rb +118 -0
  45. data/lib/graph_matching/algorithm/mcm_general.rb +289 -0
  46. data/lib/graph_matching/algorithm/mwm_bipartite.rb +147 -0
  47. data/lib/graph_matching/algorithm/mwm_general.rb +1086 -0
  48. data/lib/graph_matching/algorithm/mwmg_delta_assertions.rb +94 -0
  49. data/lib/graph_matching/assertion.rb +41 -0
  50. data/lib/graph_matching/core_ext/set.rb +36 -0
  51. data/lib/graph_matching/directed_edge_set.rb +31 -0
  52. data/lib/graph_matching/errors.rb +23 -0
  53. data/lib/graph_matching/graph/bigraph.rb +37 -0
  54. data/lib/graph_matching/graph/graph.rb +63 -0
  55. data/lib/graph_matching/graph/weighted.rb +112 -0
  56. data/lib/graph_matching/graph/weighted_bigraph.rb +17 -0
  57. data/lib/graph_matching/graph/weighted_graph.rb +17 -0
  58. data/lib/graph_matching/integer_vertexes.rb +29 -0
  59. data/lib/graph_matching/matching.rb +120 -0
  60. data/lib/graph_matching/ordered_set.rb +59 -0
  61. data/lib/graph_matching/version.rb +6 -0
  62. data/lib/graph_matching/visualize.rb +93 -0
  63. data/profile/mcm_bipartite/compare.sh +15 -0
  64. data/profile/mcm_bipartite/publish.sh +12 -0
  65. data/profile/mwm_general/compare.sh +15 -0
  66. data/profile/mwm_general/profile.rb +28 -0
  67. data/profile/mwm_general/publish.sh +12 -0
  68. data/research/1965_edmonds.pdf +0 -0
  69. data/research/1975_even_kariv.pdf +0 -0
  70. data/research/1976_gabow.pdf +0 -0
  71. data/research/1980_micali_vazirani.pdf +0 -0
  72. data/research/1985_gabow.pdf +0 -0
  73. data/research/2002_tarjan.pdf +0 -0
  74. data/research/2013_zwick.pdf +0 -0
  75. data/research/examples/unweighted_general/1.txt +86 -0
  76. data/research/goodwin.pdf +0 -0
  77. data/research/kavathekar-scribe.pdf +0 -0
  78. data/research/kusner.pdf +0 -0
  79. data/research/van_rantwijk/mwm_example.py +19 -0
  80. data/research/van_rantwijk/mwmatching.py +945 -0
  81. data/spec/graph_matching/algorithm/matching_algorithm_spec.rb +14 -0
  82. data/spec/graph_matching/algorithm/mcm_bipartite_spec.rb +98 -0
  83. data/spec/graph_matching/algorithm/mcm_general_spec.rb +159 -0
  84. data/spec/graph_matching/algorithm/mwm_bipartite_spec.rb +82 -0
  85. data/spec/graph_matching/algorithm/mwm_general_spec.rb +439 -0
  86. data/spec/graph_matching/graph/bigraph_spec.rb +73 -0
  87. data/spec/graph_matching/graph/graph_spec.rb +53 -0
  88. data/spec/graph_matching/graph/weighted_spec.rb +29 -0
  89. data/spec/graph_matching/integer_vertexes_spec.rb +21 -0
  90. data/spec/graph_matching/matching_spec.rb +89 -0
  91. data/spec/graph_matching/visualize_spec.rb +38 -0
  92. data/spec/graph_matching_spec.rb +9 -0
  93. data/spec/spec_helper.rb +26 -0
  94. metadata +263 -0
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe GraphMatching::Algorithm::MatchingAlgorithm do
6
+ let(:algorithm) { described_class.new(double) }
7
+
8
+ describe '#assert' do
9
+ it 'returns an Assertion' do
10
+ expect(algorithm.assert('banana')).to \
11
+ be_a(GraphMatching::Assertion)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,98 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe GraphMatching::Algorithm::MCMBipartite do
6
+ let(:g) { GraphMatching::Graph::Bigraph.new }
7
+
8
+ describe '.new' do
9
+ it 'requires a Bigraph' do
10
+ expect { described_class.new("banana") }.to raise_error(TypeError)
11
+ end
12
+ end
13
+
14
+ describe '#augment' do
15
+ it 'augments the matching' do
16
+ mcm = described_class.new(g)
17
+ m = [nil, nil, 3, 2]
18
+ m = mcm.send(:augment, m, [1, 2, 3, 4])
19
+ expect(m).to eq([nil, 2, 1, 4, 3])
20
+ m = mcm.send(:augment, m, [1, 2, 4, 5, 6, 7])
21
+ expect(m).to eq([nil, 2, 1, nil, 5, 4, 7, 6])
22
+ end
23
+ end
24
+
25
+ describe '#match' do
26
+ context 'empty graph' do
27
+ it 'returns empty set' do
28
+ m = described_class.new(g).match
29
+ expect(m).to be_empty
30
+ end
31
+ end
32
+
33
+ context 'single vertex' do
34
+ it 'returns empty set' do
35
+ g.add_vertex(0)
36
+ expect(described_class.new(g).match).to be_empty
37
+ end
38
+ end
39
+
40
+ context 'single edge' do
41
+ it 'returns set with one edge' do
42
+ e = [1, 2]
43
+ g.add_edge(*e)
44
+ m = described_class.new(g).match
45
+ expect(m.size).to eq(1)
46
+ expect(m.edge?(e)).to eq(true)
47
+ end
48
+ end
49
+
50
+ context 'complete bigraph with four vertexes' do
51
+ let(:edges) { [[1, 3], [1, 4], [2, 3], [2, 4]] }
52
+
53
+ it 'returns one of the two correct results' do
54
+ edges.each { |e| g.add_edge(*e) }
55
+ m = described_class.new(g).match
56
+ expect(m.size).to eq(2)
57
+ outcomes = [
58
+ RGL::AdjacencyGraph[1, 3, 2, 4],
59
+ RGL::AdjacencyGraph[1, 4, 2, 3]
60
+ ]
61
+ reconstructed = RGL::AdjacencyGraph.new
62
+ m.to_a.each { |edge| reconstructed.add_edge(*edge) }
63
+ expect(outcomes).to include(reconstructed)
64
+ end
65
+ end
66
+
67
+ # The following example is by Derrick Stolee
68
+ # http://www.youtube.com/watch?v=C9c8zEZXboA
69
+ context 'incomplete bigraph with twelve vertexes' do
70
+ let(:edges) {
71
+ [
72
+ [1, 8],
73
+ [2, 9], [2, 10],
74
+ [3, 7], [3, 9], [3, 12],
75
+ [4, 8], [4, 10],
76
+ [5, 10], [5, 11],
77
+ [6, 11]
78
+ ]
79
+ }
80
+
81
+ it 'returns one of the five correct results' do
82
+ edges.each { |e| g.add_edge(*e) }
83
+ m = described_class.new(g).match
84
+ expect(m.size).to eq(5)
85
+ outcomes = [
86
+ RGL::AdjacencyGraph[1, 8, 2, 9, 3, 7, 5, 10, 6, 11],
87
+ RGL::AdjacencyGraph[1, 8, 2, 9, 3, 7, 4, 10, 5, 11],
88
+ RGL::AdjacencyGraph[1, 8, 2, 9, 3, 7, 4, 10, 6, 11],
89
+ RGL::AdjacencyGraph[1, 8, 2, 9, 3, 12, 4, 10, 5, 11],
90
+ RGL::AdjacencyGraph[2, 9, 3, 7, 4, 8, 5, 10, 6, 11]
91
+ ]
92
+ reconstructed = RGL::AdjacencyGraph.new
93
+ m.to_a.each { |edge| reconstructed.add_edge(*edge) }
94
+ expect(outcomes).to include(reconstructed)
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,159 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe GraphMatching::Algorithm::MCMGeneral do
6
+ let(:graph_class) { GraphMatching::Graph::Graph }
7
+ let(:g) { graph_class.new }
8
+
9
+ describe '.new' do
10
+ it 'requires a Graph' do
11
+ expect { described_class.new("banana") }.to raise_error(TypeError)
12
+ end
13
+ end
14
+
15
+ describe '#match' do
16
+ def complete_graph(n)
17
+ g = graph_class.new
18
+ 1.upto(n - 1) do |i|
19
+ (i + 1).upto(n) do |j|
20
+ g.add_edge(i, j)
21
+ end
22
+ end
23
+ g
24
+ end
25
+
26
+ context 'empty graph' do
27
+ it 'returns empty set' do
28
+ expect(described_class.new(g).match).to be_empty
29
+ end
30
+ end
31
+
32
+ context 'single vertex' do
33
+ it 'returns empty set' do
34
+ g.add_vertex(1)
35
+ expect(described_class.new(g).match).to be_empty
36
+ end
37
+ end
38
+
39
+ context 'two vertexes' do
40
+ let(:g) { graph_class[1, 2] }
41
+
42
+ it 'returns one edge' do
43
+ m = described_class.new(g).match
44
+ expect(m.size).to eq(1)
45
+ expect(m).to match_edges [[1, 2]]
46
+ end
47
+ end
48
+
49
+ context 'complete graph with four vertexes' do
50
+ it 'returns two disjoint edges' do
51
+ g = graph_class[
52
+ 1, 2,
53
+ 1, 3,
54
+ 1, 4,
55
+ 2, 3,
56
+ 2, 4,
57
+ 3, 4
58
+ ]
59
+ m = described_class.new(g).match
60
+ expect(m.size).to eq(2)
61
+ expect(m.vertexes).to match_array([1, 2, 3, 4])
62
+ end
63
+ end
64
+
65
+ context 'graph with stem (123) and blossom (456)' do
66
+ it 'returns an expected result' do
67
+ g = graph_class[
68
+ 1, 2,
69
+ 2, 3,
70
+ 3, 4,
71
+ 4, 5,
72
+ 5, 6,
73
+ 6, 4
74
+ ]
75
+ m = described_class.new(g).match
76
+ expect(m.size).to eq(3)
77
+ expect(m).to match_edges [[1, 2], [3, 4], [5, 6]]
78
+ end
79
+ end
80
+
81
+ # TODO: Other algorithms (e.g. both MWM) support disconnected
82
+ # graphs. MCM General should too.
83
+ context 'disconnected graph' do
84
+ it 'raises a DisconnectedGraph error' do
85
+ 2.times { g.add_vertex(double) }
86
+ expect { described_class.new(g).match }.to \
87
+ raise_error(GraphMatching::DisconnectedGraph)
88
+ end
89
+ end
90
+
91
+ it 'simple example: graph with blossom (234)' do
92
+ g = graph_class[
93
+ 1, 2,
94
+ 2, 3,
95
+ 2, 4,
96
+ 3, 4,
97
+ 4, 5,
98
+ 5, 6
99
+ ]
100
+ m = described_class.new(g).match
101
+ expect(m.size).to eq(3)
102
+ expect(m).to match_edges [[1, 2], [3, 4], [5, 6]]
103
+ end
104
+
105
+ it 'example from West\'s Introduction to Graph Theory, p. 143' do
106
+ g = graph_class[
107
+ 1, 2,
108
+ 1, 8,
109
+ 2, 3,
110
+ 3, 4,
111
+ 3, 7,
112
+ 4, 5,
113
+ 4, 7,
114
+ 5, 6,
115
+ 7, 9,
116
+ 8, 9,
117
+ 10, 8
118
+ ]
119
+ m = described_class.new(g).match
120
+ expect(m.size).to eq(5)
121
+ expect(m).to match_edges [[1, 2], [3, 4], [5, 6], [7, 9], [8, 10]]
122
+ end
123
+
124
+ it 'example from Gabow (1976)' do
125
+ g = graph_class[
126
+ 1, 2,
127
+ 2, 3,
128
+ 1, 3,
129
+ 1, 10,
130
+ 3, 9,
131
+ 3, 4,
132
+ 4, 7,
133
+ 4, 8,
134
+ 7, 8,
135
+ 9, 5,
136
+ 5, 6,
137
+ 6, 7
138
+ ]
139
+ m = described_class.new(g).match
140
+ expect(m).to match_edges [[10, 1], [2, 3], [4, 8], [7, 6], [5, 9]]
141
+ end
142
+
143
+ it 'various complete graphs' do
144
+ [
145
+ [1, 0], # size of graph, expected size of matching
146
+ [2, 1],
147
+ [3, 1],
148
+ [4, 2],
149
+ [5, 2],
150
+ [6, 3],
151
+ [20, 10]
152
+ ].each do |test|
153
+ g = complete_graph(test[0])
154
+ m = described_class.new(g).match
155
+ expect(m.size).to eq(test[1])
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe GraphMatching::Algorithm::MWMBipartite do
6
+ let(:graph_class) { GraphMatching::Graph::WeightedBigraph }
7
+
8
+ describe '.new' do
9
+ it 'requires a WeightedBigraph' do
10
+ expect { described_class.new("banana") }.to raise_error(TypeError)
11
+ end
12
+ end
13
+
14
+ describe '#match' do
15
+ context 'empty graph' do
16
+ it 'returns the expected matching' do
17
+ g = graph_class.new
18
+ m = described_class.new(g).match
19
+ expect(m.size).to eq(0)
20
+ expect(m.weight(g)).to eq(0)
21
+ end
22
+ end
23
+
24
+ context 'trivial bigraph with two vertexes' do
25
+ it 'returns the expected matching' do
26
+ g = graph_class[[1, 2, 7]]
27
+ m = described_class.new(g).match
28
+ expect(m.size).to eq(1)
29
+ expect(m).to match_edges [[1, 2]]
30
+ expect(m.weight(g)).to eq(7)
31
+ end
32
+ end
33
+
34
+ context 'complete bigraph with three vertexes' do
35
+ it 'returns the expected matching' do
36
+ g = graph_class[
37
+ [1, 2, 1],
38
+ [1, 3, 2]
39
+ ]
40
+ m = described_class.new(g).match
41
+ expect(m).to match_edges [[1, 3]]
42
+ expect(m.weight(g)).to eq(2)
43
+ end
44
+
45
+ it 'supports negative weights' do
46
+ g = graph_class[
47
+ [1, 2, -1],
48
+ [1, 3, -3]
49
+ ]
50
+ m = described_class.new(g).match
51
+ expect(m).to match_edges [[1, 2]]
52
+ expect(m.weight(g)).to eq(-1)
53
+ end
54
+ end
55
+
56
+ context 'bigraph with two connected components' do
57
+ it 'returns one of two expected matchings' do
58
+ g = graph_class[
59
+ [1, 5, 3],
60
+ [2, 4, 2],
61
+ [2, 6, 2],
62
+ [3, 5, 3]
63
+ ]
64
+ m = described_class.new(g).match
65
+ expect(m.size).to eq(2)
66
+ expect(m.weight(g)).to eq(5)
67
+ end
68
+
69
+ it 'returns the expected matching' do
70
+ g = graph_class[
71
+ [1, 5, 4],
72
+ [2, 4, 2],
73
+ [2, 6, 1],
74
+ [3, 5, 3]
75
+ ]
76
+ m = described_class.new(g).match
77
+ expect(m).to match_edges [[1, 5], [2, 4]]
78
+ expect(m.weight(g)).to eq(6)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,439 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe GraphMatching::Algorithm::MWMGeneral do
6
+ let(:graph_class) { GraphMatching::Graph::WeightedGraph }
7
+
8
+ describe '.new' do
9
+ it 'requires a WeightedGraph' do
10
+ expect { described_class.new("banana") }.to raise_error(TypeError)
11
+ end
12
+ end
13
+
14
+ describe '#blossom_leaves' do
15
+ context 'five vertexes, one blossom' do
16
+ it 'returns array of leaves' do
17
+ g = graph_class[
18
+ [0, 1, 0],
19
+ [1, 2, 0],
20
+ [1, 3, 0],
21
+ [2, 3, 0],
22
+ [3, 4, 0]
23
+ ]
24
+ a = described_class.new(g)
25
+ a.instance_variable_set(:@blossom_children, [
26
+ nil,
27
+ nil,
28
+ nil,
29
+ nil,
30
+ nil,
31
+ [2, 3, 4]
32
+ ])
33
+ expect(a.send(:blossom_leaves, 0)).to eq([0])
34
+ expect(a.send(:blossom_leaves, 4)).to eq([4])
35
+ expect(a.send(:blossom_leaves, 5)).to eq([2, 3, 4])
36
+ end
37
+ end
38
+ end
39
+
40
+ describe '#match' do
41
+ context 'empty graph' do
42
+ it 'returns empty matching' do
43
+ g = graph_class.new
44
+ m = described_class.new(g).match(true)
45
+ expect(m).to be_empty
46
+ end
47
+ end
48
+
49
+ context 'single vertex' do
50
+ it 'returns empty matching' do
51
+ g = graph_class.new
52
+ g.add_vertex(0)
53
+ m = described_class.new(g).match(true)
54
+ expect(m).to be_empty
55
+ end
56
+ end
57
+
58
+ context 'two vertexes, but no edge' do
59
+ it 'returns empty matching' do
60
+ g = graph_class.new
61
+ g.add_vertex(0)
62
+ g.add_vertex(1)
63
+ m = described_class.new(g).match(true)
64
+ expect(m).to be_empty
65
+ end
66
+ end
67
+
68
+ context 'two vertexes' do
69
+ it 'returns matching of size 1' do
70
+ g = graph_class[[0, 1, 7]]
71
+ m = described_class.new(g).match(true)
72
+ expect(m).to match_edges [[0, 1]]
73
+ expect(m.weight(g)).to eq(7)
74
+ end
75
+ end
76
+
77
+ context 'three vertexes' do
78
+ it 'matches the edge with greater weight' do
79
+ g = graph_class[
80
+ [0, 1, 1],
81
+ [1, 2, 2],
82
+ [2, 0, 3]
83
+ ]
84
+ m = described_class.new(g).match(true)
85
+ expect(m).to match_edges [[0, 2]]
86
+ expect(m.weight(g)).to eq(3)
87
+ end
88
+ end
89
+
90
+ context 'greatest weight edge cannot be used in complete matching' do
91
+ it 'returns the complete matching with max. weight' do
92
+ # In the following graph, edge 1=3 has the greatest weight,
93
+ # but that edge cannot be used in a complete matching.
94
+ g = graph_class[
95
+ [0, 1, 2],
96
+ [1, 2, 0],
97
+ [1, 3, 6],
98
+ [2, 3, 4],
99
+ [3, 4, 2]
100
+ ]
101
+ m = described_class.new(g).match(true)
102
+ expect(m).to match_edges [[0, 1], [2, 3]]
103
+ expect(m.weight(g)).to eq(6)
104
+ end
105
+ end
106
+
107
+ it "passes Van Rantwijk test 12" do
108
+ g = graph_class[[1, 2, 10], [2, 3, 11]]
109
+ m = described_class.new(g).match(false)
110
+ expect(m).to match_edges [[2, 3]]
111
+ expect(m.weight(g)).to eq(11)
112
+ end
113
+
114
+ it "passes Van Rantwijk test 13" do
115
+ g = graph_class[
116
+ [1, 2, 5],
117
+ [2, 3, 11],
118
+ [3, 4, 5]
119
+ ]
120
+ m = described_class.new(g).match(false)
121
+ expect(m).to match_edges [[2, 3]]
122
+ expect(m.weight(g)).to eq(11)
123
+ end
124
+
125
+ it "passes Van Rantwijk test 14: max. cardinality" do
126
+ g = graph_class[
127
+ [1, 2, 5],
128
+ [2, 3, 11],
129
+ [3, 4, 5]
130
+ ]
131
+ m = described_class.new(g).match(true)
132
+ expect(m).to match_edges [[1, 2], [3, 4]]
133
+ expect(m.weight(g)).to eq(10)
134
+ end
135
+
136
+ it "passes Van Rantwijk test 15: floating-point weights" do
137
+ g = graph_class[
138
+ [1, 2, Math::PI],
139
+ [2, 3, Math.exp(1)],
140
+ [1, 3, 3.0],
141
+ [1, 4, Math.sqrt(2.0)]
142
+ ]
143
+ m = described_class.new(g).match(false)
144
+ expect(m).to match_edges [[1, 4], [2, 3]]
145
+ expect(m.weight(g)).to be_within(0.00001).of(Math.sqrt(2.0) + Math.exp(1))
146
+ end
147
+
148
+ it "passes Van Rantwijk test 16: negative weights" do
149
+ g = graph_class[
150
+ [1, 2, 2],
151
+ [1, 3, -2],
152
+ [2, 3, 1],
153
+ [2, 4, -1],
154
+ [3, 4, -6]
155
+ ]
156
+ m = described_class.new(g).match(false)
157
+ expect(m).to match_edges [[1, 2]]
158
+ expect(m.weight(g)).to eq(2)
159
+ m = described_class.new(g).match(true)
160
+ expect(m).to match_edges [[1, 3], [2, 4]]
161
+ expect(m.weight(g)).to eq(-3)
162
+ end
163
+
164
+ context "Van Rantwijk test 20: Uses S-blossom for augmentation" do
165
+ it "passes test 20-A" do
166
+ g = graph_class[
167
+ [1, 2, 8],
168
+ [1, 3, 9],
169
+ [2, 3, 10],
170
+ [3, 4, 7]
171
+ ]
172
+ m = described_class.new(g).match(false)
173
+ expect(m).to match_edges [[1, 2], [3, 4]]
174
+ expect(m.weight(g)).to eq(15)
175
+ end
176
+
177
+ it "passes test 20-B" do
178
+ g = graph_class[
179
+ [1, 2, 8],
180
+ [1, 3, 9],
181
+ [2, 3, 10],
182
+ [3, 4, 7],
183
+ [1, 6, 5],
184
+ [4, 5, 6]
185
+ ]
186
+ m = described_class.new(g).match(false)
187
+ expect(m).to match_edges [[1, 6], [2, 3], [4, 5]]
188
+ expect(m.weight(g)).to eq(21)
189
+ end
190
+ end
191
+
192
+ # Van Rantwijk test 21
193
+ context "create S-blossom, relabel as T-blossom, use for augmentation" do
194
+ it "passes test 21-A" do
195
+ g = graph_class[
196
+ [1, 2, 9],
197
+ [1, 3, 8],
198
+ [2, 3, 10],
199
+ [1, 4, 5],
200
+ [4, 5, 4],
201
+ [1, 6, 3]
202
+ ]
203
+ m = described_class.new(g).match(false)
204
+ expect(m).to match_edges [[1, 6], [2, 3], [4, 5]]
205
+ end
206
+
207
+ it "passes test 21-B" do
208
+ g = graph_class[
209
+ [1, 2, 9],
210
+ [1, 3, 8],
211
+ [2, 3, 10],
212
+ [1, 4, 5],
213
+ [4, 5, 3],
214
+ [1, 6, 4]
215
+ ]
216
+ m = described_class.new(g).match(false)
217
+ expect(m).to match_edges [[1, 6], [2, 3], [4, 5]]
218
+ end
219
+
220
+ it "passes test 21-C" do
221
+ g = graph_class[
222
+ [1, 2, 9],
223
+ [1, 3, 8],
224
+ [2, 3, 10],
225
+ [1, 4, 5],
226
+ [4, 5, 3],
227
+ [3, 6, 4]
228
+ ]
229
+ m = described_class.new(g).match(false)
230
+ expect(m).to match_edges [[1, 2], [3, 6], [4, 5]]
231
+ end
232
+ end
233
+
234
+ context "Van Rantwijk test 22" do
235
+ it "creates nested S-blossom, uses for augmentation" do
236
+ g = graph_class[
237
+ [1, 2, 9],
238
+ [1, 3, 9],
239
+ [2, 3, 10],
240
+ [2, 4, 8],
241
+ [3, 5, 8],
242
+ [4, 5, 10],
243
+ [5, 6, 6]
244
+ ]
245
+ m = described_class.new(g).match(false)
246
+ expect(m).to match_edges [[1, 3], [2, 4], [5, 6]]
247
+ end
248
+ end
249
+
250
+ context "Van Rantwijk test 23" do
251
+ it "create S-blossom, relabel as S, include in nested S-blossom" do
252
+ g = graph_class[
253
+ [1, 2, 10],
254
+ [1, 7, 10],
255
+ [2, 3, 12],
256
+ [3, 4, 20],
257
+ [3, 5, 20],
258
+ [4, 5, 25],
259
+ [5, 6, 10],
260
+ [6, 7, 10],
261
+ [7, 8, 8]
262
+ ]
263
+ m = described_class.new(g).match(false)
264
+ expect(m).to match_edges [[1, 2], [3, 4], [5, 6], [7, 8]]
265
+ end
266
+ end
267
+
268
+ context "Van Rantwijk test 24" do
269
+ it "create nested S-blossom, augment, expand recursively" do
270
+ g = graph_class[
271
+ [1, 2, 8],
272
+ [1, 3, 8],
273
+ [2, 3, 10],
274
+ [2, 4, 12],
275
+ [3, 5, 12],
276
+ [4, 5, 14],
277
+ [4, 6, 12],
278
+ [5, 7, 12],
279
+ [6, 7, 14],
280
+ [7, 8, 12]
281
+ ]
282
+ m = described_class.new(g).match(false)
283
+ expect(m).to match_edges [[1, 2], [3, 5], [4, 6], [7, 8]]
284
+ end
285
+ end
286
+
287
+ context "Van Rantwijk test 25" do
288
+ it "create S-blossom, relabel as T, expand" do
289
+ g = graph_class[
290
+ [1, 2, 23],
291
+ [1, 5, 22],
292
+ [1, 6, 15],
293
+ [2, 3, 25],
294
+ [3, 4, 22],
295
+ [4, 5, 25],
296
+ [4, 8, 14],
297
+ [5, 7, 13]
298
+ ]
299
+ m = described_class.new(g).match(false)
300
+ expect(m).to match_edges [[1, 6], [2, 3], [4, 8], [5, 7]]
301
+ end
302
+ end
303
+
304
+ context "Van Rantwijk test 26" do
305
+ it "create nested S-blossom, relabel as T, expand" do
306
+ g = graph_class[
307
+ [1, 2, 19],
308
+ [1, 3, 20],
309
+ [1, 8, 8],
310
+ [2, 3, 25],
311
+ [2, 4, 18],
312
+ [3, 5, 18],
313
+ [4, 5, 13],
314
+ [4, 7, 7],
315
+ [5, 6, 7]
316
+ ]
317
+ m = described_class.new(g).match(false)
318
+ expect(m).to match_edges [[1, 8], [2, 3], [4, 7], [5, 6]]
319
+ end
320
+ end
321
+
322
+ context "Van Rantwijk test 30" do
323
+ it "create blossom, relabel as T in more than one way, expand, augment" do
324
+ g = graph_class[
325
+ [1, 2, 45],
326
+ [1, 5, 45],
327
+ [2, 3, 50],
328
+ [3, 4, 45],
329
+ [4, 5, 50],
330
+ [1, 6, 30],
331
+ [3, 9, 35],
332
+ [4, 8, 35],
333
+ [5, 7, 26],
334
+ [9, 10, 5]
335
+ ]
336
+ m = described_class.new(g).match(false)
337
+ expect(m).to match_edges [[1, 6], [2, 3], [4, 8], [5, 7], [9, 10]]
338
+ end
339
+ end
340
+
341
+ context "Van Rantwijk test 31" do
342
+ it "similar to test 30, but slightly different" do
343
+ g = graph_class[
344
+ [1, 2, 45],
345
+ [1, 5, 45],
346
+ [2, 3, 50],
347
+ [3, 4, 45],
348
+ [4, 5, 50],
349
+ [1, 6, 30],
350
+ [3, 9, 35],
351
+ [4, 8, 26], # differs from test 30
352
+ [5, 7, 40], # differs from test 30
353
+ [9, 10, 5]
354
+ ]
355
+ m = described_class.new(g).match(false)
356
+ expect(m).to match_edges [[1, 6], [2, 3], [4, 8], [5, 7], [9, 10]]
357
+ end
358
+ end
359
+
360
+ context "Van Rantwijk test 32" do
361
+ # create blossom, relabel as T, expand such that a new
362
+ # least-slack S-to-free edge is produced, augment
363
+ it "see comment" do
364
+ g = graph_class[
365
+ [1, 2, 45],
366
+ [1, 5, 45],
367
+ [2, 3, 50],
368
+ [3, 4, 45],
369
+ [4, 5, 50],
370
+ [1, 6, 30],
371
+ [3, 9, 35],
372
+ [4, 8, 28],
373
+ [5, 7, 26],
374
+ [9, 10, 5]
375
+ ]
376
+ m = described_class.new(g).match(false)
377
+ expect(m).to match_edges [[1, 6], [2, 3], [4, 8], [5, 7], [9, 10]]
378
+ end
379
+ end
380
+
381
+ context "Van Rantwijk test 33" do
382
+ # create nested blossom, relabel as T in more than one way,
383
+ # expand outer blossom such that inner blossom ends up on
384
+ # an augmenting path
385
+ it "see comment" do
386
+ g = graph_class[
387
+ [1, 2, 45],
388
+ [1, 7, 45],
389
+ [2, 3, 50],
390
+ [3, 4, 45],
391
+ [4, 5, 95],
392
+ [4, 6, 94],
393
+ [5, 6, 94],
394
+ [6, 7, 50],
395
+ [1, 8, 30],
396
+ [3, 11, 35],
397
+ [5, 9, 36],
398
+ [7, 10, 26],
399
+ [11, 12, 5]
400
+ ]
401
+ m = described_class.new(g).match(false)
402
+ expect(m).to match_edges [
403
+ [1, 8],
404
+ [2, 3],
405
+ [4, 6],
406
+ [5, 9],
407
+ [7, 10],
408
+ [11, 12]
409
+ ]
410
+ end
411
+ end
412
+
413
+ context "Van Rantwijk test 34: nest, relabel, expand" do
414
+ it "create nested S-blossom, relabel as S, expand recursively" do
415
+ g = graph_class[
416
+ [1, 2, 40],
417
+ [1, 3, 40],
418
+ [2, 3, 60],
419
+ [2, 4, 55],
420
+ [3, 5, 55],
421
+ [4, 5, 50],
422
+ [1, 8, 15],
423
+ [5, 7, 30],
424
+ [7, 6, 10],
425
+ [8, 10, 10],
426
+ [4, 9, 30]
427
+ ]
428
+ m = described_class.new(g).match(false)
429
+ expect(m).to match_edges [
430
+ [1, 2],
431
+ [3, 5],
432
+ [4, 9],
433
+ [6, 7],
434
+ [8, 10]
435
+ ]
436
+ end
437
+ end
438
+ end
439
+ end