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.
- checksums.yaml +7 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +112 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +205 -0
- data/Rakefile +9 -0
- data/benchmark/mcm_bipartite/complete_bigraphs/benchmark.rb +33 -0
- data/benchmark/mcm_bipartite/complete_bigraphs/compare.gnuplot +19 -0
- data/benchmark/mcm_bipartite/complete_bigraphs/edges_times_vertexes.data +500 -0
- data/benchmark/mcm_bipartite/complete_bigraphs/plot.gnuplot +21 -0
- data/benchmark/mcm_bipartite/complete_bigraphs/plot.png +0 -0
- data/benchmark/mcm_bipartite/complete_bigraphs/time.data +499 -0
- data/benchmark/mcm_general/complete_graphs/benchmark.rb +30 -0
- data/benchmark/mcm_general/complete_graphs/plot.gnuplot +19 -0
- data/benchmark/mcm_general/complete_graphs/plot.png +0 -0
- data/benchmark/mcm_general/complete_graphs/time.data +499 -0
- data/benchmark/mcm_general/complete_graphs/v_cubed.data +500 -0
- data/benchmark/mwm_bipartite/complete_bigraphs/benchmark.rb +43 -0
- data/benchmark/mwm_bipartite/complete_bigraphs/nmN.data +499 -0
- data/benchmark/mwm_bipartite/complete_bigraphs/nmN.xlsx +0 -0
- data/benchmark/mwm_bipartite/complete_bigraphs/plot.gnuplot +22 -0
- data/benchmark/mwm_bipartite/complete_bigraphs/plot.png +0 -0
- data/benchmark/mwm_bipartite/complete_bigraphs/time.data +299 -0
- data/benchmark/mwm_bipartite/misc/calc_d2/benchmark.rb +29 -0
- data/benchmark/mwm_general/complete_graphs/benchmark.rb +32 -0
- data/benchmark/mwm_general/complete_graphs/compare.gnuplot +19 -0
- data/benchmark/mwm_general/complete_graphs/mn_log_n.data +299 -0
- data/benchmark/mwm_general/complete_graphs/mn_log_n.xlsx +0 -0
- data/benchmark/mwm_general/complete_graphs/plot.gnuplot +22 -0
- data/benchmark/mwm_general/complete_graphs/plot.png +0 -0
- data/benchmark/mwm_general/complete_graphs/time.data +299 -0
- data/benchmark/mwm_general/incomplete_graphs/benchmark.rb +39 -0
- data/benchmark/mwm_general/incomplete_graphs/plot.gnuplot +22 -0
- data/benchmark/mwm_general/incomplete_graphs/plot.png +0 -0
- data/benchmark/mwm_general/incomplete_graphs/time_10_pct.data +299 -0
- data/benchmark/mwm_general/incomplete_graphs/time_20_pct.data +299 -0
- data/benchmark/mwm_general/incomplete_graphs/time_30_pct.data +299 -0
- data/graph_matching.gemspec +35 -0
- data/lib/graph_matching.rb +15 -0
- data/lib/graph_matching/algorithm/matching_algorithm.rb +23 -0
- data/lib/graph_matching/algorithm/mcm_bipartite.rb +118 -0
- data/lib/graph_matching/algorithm/mcm_general.rb +289 -0
- data/lib/graph_matching/algorithm/mwm_bipartite.rb +147 -0
- data/lib/graph_matching/algorithm/mwm_general.rb +1086 -0
- data/lib/graph_matching/algorithm/mwmg_delta_assertions.rb +94 -0
- data/lib/graph_matching/assertion.rb +41 -0
- data/lib/graph_matching/core_ext/set.rb +36 -0
- data/lib/graph_matching/directed_edge_set.rb +31 -0
- data/lib/graph_matching/errors.rb +23 -0
- data/lib/graph_matching/graph/bigraph.rb +37 -0
- data/lib/graph_matching/graph/graph.rb +63 -0
- data/lib/graph_matching/graph/weighted.rb +112 -0
- data/lib/graph_matching/graph/weighted_bigraph.rb +17 -0
- data/lib/graph_matching/graph/weighted_graph.rb +17 -0
- data/lib/graph_matching/integer_vertexes.rb +29 -0
- data/lib/graph_matching/matching.rb +120 -0
- data/lib/graph_matching/ordered_set.rb +59 -0
- data/lib/graph_matching/version.rb +6 -0
- data/lib/graph_matching/visualize.rb +93 -0
- data/profile/mcm_bipartite/compare.sh +15 -0
- data/profile/mcm_bipartite/publish.sh +12 -0
- data/profile/mwm_general/compare.sh +15 -0
- data/profile/mwm_general/profile.rb +28 -0
- data/profile/mwm_general/publish.sh +12 -0
- data/research/1965_edmonds.pdf +0 -0
- data/research/1975_even_kariv.pdf +0 -0
- data/research/1976_gabow.pdf +0 -0
- data/research/1980_micali_vazirani.pdf +0 -0
- data/research/1985_gabow.pdf +0 -0
- data/research/2002_tarjan.pdf +0 -0
- data/research/2013_zwick.pdf +0 -0
- data/research/examples/unweighted_general/1.txt +86 -0
- data/research/goodwin.pdf +0 -0
- data/research/kavathekar-scribe.pdf +0 -0
- data/research/kusner.pdf +0 -0
- data/research/van_rantwijk/mwm_example.py +19 -0
- data/research/van_rantwijk/mwmatching.py +945 -0
- data/spec/graph_matching/algorithm/matching_algorithm_spec.rb +14 -0
- data/spec/graph_matching/algorithm/mcm_bipartite_spec.rb +98 -0
- data/spec/graph_matching/algorithm/mcm_general_spec.rb +159 -0
- data/spec/graph_matching/algorithm/mwm_bipartite_spec.rb +82 -0
- data/spec/graph_matching/algorithm/mwm_general_spec.rb +439 -0
- data/spec/graph_matching/graph/bigraph_spec.rb +73 -0
- data/spec/graph_matching/graph/graph_spec.rb +53 -0
- data/spec/graph_matching/graph/weighted_spec.rb +29 -0
- data/spec/graph_matching/integer_vertexes_spec.rb +21 -0
- data/spec/graph_matching/matching_spec.rb +89 -0
- data/spec/graph_matching/visualize_spec.rb +38 -0
- data/spec/graph_matching_spec.rb +9 -0
- data/spec/spec_helper.rb +26 -0
- metadata +263 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative 'weighted'
|
|
4
|
+
require_relative '../algorithm/mwm_general'
|
|
5
|
+
|
|
6
|
+
module GraphMatching
|
|
7
|
+
module Graph
|
|
8
|
+
# A graph whose edges have weights. See `Weighted`.
|
|
9
|
+
class WeightedGraph < Graph
|
|
10
|
+
include Weighted
|
|
11
|
+
|
|
12
|
+
def maximum_weighted_matching(max_cardinality)
|
|
13
|
+
Algorithm::MWMGeneral.new(self).match(max_cardinality)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module GraphMatching
|
|
4
|
+
# Converts the vertices of a graph to integers. Many graph
|
|
5
|
+
# matching algorithms require integer vertexes.
|
|
6
|
+
module IntegerVertexes
|
|
7
|
+
# Converts the vertices of `graph` to positive nonzero integers.
|
|
8
|
+
# For example, given a graph (a=b), returns a new graph (1=2).
|
|
9
|
+
# It also returns a legend, which maps the integers to the
|
|
10
|
+
# original vertexes.
|
|
11
|
+
#
|
|
12
|
+
def self.to_integers(graph)
|
|
13
|
+
fail ArgumentError unless graph.is_a?(RGL::MutableGraph)
|
|
14
|
+
legend = {}
|
|
15
|
+
reverse_legend = {}
|
|
16
|
+
new_graph = graph.class.new
|
|
17
|
+
graph.vertices.each_with_index do |vertex, ix|
|
|
18
|
+
legend[ix + 1] = vertex
|
|
19
|
+
reverse_legend[vertex] = ix + 1
|
|
20
|
+
end
|
|
21
|
+
graph.edges.each do |edge|
|
|
22
|
+
source = reverse_legend[edge.source]
|
|
23
|
+
target = reverse_legend[edge.target]
|
|
24
|
+
new_graph.add_edge(source, target)
|
|
25
|
+
end
|
|
26
|
+
return new_graph, legend
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module GraphMatching
|
|
4
|
+
# > In .. graph theory, a matching .. in a graph is a set of
|
|
5
|
+
# > edges without common vertices.
|
|
6
|
+
# > https://en.wikipedia.org/wiki/Matching_%28graph_theory%29
|
|
7
|
+
class Matching
|
|
8
|
+
# Gabow (1976) uses a simple array to store his matching. It
|
|
9
|
+
# has one element for each vertex in the graph. The value of
|
|
10
|
+
# each element is either the number of another vertex (Gabow
|
|
11
|
+
# uses sequential integers for vertex numbering) or a zero if
|
|
12
|
+
# unmatched. So, `.gabow` returns a `Matching` initialized
|
|
13
|
+
# from such an array.
|
|
14
|
+
def self.gabow(mate)
|
|
15
|
+
m = new
|
|
16
|
+
mate.each_with_index do |n1, ix|
|
|
17
|
+
next if n1.nil? || n1 == 0
|
|
18
|
+
n2 = mate[n1]
|
|
19
|
+
if n2 == ix
|
|
20
|
+
m.add([n1, n2])
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
m
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Van Rantwijk's matching is constructed from two arrays,
|
|
27
|
+
# `mate` and `endpoint`.
|
|
28
|
+
#
|
|
29
|
+
# - `endpoint` is an array where each edge is represented by
|
|
30
|
+
# two consecutive elements, which are vertex numbers.
|
|
31
|
+
# - `mate` is an array whose indexes are vertex numbers, and
|
|
32
|
+
# whose values are `endpoint` indexes, or `nil` if the vertex
|
|
33
|
+
# is single (unmatched).
|
|
34
|
+
#
|
|
35
|
+
# A matched vertex `v`'s partner is `endpoint[mate[v]]`.
|
|
36
|
+
#
|
|
37
|
+
def self.from_endpoints(endpoint, mate)
|
|
38
|
+
m = Matching.new
|
|
39
|
+
mate.each do |p|
|
|
40
|
+
m.add([endpoint[p], endpoint[p ^ 1]]) unless p.nil?
|
|
41
|
+
end
|
|
42
|
+
m
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.[](*edges)
|
|
46
|
+
new.tap { |m| edges.each { |e| m.add(e) } }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def initialize
|
|
50
|
+
@ary = []
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def [](i)
|
|
54
|
+
@ary[i]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def add(e)
|
|
58
|
+
i, j = e
|
|
59
|
+
@ary[i] = j
|
|
60
|
+
@ary[j] = i
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def delete(e)
|
|
64
|
+
i, j = e
|
|
65
|
+
@ary[i] = nil
|
|
66
|
+
@ary[j] = nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# `edges` returns an array of undirected edges, represented as
|
|
70
|
+
# two-element arrays.
|
|
71
|
+
def edges
|
|
72
|
+
undirected_edges.map(&:to_a)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def empty?
|
|
76
|
+
@ary.all?(&:nil?)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def edge?(e)
|
|
80
|
+
i, j = e
|
|
81
|
+
!@ary[i].nil? && @ary[i] == j && @ary[j] == i
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def vertex?(v)
|
|
85
|
+
@ary.include?(v)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# `size` returns number of edges
|
|
89
|
+
def size
|
|
90
|
+
@ary.compact.size / 2
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def to_a
|
|
94
|
+
result = []
|
|
95
|
+
skip = []
|
|
96
|
+
@ary.each_with_index { |e, i|
|
|
97
|
+
unless e.nil? || skip.include?(i)
|
|
98
|
+
result << [i, e]
|
|
99
|
+
skip << e
|
|
100
|
+
end
|
|
101
|
+
}
|
|
102
|
+
result
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Given a `Weighted` graph `g`, returns the sum of edge weights.
|
|
106
|
+
def weight(g)
|
|
107
|
+
edges.map { |e| g.w(e) }.reduce(0, :+)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def undirected_edges
|
|
111
|
+
@ary.each_with_index.inject(Set.new) { |set, (el, ix)|
|
|
112
|
+
el.nil? ? set : set.add(RGL::Edge::UnDirectedEdge.new(el, ix))
|
|
113
|
+
}
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def vertexes
|
|
117
|
+
@ary.compact
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module GraphMatching
|
|
4
|
+
# An `OrderedSet` acts like a `Set`, but preserves insertion order.
|
|
5
|
+
# Internally, a `Hash` is used because, as of Ruby 1.9, it
|
|
6
|
+
# preserves insertion order. The Set library happens to be built
|
|
7
|
+
# upon a Hash currently but this might change in the future.
|
|
8
|
+
class OrderedSet
|
|
9
|
+
include Enumerable
|
|
10
|
+
|
|
11
|
+
# `.[]` returns a new ordered set containing the given objects.
|
|
12
|
+
# This mimics the signature of `Set.[]` and `Array.[]`.
|
|
13
|
+
def self.[](*args)
|
|
14
|
+
new.merge(args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@hash = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# `add` `o` unless it already exists, preserving inserting order.
|
|
22
|
+
# This mimics the signature of `Set#add`. See alias `#enq`.
|
|
23
|
+
def add(o)
|
|
24
|
+
@hash[o] = true
|
|
25
|
+
end
|
|
26
|
+
alias_method :enq, :add
|
|
27
|
+
|
|
28
|
+
def deq
|
|
29
|
+
@hash.keys.first.tap do |k| @hash.delete(k) end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def each
|
|
33
|
+
@hash.each do |k, _v| yield k end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def empty?
|
|
37
|
+
@hash.empty?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# `merge` the elements of the given enumerable object to the set
|
|
41
|
+
# and returns self. This mimics the signature of `Set#merge`.
|
|
42
|
+
def merge(enum)
|
|
43
|
+
enum.each do |e| add(e) end
|
|
44
|
+
self
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Removes the last element and returns it, or nil if empty.
|
|
48
|
+
# This mimics `Array#pop`. See related `#deq`.
|
|
49
|
+
def pop
|
|
50
|
+
@hash.keys.last.tap do |k| @hash.delete(k) end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# `push` appends the given object(s) and returns self. This
|
|
54
|
+
# mimics the signature of `Array#push`.
|
|
55
|
+
def push(*args)
|
|
56
|
+
merge(args)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
require 'rgl/rdot'
|
|
5
|
+
|
|
6
|
+
module GraphMatching
|
|
7
|
+
# Renders `GraphMatching::Graph` objects using `graphviz`.
|
|
8
|
+
class Visualize
|
|
9
|
+
TMP_DIR = '/tmp/graph_matching'
|
|
10
|
+
USR_BIN_ENV = '/usr/bin/env'
|
|
11
|
+
|
|
12
|
+
attr_reader :graph
|
|
13
|
+
|
|
14
|
+
def initialize(graph)
|
|
15
|
+
@graph = graph
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# `dot` returns a string representing the graph, in .dot format.
|
|
19
|
+
# http://www.graphviz.org/content/dot-language
|
|
20
|
+
def dot
|
|
21
|
+
RGL::DOT::Graph.new('elements' => dot_edges).to_s
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# `png` writes a ".png" file with graphviz and opens it
|
|
25
|
+
def png(base_filename)
|
|
26
|
+
check_that_dot_is_installed
|
|
27
|
+
mk_tmp_dir
|
|
28
|
+
abs_path = "#{TMP_DIR}/#{base_filename}.png"
|
|
29
|
+
write_png(abs_path)
|
|
30
|
+
system "open #{abs_path}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def check_that_dot_is_installed
|
|
36
|
+
return if dot_installed?
|
|
37
|
+
$stderr.puts "Executable not found: dot"
|
|
38
|
+
$stderr.puts "Please install graphviz"
|
|
39
|
+
exit(1)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def dot_edge(u, v, label)
|
|
43
|
+
RGL::DOT::Edge.new(
|
|
44
|
+
{ 'from' => u, 'to' => v, 'label' => label },
|
|
45
|
+
['label']
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dot_edges
|
|
50
|
+
graph.edges.map { |e| dot_edge(e.source, e.target, dot_edge_label(e)) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def dot_edge_label(edge)
|
|
54
|
+
graph.is_a?(GraphMatching::Graph::Weighted) ? graph.w([*edge]) : nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def assert_usr_bin_env_exists
|
|
58
|
+
return if File.exist?(USR_BIN_ENV)
|
|
59
|
+
$stderr.puts "File not found: #{USR_BIN_ENV}"
|
|
60
|
+
exit(1)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# `dot_installed?` returns true if `dot` is installed, otherwise
|
|
64
|
+
# false. Note that `system` returns true if the command gives
|
|
65
|
+
# zero exit status, false for non-zero exit status.
|
|
66
|
+
def dot_installed?
|
|
67
|
+
assert_usr_bin_env_exists
|
|
68
|
+
system "#{USR_BIN_ENV} which dot > /dev/null"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def mk_tmp_dir
|
|
72
|
+
Dir.mkdir(TMP_DIR) unless Dir.exist?(TMP_DIR)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def safe_vertex(v)
|
|
76
|
+
if v.is_a?(Integer)
|
|
77
|
+
v
|
|
78
|
+
elsif v.respond_to?(:to_dot)
|
|
79
|
+
v.to_dot
|
|
80
|
+
else
|
|
81
|
+
v.to_s.gsub(/[^a-zA-Z0-9]/, '')
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def write_png(abs_path)
|
|
86
|
+
_so, se, st = Open3.capture3("dot -T png > #{abs_path}", stdin_data: dot)
|
|
87
|
+
return if st.success?
|
|
88
|
+
$stderr.puts "Failed to generate .png"
|
|
89
|
+
$stderr.puts se
|
|
90
|
+
exit(1)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
BENCHMARK_DIR='benchmark/mcm_bipartite/complete_bigraphs'
|
|
4
|
+
|
|
5
|
+
if [ ! -d "$BENCHMARK_DIR" ]; then
|
|
6
|
+
echo "Directory not found: $BENCHMARK_DIR" 1>&2
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
echo "Benchmarking .."
|
|
11
|
+
ruby -I lib "$BENCHMARK_DIR/benchmark.rb" > "$BENCHMARK_DIR/time2.data"
|
|
12
|
+
|
|
13
|
+
echo "Plotting .."
|
|
14
|
+
gnuplot "$BENCHMARK_DIR/compare.gnuplot"
|
|
15
|
+
open "$BENCHMARK_DIR/plot_compare.png"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
BENCHMARK_DIR='benchmark/mcm_bipartite/complete_bigraphs'
|
|
4
|
+
|
|
5
|
+
if [ ! -d "$BENCHMARK_DIR" ]; then
|
|
6
|
+
echo "Directory not found: $BENCHMARK_DIR" 1>&2
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
rm "$BENCHMARK_DIR/plot_compare.png"
|
|
11
|
+
mv "$BENCHMARK_DIR/time2.data" "$BENCHMARK_DIR/time.data"
|
|
12
|
+
gnuplot "$BENCHMARK_DIR/plot.gnuplot"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
BENCHMARK_DIR='benchmark/mwm_general/complete_graphs'
|
|
4
|
+
|
|
5
|
+
if [ ! -d "$BENCHMARK_DIR" ]; then
|
|
6
|
+
echo "Directory not found: $BENCHMARK_DIR" 1>&2
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
echo "Benchmarking .."
|
|
11
|
+
ruby -I lib "$BENCHMARK_DIR/benchmark.rb" > "$BENCHMARK_DIR/time2.data"
|
|
12
|
+
|
|
13
|
+
echo "Plotting .."
|
|
14
|
+
gnuplot "$BENCHMARK_DIR/compare.gnuplot"
|
|
15
|
+
open "$BENCHMARK_DIR/plot_compare.png"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# No shebang here. Run with:
|
|
2
|
+
#
|
|
3
|
+
# ruby -I lib profile/mwm_general/profile.rb
|
|
4
|
+
|
|
5
|
+
require 'graph_matching'
|
|
6
|
+
require 'ruby-prof'
|
|
7
|
+
|
|
8
|
+
def complete_graph(n)
|
|
9
|
+
g = GraphMatching::Graph::WeightedGraph.new
|
|
10
|
+
n_edges = (1 .. n - 1).reduce(:+)
|
|
11
|
+
0.upto(n - 2) do |i|
|
|
12
|
+
(i + 1).upto(n - 1) do |j|
|
|
13
|
+
g.add_edge(i, j)
|
|
14
|
+
g.set_w([i, j], rand(n_edges))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
g
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
g = complete_graph(100)
|
|
21
|
+
GC.disable
|
|
22
|
+
RubyProf.start
|
|
23
|
+
g.maximum_weighted_matching(true)
|
|
24
|
+
result = RubyProf.stop
|
|
25
|
+
GC.enable
|
|
26
|
+
|
|
27
|
+
printer = RubyProf::FlatPrinter.new(result)
|
|
28
|
+
printer.print(STDOUT)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
BENCHMARK_DIR='benchmark/mwm_general/complete_graphs'
|
|
4
|
+
|
|
5
|
+
if [ ! -d "$BENCHMARK_DIR" ]; then
|
|
6
|
+
echo "Directory not found: $BENCHMARK_DIR" 1>&2
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
rm "$BENCHMARK_DIR/plot_compare.png"
|
|
11
|
+
mv "$BENCHMARK_DIR/time2.data" "$BENCHMARK_DIR/time.data"
|
|
12
|
+
gnuplot "$BENCHMARK_DIR/plot.gnuplot"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
Stage 1
|
|
2
|
+
|
|
3
|
+
5 -- 7
|
|
4
|
+
/ || ||
|
|
5
|
+
1 = 3 || o
|
|
6
|
+
/ \ ||
|
|
7
|
+
r 6
|
|
8
|
+
\ /
|
|
9
|
+
2 = 4
|
|
10
|
+
/
|
|
11
|
+
a
|
|
12
|
+
|
|
13
|
+
S = [ 1 by r, 2 by r, 5 by 3, 6 by 3 ]
|
|
14
|
+
T = [ 3 by 1, 4 by 1 ]
|
|
15
|
+
|
|
16
|
+
Stage 2
|
|
17
|
+
|
|
18
|
+
1 = B -- 7
|
|
19
|
+
/ | ||
|
|
20
|
+
r | o
|
|
21
|
+
\ |
|
|
22
|
+
2 = 4
|
|
23
|
+
/
|
|
24
|
+
a
|
|
25
|
+
|
|
26
|
+
S = [ 1 by r, 2 by r, 5 by 3, 6 by 3 ]
|
|
27
|
+
T = [ 3 by 1, 4 by 1 ]
|
|
28
|
+
|
|
29
|
+
Stage 3
|
|
30
|
+
|
|
31
|
+
B -- 7
|
|
32
|
+
/ ||
|
|
33
|
+
a o
|
|
34
|
+
|
|
35
|
+
Find augmenting path from r, *through B* to a
|
|
36
|
+
|
|
37
|
+
1. AP = (a,B)
|
|
38
|
+
1. expand B
|
|
39
|
+
|
|
40
|
+
1 = B -- 7
|
|
41
|
+
/ | ||
|
|
42
|
+
r | o
|
|
43
|
+
\ |
|
|
44
|
+
2 = 4
|
|
45
|
+
/
|
|
46
|
+
a
|
|
47
|
+
|
|
48
|
+
1. Kusner says: "propagating the augmenting path through the expansion steps"
|
|
49
|
+
1. AP = (a,2), etc.. but how to find "etc"? We know we want an
|
|
50
|
+
alternating path, so starting at 2, find a matched edge.
|
|
51
|
+
1. AP = a2, 24
|
|
52
|
+
1. AP = a2, 24, 4B
|
|
53
|
+
1. B is a blossom. Expand it.
|
|
54
|
+
|
|
55
|
+
5 -- 7
|
|
56
|
+
/ || ||
|
|
57
|
+
1 = 3 || o
|
|
58
|
+
/ \ ||
|
|
59
|
+
r 6
|
|
60
|
+
\ /
|
|
61
|
+
2 = 4
|
|
62
|
+
/
|
|
63
|
+
a
|
|
64
|
+
|
|
65
|
+
1. AP = a2, 24, 46
|
|
66
|
+
1. AP = a2, 24, 46, 65
|
|
67
|
+
1. Should we follow (5,3) or (5,7), or both?
|
|
68
|
+
1. Following (5,7) doesn't reach r, and if we follow it,
|
|
69
|
+
either depth-first or breadth-first, we'll learn that.
|
|
70
|
+
1. AP = a2, 24, 46, 65, 53
|
|
71
|
+
1. AP = a2, 24, 46, 65, 53, 31
|
|
72
|
+
1. AP = a2, 24, 46, 65, 53, 31, 1r
|
|
73
|
+
1. Augmenting path: a, 2, 4, 6, 5, 3, 1, r
|
|
74
|
+
|
|
75
|
+
5 -- 7
|
|
76
|
+
//| ||
|
|
77
|
+
1 - 3 | o
|
|
78
|
+
// \|
|
|
79
|
+
r 6
|
|
80
|
+
\ //
|
|
81
|
+
2 - 4
|
|
82
|
+
//
|
|
83
|
+
a
|
|
84
|
+
|
|
85
|
+
1. size of matching = 5. Decide is maximum cardinality
|
|
86
|
+
matching. (How?)
|