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