graph_matching 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +35 -0
  3. data/.rubocop_todo.yml +29 -256
  4. data/CHANGELOG.md +8 -2
  5. data/Rakefile +3 -3
  6. data/benchmark/mcm_bipartite/complete_bigraphs/benchmark.rb +1 -1
  7. data/benchmark/mcm_general/complete_graphs/benchmark.rb +1 -1
  8. data/benchmark/mwm_bipartite/complete_bigraphs/benchmark.rb +1 -1
  9. data/benchmark/mwm_bipartite/misc/calc_d2/benchmark.rb +1 -5
  10. data/benchmark/mwm_general/complete_graphs/benchmark.rb +2 -2
  11. data/benchmark/mwm_general/incomplete_graphs/benchmark.rb +6 -7
  12. data/graph_matching.gemspec +7 -11
  13. data/lib/graph_matching/algorithm/mcm_bipartite.rb +9 -8
  14. data/lib/graph_matching/algorithm/mcm_general.rb +2 -2
  15. data/lib/graph_matching/algorithm/mwm_bipartite.rb +25 -25
  16. data/lib/graph_matching/algorithm/mwm_general.rb +121 -117
  17. data/lib/graph_matching/algorithm/mwmg_delta_assertions.rb +47 -50
  18. data/lib/graph_matching/assertion.rb +4 -4
  19. data/lib/graph_matching/core_ext/set.rb +1 -1
  20. data/lib/graph_matching/graph/bigraph.rb +1 -1
  21. data/lib/graph_matching/graph/graph.rb +1 -1
  22. data/lib/graph_matching/graph/weighted.rb +11 -8
  23. data/lib/graph_matching/integer_vertexes.rb +2 -2
  24. data/lib/graph_matching/version.rb +1 -1
  25. data/lib/graph_matching/visualize.rb +5 -5
  26. data/profile/mwm_general/profile.rb +1 -1
  27. data/spec/graph_matching/algorithm/mcm_bipartite_spec.rb +1 -1
  28. data/spec/graph_matching/algorithm/mcm_general_spec.rb +1 -1
  29. data/spec/graph_matching/algorithm/mwm_bipartite_spec.rb +1 -1
  30. data/spec/graph_matching/algorithm/mwm_general_spec.rb +44 -41
  31. data/spec/graph_matching/graph/graph_spec.rb +4 -4
  32. data/spec/graph_matching/integer_vertexes_spec.rb +2 -2
  33. data/spec/spec_helper.rb +2 -2
  34. metadata +17 -17
@@ -28,6 +28,6 @@ MIN_SIZE.upto(MAX_SIZE) do |v|
28
28
  print "%5d\t" % [v]
29
29
  g = complete_bigraph(v)
30
30
  GC.disable
31
- puts Benchmark.realtime { g.maximum_cardinality_matching }
31
+ puts(Benchmark.realtime { g.maximum_cardinality_matching })
32
32
  GC.enable
33
33
  end
@@ -25,6 +25,6 @@ MIN_SIZE.upto(MAX_SIZE) do |v|
25
25
  print "%5d\t" % [v]
26
26
  g = complete_graph(v)
27
27
  GC.disable
28
- puts Benchmark.realtime { g.maximum_cardinality_matching }
28
+ puts(Benchmark.realtime { g.maximum_cardinality_matching })
29
29
  GC.enable
30
30
  end
@@ -38,6 +38,6 @@ MIN_SIZE.upto(MAX_SIZE) do |v|
38
38
  print "%5d\t" % [v]
39
39
  g = complete_weighted_bigraph(v)
40
40
  GC.disable
41
- puts Benchmark.realtime { g.maximum_weighted_matching }
41
+ puts(Benchmark.realtime { g.maximum_weighted_matching })
42
42
  GC.enable
43
43
  end
@@ -21,9 +21,5 @@ t = Set.new
21
21
  s = Set.new(dogs)
22
22
 
23
23
  GC.disable
24
- puts Benchmark.realtime {
25
- 100_000.times do
26
- a.send(:calc_d2, s, t, u)
27
- end
28
- }
24
+ puts(Benchmark.realtime { 100_000.times { a.send(:calc_d2, s, t, u) } })
29
25
  GC.enable
@@ -13,7 +13,7 @@ $stdout.sync = true
13
13
 
14
14
  def complete_graph(n)
15
15
  g = GraphMatching::Graph::WeightedGraph.new
16
- n_edges = (1 .. n - 1).reduce(:+)
16
+ n_edges = (1..n - 1).reduce(:+)
17
17
  0.upto(n - 2) do |i|
18
18
  (i + 1).upto(n - 1) do |j|
19
19
  g.add_edge(i, j)
@@ -27,6 +27,6 @@ MIN_SIZE.upto(MAX_SIZE) do |v|
27
27
  print "%5d\t" % [v]
28
28
  g = complete_graph(v)
29
29
  GC.disable
30
- puts Benchmark.realtime { g.maximum_weighted_matching(true) }
30
+ puts(Benchmark.realtime { g.maximum_weighted_matching(true) })
31
31
  GC.enable
32
32
  end
@@ -17,14 +17,13 @@ $stdout.sync = true
17
17
  def incomplete_graph(n, completeness)
18
18
  g = GraphMatching::Graph::WeightedGraph.new
19
19
  0.upto(n - 1) do |i| g.add_vertex(i) end
20
- max_weight = ((1 .. n - 1).reduce(:+).to_f * completeness).to_i + 1
20
+ max_weight = ((1..n - 1).reduce(:+).to_f * completeness).to_i + 1
21
21
  0.upto(n - 2) do |i|
22
22
  (i + 1).upto(n - 1) do |j|
23
- if rand < completeness
24
- g.add_edge(i, j)
25
- w = rand(max_weight)
26
- g.set_w([i, j], w)
27
- end
23
+ next unless rand < completeness
24
+ g.add_edge(i, j)
25
+ w = rand(max_weight)
26
+ g.set_w([i, j], w)
28
27
  end
29
28
  end
30
29
  g
@@ -34,6 +33,6 @@ MIN_SIZE.upto(MAX_SIZE) do |v|
34
33
  print "%5d\t" % [v]
35
34
  g = incomplete_graph(v, COMPLETENESS)
36
35
  GC.disable
37
- puts Benchmark.realtime { g.maximum_weighted_matching(true) }
36
+ puts(Benchmark.realtime { g.maximum_weighted_matching(true) })
38
37
  GC.enable
39
38
  end
@@ -15,21 +15,17 @@ Gem::Specification.new do |spec|
15
15
  EOS
16
16
  spec.homepage = 'https://github.com/jaredbeck/graph_matching'
17
17
  spec.license = 'MIT'
18
-
19
18
  spec.files = `git ls-files -z`.split("\x0")
20
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
20
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
21
  spec.require_paths = ['lib']
23
-
24
- spec.required_ruby_version = '>= 2.0.0'
25
-
22
+ spec.required_ruby_version = '>= 2.2.0'
26
23
  spec.add_runtime_dependency 'rgl', '~> 0.5.0'
27
-
28
24
  spec.add_development_dependency 'bundler', '~> 1.12'
29
- spec.add_development_dependency 'pry-nav', '~> 0.2.4'
30
- spec.add_development_dependency 'rspec-core', '~> 3.2'
31
- spec.add_development_dependency 'rspec-expectations', '~> 3.2'
32
- spec.add_development_dependency 'rspec-mocks', '~> 3.2'
33
- spec.add_development_dependency 'rake', '~> 11.2'
34
- spec.add_development_dependency 'rubocop', '~> 0.42.0'
25
+ spec.add_development_dependency 'pry-byebug', '~> 3.4'
26
+ spec.add_development_dependency 'rspec-core', '~> 3.6'
27
+ spec.add_development_dependency 'rspec-expectations', '~> 3.6'
28
+ spec.add_development_dependency 'rspec-mocks', '~> 3.6'
29
+ spec.add_development_dependency 'rake', '~> 12.0'
30
+ spec.add_development_dependency 'rubocop', '~> 0.48.1'
35
31
  end
@@ -9,18 +9,16 @@ module GraphMatching
9
9
  # `MCMBipartite` implements Maximum Cardinality Matching in
10
10
  # bipartite graphs.
11
11
  class MCMBipartite < MatchingAlgorithm
12
-
13
12
  def initialize(graph)
14
13
  assert(graph).is_a(Graph::Bigraph)
15
14
  super
16
15
  end
17
16
 
18
17
  def match
19
- u, v = g.partition
18
+ u = g.partition[0]
20
19
  m = []
21
20
 
22
- while true
23
-
21
+ loop do
24
22
  # Begin each stage by clearing all labels and marks
25
23
  t = []
26
24
  predecessors = {}
@@ -31,11 +29,14 @@ module GraphMatching
31
29
  unmarked = r = u.select { |i| m[i] == nil }
32
30
 
33
31
  # While there are unmarked R-vertexes
34
- while aug_path.nil? && start = unmarked.pop
32
+ loop do
33
+ break unless aug_path.nil?
34
+ start = unmarked.pop
35
+ break unless start
35
36
 
36
37
  # Follow the unmatched edges (if any) to vertexes in V
37
38
  # ignoring any V-vertexes already labeled T
38
- unlabeled_across_unmatched_edges_from(start, g, m ,t).each do |vi|
39
+ unlabeled_across_unmatched_edges_from(start, g, m, t).each do |vi|
39
40
  t << vi
40
41
  predecessors[vi] = start
41
42
 
@@ -70,7 +71,7 @@ module GraphMatching
70
71
 
71
72
  def assert_valid_aug_path(path)
72
73
  unless path.length >= 2 && path.length.even?
73
- raise "Invalid augmenting path"
74
+ raise 'Invalid augmenting path'
74
75
  end
75
76
  end
76
77
 
@@ -93,7 +94,7 @@ module GraphMatching
93
94
 
94
95
  def backtrack_from(end_vertex, predecessors)
95
96
  augmenting_path = [end_vertex]
96
- while predecessors.has_key?(augmenting_path.last)
97
+ while predecessors.key?(augmenting_path.last)
97
98
  augmenting_path.push(predecessors[augmenting_path.last])
98
99
  end
99
100
  augmenting_path
@@ -57,7 +57,7 @@ module GraphMatching
57
57
  # assign a start label and begin a new search)
58
58
  # set LABEL(u) = FIRST(u) = 0
59
59
 
60
- while true do
60
+ loop do
61
61
  u += 1
62
62
  break if u > g.size
63
63
  if mate[u] != 0
@@ -275,7 +275,7 @@ module GraphMatching
275
275
  r(x, y, label, mate)
276
276
  r(y, x, label, mate)
277
277
  else
278
- fail "Vertex #{v} has an unexpected label type"
278
+ raise "Vertex #{v} has an unexpected label type"
279
279
  end
280
280
  end
281
281
 
@@ -28,8 +28,7 @@ module GraphMatching
28
28
  u = init_duals(cats, dogs)
29
29
 
30
30
  # For each stage
31
- while true do
32
-
31
+ loop do
33
32
  # Clear all labels and marks
34
33
  # Label all single dogs with S
35
34
  aug_path = nil
@@ -39,29 +38,31 @@ module GraphMatching
39
38
  q = s.dup.to_a
40
39
 
41
40
  # While searching
42
- while aug_path.nil? && i = q.pop do
41
+ loop do
42
+ break unless aug_path.nil?
43
+ i = q.pop
44
+ break unless i
43
45
 
44
46
  # Follow the unmatched edges (if any) to free (unlabeled)
45
47
  # cats. Only consider edges with slack (π) of 0.
46
- unlabeled_across_unmatched_edges_from(i, g, m ,t).each do |j|
47
- if π(u, i, j) == 0
48
- t << j
49
- predecessors[j] = i
50
-
51
- # If there are matched edges, follow each to a dog and
52
- # label the dog with S. Otherwise, backtrack to
53
- # construct an augmenting path.
54
- m_dogs = matched_adjacent(j, i, g, m)
55
-
56
- m_dogs.each do |md|
57
- s << md
58
- predecessors[md] = j
59
- end
60
-
61
- if m_dogs.empty?
62
- aug_path = backtrack_from(j, predecessors)
63
- break
64
- end
48
+ unlabeled_across_unmatched_edges_from(i, g, m, t).each do |j|
49
+ next unless slack(u, i, j) == 0
50
+ t << j
51
+ predecessors[j] = i
52
+
53
+ # If there are matched edges, follow each to a dog and
54
+ # label the dog with S. Otherwise, backtrack to
55
+ # construct an augmenting path.
56
+ m_dogs = matched_adjacent(j, i, g, m)
57
+
58
+ m_dogs.each do |md|
59
+ s << md
60
+ predecessors[md] = j
61
+ end
62
+
63
+ if m_dogs.empty?
64
+ aug_path = backtrack_from(j, predecessors)
65
+ break
65
66
  end
66
67
  end
67
68
  end
@@ -116,7 +117,7 @@ module GraphMatching
116
117
  s.each do |s_dog|
117
118
  g.each_adjacent(s_dog) do |cat|
118
119
  unless t.include?(cat)
119
- slacks.push π(u, s_dog, cat)
120
+ slacks.push slack(u, s_dog, cat)
120
121
  end
121
122
  end
122
123
  end
@@ -138,10 +139,9 @@ module GraphMatching
138
139
 
139
140
  # Returns the "slack" of an edge (Galil, 1986, p.30), the
140
141
  # difference between an edge's duals and its weight.
141
- def π(u, i, j)
142
+ def slack(u, i, j)
142
143
  u[i] + u[j] - @weight[i - 1][j - 1]
143
144
  end
144
-
145
145
  end
146
146
  end
147
147
  end
@@ -19,7 +19,7 @@ module GraphMatching
19
19
  LBL_S = 1
20
20
  LBL_T = 2
21
21
  LBL_CRUMB = 5
22
- LBL_NAMES = %w(Free S T Crumb)
22
+ LBL_NAMES = %w(Free S T Crumb).freeze
23
23
 
24
24
  def initialize(graph)
25
25
  assert(graph).is_a(Graph::WeightedGraph)
@@ -42,13 +42,12 @@ module GraphMatching
42
42
 
43
43
  # Iterative *stages*. Each stage augments the matching.
44
44
  # There can be at most n stages, where n is num. vertexes.
45
- while true do
45
+ loop do
46
46
  init_stage
47
47
 
48
48
  # *sub-stages* either augment or scale the duals.
49
49
  augmented = false
50
- while true do
51
-
50
+ loop do
52
51
  # > The search is conducted by scanning the S-vertices
53
52
  # > in turn. (Galil, 1986, p. 26)
54
53
  until augmented || @queue.empty?
@@ -60,9 +59,9 @@ module GraphMatching
60
59
  # > There is no augmenting path under these constraints;
61
60
  # > compute delta and reduce slack in the optimization problem.
62
61
  # > (Van Rantwijk, mwmatching.py, line 732)
63
- delta, delta_type, delta_edge, delta_blossom = calc_delta(max_cardinality)
62
+ delta, d_type, d_edge, d_blossom = calc_delta(max_cardinality)
64
63
  update_duals(delta)
65
- optimum = act_on_minimum_delta(delta_type, delta_edge, delta_blossom)
64
+ optimum = act_on_minimum_delta(d_type, d_edge, d_blossom)
66
65
  break if optimum
67
66
  end
68
67
 
@@ -91,21 +90,23 @@ module GraphMatching
91
90
  @tight_edge[delta_edge] = true
92
91
  i, j = @edges[delta_edge].to_a
93
92
  if @label[@in_blossom[i]] == LBL_FREE
94
- i, j = j, i
93
+ # Think of this as swapping i and j, but the corresponding
94
+ # assignment `j = i` happens to be unnecessary.
95
+ i = j
95
96
  end
96
97
  assert_label(@in_blossom[i], LBL_S)
97
98
  @queue.push(i)
98
99
  when 3
99
100
  # > Use the least-slack edge to continue the search.
100
101
  @tight_edge[delta_edge] = true
101
- i, j = @edges[delta_edge].to_a
102
+ i = @edges[delta_edge].to_a[0]
102
103
  assert_label(@in_blossom[i], LBL_S)
103
104
  @queue.push(i)
104
105
  when 4
105
106
  # > Expand the least-z blossom.
106
107
  expand_blossom(delta_blossom, false)
107
108
  else
108
- fail "Invalid delta_type: #{delta_type}"
109
+ raise "Invalid delta_type: #{delta_type}"
109
110
  end
110
111
  optimum
111
112
  end
@@ -138,19 +139,19 @@ module GraphMatching
138
139
  #
139
140
  @blossom_children[b] = []
140
141
  @blossom_endps[b] = []
141
- trace_to_base(bv, bb) do |bv|
142
- @blossom_parent[bv] = b
143
- @blossom_children[b] << bv
144
- @blossom_endps[b] << @label_end[bv]
142
+ trace_to_base(bv, bb) do |bv2|
143
+ @blossom_parent[bv2] = b
144
+ @blossom_children[b] << bv2
145
+ @blossom_endps[b] << @label_end[bv2]
145
146
  end
146
147
  @blossom_children[b] << bb
147
148
  @blossom_children[b].reverse!
148
149
  @blossom_endps[b].reverse!
149
150
  @blossom_endps[b] << 2 * k
150
- trace_to_base(bw, bb) do |bw|
151
- @blossom_parent[bw] = b
152
- @blossom_children[b] << bw
153
- @blossom_endps[b] << (@label_end[bw] ^ 1)
151
+ trace_to_base(bw, bb) do |bw2|
152
+ @blossom_parent[bw2] = b
153
+ @blossom_children[b] << bw2
154
+ @blossom_endps[b] << (@label_end[bw2] ^ 1)
154
155
  end
155
156
 
156
157
  # > Set label to S
@@ -162,58 +163,58 @@ module GraphMatching
162
163
  @dual[b] = 0
163
164
 
164
165
  # > Relabel vertices.
165
- blossom_leaves(b).each do |v|
166
- if @label[@in_blossom[v]] == LBL_T
166
+ blossom_leaves(b).each do |leaf|
167
+ if @label[@in_blossom[leaf]] == LBL_T
167
168
  # > This T-vertex now turns into an S-vertex because it
168
169
  # > becomes part of an S-blossom; add it to the queue.
169
- @queue << v
170
+ @queue << leaf
170
171
  end
171
- @in_blossom[v] = b
172
+ @in_blossom[leaf] = b
172
173
  end
173
174
 
174
175
  # > Compute blossombestedges[b].
175
176
  best_edge_to = rantwijk_array(nil)
176
- @blossom_children[b].each do |bv|
177
- if @blossom_best_edges[bv].nil?
177
+ @blossom_children[b].each do |child|
178
+ if @blossom_best_edges[child].nil?
178
179
  # > This subblossom [bv] does not have a list of least-
179
180
  # > slack edges. Get the information from the vertices.
180
- nblists = blossom_leaves(bv).map { |v|
181
- @neighb_end[v].map { |p|
182
- p / 2 # floor division
181
+ nblists = blossom_leaves(child).map { |leaf|
182
+ @neighb_end[leaf].map { |p|
183
+ p / 2 # Intentional floor division
183
184
  }
184
185
  }
185
186
  else
186
187
  # > Walk this subblossom's least-slack edges.
187
- nblists = [@blossom_best_edges[bv]]
188
+ nblists = [@blossom_best_edges[child]]
188
189
  end
189
190
 
190
191
  nblists.each do |nblist|
191
- nblist.each do |k|
192
- i, j = @edges[k].to_a
192
+ nblist.each do |x|
193
+ i, j = @edges[x].to_a
193
194
  if @in_blossom[j] == b
194
- i, j = j, i
195
+ # Think of this as swapping i and j, but the corresponding
196
+ # assignment `i = j` happens to be unnecessary.
197
+ j = i
195
198
  end
196
199
  bj = @in_blossom[j]
197
- if bj != b &&
198
- @label[bj] == LBL_S &&
199
- (best_edge_to[bj] == nil || slack(k) < slack(best_edge_to[bj]))
200
- best_edge_to[bj] = k
200
+ if better_edge_to?(bj, x, b, best_edge_to)
201
+ best_edge_to[bj] = x
201
202
  end
202
203
  end
203
204
  end
204
205
 
205
206
  # > Forget about least-slack edges of the subblossom.
206
- @blossom_best_edges[bv] = nil
207
- @best_edge[bv] = nil
207
+ @blossom_best_edges[child] = nil
208
+ @best_edge[child] = nil
208
209
  end
209
210
 
210
211
  @blossom_best_edges[b] = best_edge_to.compact
211
212
 
212
213
  # > Select bestedge[b]
213
214
  @best_edge[b] = nil
214
- @blossom_best_edges[b].each do |k|
215
- if @best_edge[b].nil? || slack(k) < slack(@best_edge[b])
216
- @best_edge[b] = k
215
+ @blossom_best_edges[b].each do |edge|
216
+ if @best_edge[b].nil? || slack(edge) < slack(@best_edge[b])
217
+ @best_edge[b] = edge
217
218
  end
218
219
  end
219
220
  end
@@ -223,7 +224,7 @@ module GraphMatching
223
224
  s = @label[b] == LBL_S
224
225
  m = @label_end[b] == @mate[@blossom_base[b]]
225
226
  unless t || s && m
226
- fail <<-EOS
227
+ raise <<-EOS
227
228
  Assertion failed: Expected either:
228
229
  1. Current Bv to be a T-blossom, or
229
230
  2. Bv is an S-blossom and its base is matched to @label_end[bv]
@@ -233,7 +234,7 @@ module GraphMatching
233
234
 
234
235
  def assert_label(ix, lbl)
235
236
  unless @label[ix] == lbl
236
- fail "Expected label at #{ix} to be #{LBL_NAMES[lbl]}"
237
+ raise "Expected label at #{ix} to be #{LBL_NAMES[lbl]}"
237
238
  end
238
239
  end
239
240
 
@@ -258,7 +259,7 @@ module GraphMatching
258
259
  # with an external mate.)
259
260
  base = @blossom_base[b]
260
261
  if @mate[base].nil?
261
- fail "Expected blossom #{b}'s base (#{base}) to be matched"
262
+ raise "Expected blossom #{b}'s base (#{base}) to be matched"
262
263
  end
263
264
 
264
265
  # Assign label S to the mate of blossom b's base.
@@ -266,9 +267,13 @@ module GraphMatching
266
267
  # The bitwise XOR is very clever. `mate[x]` and `mate[x] ^ 1`
267
268
  # are connected "endpoints".
268
269
  base_edge_endpoints = [@mate[base], @mate[base] ^ 1]
269
- assign_label(@endpoint[base_edge_endpoints[0]], LBL_S, base_edge_endpoints[1])
270
+ assign_label(
271
+ @endpoint[base_edge_endpoints[0]],
272
+ LBL_S,
273
+ base_edge_endpoints[1]
274
+ )
270
275
  else
271
- fail ArgumentError, "Unexpected label: #{t}"
276
+ raise ArgumentError, "Unexpected label: #{t}"
272
277
  end
273
278
  end
274
279
 
@@ -329,7 +334,7 @@ module GraphMatching
329
334
  # > until we find a single vertex, swapping matched and unmatched
330
335
  # > edges as we go.
331
336
  # > (Van Rantwijk, mwmatching.py, line 504)
332
- while true
337
+ loop do
333
338
  bs = @in_blossom[s]
334
339
  assert_label(bs, LBL_S)
335
340
  assert(@label_end[bs]).eq(@mate[@blossom_base[bs]])
@@ -361,6 +366,12 @@ module GraphMatching
361
366
  end
362
367
  end
363
368
 
369
+ def better_edge_to?(bj, k, b, best_edge_to)
370
+ bj != b &&
371
+ @label[bj] == LBL_S &&
372
+ (best_edge_to[bj] == nil || slack(k) < slack(best_edge_to[bj]))
373
+ end
374
+
364
375
  # TODO: Optimize by returning lazy iterator
365
376
  def blossom_leaves(b, ary = [])
366
377
  if b < @nvertex
@@ -391,7 +402,7 @@ module GraphMatching
391
402
  jstep = -1
392
403
  endptrick = 1
393
404
  end
394
- return j, jstep, endptrick
405
+ [j, jstep, endptrick]
395
406
  end
396
407
 
397
408
  def calc_delta(max_cardinality)
@@ -410,41 +421,40 @@ module GraphMatching
410
421
  # > Compute delta2: the minimum slack on any edge between
411
422
  # > an S-vertex and a free vertex.
412
423
  # > (Van Rantwijk, mwmatching.py)
413
- (0 ... @nvertex).each do |v|
414
- if @label[@in_blossom[v]] == LBL_FREE && !@best_edge[v].nil?
415
- d = slack(@best_edge[v])
416
- if delta_type == nil || d < delta
417
- delta = d
418
- delta_type = 2
419
- delta_edge = @best_edge[v]
420
- end
421
- end
424
+ (0...@nvertex).each do |v|
425
+ next unless @label[@in_blossom[v]] == LBL_FREE && !@best_edge[v].nil?
426
+ d = slack(@best_edge[v])
427
+ next unless delta_type == nil || d < delta
428
+ delta = d
429
+ delta_type = 2
430
+ delta_edge = @best_edge[v]
422
431
  end
423
432
 
424
433
  # > Compute delta3: half the minimum slack on any edge between
425
434
  # > a pair of S-blossoms.
426
435
  # > (Van Rantwijk, mwmatching.py)
427
- (0 ... 2 * @nvertex).each do |b|
428
- if @blossom_parent[b].nil? && @label[b] == LBL_S && !@best_edge[b].nil?
429
- kslack = slack(@best_edge[b])
430
- d = kslack / 2 # Van Rantwijk had some type checking here. Why?
431
- if delta_type.nil? || d < delta
432
- delta = d
433
- delta_type = 3
434
- delta_edge = @best_edge[b]
435
- end
436
+ (0...2 * @nvertex).each do |b|
437
+ unless @blossom_parent[b].nil? &&
438
+ @label[b] == LBL_S &&
439
+ !@best_edge[b].nil?
440
+ next
436
441
  end
442
+ kslack = slack(@best_edge[b])
443
+ d = kslack / 2 # Van Rantwijk had some type checking here. Why?
444
+ next unless delta_type.nil? || d < delta
445
+ delta = d
446
+ delta_type = 3
447
+ delta_edge = @best_edge[b]
437
448
  end
438
449
 
439
450
  # > Compute delta4: minimum z variable of any T-blossom.
440
451
  # > (Van Rantwijk, mwmatching.py)
441
- (@nvertex ... 2 * @nvertex).each do |b|
452
+ (@nvertex...2 * @nvertex).each do |b|
442
453
  top_t_blossom = top_level_blossom?(b) && @label[b] == LBL_T
443
- if top_t_blossom && (delta_type.nil? || @dual[b] < delta)
444
- delta = @dual[b]
445
- delta_type = 4
446
- delta_blossom = b
447
- end
454
+ next unless top_t_blossom && (delta_type.nil? || @dual[b] < delta)
455
+ delta = @dual[b]
456
+ delta_type = 4
457
+ delta_blossom = b
448
458
  end
449
459
 
450
460
  if delta_type.nil?
@@ -457,7 +467,7 @@ module GraphMatching
457
467
  delta = [0, @dual[0, @nvertex].min].max
458
468
  end
459
469
 
460
- return delta, delta_type, delta_edge, delta_blossom
470
+ [delta, delta_type, delta_edge, delta_blossom]
461
471
  end
462
472
 
463
473
  # Returns nil if `k` is known to be an endpoint of a tight
@@ -609,16 +619,20 @@ module GraphMatching
609
619
 
610
620
  # > Relabel the T-sub-blossom.
611
621
  @label[@endpoint[p ^ 1]] = LBL_FREE
612
- @label[@endpoint[@blossom_endps[b][j - endptrick] ^ endptrick ^ 1]] = LBL_FREE
622
+ @label[
623
+ @endpoint[@blossom_endps[b][j - endptrick] ^ endptrick ^ 1]
624
+ ] = LBL_FREE
613
625
  assign_label(@endpoint[p ^ 1], LBL_T, p)
614
626
 
615
627
  # > Step to the next S-sub-blossom and note its forward endpoint.
616
- @tight_edge[@blossom_endps[b][j - endptrick] / 2] = true # floor division
628
+ # Intentional floor division
629
+ @tight_edge[@blossom_endps[b][j - endptrick] / 2] = true
617
630
  j += jstep
618
631
  p = @blossom_endps[b][j - endptrick] ^ endptrick
619
632
 
620
633
  # > Step to the next T-sub-blossom.
621
- @tight_edge[p / 2] = true # floor division
634
+ # Intentional floor division
635
+ @tight_edge[p / 2] = true
622
636
  j += jstep
623
637
  end
624
638
 
@@ -662,7 +676,7 @@ module GraphMatching
662
676
  # > End of a stage; expand all S-blossoms which have dualvar = 0.
663
677
  # > (Van Rantwijk, mwmatching.py)
664
678
  def expand_tight_s_blossoms
665
- (@nvertex ... 2 * @nvertex).each do |b|
679
+ (@nvertex...2 * @nvertex).each do |b|
666
680
  if top_level_blossom?(b) && @label[b] == LBL_S && @dual[b] == 0
667
681
  expand_blossom(b, true)
668
682
  end
@@ -686,11 +700,10 @@ module GraphMatching
686
700
  # Data structures used throughout the algorithm.
687
701
  def init_algorithm_structures
688
702
  # > If v is a vertex,
689
- # > mate[v] is the remote endpoint of its matched edge, or -1 if it is single
690
- # > (i.e. endpoint[mate[v]] is v's partner vertex).
703
+ # > mate[v] is the remote endpoint of its matched edge, or -1 if it is
704
+ # > single (i.e. endpoint[mate[v]] is v's partner vertex).
691
705
  # > Initially all vertices are single; updated during augmentation.
692
706
  # > (Van Rantwijk, mwmatching.py)
693
- #
694
707
  @mate = Array.new(@nvertex, nil)
695
708
 
696
709
  # > If b is a top-level blossom,
@@ -699,37 +712,29 @@ module GraphMatching
699
712
  # > 2 if b is a T-vertex/blossom.
700
713
  # > The label of a vertex is found by looking at the label of its
701
714
  # > top-level containing blossom.
702
- # > If v is a vertex inside a T-blossom,
703
- # > label[v] is 2 iff v is reachable from an S-vertex outside the blossom.
704
- # > Labels are assigned during a stage and reset after each augmentation.
715
+ # > If v is a vertex inside a T-blossom, label[v] is 2 iff v is
716
+ # > reachable from an S-vertex outside the blossom. Labels are assigned
717
+ # > during a stage and reset after each augmentation.
705
718
  # > (Van Rantwijk, mwmatching.py)
706
- #
707
719
  @label = rantwijk_array(LBL_FREE)
708
720
 
709
- # > If b is a labeled top-level blossom,
710
- # > labelend[b] is the remote endpoint of the edge through which b obtained
711
- # > its label, or -1 if b's base vertex is single.
712
- # > If v is a vertex inside a T-blossom and label[v] == 2,
713
- # > labelend[v] is the remote endpoint of the edge through which v is
714
- # > reachable from outside the blossom.
715
- # > (Van Rantwijk, mwmatching.py)
716
- #
721
+ # > If b is a labeled top-level blossom, labelend[b] is the remote
722
+ # > endpoint of the edge through which b obtained its label, or -1 if
723
+ # > b's base vertex is single. If v is a vertex inside a T-blossom and
724
+ # > label[v] == 2, labelend[v] is the remote endpoint of the edge
725
+ # > through which v is reachable from outside the blossom. (Van
726
+ # > Rantwijk, mwmatching.py)
717
727
  @label_end = rantwijk_array(nil)
718
728
 
719
- # > If v is a vertex,
720
- # > inblossom[v] is the top-level blossom to which v belongs.
721
- # > If v is a top-level vertex, v is itself a blossom (a trivial blossom)
722
- # > and inblossom[v] == v.
723
- # > Initially all vertices are top-level trivial blossoms.
724
- # > (Van Rantwijk, mwmatching.py)
725
- #
726
- @in_blossom = (0 ... @nvertex).to_a
729
+ # > If v is a vertex, inblossom[v] is the top-level blossom to which v
730
+ # > belongs. If v is a top-level vertex, v is itself a blossom (a
731
+ # > trivial blossom) and inblossom[v] == v. Initially all vertices are
732
+ # > top-level trivial blossoms. (Van Rantwijk, mwmatching.py)
733
+ @in_blossom = (0...@nvertex).to_a
727
734
 
728
- # > If b is a sub-blossom,
729
- # > blossomparent[b] is its immediate parent (sub-)blossom.
730
- # > If b is a top-level blossom, blossomparent[b] is -1.
735
+ # > If b is a sub-blossom, blossomparent[b] is its immediate parent
736
+ # > (sub-)blossom. If b is a top-level blossom, blossomparent[b] is -1.
731
737
  # > (Van Rantwijk, mwmatching.py)
732
- #
733
738
  @blossom_parent = rantwijk_array(nil)
734
739
 
735
740
  # A 2D array representing a tree of blossoms.
@@ -760,7 +765,7 @@ module GraphMatching
760
765
  # > blossombase[b] is its base VERTEX (i.e. recursive sub-blossom).
761
766
  # > (Van Rantwijk, mwmatching.py, line 153)
762
767
  #
763
- @blossom_base = (0 ... @nvertex).to_a + Array.new(@nvertex, nil)
768
+ @blossom_base = (0...@nvertex).to_a + Array.new(@nvertex, nil)
764
769
 
765
770
  # > If b is a non-trivial (sub-)blossom,
766
771
  # > blossomendps[b] is a list of endpoints on its connecting edges,
@@ -792,7 +797,7 @@ module GraphMatching
792
797
 
793
798
  # > List of currently unused blossom numbers.
794
799
  # > (Van Rantwijk, mwmatching.py, line 174)
795
- @unused_blossoms = (@nvertex ... 2 * @nvertex).to_a
800
+ @unused_blossoms = (@nvertex...2 * @nvertex).to_a
796
801
 
797
802
  # > If v is a vertex,
798
803
  # > dualvar[v] = 2 * u(v) where u(v) is the v's variable in the dual
@@ -884,7 +889,7 @@ module GraphMatching
884
889
  # > the queue. (Van Rantwijk, mwmatching.py, line 649)
885
890
  def init_stage_labels
886
891
  @label = rantwijk_array(LBL_FREE)
887
- (0 ... @nvertex).each do |v|
892
+ (0...@nvertex).each do |v|
888
893
  if @mate[v].nil? && @label[@in_blossom[v]] == LBL_FREE
889
894
  assign_label(v, LBL_S)
890
895
  end
@@ -1006,7 +1011,7 @@ module GraphMatching
1006
1011
  augmented = false
1007
1012
 
1008
1013
  @neighb_end[v].each do |p|
1009
- k = p / 2 # floor division
1014
+ k = p / 2 # Intentional floor division
1010
1015
  w = @endpoint[p]
1011
1016
 
1012
1017
  if @in_blossom[v] == @in_blossom[w]
@@ -1050,7 +1055,7 @@ module GraphMatching
1050
1055
  # > .. we make the following changes in the dual
1051
1056
  # > variables. (Galil, 1986, p. 32)
1052
1057
  def update_duals(delta)
1053
- (0 ... @nvertex).each do |v|
1058
+ (0...@nvertex).each do |v|
1054
1059
  case @label[@in_blossom[v]]
1055
1060
  when LBL_S
1056
1061
  @dual[v] -= delta
@@ -1060,16 +1065,15 @@ module GraphMatching
1060
1065
  # No change to free vertexes
1061
1066
  end
1062
1067
  end
1063
- (@nvertex ... 2 * @nvertex).each do |b|
1064
- if top_level_blossom?(b)
1065
- case @label[b]
1066
- when LBL_S
1067
- @dual[b] += delta
1068
- when LBL_T
1069
- @dual[b] -= delta
1070
- else
1071
- # No change to free blossoms
1072
- end
1068
+ (@nvertex...2 * @nvertex).each do |b|
1069
+ next unless top_level_blossom?(b)
1070
+ case @label[b]
1071
+ when LBL_S
1072
+ @dual[b] += delta
1073
+ when LBL_T
1074
+ @dual[b] -= delta
1075
+ else
1076
+ # No change to free blossoms
1073
1077
  end
1074
1078
  end
1075
1079
  end