deba@948: /* -*- mode: C++; indent-tabs-mode: nil; -*- deba@948: * deba@948: * This file is a part of LEMON, a generic C++ optimization library. deba@948: * alpar@956: * Copyright (C) 2003-2010 deba@948: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport deba@948: * (Egervary Research Group on Combinatorial Optimization, EGRES). deba@948: * deba@948: * Permission to use, modify and distribute this software is granted deba@948: * provided that this copyright notice appears in all copies. For deba@948: * precise terms see the accompanying LICENSE file. deba@948: * deba@948: * This software is provided "AS IS" with no warranty of any kind, deba@948: * express or implied, and with no claim as to its suitability for any deba@948: * purpose. deba@948: * deba@948: */ deba@948: deba@948: #include deba@948: #include deba@948: #include deba@948: #include deba@948: #include deba@948: deba@948: #include deba@948: #include deba@948: #include deba@948: #include deba@948: #include deba@948: #include deba@948: deba@948: #include "test_tools.h" deba@948: deba@948: using namespace std; deba@948: using namespace lemon; deba@948: deba@948: GRAPH_TYPEDEFS(SmartGraph); deba@948: deba@948: deba@948: const int lgfn = 4; deba@948: const std::string lgf[lgfn] = { deba@948: "@nodes\n" deba@948: "label\n" deba@948: "0\n" deba@948: "1\n" deba@948: "2\n" deba@948: "3\n" deba@948: "4\n" deba@948: "5\n" deba@948: "6\n" deba@948: "7\n" deba@948: "@edges\n" deba@948: " label weight\n" deba@948: "7 4 0 984\n" deba@948: "0 7 1 73\n" deba@948: "7 1 2 204\n" deba@948: "2 3 3 583\n" deba@948: "2 7 4 565\n" deba@948: "2 1 5 582\n" deba@948: "0 4 6 551\n" deba@948: "2 5 7 385\n" deba@948: "1 5 8 561\n" deba@948: "5 3 9 484\n" deba@948: "7 5 10 904\n" deba@948: "3 6 11 47\n" deba@948: "7 6 12 888\n" deba@948: "3 0 13 747\n" deba@948: "6 1 14 310\n", deba@948: deba@948: "@nodes\n" deba@948: "label\n" deba@948: "0\n" deba@948: "1\n" deba@948: "2\n" deba@948: "3\n" deba@948: "4\n" deba@948: "5\n" deba@948: "6\n" deba@948: "7\n" deba@948: "@edges\n" deba@948: " label weight\n" deba@948: "2 5 0 710\n" deba@948: "0 5 1 241\n" deba@948: "2 4 2 856\n" deba@948: "2 6 3 762\n" deba@948: "4 1 4 747\n" deba@948: "6 1 5 962\n" deba@948: "4 7 6 723\n" deba@948: "1 7 7 661\n" deba@948: "2 3 8 376\n" deba@948: "1 0 9 416\n" deba@948: "6 7 10 391\n", deba@948: deba@948: "@nodes\n" deba@948: "label\n" deba@948: "0\n" deba@948: "1\n" deba@948: "2\n" deba@948: "3\n" deba@948: "4\n" deba@948: "5\n" deba@948: "6\n" deba@948: "7\n" deba@948: "@edges\n" deba@948: " label weight\n" deba@948: "6 2 0 553\n" deba@948: "0 7 1 653\n" deba@948: "6 3 2 22\n" deba@948: "4 7 3 846\n" deba@948: "7 2 4 981\n" deba@948: "7 6 5 250\n" deba@948: "5 2 6 539\n", deba@948: deba@948: "@nodes\n" deba@948: "label\n" deba@948: "0\n" deba@948: "@edges\n" deba@948: " label weight\n" deba@948: "0 0 0 100\n" deba@948: }; deba@948: deba@948: void checkMaxFractionalMatchingCompile() deba@948: { deba@948: typedef concepts::Graph Graph; deba@948: typedef Graph::Node Node; deba@948: typedef Graph::Edge Edge; deba@948: deba@948: Graph g; deba@948: Node n; deba@948: Edge e; deba@948: deba@948: MaxFractionalMatching mat_test(g); deba@948: const MaxFractionalMatching& deba@948: const_mat_test = mat_test; deba@948: deba@948: mat_test.init(); deba@948: mat_test.start(); deba@948: mat_test.start(true); deba@948: mat_test.startPerfect(); deba@948: mat_test.startPerfect(true); deba@948: mat_test.run(); deba@948: mat_test.run(true); deba@948: mat_test.runPerfect(); deba@948: mat_test.runPerfect(true); deba@948: deba@948: const_mat_test.matchingSize(); deba@948: const_mat_test.matching(e); deba@948: const_mat_test.matching(n); deba@948: const MaxFractionalMatching::MatchingMap& mmap = deba@948: const_mat_test.matchingMap(); deba@948: e = mmap[n]; deba@948: deba@948: const_mat_test.barrier(n); deba@948: } deba@948: deba@948: void checkMaxWeightedFractionalMatchingCompile() deba@948: { deba@948: typedef concepts::Graph Graph; deba@948: typedef Graph::Node Node; deba@948: typedef Graph::Edge Edge; deba@948: typedef Graph::EdgeMap WeightMap; deba@948: deba@948: Graph g; deba@948: Node n; deba@948: Edge e; deba@948: WeightMap w(g); deba@948: deba@948: MaxWeightedFractionalMatching mat_test(g, w); deba@948: const MaxWeightedFractionalMatching& deba@948: const_mat_test = mat_test; deba@948: deba@948: mat_test.init(); deba@948: mat_test.start(); deba@948: mat_test.run(); deba@948: deba@948: const_mat_test.matchingWeight(); deba@948: const_mat_test.matchingSize(); deba@948: const_mat_test.matching(e); deba@948: const_mat_test.matching(n); deba@948: const MaxWeightedFractionalMatching::MatchingMap& mmap = deba@948: const_mat_test.matchingMap(); deba@948: e = mmap[n]; deba@948: deba@948: const_mat_test.dualValue(); deba@948: const_mat_test.nodeValue(n); deba@948: } deba@948: deba@948: void checkMaxWeightedPerfectFractionalMatchingCompile() deba@948: { deba@948: typedef concepts::Graph Graph; deba@948: typedef Graph::Node Node; deba@948: typedef Graph::Edge Edge; deba@948: typedef Graph::EdgeMap WeightMap; deba@948: deba@948: Graph g; deba@948: Node n; deba@948: Edge e; deba@948: WeightMap w(g); deba@948: deba@948: MaxWeightedPerfectFractionalMatching mat_test(g, w); deba@948: const MaxWeightedPerfectFractionalMatching& deba@948: const_mat_test = mat_test; deba@948: deba@948: mat_test.init(); deba@948: mat_test.start(); deba@948: mat_test.run(); deba@948: deba@948: const_mat_test.matchingWeight(); deba@948: const_mat_test.matching(e); deba@948: const_mat_test.matching(n); deba@948: const MaxWeightedPerfectFractionalMatching::MatchingMap& mmap = deba@948: const_mat_test.matchingMap(); deba@948: e = mmap[n]; deba@948: deba@948: const_mat_test.dualValue(); deba@948: const_mat_test.nodeValue(n); deba@948: } deba@948: deba@948: void checkFractionalMatching(const SmartGraph& graph, deba@948: const MaxFractionalMatching& mfm, deba@948: bool allow_loops = true) { deba@948: int pv = 0; deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: int indeg = 0; deba@948: for (InArcIt a(graph, n); a != INVALID; ++a) { deba@948: if (mfm.matching(graph.source(a)) == a) { deba@948: ++indeg; deba@948: } deba@948: } deba@948: if (mfm.matching(n) != INVALID) { deba@948: check(indeg == 1, "Invalid matching"); deba@948: ++pv; deba@948: } else { deba@948: check(indeg == 0, "Invalid matching"); deba@948: } deba@948: } deba@948: check(pv == mfm.matchingSize(), "Wrong matching size"); deba@948: deba@951: for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { deba@951: check((e == mfm.matching(graph.u(e)) ? 1 : 0) + alpar@956: (e == mfm.matching(graph.v(e)) ? 1 : 0) == deba@951: mfm.matching(e), "Invalid matching"); deba@951: } deba@951: deba@948: SmartGraph::NodeMap processed(graph, false); deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: if (processed[n]) continue; deba@948: processed[n] = true; deba@948: if (mfm.matching(n) == INVALID) continue; deba@948: int num = 1; deba@948: Node v = graph.target(mfm.matching(n)); deba@948: while (v != n) { deba@948: processed[v] = true; deba@948: ++num; deba@948: v = graph.target(mfm.matching(v)); deba@948: } deba@948: check(num == 2 || num % 2 == 1, "Wrong cycle size"); deba@948: check(allow_loops || num != 1, "Wrong cycle size"); deba@948: } deba@948: deba@948: int anum = 0, bnum = 0; deba@948: SmartGraph::NodeMap neighbours(graph, false); deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: if (!mfm.barrier(n)) continue; deba@948: ++anum; deba@948: for (SmartGraph::InArcIt a(graph, n); a != INVALID; ++a) { deba@948: Node u = graph.source(a); deba@948: if (!allow_loops && u == n) continue; deba@948: if (!neighbours[u]) { deba@948: neighbours[u] = true; deba@948: ++bnum; deba@948: } deba@948: } deba@948: } deba@948: check(anum - bnum + mfm.matchingSize() == countNodes(graph), deba@948: "Wrong barrier"); deba@948: } deba@948: deba@948: void checkPerfectFractionalMatching(const SmartGraph& graph, deba@948: const MaxFractionalMatching& mfm, deba@948: bool perfect, bool allow_loops = true) { deba@948: if (perfect) { deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: int indeg = 0; deba@948: for (InArcIt a(graph, n); a != INVALID; ++a) { deba@948: if (mfm.matching(graph.source(a)) == a) { deba@948: ++indeg; deba@948: } deba@948: } deba@948: check(mfm.matching(n) != INVALID, "Invalid matching"); deba@948: check(indeg == 1, "Invalid matching"); deba@948: } deba@951: for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { deba@951: check((e == mfm.matching(graph.u(e)) ? 1 : 0) + alpar@956: (e == mfm.matching(graph.v(e)) ? 1 : 0) == deba@951: mfm.matching(e), "Invalid matching"); deba@951: } deba@948: } else { deba@948: int anum = 0, bnum = 0; deba@948: SmartGraph::NodeMap neighbours(graph, false); deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: if (!mfm.barrier(n)) continue; deba@948: ++anum; deba@948: for (SmartGraph::InArcIt a(graph, n); a != INVALID; ++a) { deba@948: Node u = graph.source(a); deba@948: if (!allow_loops && u == n) continue; deba@948: if (!neighbours[u]) { deba@948: neighbours[u] = true; deba@948: ++bnum; deba@948: } deba@948: } deba@948: } deba@948: check(anum - bnum > 0, "Wrong barrier"); deba@948: } deba@948: } deba@948: deba@948: void checkWeightedFractionalMatching(const SmartGraph& graph, deba@948: const SmartGraph::EdgeMap& weight, deba@948: const MaxWeightedFractionalMatching& mwfm, deba@948: bool allow_loops = true) { deba@948: for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { deba@948: if (graph.u(e) == graph.v(e) && !allow_loops) continue; deba@948: int rw = mwfm.nodeValue(graph.u(e)) + mwfm.nodeValue(graph.v(e)) deba@948: - weight[e] * mwfm.dualScale; deba@948: deba@948: check(rw >= 0, "Negative reduced weight"); deba@948: check(rw == 0 || !mwfm.matching(e), deba@948: "Non-zero reduced weight on matching edge"); deba@948: } deba@948: deba@948: int pv = 0; deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: int indeg = 0; deba@948: for (InArcIt a(graph, n); a != INVALID; ++a) { deba@948: if (mwfm.matching(graph.source(a)) == a) { deba@948: ++indeg; deba@948: } deba@948: } deba@948: check(indeg <= 1, "Invalid matching"); deba@948: if (mwfm.matching(n) != INVALID) { deba@948: check(mwfm.nodeValue(n) >= 0, "Invalid node value"); deba@948: check(indeg == 1, "Invalid matching"); deba@948: pv += weight[mwfm.matching(n)]; deba@948: SmartGraph::Node o = graph.target(mwfm.matching(n)); alpar@1160: ignore_unused_variable_warning(o); deba@948: } else { deba@948: check(mwfm.nodeValue(n) == 0, "Invalid matching"); deba@948: check(indeg == 0, "Invalid matching"); deba@948: } deba@948: } deba@948: deba@951: for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { deba@951: check((e == mwfm.matching(graph.u(e)) ? 1 : 0) + alpar@956: (e == mwfm.matching(graph.v(e)) ? 1 : 0) == deba@951: mwfm.matching(e), "Invalid matching"); deba@951: } deba@951: deba@948: int dv = 0; deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: dv += mwfm.nodeValue(n); deba@948: } deba@948: deba@948: check(pv * mwfm.dualScale == dv * 2, "Wrong duality"); deba@948: deba@948: SmartGraph::NodeMap processed(graph, false); deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: if (processed[n]) continue; deba@948: processed[n] = true; deba@948: if (mwfm.matching(n) == INVALID) continue; deba@948: int num = 1; deba@948: Node v = graph.target(mwfm.matching(n)); deba@948: while (v != n) { deba@948: processed[v] = true; deba@948: ++num; deba@948: v = graph.target(mwfm.matching(v)); deba@948: } deba@948: check(num == 2 || num % 2 == 1, "Wrong cycle size"); deba@948: check(allow_loops || num != 1, "Wrong cycle size"); deba@948: } deba@948: deba@948: return; deba@948: } deba@948: deba@948: void checkWeightedPerfectFractionalMatching(const SmartGraph& graph, deba@948: const SmartGraph::EdgeMap& weight, deba@948: const MaxWeightedPerfectFractionalMatching& mwpfm, deba@948: bool allow_loops = true) { deba@948: for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { deba@948: if (graph.u(e) == graph.v(e) && !allow_loops) continue; deba@948: int rw = mwpfm.nodeValue(graph.u(e)) + mwpfm.nodeValue(graph.v(e)) deba@948: - weight[e] * mwpfm.dualScale; deba@948: deba@948: check(rw >= 0, "Negative reduced weight"); deba@948: check(rw == 0 || !mwpfm.matching(e), deba@948: "Non-zero reduced weight on matching edge"); deba@948: } deba@948: deba@948: int pv = 0; deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: int indeg = 0; deba@948: for (InArcIt a(graph, n); a != INVALID; ++a) { deba@948: if (mwpfm.matching(graph.source(a)) == a) { deba@948: ++indeg; deba@948: } deba@948: } deba@948: check(mwpfm.matching(n) != INVALID, "Invalid perfect matching"); deba@948: check(indeg == 1, "Invalid perfect matching"); deba@948: pv += weight[mwpfm.matching(n)]; deba@948: SmartGraph::Node o = graph.target(mwpfm.matching(n)); alpar@1160: ignore_unused_variable_warning(o); deba@948: } deba@948: deba@951: for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) { deba@951: check((e == mwpfm.matching(graph.u(e)) ? 1 : 0) + alpar@956: (e == mwpfm.matching(graph.v(e)) ? 1 : 0) == deba@951: mwpfm.matching(e), "Invalid matching"); deba@951: } deba@951: deba@948: int dv = 0; deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: dv += mwpfm.nodeValue(n); deba@948: } deba@948: deba@948: check(pv * mwpfm.dualScale == dv * 2, "Wrong duality"); deba@948: deba@948: SmartGraph::NodeMap processed(graph, false); deba@948: for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) { deba@948: if (processed[n]) continue; deba@948: processed[n] = true; deba@948: if (mwpfm.matching(n) == INVALID) continue; deba@948: int num = 1; deba@948: Node v = graph.target(mwpfm.matching(n)); deba@948: while (v != n) { deba@948: processed[v] = true; deba@948: ++num; deba@948: v = graph.target(mwpfm.matching(v)); deba@948: } deba@948: check(num == 2 || num % 2 == 1, "Wrong cycle size"); deba@948: check(allow_loops || num != 1, "Wrong cycle size"); deba@948: } deba@948: deba@948: return; deba@948: } deba@948: deba@948: deba@948: int main() { deba@948: deba@948: for (int i = 0; i < lgfn; ++i) { deba@948: SmartGraph graph; deba@948: SmartGraph::EdgeMap weight(graph); deba@948: deba@948: istringstream lgfs(lgf[i]); deba@948: graphReader(graph, lgfs). deba@948: edgeMap("weight", weight).run(); deba@948: deba@948: bool perfect_with_loops; deba@948: { deba@948: MaxFractionalMatching mfm(graph, true); deba@948: mfm.run(); deba@948: checkFractionalMatching(graph, mfm, true); deba@948: perfect_with_loops = mfm.matchingSize() == countNodes(graph); deba@948: } deba@948: deba@948: bool perfect_without_loops; deba@948: { deba@948: MaxFractionalMatching mfm(graph, false); deba@948: mfm.run(); deba@948: checkFractionalMatching(graph, mfm, false); deba@948: perfect_without_loops = mfm.matchingSize() == countNodes(graph); deba@948: } deba@948: deba@948: { deba@948: MaxFractionalMatching mfm(graph, true); deba@948: bool result = mfm.runPerfect(); deba@948: checkPerfectFractionalMatching(graph, mfm, result, true); deba@948: check(result == perfect_with_loops, "Wrong perfect matching"); deba@948: } deba@948: deba@948: { deba@948: MaxFractionalMatching mfm(graph, false); deba@948: bool result = mfm.runPerfect(); deba@948: checkPerfectFractionalMatching(graph, mfm, result, false); deba@948: check(result == perfect_without_loops, "Wrong perfect matching"); deba@948: } deba@948: deba@948: { deba@948: MaxWeightedFractionalMatching mwfm(graph, weight, true); deba@948: mwfm.run(); deba@948: checkWeightedFractionalMatching(graph, weight, mwfm, true); deba@948: } deba@948: deba@948: { deba@948: MaxWeightedFractionalMatching mwfm(graph, weight, false); deba@948: mwfm.run(); deba@948: checkWeightedFractionalMatching(graph, weight, mwfm, false); deba@948: } deba@948: deba@948: { deba@948: MaxWeightedPerfectFractionalMatching mwpfm(graph, weight, deba@948: true); deba@948: bool perfect = mwpfm.run(); deba@948: check(perfect == (mwpfm.matchingSize() == countNodes(graph)), deba@948: "Perfect matching found"); deba@948: check(perfect == perfect_with_loops, "Wrong perfect matching"); deba@948: deba@948: if (perfect) { deba@948: checkWeightedPerfectFractionalMatching(graph, weight, mwpfm, true); deba@948: } deba@948: } deba@948: deba@948: { deba@948: MaxWeightedPerfectFractionalMatching mwpfm(graph, weight, deba@948: false); deba@948: bool perfect = mwpfm.run(); deba@948: check(perfect == (mwpfm.matchingSize() == countNodes(graph)), deba@948: "Perfect matching found"); deba@948: check(perfect == perfect_without_loops, "Wrong perfect matching"); deba@948: deba@948: if (perfect) { deba@948: checkWeightedPerfectFractionalMatching(graph, weight, mwpfm, false); deba@948: } deba@948: } deba@948: deba@948: } deba@948: deba@948: return 0; deba@948: }