test/matching_test.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Tue, 15 Mar 2011 19:32:21 +0100
changeset 1047 ddd3c0d3d9bf
parent 949 61120524af27
child 1173 d216e1c8b3fa
permissions -rw-r--r--
Implement the scaling Price Refinement heuristic in CostScaling (#417)
instead of Early Termination.

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