test/matching_test.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Tue, 15 Mar 2011 19:32:21 +0100
changeset 936 ddd3c0d3d9bf
parent 870 61120524af27
child 1008 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@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@877
     5
 * Copyright (C) 2003-2010
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@594
    25
#include <lemon/matching.h>
deba@327
    26
#include <lemon/smart_graph.h>
kpeter@590
    27
#include <lemon/concepts/graph.h>
kpeter@590
    28
#include <lemon/concepts/maps.h>
deba@327
    29
#include <lemon/lgf_reader.h>
kpeter@590
    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@590
   115
void checkMaxMatchingCompile()
kpeter@590
   116
{
kpeter@590
   117
  typedef concepts::Graph Graph;
kpeter@590
   118
  typedef Graph::Node Node;
kpeter@590
   119
  typedef Graph::Edge Edge;
kpeter@590
   120
  typedef Graph::EdgeMap<bool> MatMap;
kpeter@590
   121
kpeter@590
   122
  Graph g;
kpeter@590
   123
  Node n;
kpeter@590
   124
  Edge e;
kpeter@590
   125
  MatMap mat(g);
kpeter@590
   126
kpeter@590
   127
  MaxMatching<Graph> mat_test(g);
kpeter@590
   128
  const MaxMatching<Graph>&
kpeter@590
   129
    const_mat_test = mat_test;
kpeter@590
   130
kpeter@590
   131
  mat_test.init();
kpeter@590
   132
  mat_test.greedyInit();
kpeter@590
   133
  mat_test.matchingInit(mat);
kpeter@590
   134
  mat_test.startSparse();
kpeter@590
   135
  mat_test.startDense();
kpeter@590
   136
  mat_test.run();
alpar@877
   137
kpeter@590
   138
  const_mat_test.matchingSize();
kpeter@590
   139
  const_mat_test.matching(e);
kpeter@590
   140
  const_mat_test.matching(n);
kpeter@593
   141
  const MaxMatching<Graph>::MatchingMap& mmap =
kpeter@593
   142
    const_mat_test.matchingMap();
kpeter@593
   143
  e = mmap[n];
kpeter@590
   144
  const_mat_test.mate(n);
kpeter@590
   145
alpar@877
   146
  MaxMatching<Graph>::Status stat =
kpeter@593
   147
    const_mat_test.status(n);
kpeter@593
   148
  const MaxMatching<Graph>::StatusMap& smap =
kpeter@593
   149
    const_mat_test.statusMap();
kpeter@593
   150
  stat = smap[n];
kpeter@590
   151
  const_mat_test.barrier(n);
kpeter@590
   152
}
kpeter@590
   153
kpeter@590
   154
void checkMaxWeightedMatchingCompile()
kpeter@590
   155
{
kpeter@590
   156
  typedef concepts::Graph Graph;
kpeter@590
   157
  typedef Graph::Node Node;
kpeter@590
   158
  typedef Graph::Edge Edge;
kpeter@590
   159
  typedef Graph::EdgeMap<int> WeightMap;
kpeter@590
   160
kpeter@590
   161
  Graph g;
kpeter@590
   162
  Node n;
kpeter@590
   163
  Edge e;
kpeter@590
   164
  WeightMap w(g);
kpeter@590
   165
kpeter@590
   166
  MaxWeightedMatching<Graph> mat_test(g, w);
kpeter@590
   167
  const MaxWeightedMatching<Graph>&
kpeter@590
   168
    const_mat_test = mat_test;
kpeter@590
   169
kpeter@590
   170
  mat_test.init();
kpeter@590
   171
  mat_test.start();
kpeter@590
   172
  mat_test.run();
alpar@877
   173
kpeter@593
   174
  const_mat_test.matchingWeight();
kpeter@590
   175
  const_mat_test.matchingSize();
kpeter@590
   176
  const_mat_test.matching(e);
kpeter@590
   177
  const_mat_test.matching(n);
kpeter@593
   178
  const MaxWeightedMatching<Graph>::MatchingMap& mmap =
kpeter@593
   179
    const_mat_test.matchingMap();
kpeter@593
   180
  e = mmap[n];
kpeter@590
   181
  const_mat_test.mate(n);
alpar@877
   182
kpeter@590
   183
  int k = 0;
kpeter@590
   184
  const_mat_test.dualValue();
kpeter@590
   185
  const_mat_test.nodeValue(n);
kpeter@590
   186
  const_mat_test.blossomNum();
kpeter@590
   187
  const_mat_test.blossomSize(k);
kpeter@590
   188
  const_mat_test.blossomValue(k);
kpeter@590
   189
}
kpeter@590
   190
kpeter@590
   191
void checkMaxWeightedPerfectMatchingCompile()
kpeter@590
   192
{
kpeter@590
   193
  typedef concepts::Graph Graph;
kpeter@590
   194
  typedef Graph::Node Node;
kpeter@590
   195
  typedef Graph::Edge Edge;
kpeter@590
   196
  typedef Graph::EdgeMap<int> WeightMap;
kpeter@590
   197
kpeter@590
   198
  Graph g;
kpeter@590
   199
  Node n;
kpeter@590
   200
  Edge e;
kpeter@590
   201
  WeightMap w(g);
kpeter@590
   202
kpeter@590
   203
  MaxWeightedPerfectMatching<Graph> mat_test(g, w);
kpeter@590
   204
  const MaxWeightedPerfectMatching<Graph>&
kpeter@590
   205
    const_mat_test = mat_test;
kpeter@590
   206
kpeter@590
   207
  mat_test.init();
kpeter@590
   208
  mat_test.start();
kpeter@590
   209
  mat_test.run();
alpar@877
   210
kpeter@593
   211
  const_mat_test.matchingWeight();
kpeter@590
   212
  const_mat_test.matching(e);
kpeter@590
   213
  const_mat_test.matching(n);
kpeter@593
   214
  const MaxWeightedPerfectMatching<Graph>::MatchingMap& mmap =
kpeter@593
   215
    const_mat_test.matchingMap();
kpeter@593
   216
  e = mmap[n];
kpeter@590
   217
  const_mat_test.mate(n);
alpar@877
   218
kpeter@590
   219
  int k = 0;
kpeter@590
   220
  const_mat_test.dualValue();
kpeter@590
   221
  const_mat_test.nodeValue(n);
kpeter@590
   222
  const_mat_test.blossomNum();
kpeter@590
   223
  const_mat_test.blossomSize(k);
kpeter@590
   224
  const_mat_test.blossomValue(k);
kpeter@590
   225
}
kpeter@590
   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@593
   237
    check(mm.status(n) == MaxMatching<SmartGraph>::EVEN ||
deba@327
   238
          mm.matching(n) != INVALID, "Wrong Gallai-Edmonds decomposition");
kpeter@593
   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@593
   252
    check(mm.status(graph.u(e)) != MaxMatching<SmartGraph>::EVEN ||
kpeter@593
   253
          mm.status(graph.v(e)) != MaxMatching<SmartGraph>::MATCHED,
deba@327
   254
          "Wrong Gallai-Edmonds decomposition");
deba@327
   255
kpeter@593
   256
    check(mm.status(graph.v(e)) != MaxMatching<SmartGraph>::EVEN ||
kpeter@593
   257
          mm.status(graph.u(e)) != MaxMatching<SmartGraph>::MATCHED,
deba@327
   258
          "Wrong Gallai-Edmonds decomposition");
deba@327
   259
kpeter@593
   260
    if (mm.status(graph.u(e)) != MaxMatching<SmartGraph>::ODD &&
kpeter@593
   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@593
   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@870
   404
    bool perfect;
deba@870
   405
    {
deba@870
   406
      MaxMatching<SmartGraph> mm(graph);
deba@870
   407
      mm.run();
deba@870
   408
      checkMatching(graph, mm);
deba@870
   409
      perfect = 2 * mm.matchingSize() == countNodes(graph);
deba@870
   410
    }
deba@326
   411
deba@870
   412
    {
deba@870
   413
      MaxWeightedMatching<SmartGraph> mwm(graph, weight);
deba@870
   414
      mwm.run();
deba@870
   415
      checkWeightedMatching(graph, weight, mwm);
deba@870
   416
    }
deba@326
   417
deba@870
   418
    {
deba@870
   419
      MaxWeightedMatching<SmartGraph> mwm(graph, weight);
deba@870
   420
      mwm.init();
deba@870
   421
      mwm.start();
deba@870
   422
      checkWeightedMatching(graph, weight, mwm);
deba@870
   423
    }
deba@326
   424
deba@870
   425
    {
deba@870
   426
      MaxWeightedPerfectMatching<SmartGraph> mwpm(graph, weight);
deba@870
   427
      bool result = mwpm.run();
alpar@877
   428
deba@870
   429
      check(result == perfect, "Perfect matching found");
deba@870
   430
      if (perfect) {
deba@870
   431
        checkWeightedPerfectMatching(graph, weight, mwpm);
deba@870
   432
      }
deba@870
   433
    }
deba@326
   434
deba@870
   435
    {
deba@870
   436
      MaxWeightedPerfectMatching<SmartGraph> mwpm(graph, weight);
deba@870
   437
      mwpm.init();
deba@870
   438
      bool result = mwpm.start();
alpar@877
   439
deba@870
   440
      check(result == perfect, "Perfect matching found");
deba@870
   441
      if (perfect) {
deba@870
   442
        checkWeightedPerfectMatching(graph, weight, mwpm);
deba@870
   443
      }
deba@326
   444
    }
deba@326
   445
  }
deba@326
   446
deba@326
   447
  return 0;
deba@326
   448
}