test/matching_test.cc
author Balazs Dezso <deba@inf.elte.hu>
Thu, 24 Jun 2010 09:27:53 +0200
changeset 732 bb70ad62c95f
parent 585 7ac52d6a268e
child 761 f1398882a928
child 796 7e368d9b67f7
permissions -rw-r--r--
Fix critical bug in preflow (#372)

The wrong transition between the bound decrease and highest active
heuristics caused the bug. The last node chosen in bound decrease mode
is used in the first iteration in highest active mode.
deba@326
     1
/* -*- mode: C++; indent-tabs-mode: nil; -*-
deba@326
     2
 *
deba@326
     3
 * This file is a part of LEMON, a generic C++ optimization library.
deba@326
     4
 *
alpar@440
     5
 * Copyright (C) 2003-2009
deba@326
     6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
deba@326
     7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
deba@326
     8
 *
deba@326
     9
 * Permission to use, modify and distribute this software is granted
deba@326
    10
 * provided that this copyright notice appears in all copies. For
deba@326
    11
 * precise terms see the accompanying LICENSE file.
deba@326
    12
 *
deba@326
    13
 * This software is provided "AS IS" with no warranty of any kind,
deba@326
    14
 * express or implied, and with no claim as to its suitability for any
deba@326
    15
 * purpose.
deba@326
    16
 *
deba@326
    17
 */
deba@326
    18
deba@326
    19
#include <iostream>
deba@327
    20
#include <sstream>
deba@326
    21
#include <vector>
deba@326
    22
#include <queue>
deba@326
    23
#include <cstdlib>
deba@326
    24
kpeter@586
    25
#include <lemon/matching.h>
deba@327
    26
#include <lemon/smart_graph.h>
kpeter@582
    27
#include <lemon/concepts/graph.h>
kpeter@582
    28
#include <lemon/concepts/maps.h>
deba@327
    29
#include <lemon/lgf_reader.h>
kpeter@582
    30
#include <lemon/math.h>
deba@327
    31
deba@326
    32
#include "test_tools.h"
deba@326
    33
deba@326
    34
using namespace std;
deba@326
    35
using namespace lemon;
deba@326
    36
deba@327
    37
GRAPH_TYPEDEFS(SmartGraph);
deba@327
    38
deba@327
    39
deba@327
    40
const int lgfn = 3;
deba@327
    41
const std::string lgf[lgfn] = {
deba@327
    42
  "@nodes\n"
deba@327
    43
  "label\n"
deba@327
    44
  "0\n"
deba@327
    45
  "1\n"
deba@327
    46
  "2\n"
deba@327
    47
  "3\n"
deba@327
    48
  "4\n"
deba@327
    49
  "5\n"
deba@327
    50
  "6\n"
deba@327
    51
  "7\n"
deba@327
    52
  "@edges\n"
deba@327
    53
  "     label  weight\n"
deba@327
    54
  "7 4  0      984\n"
deba@327
    55
  "0 7  1      73\n"
deba@327
    56
  "7 1  2      204\n"
deba@327
    57
  "2 3  3      583\n"
deba@327
    58
  "2 7  4      565\n"
deba@327
    59
  "2 1  5      582\n"
deba@327
    60
  "0 4  6      551\n"
deba@327
    61
  "2 5  7      385\n"
deba@327
    62
  "1 5  8      561\n"
deba@327
    63
  "5 3  9      484\n"
deba@327
    64
  "7 5  10     904\n"
deba@327
    65
  "3 6  11     47\n"
deba@327
    66
  "7 6  12     888\n"
deba@327
    67
  "3 0  13     747\n"
deba@327
    68
  "6 1  14     310\n",
deba@327
    69
deba@327
    70
  "@nodes\n"
deba@327
    71
  "label\n"
deba@327
    72
  "0\n"
deba@327
    73
  "1\n"
deba@327
    74
  "2\n"
deba@327
    75
  "3\n"
deba@327
    76
  "4\n"
deba@327
    77
  "5\n"
deba@327
    78
  "6\n"
deba@327
    79
  "7\n"
deba@327
    80
  "@edges\n"
deba@327
    81
  "     label  weight\n"
deba@327
    82
  "2 5  0      710\n"
deba@327
    83
  "0 5  1      241\n"
deba@327
    84
  "2 4  2      856\n"
deba@327
    85
  "2 6  3      762\n"
deba@327
    86
  "4 1  4      747\n"
deba@327
    87
  "6 1  5      962\n"
deba@327
    88
  "4 7  6      723\n"
deba@327
    89
  "1 7  7      661\n"
deba@327
    90
  "2 3  8      376\n"
deba@327
    91
  "1 0  9      416\n"
deba@327
    92
  "6 7  10     391\n",
deba@327
    93
deba@327
    94
  "@nodes\n"
deba@327
    95
  "label\n"
deba@327
    96
  "0\n"
deba@327
    97
  "1\n"
deba@327
    98
  "2\n"
deba@327
    99
  "3\n"
deba@327
   100
  "4\n"
deba@327
   101
  "5\n"
deba@327
   102
  "6\n"
deba@327
   103
  "7\n"
deba@327
   104
  "@edges\n"
deba@327
   105
  "     label  weight\n"
deba@327
   106
  "6 2  0      553\n"
deba@327
   107
  "0 7  1      653\n"
deba@327
   108
  "6 3  2      22\n"
deba@327
   109
  "4 7  3      846\n"
deba@327
   110
  "7 2  4      981\n"
deba@327
   111
  "7 6  5      250\n"
deba@327
   112
  "5 2  6      539\n",
deba@327
   113
};
deba@327
   114
kpeter@582
   115
void checkMaxMatchingCompile()
kpeter@582
   116
{
kpeter@582
   117
  typedef concepts::Graph Graph;
kpeter@582
   118
  typedef Graph::Node Node;
kpeter@582
   119
  typedef Graph::Edge Edge;
kpeter@582
   120
  typedef Graph::EdgeMap<bool> MatMap;
kpeter@582
   121
kpeter@582
   122
  Graph g;
kpeter@582
   123
  Node n;
kpeter@582
   124
  Edge e;
kpeter@582
   125
  MatMap mat(g);
kpeter@582
   126
kpeter@582
   127
  MaxMatching<Graph> mat_test(g);
kpeter@582
   128
  const MaxMatching<Graph>&
kpeter@582
   129
    const_mat_test = mat_test;
kpeter@582
   130
kpeter@582
   131
  mat_test.init();
kpeter@582
   132
  mat_test.greedyInit();
kpeter@582
   133
  mat_test.matchingInit(mat);
kpeter@582
   134
  mat_test.startSparse();
kpeter@582
   135
  mat_test.startDense();
kpeter@582
   136
  mat_test.run();
kpeter@582
   137
  
kpeter@582
   138
  const_mat_test.matchingSize();
kpeter@582
   139
  const_mat_test.matching(e);
kpeter@582
   140
  const_mat_test.matching(n);
kpeter@585
   141
  const MaxMatching<Graph>::MatchingMap& mmap =
kpeter@585
   142
    const_mat_test.matchingMap();
kpeter@585
   143
  e = mmap[n];
kpeter@582
   144
  const_mat_test.mate(n);
kpeter@582
   145
kpeter@582
   146
  MaxMatching<Graph>::Status stat = 
kpeter@585
   147
    const_mat_test.status(n);
kpeter@585
   148
  const MaxMatching<Graph>::StatusMap& smap =
kpeter@585
   149
    const_mat_test.statusMap();
kpeter@585
   150
  stat = smap[n];
kpeter@582
   151
  const_mat_test.barrier(n);
kpeter@582
   152
}
kpeter@582
   153
kpeter@582
   154
void checkMaxWeightedMatchingCompile()
kpeter@582
   155
{
kpeter@582
   156
  typedef concepts::Graph Graph;
kpeter@582
   157
  typedef Graph::Node Node;
kpeter@582
   158
  typedef Graph::Edge Edge;
kpeter@582
   159
  typedef Graph::EdgeMap<int> WeightMap;
kpeter@582
   160
kpeter@582
   161
  Graph g;
kpeter@582
   162
  Node n;
kpeter@582
   163
  Edge e;
kpeter@582
   164
  WeightMap w(g);
kpeter@582
   165
kpeter@582
   166
  MaxWeightedMatching<Graph> mat_test(g, w);
kpeter@582
   167
  const MaxWeightedMatching<Graph>&
kpeter@582
   168
    const_mat_test = mat_test;
kpeter@582
   169
kpeter@582
   170
  mat_test.init();
kpeter@582
   171
  mat_test.start();
kpeter@582
   172
  mat_test.run();
kpeter@582
   173
  
kpeter@585
   174
  const_mat_test.matchingWeight();
kpeter@582
   175
  const_mat_test.matchingSize();
kpeter@582
   176
  const_mat_test.matching(e);
kpeter@582
   177
  const_mat_test.matching(n);
kpeter@585
   178
  const MaxWeightedMatching<Graph>::MatchingMap& mmap =
kpeter@585
   179
    const_mat_test.matchingMap();
kpeter@585
   180
  e = mmap[n];
kpeter@582
   181
  const_mat_test.mate(n);
kpeter@582
   182
  
kpeter@582
   183
  int k = 0;
kpeter@582
   184
  const_mat_test.dualValue();
kpeter@582
   185
  const_mat_test.nodeValue(n);
kpeter@582
   186
  const_mat_test.blossomNum();
kpeter@582
   187
  const_mat_test.blossomSize(k);
kpeter@582
   188
  const_mat_test.blossomValue(k);
kpeter@582
   189
}
kpeter@582
   190
kpeter@582
   191
void checkMaxWeightedPerfectMatchingCompile()
kpeter@582
   192
{
kpeter@582
   193
  typedef concepts::Graph Graph;
kpeter@582
   194
  typedef Graph::Node Node;
kpeter@582
   195
  typedef Graph::Edge Edge;
kpeter@582
   196
  typedef Graph::EdgeMap<int> WeightMap;
kpeter@582
   197
kpeter@582
   198
  Graph g;
kpeter@582
   199
  Node n;
kpeter@582
   200
  Edge e;
kpeter@582
   201
  WeightMap w(g);
kpeter@582
   202
kpeter@582
   203
  MaxWeightedPerfectMatching<Graph> mat_test(g, w);
kpeter@582
   204
  const MaxWeightedPerfectMatching<Graph>&
kpeter@582
   205
    const_mat_test = mat_test;
kpeter@582
   206
kpeter@582
   207
  mat_test.init();
kpeter@582
   208
  mat_test.start();
kpeter@582
   209
  mat_test.run();
kpeter@582
   210
  
kpeter@585
   211
  const_mat_test.matchingWeight();
kpeter@582
   212
  const_mat_test.matching(e);
kpeter@582
   213
  const_mat_test.matching(n);
kpeter@585
   214
  const MaxWeightedPerfectMatching<Graph>::MatchingMap& mmap =
kpeter@585
   215
    const_mat_test.matchingMap();
kpeter@585
   216
  e = mmap[n];
kpeter@582
   217
  const_mat_test.mate(n);
kpeter@582
   218
  
kpeter@582
   219
  int k = 0;
kpeter@582
   220
  const_mat_test.dualValue();
kpeter@582
   221
  const_mat_test.nodeValue(n);
kpeter@582
   222
  const_mat_test.blossomNum();
kpeter@582
   223
  const_mat_test.blossomSize(k);
kpeter@582
   224
  const_mat_test.blossomValue(k);
kpeter@582
   225
}
kpeter@582
   226
deba@327
   227
void checkMatching(const SmartGraph& graph,
deba@327
   228
                   const MaxMatching<SmartGraph>& mm) {
deba@327
   229
  int num = 0;
deba@327
   230
deba@327
   231
  IntNodeMap comp_index(graph);
deba@327
   232
  UnionFind<IntNodeMap> comp(comp_index);
deba@327
   233
deba@327
   234
  int barrier_num = 0;
deba@327
   235
deba@327
   236
  for (NodeIt n(graph); n != INVALID; ++n) {
kpeter@585
   237
    check(mm.status(n) == MaxMatching<SmartGraph>::EVEN ||
deba@327
   238
          mm.matching(n) != INVALID, "Wrong Gallai-Edmonds decomposition");
kpeter@585
   239
    if (mm.status(n) == MaxMatching<SmartGraph>::ODD) {
deba@327
   240
      ++barrier_num;
deba@327
   241
    } else {
deba@327
   242
      comp.insert(n);
deba@327
   243
    }
deba@327
   244
  }
deba@327
   245
deba@327
   246
  for (EdgeIt e(graph); e != INVALID; ++e) {
deba@327
   247
    if (mm.matching(e)) {
deba@327
   248
      check(e == mm.matching(graph.u(e)), "Wrong matching");
deba@327
   249
      check(e == mm.matching(graph.v(e)), "Wrong matching");
deba@327
   250
      ++num;
deba@327
   251
    }
kpeter@585
   252
    check(mm.status(graph.u(e)) != MaxMatching<SmartGraph>::EVEN ||
kpeter@585
   253
          mm.status(graph.v(e)) != MaxMatching<SmartGraph>::MATCHED,
deba@327
   254
          "Wrong Gallai-Edmonds decomposition");
deba@327
   255
kpeter@585
   256
    check(mm.status(graph.v(e)) != MaxMatching<SmartGraph>::EVEN ||
kpeter@585
   257
          mm.status(graph.u(e)) != MaxMatching<SmartGraph>::MATCHED,
deba@327
   258
          "Wrong Gallai-Edmonds decomposition");
deba@327
   259
kpeter@585
   260
    if (mm.status(graph.u(e)) != MaxMatching<SmartGraph>::ODD &&
kpeter@585
   261
        mm.status(graph.v(e)) != MaxMatching<SmartGraph>::ODD) {
deba@327
   262
      comp.join(graph.u(e), graph.v(e));
deba@327
   263
    }
deba@327
   264
  }
deba@327
   265
deba@327
   266
  std::set<int> comp_root;
deba@327
   267
  int odd_comp_num = 0;
deba@327
   268
  for (NodeIt n(graph); n != INVALID; ++n) {
kpeter@585
   269
    if (mm.status(n) != MaxMatching<SmartGraph>::ODD) {
deba@327
   270
      int root = comp.find(n);
deba@327
   271
      if (comp_root.find(root) == comp_root.end()) {
deba@327
   272
        comp_root.insert(root);
deba@327
   273
        if (comp.size(n) % 2 == 1) {
deba@327
   274
          ++odd_comp_num;
deba@327
   275
        }
deba@327
   276
      }
deba@327
   277
    }
deba@327
   278
  }
deba@327
   279
deba@327
   280
  check(mm.matchingSize() == num, "Wrong matching");
deba@327
   281
  check(2 * num == countNodes(graph) - (odd_comp_num - barrier_num),
deba@327
   282
         "Wrong matching");
deba@327
   283
  return;
deba@327
   284
}
deba@327
   285
deba@327
   286
void checkWeightedMatching(const SmartGraph& graph,
deba@327
   287
                   const SmartGraph::EdgeMap<int>& weight,
deba@327
   288
                   const MaxWeightedMatching<SmartGraph>& mwm) {
deba@327
   289
  for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) {
deba@327
   290
    if (graph.u(e) == graph.v(e)) continue;
deba@327
   291
    int rw = mwm.nodeValue(graph.u(e)) + mwm.nodeValue(graph.v(e));
deba@327
   292
deba@327
   293
    for (int i = 0; i < mwm.blossomNum(); ++i) {
deba@327
   294
      bool s = false, t = false;
deba@327
   295
      for (MaxWeightedMatching<SmartGraph>::BlossomIt n(mwm, i);
deba@327
   296
           n != INVALID; ++n) {
deba@327
   297
        if (graph.u(e) == n) s = true;
deba@327
   298
        if (graph.v(e) == n) t = true;
deba@327
   299
      }
deba@327
   300
      if (s == true && t == true) {
deba@327
   301
        rw += mwm.blossomValue(i);
deba@327
   302
      }
deba@327
   303
    }
deba@327
   304
    rw -= weight[e] * mwm.dualScale;
deba@327
   305
deba@327
   306
    check(rw >= 0, "Negative reduced weight");
deba@327
   307
    check(rw == 0 || !mwm.matching(e),
deba@327
   308
          "Non-zero reduced weight on matching edge");
deba@327
   309
  }
deba@327
   310
deba@327
   311
  int pv = 0;
deba@327
   312
  for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
deba@327
   313
    if (mwm.matching(n) != INVALID) {
deba@327
   314
      check(mwm.nodeValue(n) >= 0, "Invalid node value");
deba@327
   315
      pv += weight[mwm.matching(n)];
deba@327
   316
      SmartGraph::Node o = graph.target(mwm.matching(n));
deba@327
   317
      check(mwm.mate(n) == o, "Invalid matching");
deba@327
   318
      check(mwm.matching(n) == graph.oppositeArc(mwm.matching(o)),
deba@327
   319
            "Invalid matching");
deba@327
   320
    } else {
deba@327
   321
      check(mwm.mate(n) == INVALID, "Invalid matching");
deba@327
   322
      check(mwm.nodeValue(n) == 0, "Invalid matching");
deba@327
   323
    }
deba@327
   324
  }
deba@327
   325
deba@327
   326
  int dv = 0;
deba@327
   327
  for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
deba@327
   328
    dv += mwm.nodeValue(n);
deba@327
   329
  }
deba@327
   330
deba@327
   331
  for (int i = 0; i < mwm.blossomNum(); ++i) {
deba@327
   332
    check(mwm.blossomValue(i) >= 0, "Invalid blossom value");
deba@327
   333
    check(mwm.blossomSize(i) % 2 == 1, "Even blossom size");
deba@327
   334
    dv += mwm.blossomValue(i) * ((mwm.blossomSize(i) - 1) / 2);
deba@327
   335
  }
deba@327
   336
deba@327
   337
  check(pv * mwm.dualScale == dv * 2, "Wrong duality");
deba@327
   338
deba@327
   339
  return;
deba@327
   340
}
deba@327
   341
deba@327
   342
void checkWeightedPerfectMatching(const SmartGraph& graph,
deba@327
   343
                          const SmartGraph::EdgeMap<int>& weight,
deba@327
   344
                          const MaxWeightedPerfectMatching<SmartGraph>& mwpm) {
deba@327
   345
  for (SmartGraph::EdgeIt e(graph); e != INVALID; ++e) {
deba@327
   346
    if (graph.u(e) == graph.v(e)) continue;
deba@327
   347
    int rw = mwpm.nodeValue(graph.u(e)) + mwpm.nodeValue(graph.v(e));
deba@327
   348
deba@327
   349
    for (int i = 0; i < mwpm.blossomNum(); ++i) {
deba@327
   350
      bool s = false, t = false;
deba@327
   351
      for (MaxWeightedPerfectMatching<SmartGraph>::BlossomIt n(mwpm, i);
deba@327
   352
           n != INVALID; ++n) {
deba@327
   353
        if (graph.u(e) == n) s = true;
deba@327
   354
        if (graph.v(e) == n) t = true;
deba@327
   355
      }
deba@327
   356
      if (s == true && t == true) {
deba@327
   357
        rw += mwpm.blossomValue(i);
deba@327
   358
      }
deba@327
   359
    }
deba@327
   360
    rw -= weight[e] * mwpm.dualScale;
deba@327
   361
deba@327
   362
    check(rw >= 0, "Negative reduced weight");
deba@327
   363
    check(rw == 0 || !mwpm.matching(e),
deba@327
   364
          "Non-zero reduced weight on matching edge");
deba@327
   365
  }
deba@327
   366
deba@327
   367
  int pv = 0;
deba@327
   368
  for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
deba@327
   369
    check(mwpm.matching(n) != INVALID, "Non perfect");
deba@327
   370
    pv += weight[mwpm.matching(n)];
deba@327
   371
    SmartGraph::Node o = graph.target(mwpm.matching(n));
deba@327
   372
    check(mwpm.mate(n) == o, "Invalid matching");
deba@327
   373
    check(mwpm.matching(n) == graph.oppositeArc(mwpm.matching(o)),
deba@327
   374
          "Invalid matching");
deba@327
   375
  }
deba@327
   376
deba@327
   377
  int dv = 0;
deba@327
   378
  for (SmartGraph::NodeIt n(graph); n != INVALID; ++n) {
deba@327
   379
    dv += mwpm.nodeValue(n);
deba@327
   380
  }
deba@327
   381
deba@327
   382
  for (int i = 0; i < mwpm.blossomNum(); ++i) {
deba@327
   383
    check(mwpm.blossomValue(i) >= 0, "Invalid blossom value");
deba@327
   384
    check(mwpm.blossomSize(i) % 2 == 1, "Even blossom size");
deba@327
   385
    dv += mwpm.blossomValue(i) * ((mwpm.blossomSize(i) - 1) / 2);
deba@327
   386
  }
deba@327
   387
deba@327
   388
  check(pv * mwpm.dualScale == dv * 2, "Wrong duality");
deba@327
   389
deba@327
   390
  return;
deba@327
   391
}
deba@327
   392
deba@327
   393
deba@326
   394
int main() {
deba@326
   395
deba@327
   396
  for (int i = 0; i < lgfn; ++i) {
deba@327
   397
    SmartGraph graph;
deba@327
   398
    SmartGraph::EdgeMap<int> weight(graph);
deba@326
   399
deba@327
   400
    istringstream lgfs(lgf[i]);
deba@327
   401
    graphReader(graph, lgfs).
deba@327
   402
      edgeMap("weight", weight).run();
deba@326
   403
deba@327
   404
    MaxMatching<SmartGraph> mm(graph);
deba@327
   405
    mm.run();
deba@327
   406
    checkMatching(graph, mm);
deba@326
   407
deba@327
   408
    MaxWeightedMatching<SmartGraph> mwm(graph, weight);
deba@327
   409
    mwm.run();
deba@327
   410
    checkWeightedMatching(graph, weight, mwm);
deba@326
   411
deba@327
   412
    MaxWeightedPerfectMatching<SmartGraph> mwpm(graph, weight);
deba@327
   413
    bool perfect = mwpm.run();
deba@326
   414
deba@327
   415
    check(perfect == (mm.matchingSize() * 2 == countNodes(graph)),
deba@327
   416
          "Perfect matching found");
deba@326
   417
deba@327
   418
    if (perfect) {
deba@327
   419
      checkWeightedPerfectMatching(graph, weight, mwpm);
deba@326
   420
    }
deba@326
   421
  }
deba@326
   422
deba@326
   423
  return 0;
deba@326
   424
}