test/fractional_matching_test.cc
author Balazs Dezso <deba@inf.elte.hu>
Fri, 25 Sep 2009 21:51:36 +0200
changeset 869 636dadefe1e6
child 872 41d7ac528c3a
permissions -rw-r--r--
Add fractional matching algorithms (#314)
     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 }