test/min_cost_flow_test.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Fri, 03 Apr 2009 18:59:15 +0200
changeset 655 6ac5d9ae1d3d
parent 653 c7d160f73d52
child 656 e6927fe719e6
permissions -rw-r--r--
Support real types + numerical stability fix in NS (#254)

- Real types are supported by appropriate inicialization.
- A feature of the XTI spanning tree structure is removed to ensure
numerical stability (could cause problems using integer types).
The node potentials are updated always on the lower subtree,
in order to prevent overflow problems.
The former method isn't notably faster during to our tests.
kpeter@648
     1
/* -*- mode: C++; indent-tabs-mode: nil; -*-
kpeter@648
     2
 *
kpeter@648
     3
 * This file is a part of LEMON, a generic C++ optimization library.
kpeter@648
     4
 *
kpeter@648
     5
 * Copyright (C) 2003-2009
kpeter@648
     6
 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
kpeter@648
     7
 * (Egervary Research Group on Combinatorial Optimization, EGRES).
kpeter@648
     8
 *
kpeter@648
     9
 * Permission to use, modify and distribute this software is granted
kpeter@648
    10
 * provided that this copyright notice appears in all copies. For
kpeter@648
    11
 * precise terms see the accompanying LICENSE file.
kpeter@648
    12
 *
kpeter@648
    13
 * This software is provided "AS IS" with no warranty of any kind,
kpeter@648
    14
 * express or implied, and with no claim as to its suitability for any
kpeter@648
    15
 * purpose.
kpeter@648
    16
 *
kpeter@648
    17
 */
kpeter@648
    18
kpeter@648
    19
#include <iostream>
kpeter@648
    20
#include <fstream>
kpeter@648
    21
kpeter@648
    22
#include <lemon/list_graph.h>
kpeter@648
    23
#include <lemon/lgf_reader.h>
kpeter@648
    24
kpeter@648
    25
#include <lemon/network_simplex.h>
kpeter@648
    26
kpeter@648
    27
#include <lemon/concepts/digraph.h>
kpeter@648
    28
#include <lemon/concept_check.h>
kpeter@648
    29
kpeter@648
    30
#include "test_tools.h"
kpeter@648
    31
kpeter@648
    32
using namespace lemon;
kpeter@648
    33
kpeter@648
    34
char test_lgf[] =
kpeter@648
    35
  "@nodes\n"
kpeter@648
    36
  "label  sup1 sup2 sup3\n"
kpeter@648
    37
  "    1    20   27    0\n"
kpeter@648
    38
  "    2    -4    0    0\n"
kpeter@648
    39
  "    3     0    0    0\n"
kpeter@648
    40
  "    4     0    0    0\n"
kpeter@648
    41
  "    5     9    0    0\n"
kpeter@648
    42
  "    6    -6    0    0\n"
kpeter@648
    43
  "    7     0    0    0\n"
kpeter@648
    44
  "    8     0    0    0\n"
kpeter@648
    45
  "    9     3    0    0\n"
kpeter@648
    46
  "   10    -2    0    0\n"
kpeter@648
    47
  "   11     0    0    0\n"
kpeter@648
    48
  "   12   -20  -27    0\n"
kpeter@648
    49
  "\n"
kpeter@648
    50
  "@arcs\n"
kpeter@648
    51
  "       cost  cap low1 low2\n"
kpeter@648
    52
  " 1  2    70   11    0    8\n"
kpeter@648
    53
  " 1  3   150    3    0    1\n"
kpeter@648
    54
  " 1  4    80   15    0    2\n"
kpeter@648
    55
  " 2  8    80   12    0    0\n"
kpeter@648
    56
  " 3  5   140    5    0    3\n"
kpeter@648
    57
  " 4  6    60   10    0    1\n"
kpeter@648
    58
  " 4  7    80    2    0    0\n"
kpeter@648
    59
  " 4  8   110    3    0    0\n"
kpeter@648
    60
  " 5  7    60   14    0    0\n"
kpeter@648
    61
  " 5 11   120   12    0    0\n"
kpeter@648
    62
  " 6  3     0    3    0    0\n"
kpeter@648
    63
  " 6  9   140    4    0    0\n"
kpeter@648
    64
  " 6 10    90    8    0    0\n"
kpeter@648
    65
  " 7  1    30    5    0    0\n"
kpeter@648
    66
  " 8 12    60   16    0    4\n"
kpeter@648
    67
  " 9 12    50    6    0    0\n"
kpeter@648
    68
  "10 12    70   13    0    5\n"
kpeter@648
    69
  "10  2   100    7    0    0\n"
kpeter@648
    70
  "10  7    60   10    0    0\n"
kpeter@648
    71
  "11 10    20   14    0    6\n"
kpeter@648
    72
  "12 11    30   10    0    0\n"
kpeter@648
    73
  "\n"
kpeter@648
    74
  "@attributes\n"
kpeter@648
    75
  "source 1\n"
kpeter@648
    76
  "target 12\n";
kpeter@648
    77
kpeter@648
    78
kpeter@648
    79
// Check the interface of an MCF algorithm
kpeter@654
    80
template <typename GR, typename Flow, typename Cost>
kpeter@648
    81
class McfClassConcept
kpeter@648
    82
{
kpeter@648
    83
public:
kpeter@648
    84
kpeter@648
    85
  template <typename MCF>
kpeter@648
    86
  struct Constraints {
kpeter@648
    87
    void constraints() {
kpeter@648
    88
      checkConcept<concepts::Digraph, GR>();
kpeter@648
    89
kpeter@652
    90
      MCF mcf(g);
kpeter@648
    91
kpeter@653
    92
      b = mcf.reset()
kpeter@653
    93
             .lowerMap(lower)
kpeter@652
    94
             .upperMap(upper)
kpeter@652
    95
             .capacityMap(upper)
kpeter@652
    96
             .boundMaps(lower, upper)
kpeter@652
    97
             .costMap(cost)
kpeter@652
    98
             .supplyMap(sup)
kpeter@652
    99
             .stSupply(n, n, k)
kpeter@652
   100
             .run();
kpeter@648
   101
kpeter@652
   102
      const typename MCF::FlowMap &fm = mcf.flowMap();
kpeter@652
   103
      const typename MCF::PotentialMap &pm = mcf.potentialMap();
kpeter@652
   104
kpeter@652
   105
      v = mcf.totalCost();
kpeter@652
   106
      double x = mcf.template totalCost<double>();
kpeter@652
   107
      v = mcf.flow(a);
kpeter@652
   108
      v = mcf.potential(n);
kpeter@652
   109
      mcf.flowMap(flow);
kpeter@652
   110
      mcf.potentialMap(pot);
kpeter@652
   111
kpeter@648
   112
      ignore_unused_variable_warning(fm);
kpeter@648
   113
      ignore_unused_variable_warning(pm);
kpeter@652
   114
      ignore_unused_variable_warning(x);
kpeter@648
   115
    }
kpeter@648
   116
kpeter@648
   117
    typedef typename GR::Node Node;
kpeter@648
   118
    typedef typename GR::Arc Arc;
kpeter@654
   119
    typedef concepts::ReadMap<Node, Flow> NM;
kpeter@654
   120
    typedef concepts::ReadMap<Arc, Flow> FAM;
kpeter@654
   121
    typedef concepts::ReadMap<Arc, Cost> CAM;
kpeter@648
   122
kpeter@648
   123
    const GR &g;
kpeter@654
   124
    const FAM &lower;
kpeter@654
   125
    const FAM &upper;
kpeter@654
   126
    const CAM &cost;
kpeter@648
   127
    const NM &sup;
kpeter@648
   128
    const Node &n;
kpeter@648
   129
    const Arc &a;
kpeter@654
   130
    const Flow &k;
kpeter@654
   131
    Flow v;
kpeter@652
   132
    bool b;
kpeter@648
   133
kpeter@648
   134
    typename MCF::FlowMap &flow;
kpeter@648
   135
    typename MCF::PotentialMap &pot;
kpeter@648
   136
  };
kpeter@648
   137
kpeter@648
   138
};
kpeter@648
   139
kpeter@648
   140
kpeter@648
   141
// Check the feasibility of the given flow (primal soluiton)
kpeter@648
   142
template < typename GR, typename LM, typename UM,
kpeter@648
   143
           typename SM, typename FM >
kpeter@648
   144
bool checkFlow( const GR& gr, const LM& lower, const UM& upper,
kpeter@648
   145
                const SM& supply, const FM& flow )
kpeter@648
   146
{
kpeter@648
   147
  TEMPLATE_DIGRAPH_TYPEDEFS(GR);
kpeter@648
   148
kpeter@648
   149
  for (ArcIt e(gr); e != INVALID; ++e) {
kpeter@648
   150
    if (flow[e] < lower[e] || flow[e] > upper[e]) return false;
kpeter@648
   151
  }
kpeter@648
   152
kpeter@648
   153
  for (NodeIt n(gr); n != INVALID; ++n) {
kpeter@648
   154
    typename SM::Value sum = 0;
kpeter@648
   155
    for (OutArcIt e(gr, n); e != INVALID; ++e)
kpeter@648
   156
      sum += flow[e];
kpeter@648
   157
    for (InArcIt e(gr, n); e != INVALID; ++e)
kpeter@648
   158
      sum -= flow[e];
kpeter@648
   159
    if (sum != supply[n]) return false;
kpeter@648
   160
  }
kpeter@648
   161
kpeter@648
   162
  return true;
kpeter@648
   163
}
kpeter@648
   164
kpeter@648
   165
// Check the feasibility of the given potentials (dual soluiton)
kpeter@652
   166
// using the "Complementary Slackness" optimality condition
kpeter@648
   167
template < typename GR, typename LM, typename UM,
kpeter@648
   168
           typename CM, typename FM, typename PM >
kpeter@648
   169
bool checkPotential( const GR& gr, const LM& lower, const UM& upper,
kpeter@648
   170
                     const CM& cost, const FM& flow, const PM& pi )
kpeter@648
   171
{
kpeter@648
   172
  TEMPLATE_DIGRAPH_TYPEDEFS(GR);
kpeter@648
   173
kpeter@648
   174
  bool opt = true;
kpeter@648
   175
  for (ArcIt e(gr); opt && e != INVALID; ++e) {
kpeter@648
   176
    typename CM::Value red_cost =
kpeter@648
   177
      cost[e] + pi[gr.source(e)] - pi[gr.target(e)];
kpeter@648
   178
    opt = red_cost == 0 ||
kpeter@648
   179
          (red_cost > 0 && flow[e] == lower[e]) ||
kpeter@648
   180
          (red_cost < 0 && flow[e] == upper[e]);
kpeter@648
   181
  }
kpeter@648
   182
  return opt;
kpeter@648
   183
}
kpeter@648
   184
kpeter@648
   185
// Run a minimum cost flow algorithm and check the results
kpeter@648
   186
template < typename MCF, typename GR,
kpeter@648
   187
           typename LM, typename UM,
kpeter@648
   188
           typename CM, typename SM >
kpeter@648
   189
void checkMcf( const MCF& mcf, bool mcf_result,
kpeter@648
   190
               const GR& gr, const LM& lower, const UM& upper,
kpeter@648
   191
               const CM& cost, const SM& supply,
kpeter@648
   192
               bool result, typename CM::Value total,
kpeter@648
   193
               const std::string &test_id = "" )
kpeter@648
   194
{
kpeter@648
   195
  check(mcf_result == result, "Wrong result " + test_id);
kpeter@648
   196
  if (result) {
kpeter@648
   197
    check(checkFlow(gr, lower, upper, supply, mcf.flowMap()),
kpeter@648
   198
          "The flow is not feasible " + test_id);
kpeter@648
   199
    check(mcf.totalCost() == total, "The flow is not optimal " + test_id);
kpeter@648
   200
    check(checkPotential(gr, lower, upper, cost, mcf.flowMap(),
kpeter@648
   201
                         mcf.potentialMap()),
kpeter@648
   202
          "Wrong potentials " + test_id);
kpeter@648
   203
  }
kpeter@648
   204
}
kpeter@648
   205
kpeter@648
   206
int main()
kpeter@648
   207
{
kpeter@648
   208
  // Check the interfaces
kpeter@648
   209
  {
kpeter@654
   210
    typedef int Flow;
kpeter@654
   211
    typedef int Cost;
kpeter@652
   212
    // TODO: This typedef should be enabled if the standard maps are
kpeter@652
   213
    // reference maps in the graph concepts (See #190).
kpeter@652
   214
/**/
kpeter@648
   215
    //typedef concepts::Digraph GR;
kpeter@648
   216
    typedef ListDigraph GR;
kpeter@652
   217
/**/
kpeter@654
   218
    checkConcept< McfClassConcept<GR, Flow, Cost>,
kpeter@654
   219
                  NetworkSimplex<GR, Flow, Cost> >();
kpeter@648
   220
  }
kpeter@648
   221
kpeter@648
   222
  // Run various MCF tests
kpeter@648
   223
  typedef ListDigraph Digraph;
kpeter@648
   224
  DIGRAPH_TYPEDEFS(ListDigraph);
kpeter@648
   225
kpeter@648
   226
  // Read the test digraph
kpeter@648
   227
  Digraph gr;
kpeter@648
   228
  Digraph::ArcMap<int> c(gr), l1(gr), l2(gr), u(gr);
kpeter@648
   229
  Digraph::NodeMap<int> s1(gr), s2(gr), s3(gr);
kpeter@652
   230
  ConstMap<Arc, int> cc(1), cu(std::numeric_limits<int>::max());
kpeter@648
   231
  Node v, w;
kpeter@648
   232
kpeter@648
   233
  std::istringstream input(test_lgf);
kpeter@648
   234
  DigraphReader<Digraph>(gr, input)
kpeter@648
   235
    .arcMap("cost", c)
kpeter@648
   236
    .arcMap("cap", u)
kpeter@648
   237
    .arcMap("low1", l1)
kpeter@648
   238
    .arcMap("low2", l2)
kpeter@648
   239
    .nodeMap("sup1", s1)
kpeter@648
   240
    .nodeMap("sup2", s2)
kpeter@648
   241
    .nodeMap("sup3", s3)
kpeter@648
   242
    .node("source", v)
kpeter@648
   243
    .node("target", w)
kpeter@648
   244
    .run();
kpeter@648
   245
kpeter@652
   246
  // A. Test NetworkSimplex with the default pivot rule
kpeter@648
   247
  {
kpeter@653
   248
    NetworkSimplex<Digraph> mcf(gr);
kpeter@648
   249
kpeter@653
   250
    mcf.upperMap(u).costMap(c);
kpeter@653
   251
    checkMcf(mcf, mcf.supplyMap(s1).run(),
kpeter@652
   252
             gr, l1, u, c, s1, true,  5240, "#A1");
kpeter@653
   253
    checkMcf(mcf, mcf.stSupply(v, w, 27).run(),
kpeter@652
   254
             gr, l1, u, c, s2, true,  7620, "#A2");
kpeter@653
   255
    mcf.lowerMap(l2);
kpeter@653
   256
    checkMcf(mcf, mcf.supplyMap(s1).run(),
kpeter@652
   257
             gr, l2, u, c, s1, true,  5970, "#A3");
kpeter@653
   258
    checkMcf(mcf, mcf.stSupply(v, w, 27).run(),
kpeter@652
   259
             gr, l2, u, c, s2, true,  8010, "#A4");
kpeter@653
   260
    mcf.reset();
kpeter@653
   261
    checkMcf(mcf, mcf.supplyMap(s1).run(),
kpeter@652
   262
             gr, l1, cu, cc, s1, true,  74, "#A5");
kpeter@653
   263
    checkMcf(mcf, mcf.lowerMap(l2).stSupply(v, w, 27).run(),
kpeter@652
   264
             gr, l2, cu, cc, s2, true,  94, "#A6");
kpeter@653
   265
    mcf.reset();
kpeter@653
   266
    checkMcf(mcf, mcf.run(),
kpeter@652
   267
             gr, l1, cu, cc, s3, true,   0, "#A7");
kpeter@653
   268
    checkMcf(mcf, mcf.boundMaps(l2, u).run(),
kpeter@652
   269
             gr, l2, u, cc, s3, false,   0, "#A8");
kpeter@648
   270
  }
kpeter@648
   271
kpeter@652
   272
  // B. Test NetworkSimplex with each pivot rule
kpeter@648
   273
  {
kpeter@653
   274
    NetworkSimplex<Digraph> mcf(gr);
kpeter@653
   275
    mcf.supplyMap(s1).costMap(c).capacityMap(u).lowerMap(l2);
kpeter@648
   276
kpeter@653
   277
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::FIRST_ELIGIBLE),
kpeter@652
   278
             gr, l2, u, c, s1, true,  5970, "#B1");
kpeter@653
   279
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::BEST_ELIGIBLE),
kpeter@652
   280
             gr, l2, u, c, s1, true,  5970, "#B2");
kpeter@653
   281
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::BLOCK_SEARCH),
kpeter@652
   282
             gr, l2, u, c, s1, true,  5970, "#B3");
kpeter@653
   283
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::CANDIDATE_LIST),
kpeter@652
   284
             gr, l2, u, c, s1, true,  5970, "#B4");
kpeter@653
   285
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::ALTERING_LIST),
kpeter@652
   286
             gr, l2, u, c, s1, true,  5970, "#B5");
kpeter@648
   287
  }
kpeter@648
   288
kpeter@648
   289
  return 0;
kpeter@648
   290
}