|
1 /* -*- mode: C++; indent-tabs-mode: nil; -*- |
|
2 * |
|
3 * This file is a part of LEMON, a generic C++ optimization library. |
|
4 * |
|
5 * Copyright (C) 2003-2009 |
|
6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport |
|
7 * (Egervary Research Group on Combinatorial Optimization, EGRES). |
|
8 * |
|
9 * Permission to use, modify and distribute this software is granted |
|
10 * provided that this copyright notice appears in all copies. For |
|
11 * precise terms see the accompanying LICENSE file. |
|
12 * |
|
13 * This software is provided "AS IS" with no warranty of any kind, |
|
14 * express or implied, and with no claim as to its suitability for any |
|
15 * purpose. |
|
16 * |
|
17 */ |
|
18 |
|
19 #include <iostream> |
|
20 #include <sstream> |
|
21 #include <vector> |
|
22 #include <queue> |
|
23 #include <cstdlib> |
|
24 |
|
25 #include <lemon/fractional_matching.h> |
|
26 #include <lemon/smart_graph.h> |
|
27 #include <lemon/concepts/graph.h> |
|
28 #include <lemon/concepts/maps.h> |
|
29 #include <lemon/lgf_reader.h> |
|
30 #include <lemon/math.h> |
|
31 |
|
32 #include "test_tools.h" |
|
33 |
|
34 using namespace std; |
|
35 using namespace lemon; |
|
36 |
|
37 GRAPH_TYPEDEFS(SmartGraph); |
|
38 |
|
39 |
|
40 const int lgfn = 4; |
|
41 const std::string lgf[lgfn] = { |
|
42 "@nodes\n" |
|
43 "label\n" |
|
44 "0\n" |
|
45 "1\n" |
|
46 "2\n" |
|
47 "3\n" |
|
48 "4\n" |
|
49 "5\n" |
|
50 "6\n" |
|
51 "7\n" |
|
52 "@edges\n" |
|
53 " label weight\n" |
|
54 "7 4 0 984\n" |
|
55 "0 7 1 73\n" |
|
56 "7 1 2 204\n" |
|
57 "2 3 3 583\n" |
|
58 "2 7 4 565\n" |
|
59 "2 1 5 582\n" |
|
60 "0 4 6 551\n" |
|
61 "2 5 7 385\n" |
|
62 "1 5 8 561\n" |
|
63 "5 3 9 484\n" |
|
64 "7 5 10 904\n" |
|
65 "3 6 11 47\n" |
|
66 "7 6 12 888\n" |
|
67 "3 0 13 747\n" |
|
68 "6 1 14 310\n", |
|
69 |
|
70 "@nodes\n" |
|
71 "label\n" |
|
72 "0\n" |
|
73 "1\n" |
|
74 "2\n" |
|
75 "3\n" |
|
76 "4\n" |
|
77 "5\n" |
|
78 "6\n" |
|
79 "7\n" |
|
80 "@edges\n" |
|
81 " label weight\n" |
|
82 "2 5 0 710\n" |
|
83 "0 5 1 241\n" |
|
84 "2 4 2 856\n" |
|
85 "2 6 3 762\n" |
|
86 "4 1 4 747\n" |
|
87 "6 1 5 962\n" |
|
88 "4 7 6 723\n" |
|
89 "1 7 7 661\n" |
|
90 "2 3 8 376\n" |
|
91 "1 0 9 416\n" |
|
92 "6 7 10 391\n", |
|
93 |
|
94 "@nodes\n" |
|
95 "label\n" |
|
96 "0\n" |
|
97 "1\n" |
|
98 "2\n" |
|
99 "3\n" |
|
100 "4\n" |
|
101 "5\n" |
|
102 "6\n" |
|
103 "7\n" |
|
104 "@edges\n" |
|
105 " label weight\n" |
|
106 "6 2 0 553\n" |
|
107 "0 7 1 653\n" |
|
108 "6 3 2 22\n" |
|
109 "4 7 3 846\n" |
|
110 "7 2 4 981\n" |
|
111 "7 6 5 250\n" |
|
112 "5 2 6 539\n", |
|
113 |
|
114 "@nodes\n" |
|
115 "label\n" |
|
116 "0\n" |
|
117 "@edges\n" |
|
118 " label weight\n" |
|
119 "0 0 0 100\n" |
|
120 }; |
|
121 |
|
122 void checkMaxFractionalMatchingCompile() |
|
123 { |
|
124 typedef concepts::Graph Graph; |
|
125 typedef Graph::Node Node; |
|
126 typedef Graph::Edge Edge; |
|
127 |
|
128 Graph g; |
|
129 Node n; |
|
130 Edge e; |
|
131 |
|
132 MaxFractionalMatching<Graph> mat_test(g); |
|
133 const MaxFractionalMatching<Graph>& |
|
134 const_mat_test = mat_test; |
|
135 |
|
136 mat_test.init(); |
|
137 mat_test.start(); |
|
138 mat_test.start(true); |
|
139 mat_test.startPerfect(); |
|
140 mat_test.startPerfect(true); |
|
141 mat_test.run(); |
|
142 mat_test.run(true); |
|
143 mat_test.runPerfect(); |
|
144 mat_test.runPerfect(true); |
|
145 |
|
146 const_mat_test.matchingSize(); |
|
147 const_mat_test.matching(e); |
|
148 const_mat_test.matching(n); |
|
149 const MaxFractionalMatching<Graph>::MatchingMap& mmap = |
|
150 const_mat_test.matchingMap(); |
|
151 e = mmap[n]; |
|
152 |
|
153 const_mat_test.barrier(n); |
|
154 } |
|
155 |
|
156 void checkMaxWeightedFractionalMatchingCompile() |
|
157 { |
|
158 typedef concepts::Graph Graph; |
|
159 typedef Graph::Node Node; |
|
160 typedef Graph::Edge Edge; |
|
161 typedef Graph::EdgeMap<int> WeightMap; |
|
162 |
|
163 Graph g; |
|
164 Node n; |
|
165 Edge e; |
|
166 WeightMap w(g); |
|
167 |
|
168 MaxWeightedFractionalMatching<Graph> mat_test(g, w); |
|
169 const MaxWeightedFractionalMatching<Graph>& |
|
170 const_mat_test = mat_test; |
|
171 |
|
172 mat_test.init(); |
|
173 mat_test.start(); |
|
174 mat_test.run(); |
|
175 |
|
176 const_mat_test.matchingWeight(); |
|
177 const_mat_test.matchingSize(); |
|
178 const_mat_test.matching(e); |
|
179 const_mat_test.matching(n); |
|
180 const MaxWeightedFractionalMatching<Graph>::MatchingMap& mmap = |
|
181 const_mat_test.matchingMap(); |
|
182 e = mmap[n]; |
|
183 |
|
184 const_mat_test.dualValue(); |
|
185 const_mat_test.nodeValue(n); |
|
186 } |
|
187 |
|
188 void checkMaxWeightedPerfectFractionalMatchingCompile() |
|
189 { |
|
190 typedef concepts::Graph Graph; |
|
191 typedef Graph::Node Node; |
|
192 typedef Graph::Edge Edge; |
|
193 typedef Graph::EdgeMap<int> WeightMap; |
|
194 |
|
195 Graph g; |
|
196 Node n; |
|
197 Edge e; |
|
198 WeightMap w(g); |
|
199 |
|
200 MaxWeightedPerfectFractionalMatching<Graph> mat_test(g, w); |
|
201 const MaxWeightedPerfectFractionalMatching<Graph>& |
|
202 const_mat_test = mat_test; |
|
203 |
|
204 mat_test.init(); |
|
205 mat_test.start(); |
|
206 mat_test.run(); |
|
207 |
|
208 const_mat_test.matchingWeight(); |
|
209 const_mat_test.matching(e); |
|
210 const_mat_test.matching(n); |
|
211 const MaxWeightedPerfectFractionalMatching<Graph>::MatchingMap& mmap = |
|
212 const_mat_test.matchingMap(); |
|
213 e = mmap[n]; |
|
214 |
|
215 const_mat_test.dualValue(); |
|
216 const_mat_test.nodeValue(n); |
|
217 } |
|
218 |
|
219 void checkFractionalMatching(const SmartGraph& graph, |
|
220 const MaxFractionalMatching<SmartGraph>& mfm, |
|
221 bool allow_loops = true) { |
|
222 int pv = 0; |
|
223 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
224 int indeg = 0; |
|
225 for (InArcIt a(graph, n); a != INVALID; ++a) { |
|
226 if (mfm.matching(graph.source(a)) == a) { |
|
227 ++indeg; |
|
228 } |
|
229 } |
|
230 if (mfm.matching(n) != INVALID) { |
|
231 check(indeg == 1, "Invalid matching"); |
|
232 ++pv; |
|
233 } else { |
|
234 check(indeg == 0, "Invalid matching"); |
|
235 } |
|
236 } |
|
237 check(pv == mfm.matchingSize(), "Wrong matching size"); |
|
238 |
|
239 SmartGraph::NodeMap<bool> processed(graph, false); |
|
240 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
241 if (processed[n]) continue; |
|
242 processed[n] = true; |
|
243 if (mfm.matching(n) == INVALID) continue; |
|
244 int num = 1; |
|
245 Node v = graph.target(mfm.matching(n)); |
|
246 while (v != n) { |
|
247 processed[v] = true; |
|
248 ++num; |
|
249 v = graph.target(mfm.matching(v)); |
|
250 } |
|
251 check(num == 2 || num % 2 == 1, "Wrong cycle size"); |
|
252 check(allow_loops || num != 1, "Wrong cycle size"); |
|
253 } |
|
254 |
|
255 int anum = 0, bnum = 0; |
|
256 SmartGraph::NodeMap<bool> neighbours(graph, false); |
|
257 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
258 if (!mfm.barrier(n)) continue; |
|
259 ++anum; |
|
260 for (SmartGraph::InArcIt a(graph, n); a != INVALID; ++a) { |
|
261 Node u = graph.source(a); |
|
262 if (!allow_loops && u == n) continue; |
|
263 if (!neighbours[u]) { |
|
264 neighbours[u] = true; |
|
265 ++bnum; |
|
266 } |
|
267 } |
|
268 } |
|
269 check(anum - bnum + mfm.matchingSize() == countNodes(graph), |
|
270 "Wrong barrier"); |
|
271 } |
|
272 |
|
273 void checkPerfectFractionalMatching(const SmartGraph& graph, |
|
274 const MaxFractionalMatching<SmartGraph>& mfm, |
|
275 bool perfect, bool allow_loops = true) { |
|
276 if (perfect) { |
|
277 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
278 int indeg = 0; |
|
279 for (InArcIt a(graph, n); a != INVALID; ++a) { |
|
280 if (mfm.matching(graph.source(a)) == a) { |
|
281 ++indeg; |
|
282 } |
|
283 } |
|
284 check(mfm.matching(n) != INVALID, "Invalid matching"); |
|
285 check(indeg == 1, "Invalid matching"); |
|
286 } |
|
287 } else { |
|
288 int anum = 0, bnum = 0; |
|
289 SmartGraph::NodeMap<bool> neighbours(graph, false); |
|
290 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
291 if (!mfm.barrier(n)) continue; |
|
292 ++anum; |
|
293 for (SmartGraph::InArcIt a(graph, n); a != INVALID; ++a) { |
|
294 Node u = graph.source(a); |
|
295 if (!allow_loops && u == n) continue; |
|
296 if (!neighbours[u]) { |
|
297 neighbours[u] = true; |
|
298 ++bnum; |
|
299 } |
|
300 } |
|
301 } |
|
302 check(anum - bnum > 0, "Wrong barrier"); |
|
303 } |
|
304 } |
|
305 |
|
306 void checkWeightedFractionalMatching(const SmartGraph& graph, |
|
307 const SmartGraph::EdgeMap<int>& weight, |
|
308 const MaxWeightedFractionalMatching<SmartGraph>& mwfm, |
|
309 bool allow_loops = true) { |
|
310 for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { |
|
311 if (graph.u(e) == graph.v(e) && !allow_loops) continue; |
|
312 int rw = mwfm.nodeValue(graph.u(e)) + mwfm.nodeValue(graph.v(e)) |
|
313 - weight[e] * mwfm.dualScale; |
|
314 |
|
315 check(rw >= 0, "Negative reduced weight"); |
|
316 check(rw == 0 || !mwfm.matching(e), |
|
317 "Non-zero reduced weight on matching edge"); |
|
318 } |
|
319 |
|
320 int pv = 0; |
|
321 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
322 int indeg = 0; |
|
323 for (InArcIt a(graph, n); a != INVALID; ++a) { |
|
324 if (mwfm.matching(graph.source(a)) == a) { |
|
325 ++indeg; |
|
326 } |
|
327 } |
|
328 check(indeg <= 1, "Invalid matching"); |
|
329 if (mwfm.matching(n) != INVALID) { |
|
330 check(mwfm.nodeValue(n) >= 0, "Invalid node value"); |
|
331 check(indeg == 1, "Invalid matching"); |
|
332 pv += weight[mwfm.matching(n)]; |
|
333 SmartGraph::Node o = graph.target(mwfm.matching(n)); |
|
334 } else { |
|
335 check(mwfm.nodeValue(n) == 0, "Invalid matching"); |
|
336 check(indeg == 0, "Invalid matching"); |
|
337 } |
|
338 } |
|
339 |
|
340 int dv = 0; |
|
341 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
342 dv += mwfm.nodeValue(n); |
|
343 } |
|
344 |
|
345 check(pv * mwfm.dualScale == dv * 2, "Wrong duality"); |
|
346 |
|
347 SmartGraph::NodeMap<bool> processed(graph, false); |
|
348 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
349 if (processed[n]) continue; |
|
350 processed[n] = true; |
|
351 if (mwfm.matching(n) == INVALID) continue; |
|
352 int num = 1; |
|
353 Node v = graph.target(mwfm.matching(n)); |
|
354 while (v != n) { |
|
355 processed[v] = true; |
|
356 ++num; |
|
357 v = graph.target(mwfm.matching(v)); |
|
358 } |
|
359 check(num == 2 || num % 2 == 1, "Wrong cycle size"); |
|
360 check(allow_loops || num != 1, "Wrong cycle size"); |
|
361 } |
|
362 |
|
363 return; |
|
364 } |
|
365 |
|
366 void checkWeightedPerfectFractionalMatching(const SmartGraph& graph, |
|
367 const SmartGraph::EdgeMap<int>& weight, |
|
368 const MaxWeightedPerfectFractionalMatching<SmartGraph>& mwpfm, |
|
369 bool allow_loops = true) { |
|
370 for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { |
|
371 if (graph.u(e) == graph.v(e) && !allow_loops) continue; |
|
372 int rw = mwpfm.nodeValue(graph.u(e)) + mwpfm.nodeValue(graph.v(e)) |
|
373 - weight[e] * mwpfm.dualScale; |
|
374 |
|
375 check(rw >= 0, "Negative reduced weight"); |
|
376 check(rw == 0 || !mwpfm.matching(e), |
|
377 "Non-zero reduced weight on matching edge"); |
|
378 } |
|
379 |
|
380 int pv = 0; |
|
381 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
382 int indeg = 0; |
|
383 for (InArcIt a(graph, n); a != INVALID; ++a) { |
|
384 if (mwpfm.matching(graph.source(a)) == a) { |
|
385 ++indeg; |
|
386 } |
|
387 } |
|
388 check(mwpfm.matching(n) != INVALID, "Invalid perfect matching"); |
|
389 check(indeg == 1, "Invalid perfect matching"); |
|
390 pv += weight[mwpfm.matching(n)]; |
|
391 SmartGraph::Node o = graph.target(mwpfm.matching(n)); |
|
392 } |
|
393 |
|
394 int dv = 0; |
|
395 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
396 dv += mwpfm.nodeValue(n); |
|
397 } |
|
398 |
|
399 check(pv * mwpfm.dualScale == dv * 2, "Wrong duality"); |
|
400 |
|
401 SmartGraph::NodeMap<bool> processed(graph, false); |
|
402 for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { |
|
403 if (processed[n]) continue; |
|
404 processed[n] = true; |
|
405 if (mwpfm.matching(n) == INVALID) continue; |
|
406 int num = 1; |
|
407 Node v = graph.target(mwpfm.matching(n)); |
|
408 while (v != n) { |
|
409 processed[v] = true; |
|
410 ++num; |
|
411 v = graph.target(mwpfm.matching(v)); |
|
412 } |
|
413 check(num == 2 || num % 2 == 1, "Wrong cycle size"); |
|
414 check(allow_loops || num != 1, "Wrong cycle size"); |
|
415 } |
|
416 |
|
417 return; |
|
418 } |
|
419 |
|
420 |
|
421 int main() { |
|
422 |
|
423 for (int i = 0; i < lgfn; ++i) { |
|
424 SmartGraph graph; |
|
425 SmartGraph::EdgeMap<int> weight(graph); |
|
426 |
|
427 istringstream lgfs(lgf[i]); |
|
428 graphReader(graph, lgfs). |
|
429 edgeMap("weight", weight).run(); |
|
430 |
|
431 bool perfect_with_loops; |
|
432 { |
|
433 MaxFractionalMatching<SmartGraph> mfm(graph, true); |
|
434 mfm.run(); |
|
435 checkFractionalMatching(graph, mfm, true); |
|
436 perfect_with_loops = mfm.matchingSize() == countNodes(graph); |
|
437 } |
|
438 |
|
439 bool perfect_without_loops; |
|
440 { |
|
441 MaxFractionalMatching<SmartGraph> mfm(graph, false); |
|
442 mfm.run(); |
|
443 checkFractionalMatching(graph, mfm, false); |
|
444 perfect_without_loops = mfm.matchingSize() == countNodes(graph); |
|
445 } |
|
446 |
|
447 { |
|
448 MaxFractionalMatching<SmartGraph> mfm(graph, true); |
|
449 bool result = mfm.runPerfect(); |
|
450 checkPerfectFractionalMatching(graph, mfm, result, true); |
|
451 check(result == perfect_with_loops, "Wrong perfect matching"); |
|
452 } |
|
453 |
|
454 { |
|
455 MaxFractionalMatching<SmartGraph> mfm(graph, false); |
|
456 bool result = mfm.runPerfect(); |
|
457 checkPerfectFractionalMatching(graph, mfm, result, false); |
|
458 check(result == perfect_without_loops, "Wrong perfect matching"); |
|
459 } |
|
460 |
|
461 { |
|
462 MaxWeightedFractionalMatching<SmartGraph> mwfm(graph, weight, true); |
|
463 mwfm.run(); |
|
464 checkWeightedFractionalMatching(graph, weight, mwfm, true); |
|
465 } |
|
466 |
|
467 { |
|
468 MaxWeightedFractionalMatching<SmartGraph> mwfm(graph, weight, false); |
|
469 mwfm.run(); |
|
470 checkWeightedFractionalMatching(graph, weight, mwfm, false); |
|
471 } |
|
472 |
|
473 { |
|
474 MaxWeightedPerfectFractionalMatching<SmartGraph> mwpfm(graph, weight, |
|
475 true); |
|
476 bool perfect = mwpfm.run(); |
|
477 check(perfect == (mwpfm.matchingSize() == countNodes(graph)), |
|
478 "Perfect matching found"); |
|
479 check(perfect == perfect_with_loops, "Wrong perfect matching"); |
|
480 |
|
481 if (perfect) { |
|
482 checkWeightedPerfectFractionalMatching(graph, weight, mwpfm, true); |
|
483 } |
|
484 } |
|
485 |
|
486 { |
|
487 MaxWeightedPerfectFractionalMatching<SmartGraph> mwpfm(graph, weight, |
|
488 false); |
|
489 bool perfect = mwpfm.run(); |
|
490 check(perfect == (mwpfm.matchingSize() == countNodes(graph)), |
|
491 "Perfect matching found"); |
|
492 check(perfect == perfect_without_loops, "Wrong perfect matching"); |
|
493 |
|
494 if (perfect) { |
|
495 checkWeightedPerfectFractionalMatching(graph, weight, mwpfm, false); |
|
496 } |
|
497 } |
|
498 |
|
499 } |
|
500 |
|
501 return 0; |
|
502 } |