1 | //======================================================================= |
---|
2 | // Copyright (c) 2005 Aaron Windsor |
---|
3 | // |
---|
4 | // Distributed under the Boost Software License, Version 1.0. |
---|
5 | // (See accompanying file LICENSE_1_0.txt or copy at |
---|
6 | // http://www.boost.org/LICENSE_1_0.txt) |
---|
7 | // |
---|
8 | //======================================================================= |
---|
9 | |
---|
10 | #include <boost/graph/max_cardinality_matching.hpp> |
---|
11 | |
---|
12 | #include <iostream> // for std::cout |
---|
13 | #include <boost/vector_property_map.hpp> |
---|
14 | #include <boost/graph/adjacency_list.hpp> |
---|
15 | #include <boost/graph/adjacency_matrix.hpp> |
---|
16 | #include <boost/graph/random.hpp> |
---|
17 | #include <ctime> |
---|
18 | #include <boost/random.hpp> |
---|
19 | #include <boost/test/minimal.hpp> |
---|
20 | |
---|
21 | using namespace boost; |
---|
22 | |
---|
23 | typedef adjacency_list<vecS, |
---|
24 | vecS, |
---|
25 | undirectedS, |
---|
26 | property<vertex_index_t, int> > undirected_graph; |
---|
27 | |
---|
28 | typedef adjacency_list<listS, |
---|
29 | listS, |
---|
30 | undirectedS, |
---|
31 | property<vertex_index_t, int> > undirected_list_graph; |
---|
32 | |
---|
33 | typedef adjacency_matrix<undirectedS, |
---|
34 | property<vertex_index_t,int> > undirected_adjacency_matrix_graph; |
---|
35 | |
---|
36 | |
---|
37 | template <typename Graph> |
---|
38 | struct vertex_index_installer |
---|
39 | { |
---|
40 | static void install(Graph& g) {} |
---|
41 | }; |
---|
42 | |
---|
43 | |
---|
44 | template <> |
---|
45 | struct vertex_index_installer<undirected_list_graph> |
---|
46 | { |
---|
47 | static void install(undirected_list_graph& g) |
---|
48 | { |
---|
49 | typedef graph_traits<undirected_list_graph>::vertex_iterator vertex_iterator_t; |
---|
50 | typedef graph_traits<undirected_list_graph>::vertices_size_type v_size_t; |
---|
51 | |
---|
52 | vertex_iterator_t vi, vi_end; |
---|
53 | v_size_t i = 0; |
---|
54 | for(tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi, ++i) |
---|
55 | put(vertex_index, g, *vi, i); |
---|
56 | } |
---|
57 | }; |
---|
58 | |
---|
59 | |
---|
60 | |
---|
61 | template <typename Graph> |
---|
62 | void complete_graph(Graph& g, int n) |
---|
63 | { |
---|
64 | //creates the complete graph on n vertices |
---|
65 | typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator_t; |
---|
66 | |
---|
67 | g = Graph(n); |
---|
68 | vertex_iterator_t vi, vi_end, wi; |
---|
69 | tie(vi,vi_end) = vertices(g); |
---|
70 | for(tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) |
---|
71 | { |
---|
72 | wi = vi; |
---|
73 | ++wi; |
---|
74 | for(; wi != vi_end; ++wi) |
---|
75 | add_edge(*vi,*wi,g); |
---|
76 | } |
---|
77 | } |
---|
78 | |
---|
79 | |
---|
80 | |
---|
81 | template <typename Graph> |
---|
82 | void gabows_graph(Graph& g, int n) |
---|
83 | { |
---|
84 | //creates a graph with 2n vertices, consisting of the complete graph |
---|
85 | //on n vertices plus n vertices of degree one, each adjacent to one |
---|
86 | //vertex in the complete graph. without any initial matching, this |
---|
87 | //graph forces edmonds' algorithm into worst-case behavior. |
---|
88 | |
---|
89 | typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator_t; |
---|
90 | |
---|
91 | g = Graph(2* n); |
---|
92 | |
---|
93 | vertex_iterator_t vi, vi_end, ui, ui_end, halfway; |
---|
94 | |
---|
95 | tie(ui,ui_end) = vertices(g); |
---|
96 | |
---|
97 | halfway = ui; |
---|
98 | for(int i = 0; i < n; ++i) |
---|
99 | ++halfway; |
---|
100 | |
---|
101 | |
---|
102 | while(ui != halfway) |
---|
103 | { |
---|
104 | vi = ui; |
---|
105 | ++vi; |
---|
106 | while (vi != halfway) |
---|
107 | { |
---|
108 | add_edge(*ui,*vi,g); |
---|
109 | ++vi; |
---|
110 | } |
---|
111 | ++ui; |
---|
112 | } |
---|
113 | |
---|
114 | tie(ui,ui_end) = vertices(g); |
---|
115 | |
---|
116 | while(halfway != ui_end) |
---|
117 | { |
---|
118 | add_edge(*ui, *halfway, g); |
---|
119 | ++ui; |
---|
120 | ++halfway; |
---|
121 | } |
---|
122 | |
---|
123 | } |
---|
124 | |
---|
125 | |
---|
126 | |
---|
127 | template <typename Graph> |
---|
128 | void matching_test(std::size_t num_v, const std::string& graph_name) |
---|
129 | { |
---|
130 | typedef typename property_map<Graph,vertex_index_t>::type vertex_index_map_t; |
---|
131 | typedef vector_property_map< typename graph_traits<Graph>::vertex_descriptor, vertex_index_map_t > mate_t; |
---|
132 | typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator_t; |
---|
133 | typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor_t; |
---|
134 | typedef typename graph_traits<Graph>::vertices_size_type v_size_t; |
---|
135 | |
---|
136 | const std::size_t double_num_v = num_v * 2; |
---|
137 | |
---|
138 | bool all_tests_passed = true; |
---|
139 | |
---|
140 | //form the complete graph on 2*n vertices; the maximum cardinality matching, greedy matching, |
---|
141 | //and extra greedy matching should all be the same - a matching of size n. |
---|
142 | |
---|
143 | Graph g(double_num_v); |
---|
144 | complete_graph(g,double_num_v); |
---|
145 | |
---|
146 | vertex_index_installer<Graph>::install(g); |
---|
147 | |
---|
148 | mate_t edmonds_mate(double_num_v); |
---|
149 | mate_t greedy_mate(double_num_v); |
---|
150 | mate_t extra_greedy_mate(double_num_v); |
---|
151 | |
---|
152 | //find a maximum cardinality matching using edmonds' blossom-shrinking algorithm, starting |
---|
153 | //with an empty matching. |
---|
154 | bool edmonds_result = |
---|
155 | matching < Graph, mate_t, vertex_index_map_t, |
---|
156 | edmonds_augmenting_path_finder, empty_matching, maximum_cardinality_matching_verifier> |
---|
157 | (g,edmonds_mate, get(vertex_index,g)); |
---|
158 | |
---|
159 | BOOST_CHECK (edmonds_result); |
---|
160 | if (!edmonds_result) |
---|
161 | { |
---|
162 | std::cout << "Verifier reporting a problem finding a perfect matching on" << std::endl |
---|
163 | << "the complete graph using " << graph_name << std::endl; |
---|
164 | all_tests_passed = false; |
---|
165 | } |
---|
166 | |
---|
167 | //find a greedy matching |
---|
168 | bool greedy_result = |
---|
169 | matching<Graph, mate_t, vertex_index_map_t, |
---|
170 | no_augmenting_path_finder, greedy_matching, maximum_cardinality_matching_verifier> |
---|
171 | (g,greedy_mate, get(vertex_index,g)); |
---|
172 | |
---|
173 | BOOST_CHECK (greedy_result); |
---|
174 | if (!greedy_result) |
---|
175 | { |
---|
176 | std::cout << "Verifier reporting a problem finding a greedy matching on" << std::endl |
---|
177 | << "the complete graph using " << graph_name << std::endl; |
---|
178 | all_tests_passed = false; |
---|
179 | } |
---|
180 | |
---|
181 | //find an extra greedy matching |
---|
182 | bool extra_greedy_result = |
---|
183 | matching<Graph, mate_t, vertex_index_map_t, |
---|
184 | no_augmenting_path_finder, extra_greedy_matching, maximum_cardinality_matching_verifier> |
---|
185 | (g,extra_greedy_mate,get(vertex_index,g)); |
---|
186 | |
---|
187 | BOOST_CHECK (extra_greedy_result); |
---|
188 | if (!extra_greedy_result) |
---|
189 | { |
---|
190 | std::cout << "Verifier reporting a problem finding an extra greedy matching on" << std::endl |
---|
191 | << "the complete graph using " << graph_name << std::endl; |
---|
192 | all_tests_passed = false; |
---|
193 | } |
---|
194 | |
---|
195 | //as a sanity check, make sure that each of the matchings returned is a valid matching and has |
---|
196 | //1000 edges. |
---|
197 | |
---|
198 | bool edmonds_sanity_check = |
---|
199 | is_a_matching(g,edmonds_mate) && matching_size(g,edmonds_mate) == num_v; |
---|
200 | |
---|
201 | BOOST_CHECK (edmonds_sanity_check); |
---|
202 | if (edmonds_result && !edmonds_sanity_check) |
---|
203 | { |
---|
204 | std::cout << "Verifier okayed edmonds' algorithm on the complete graph, but" << std::endl |
---|
205 | << "the matching returned either wasn't a valid matching or wasn't" << std::endl |
---|
206 | << "actually a maximum cardinality matching." << std::endl; |
---|
207 | all_tests_passed = false; |
---|
208 | } |
---|
209 | |
---|
210 | bool greedy_sanity_check = |
---|
211 | is_a_matching(g,greedy_mate) && matching_size(g,greedy_mate) == num_v; |
---|
212 | |
---|
213 | BOOST_CHECK (greedy_sanity_check); |
---|
214 | if (greedy_result && !greedy_sanity_check) |
---|
215 | { |
---|
216 | std::cout << "Verifier okayed greedy algorithm on the complete graph, but" << std::endl |
---|
217 | << "the matching returned either wasn't a valid matching or wasn't" << std::endl |
---|
218 | << "actually a maximum cardinality matching." << std::endl; |
---|
219 | all_tests_passed = false; |
---|
220 | } |
---|
221 | |
---|
222 | bool extra_greedy_sanity_check = |
---|
223 | is_a_matching(g,extra_greedy_mate) && matching_size(g,extra_greedy_mate) == num_v; |
---|
224 | |
---|
225 | BOOST_CHECK (extra_greedy_sanity_check); |
---|
226 | if (extra_greedy_result && !extra_greedy_sanity_check) |
---|
227 | { |
---|
228 | std::cout << "Verifier okayed extra greedy algorithm on the complete graph, but" << std::endl |
---|
229 | << "the matching returned either wasn't a valid matching or wasn't" << std::endl |
---|
230 | << "actually a maximum cardinality matching." << std::endl; |
---|
231 | all_tests_passed = false; |
---|
232 | } |
---|
233 | |
---|
234 | //Now remove an edge from the edmonds_mate matching. |
---|
235 | vertex_iterator_t vi,vi_end; |
---|
236 | for(tie(vi,vi_end) = vertices(g); vi != vi_end; ++vi) |
---|
237 | if (edmonds_mate[*vi] != graph_traits<Graph>::null_vertex()) |
---|
238 | break; |
---|
239 | |
---|
240 | edmonds_mate[edmonds_mate[*vi]] = graph_traits<Graph>::null_vertex(); |
---|
241 | edmonds_mate[*vi] = graph_traits<Graph>::null_vertex(); |
---|
242 | |
---|
243 | //...and run the matching verifier - it should tell us that the matching isn't |
---|
244 | //a maximum matching. |
---|
245 | bool modified_edmonds_verification_result = |
---|
246 | maximum_cardinality_matching_verifier<Graph,mate_t,vertex_index_map_t>::verify_matching(g,edmonds_mate,get(vertex_index,g)); |
---|
247 | |
---|
248 | BOOST_CHECK (!modified_edmonds_verification_result); |
---|
249 | if (modified_edmonds_verification_result) |
---|
250 | { |
---|
251 | std::cout << "Verifier was fooled into thinking that a non-maximum matching was maximum" << std::endl; |
---|
252 | all_tests_passed = false; |
---|
253 | } |
---|
254 | |
---|
255 | Graph h(double_num_v); |
---|
256 | gabows_graph(h,num_v); |
---|
257 | |
---|
258 | vertex_index_installer<Graph>::install(h); |
---|
259 | |
---|
260 | //gabow's graph always has a perfect matching. it's also a good example of why |
---|
261 | //one should initialize with the extra_greedy_matching in most cases. |
---|
262 | |
---|
263 | mate_t gabow_mate(double_num_v); |
---|
264 | |
---|
265 | bool gabows_graph_result = checked_edmonds_maximum_cardinality_matching(h,gabow_mate); |
---|
266 | |
---|
267 | BOOST_CHECK (gabows_graph_result); |
---|
268 | if (!gabows_graph_result) |
---|
269 | { |
---|
270 | std::cout << "Problem on Gabow's Graph with " << graph_name << ":" << std::endl |
---|
271 | << " Verifier reporting a maximum cardinality matching not found." << std::endl; |
---|
272 | all_tests_passed = false; |
---|
273 | } |
---|
274 | |
---|
275 | BOOST_CHECK (matching_size(h,gabow_mate) == num_v); |
---|
276 | if (gabows_graph_result && matching_size(h,gabow_mate) != num_v) |
---|
277 | { |
---|
278 | std::cout << "Problem on Gabow's Graph with " << graph_name << ":" << std::endl |
---|
279 | << " Verifier reported a maximum cardinality matching found," << std::endl |
---|
280 | << " but matching size is " << matching_size(h,gabow_mate) |
---|
281 | << " when it should be " << num_v << std::endl; |
---|
282 | all_tests_passed = false; |
---|
283 | } |
---|
284 | |
---|
285 | Graph j(double_num_v); |
---|
286 | vertex_index_installer<Graph>::install(j); |
---|
287 | |
---|
288 | typedef boost::mt19937 base_generator_type; |
---|
289 | base_generator_type generator(static_cast<unsigned int>(std::time(0))); |
---|
290 | boost::uniform_int<> distribution(0, double_num_v-1); |
---|
291 | boost::variate_generator<base_generator_type&, boost::uniform_int<> > rand_num(generator, distribution); |
---|
292 | |
---|
293 | std::size_t num_edges = 0; |
---|
294 | bool success; |
---|
295 | |
---|
296 | while (num_edges < 4*double_num_v) |
---|
297 | { |
---|
298 | vertex_descriptor_t u = random_vertex(j,rand_num); |
---|
299 | vertex_descriptor_t v = random_vertex(j,rand_num); |
---|
300 | if (u != v) |
---|
301 | { |
---|
302 | tie(tuples::ignore, success) = add_edge(u, v, j); |
---|
303 | if (success) |
---|
304 | num_edges++; |
---|
305 | } |
---|
306 | } |
---|
307 | |
---|
308 | mate_t random_mate(double_num_v); |
---|
309 | bool random_graph_result = checked_edmonds_maximum_cardinality_matching(j,random_mate); |
---|
310 | |
---|
311 | BOOST_CHECK(random_graph_result); |
---|
312 | if (!random_graph_result) |
---|
313 | { |
---|
314 | std::cout << "Matching in random graph not maximum for " << graph_name << std::endl; |
---|
315 | all_tests_passed = false; |
---|
316 | } |
---|
317 | |
---|
318 | //Now remove an edge from the random_mate matching. |
---|
319 | for(tie(vi,vi_end) = vertices(j); vi != vi_end; ++vi) |
---|
320 | if (random_mate[*vi] != graph_traits<Graph>::null_vertex()) |
---|
321 | break; |
---|
322 | |
---|
323 | random_mate[random_mate[*vi]] = graph_traits<Graph>::null_vertex(); |
---|
324 | random_mate[*vi] = graph_traits<Graph>::null_vertex(); |
---|
325 | |
---|
326 | //...and run the matching verifier - it should tell us that the matching isn't |
---|
327 | //a maximum matching. |
---|
328 | bool modified_random_verification_result = |
---|
329 | maximum_cardinality_matching_verifier<Graph,mate_t,vertex_index_map_t>::verify_matching(j,random_mate,get(vertex_index,j)); |
---|
330 | |
---|
331 | BOOST_CHECK(!modified_random_verification_result); |
---|
332 | if (modified_random_verification_result) |
---|
333 | { |
---|
334 | std::cout << "Verifier was fooled into thinking that a non-maximum matching was maximum" << std::endl; |
---|
335 | all_tests_passed = false; |
---|
336 | } |
---|
337 | |
---|
338 | BOOST_CHECK(all_tests_passed); |
---|
339 | if (all_tests_passed) |
---|
340 | { |
---|
341 | std::cout << graph_name << " passed all tests for n = " << num_v << '.' << std::endl; |
---|
342 | } |
---|
343 | |
---|
344 | } |
---|
345 | |
---|
346 | |
---|
347 | |
---|
348 | |
---|
349 | int test_main(int argc, char* argv[]) |
---|
350 | { |
---|
351 | |
---|
352 | matching_test<undirected_graph>(10, "adjacency_list (using vectors)"); |
---|
353 | matching_test<undirected_list_graph>(10, "adjacency_list (using lists)"); |
---|
354 | matching_test<undirected_adjacency_matrix_graph>(10, "adjacency_matrix"); |
---|
355 | |
---|
356 | matching_test<undirected_graph>(50, "adjacency_list (using vectors)"); |
---|
357 | matching_test<undirected_list_graph>(50, "adjacency_list (using lists)"); |
---|
358 | matching_test<undirected_adjacency_matrix_graph>(50, "adjacency_matrix"); |
---|
359 | |
---|
360 | matching_test<undirected_graph>(51, "adjacency_list (using vectors)"); |
---|
361 | matching_test<undirected_list_graph>(51, "adjacency_list (using lists)"); |
---|
362 | matching_test<undirected_adjacency_matrix_graph>(51, "adjacency_matrix"); |
---|
363 | |
---|
364 | return 0; |
---|
365 | } |
---|
366 | |
---|
367 | |
---|