test/max_matching_test.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Fri, 17 Apr 2009 18:04:36 +0200
changeset 656 e6927fe719e6
parent 339 91d63b8b1a4c
child 637 b61354458b59
permissions -rw-r--r--
Support >= and <= constraints in NetworkSimplex (#219, #234)

By default the same inequality constraints are supported as by
Circulation (the GEQ form), but the LEQ form can also be selected
using the problemType() function.

The documentation of the min. cost flow module is reworked and
extended with important notes and explanations about the different
variants of the problem and about the dual solution and optimality
conditions.
     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 <lemon/math.h>
    24 #include <cstdlib>
    25 
    26 #include <lemon/max_matching.h>
    27 #include <lemon/smart_graph.h>
    28 #include <lemon/lgf_reader.h>
    29 
    30 #include "test_tools.h"
    31 
    32 using namespace std;
    33 using namespace lemon;
    34 
    35 GRAPH_TYPEDEFS(SmartGraph);
    36 
    37 
    38 const int lgfn = 3;
    39 const std::string lgf[lgfn] = {
    40   "@nodes\n"
    41   "label\n"
    42   "0\n"
    43   "1\n"
    44   "2\n"
    45   "3\n"
    46   "4\n"
    47   "5\n"
    48   "6\n"
    49   "7\n"
    50   "@edges\n"
    51   "     label  weight\n"
    52   "7 4  0      984\n"
    53   "0 7  1      73\n"
    54   "7 1  2      204\n"
    55   "2 3  3      583\n"
    56   "2 7  4      565\n"
    57   "2 1  5      582\n"
    58   "0 4  6      551\n"
    59   "2 5  7      385\n"
    60   "1 5  8      561\n"
    61   "5 3  9      484\n"
    62   "7 5  10     904\n"
    63   "3 6  11     47\n"
    64   "7 6  12     888\n"
    65   "3 0  13     747\n"
    66   "6 1  14     310\n",
    67 
    68   "@nodes\n"
    69   "label\n"
    70   "0\n"
    71   "1\n"
    72   "2\n"
    73   "3\n"
    74   "4\n"
    75   "5\n"
    76   "6\n"
    77   "7\n"
    78   "@edges\n"
    79   "     label  weight\n"
    80   "2 5  0      710\n"
    81   "0 5  1      241\n"
    82   "2 4  2      856\n"
    83   "2 6  3      762\n"
    84   "4 1  4      747\n"
    85   "6 1  5      962\n"
    86   "4 7  6      723\n"
    87   "1 7  7      661\n"
    88   "2 3  8      376\n"
    89   "1 0  9      416\n"
    90   "6 7  10     391\n",
    91 
    92   "@nodes\n"
    93   "label\n"
    94   "0\n"
    95   "1\n"
    96   "2\n"
    97   "3\n"
    98   "4\n"
    99   "5\n"
   100   "6\n"
   101   "7\n"
   102   "@edges\n"
   103   "     label  weight\n"
   104   "6 2  0      553\n"
   105   "0 7  1      653\n"
   106   "6 3  2      22\n"
   107   "4 7  3      846\n"
   108   "7 2  4      981\n"
   109   "7 6  5      250\n"
   110   "5 2  6      539\n",
   111 };
   112 
   113 void checkMatching(const SmartGraph& graph,
   114                    const MaxMatching<SmartGraph>& mm) {
   115   int num = 0;
   116 
   117   IntNodeMap comp_index(graph);
   118   UnionFind<IntNodeMap> comp(comp_index);
   119 
   120   int barrier_num = 0;
   121 
   122   for (NodeIt n(graph); n != INVALID; ++n) {
   123     check(mm.decomposition(n) == MaxMatching<SmartGraph>::EVEN ||
   124           mm.matching(n) != INVALID, "Wrong Gallai-Edmonds decomposition");
   125     if (mm.decomposition(n) == MaxMatching<SmartGraph>::ODD) {
   126       ++barrier_num;
   127     } else {
   128       comp.insert(n);
   129     }
   130   }
   131 
   132   for (EdgeIt e(graph); e != INVALID; ++e) {
   133     if (mm.matching(e)) {
   134       check(e == mm.matching(graph.u(e)), "Wrong matching");
   135       check(e == mm.matching(graph.v(e)), "Wrong matching");
   136       ++num;
   137     }
   138     check(mm.decomposition(graph.u(e)) != MaxMatching<SmartGraph>::EVEN ||
   139           mm.decomposition(graph.v(e)) != MaxMatching<SmartGraph>::MATCHED,
   140           "Wrong Gallai-Edmonds decomposition");
   141 
   142     check(mm.decomposition(graph.v(e)) != MaxMatching<SmartGraph>::EVEN ||
   143           mm.decomposition(graph.u(e)) != MaxMatching<SmartGraph>::MATCHED,
   144           "Wrong Gallai-Edmonds decomposition");
   145 
   146     if (mm.decomposition(graph.u(e)) != MaxMatching<SmartGraph>::ODD &&
   147         mm.decomposition(graph.v(e)) != MaxMatching<SmartGraph>::ODD) {
   148       comp.join(graph.u(e), graph.v(e));
   149     }
   150   }
   151 
   152   std::set<int> comp_root;
   153   int odd_comp_num = 0;
   154   for (NodeIt n(graph); n != INVALID; ++n) {
   155     if (mm.decomposition(n) != MaxMatching<SmartGraph>::ODD) {
   156       int root = comp.find(n);
   157       if (comp_root.find(root) == comp_root.end()) {
   158         comp_root.insert(root);
   159         if (comp.size(n) % 2 == 1) {
   160           ++odd_comp_num;
   161         }
   162       }
   163     }
   164   }
   165 
   166   check(mm.matchingSize() == num, "Wrong matching");
   167   check(2 * num == countNodes(graph) - (odd_comp_num - barrier_num),
   168          "Wrong matching");
   169   return;
   170 }
   171 
   172 void checkWeightedMatching(const SmartGraph& graph,
   173                    const SmartGraph::EdgeMap<int>& weight,
   174                    const MaxWeightedMatching<SmartGraph>& mwm) {
   175   for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) {
   176     if (graph.u(e) == graph.v(e)) continue;
   177     int rw = mwm.nodeValue(graph.u(e)) + mwm.nodeValue(graph.v(e));
   178 
   179     for (int i = 0; i < mwm.blossomNum(); ++i) {
   180       bool s = false, t = false;
   181       for (MaxWeightedMatching<SmartGraph>::BlossomIt n(mwm, i);
   182            n != INVALID; ++n) {
   183         if (graph.u(e) == n) s = true;
   184         if (graph.v(e) == n) t = true;
   185       }
   186       if (s == true && t == true) {
   187         rw += mwm.blossomValue(i);
   188       }
   189     }
   190     rw -= weight[e] * mwm.dualScale;
   191 
   192     check(rw >= 0, "Negative reduced weight");
   193     check(rw == 0 || !mwm.matching(e),
   194           "Non-zero reduced weight on matching edge");
   195   }
   196 
   197   int pv = 0;
   198   for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
   199     if (mwm.matching(n) != INVALID) {
   200       check(mwm.nodeValue(n) >= 0, "Invalid node value");
   201       pv += weight[mwm.matching(n)];
   202       SmartGraph::Node o = graph.target(mwm.matching(n));
   203       check(mwm.mate(n) == o, "Invalid matching");
   204       check(mwm.matching(n) == graph.oppositeArc(mwm.matching(o)),
   205             "Invalid matching");
   206     } else {
   207       check(mwm.mate(n) == INVALID, "Invalid matching");
   208       check(mwm.nodeValue(n) == 0, "Invalid matching");
   209     }
   210   }
   211 
   212   int dv = 0;
   213   for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
   214     dv += mwm.nodeValue(n);
   215   }
   216 
   217   for (int i = 0; i < mwm.blossomNum(); ++i) {
   218     check(mwm.blossomValue(i) >= 0, "Invalid blossom value");
   219     check(mwm.blossomSize(i) % 2 == 1, "Even blossom size");
   220     dv += mwm.blossomValue(i) * ((mwm.blossomSize(i) - 1) / 2);
   221   }
   222 
   223   check(pv * mwm.dualScale == dv * 2, "Wrong duality");
   224 
   225   return;
   226 }
   227 
   228 void checkWeightedPerfectMatching(const SmartGraph& graph,
   229                           const SmartGraph::EdgeMap<int>& weight,
   230                           const MaxWeightedPerfectMatching<SmartGraph>& mwpm) {
   231   for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) {
   232     if (graph.u(e) == graph.v(e)) continue;
   233     int rw = mwpm.nodeValue(graph.u(e)) + mwpm.nodeValue(graph.v(e));
   234 
   235     for (int i = 0; i < mwpm.blossomNum(); ++i) {
   236       bool s = false, t = false;
   237       for (MaxWeightedPerfectMatching<SmartGraph>::BlossomIt n(mwpm, i);
   238            n != INVALID; ++n) {
   239         if (graph.u(e) == n) s = true;
   240         if (graph.v(e) == n) t = true;
   241       }
   242       if (s == true && t == true) {
   243         rw += mwpm.blossomValue(i);
   244       }
   245     }
   246     rw -= weight[e] * mwpm.dualScale;
   247 
   248     check(rw >= 0, "Negative reduced weight");
   249     check(rw == 0 || !mwpm.matching(e),
   250           "Non-zero reduced weight on matching edge");
   251   }
   252 
   253   int pv = 0;
   254   for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
   255     check(mwpm.matching(n) != INVALID, "Non perfect");
   256     pv += weight[mwpm.matching(n)];
   257     SmartGraph::Node o = graph.target(mwpm.matching(n));
   258     check(mwpm.mate(n) == o, "Invalid matching");
   259     check(mwpm.matching(n) == graph.oppositeArc(mwpm.matching(o)),
   260           "Invalid matching");
   261   }
   262 
   263   int dv = 0;
   264   for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
   265     dv += mwpm.nodeValue(n);
   266   }
   267 
   268   for (int i = 0; i < mwpm.blossomNum(); ++i) {
   269     check(mwpm.blossomValue(i) >= 0, "Invalid blossom value");
   270     check(mwpm.blossomSize(i) % 2 == 1, "Even blossom size");
   271     dv += mwpm.blossomValue(i) * ((mwpm.blossomSize(i) - 1) / 2);
   272   }
   273 
   274   check(pv * mwpm.dualScale == dv * 2, "Wrong duality");
   275 
   276   return;
   277 }
   278 
   279 
   280 int main() {
   281 
   282   for (int i = 0; i < lgfn; ++i) {
   283     SmartGraph graph;
   284     SmartGraph::EdgeMap<int> weight(graph);
   285 
   286     istringstream lgfs(lgf[i]);
   287     graphReader(graph, lgfs).
   288       edgeMap("weight", weight).run();
   289 
   290     MaxMatching<SmartGraph> mm(graph);
   291     mm.run();
   292     checkMatching(graph, mm);
   293 
   294     MaxWeightedMatching<SmartGraph> mwm(graph, weight);
   295     mwm.run();
   296     checkWeightedMatching(graph, weight, mwm);
   297 
   298     MaxWeightedPerfectMatching<SmartGraph> mwpm(graph, weight);
   299     bool perfect = mwpm.run();
   300 
   301     check(perfect == (mm.matchingSize() * 2 == countNodes(graph)),
   302           "Perfect matching found");
   303 
   304     if (perfect) {
   305       checkWeightedPerfectMatching(graph, weight, mwpm);
   306     }
   307   }
   308 
   309   return 0;
   310 }