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