test/min_cost_flow_test.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Fri, 17 Apr 2009 18:04:36 +0200
changeset 656 e6927fe719e6
parent 654 9ad8d2122b50
child 662 e3d9bff447ed
permissions -rw-r--r--
Support >= and <= constraints in NetworkSimplex (#219, #234)

By default the same inequality constraints are supported as by
Circulation (the GEQ form), but the LEQ form can also be selected
using the problemType() function.

The documentation of the min. cost flow module is reworked and
extended with important notes and explanations about the different
variants of the problem and about the dual solution and optimality
conditions.
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@656
    36
  "label  sup1 sup2 sup3 sup4 sup5\n"
kpeter@656
    37
  "    1    20   27    0   20   30\n"
kpeter@656
    38
  "    2    -4    0    0   -8   -3\n"
kpeter@656
    39
  "    3     0    0    0    0    0\n"
kpeter@656
    40
  "    4     0    0    0    0    0\n"
kpeter@656
    41
  "    5     9    0    0    6   11\n"
kpeter@656
    42
  "    6    -6    0    0   -5   -6\n"
kpeter@656
    43
  "    7     0    0    0    0    0\n"
kpeter@656
    44
  "    8     0    0    0    0    3\n"
kpeter@656
    45
  "    9     3    0    0    0    0\n"
kpeter@656
    46
  "   10    -2    0    0   -7   -2\n"
kpeter@656
    47
  "   11     0    0    0  -10    0\n"
kpeter@656
    48
  "   12   -20  -27    0  -30  -20\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@656
    79
enum ProblemType {
kpeter@656
    80
  EQ,
kpeter@656
    81
  GEQ,
kpeter@656
    82
  LEQ
kpeter@656
    83
};
kpeter@656
    84
kpeter@648
    85
// Check the interface of an MCF algorithm
kpeter@654
    86
template <typename GR, typename Flow, typename Cost>
kpeter@648
    87
class McfClassConcept
kpeter@648
    88
{
kpeter@648
    89
public:
kpeter@648
    90
kpeter@648
    91
  template <typename MCF>
kpeter@648
    92
  struct Constraints {
kpeter@648
    93
    void constraints() {
kpeter@648
    94
      checkConcept<concepts::Digraph, GR>();
kpeter@648
    95
kpeter@652
    96
      MCF mcf(g);
kpeter@648
    97
kpeter@653
    98
      b = mcf.reset()
kpeter@653
    99
             .lowerMap(lower)
kpeter@652
   100
             .upperMap(upper)
kpeter@652
   101
             .capacityMap(upper)
kpeter@652
   102
             .boundMaps(lower, upper)
kpeter@652
   103
             .costMap(cost)
kpeter@652
   104
             .supplyMap(sup)
kpeter@652
   105
             .stSupply(n, n, k)
kpeter@656
   106
             .flowMap(flow)
kpeter@656
   107
             .potentialMap(pot)
kpeter@652
   108
             .run();
kpeter@656
   109
      
kpeter@656
   110
      const MCF& const_mcf = mcf;
kpeter@648
   111
kpeter@656
   112
      const typename MCF::FlowMap &fm = const_mcf.flowMap();
kpeter@656
   113
      const typename MCF::PotentialMap &pm = const_mcf.potentialMap();
kpeter@652
   114
kpeter@656
   115
      v = const_mcf.totalCost();
kpeter@656
   116
      double x = const_mcf.template totalCost<double>();
kpeter@656
   117
      v = const_mcf.flow(a);
kpeter@656
   118
      v = const_mcf.potential(n);
kpeter@652
   119
kpeter@648
   120
      ignore_unused_variable_warning(fm);
kpeter@648
   121
      ignore_unused_variable_warning(pm);
kpeter@652
   122
      ignore_unused_variable_warning(x);
kpeter@648
   123
    }
kpeter@648
   124
kpeter@648
   125
    typedef typename GR::Node Node;
kpeter@648
   126
    typedef typename GR::Arc Arc;
kpeter@654
   127
    typedef concepts::ReadMap<Node, Flow> NM;
kpeter@654
   128
    typedef concepts::ReadMap<Arc, Flow> FAM;
kpeter@654
   129
    typedef concepts::ReadMap<Arc, Cost> CAM;
kpeter@648
   130
kpeter@648
   131
    const GR &g;
kpeter@654
   132
    const FAM &lower;
kpeter@654
   133
    const FAM &upper;
kpeter@654
   134
    const CAM &cost;
kpeter@648
   135
    const NM &sup;
kpeter@648
   136
    const Node &n;
kpeter@648
   137
    const Arc &a;
kpeter@654
   138
    const Flow &k;
kpeter@654
   139
    Flow v;
kpeter@652
   140
    bool b;
kpeter@648
   141
kpeter@648
   142
    typename MCF::FlowMap &flow;
kpeter@648
   143
    typename MCF::PotentialMap &pot;
kpeter@648
   144
  };
kpeter@648
   145
kpeter@648
   146
};
kpeter@648
   147
kpeter@648
   148
kpeter@648
   149
// Check the feasibility of the given flow (primal soluiton)
kpeter@648
   150
template < typename GR, typename LM, typename UM,
kpeter@648
   151
           typename SM, typename FM >
kpeter@648
   152
bool checkFlow( const GR& gr, const LM& lower, const UM& upper,
kpeter@656
   153
                const SM& supply, const FM& flow,
kpeter@656
   154
                ProblemType type = EQ )
kpeter@648
   155
{
kpeter@648
   156
  TEMPLATE_DIGRAPH_TYPEDEFS(GR);
kpeter@648
   157
kpeter@648
   158
  for (ArcIt e(gr); e != INVALID; ++e) {
kpeter@648
   159
    if (flow[e] < lower[e] || flow[e] > upper[e]) return false;
kpeter@648
   160
  }
kpeter@648
   161
kpeter@648
   162
  for (NodeIt n(gr); n != INVALID; ++n) {
kpeter@648
   163
    typename SM::Value sum = 0;
kpeter@648
   164
    for (OutArcIt e(gr, n); e != INVALID; ++e)
kpeter@648
   165
      sum += flow[e];
kpeter@648
   166
    for (InArcIt e(gr, n); e != INVALID; ++e)
kpeter@648
   167
      sum -= flow[e];
kpeter@656
   168
    bool b = (type ==  EQ && sum == supply[n]) ||
kpeter@656
   169
             (type == GEQ && sum >= supply[n]) ||
kpeter@656
   170
             (type == LEQ && sum <= supply[n]);
kpeter@656
   171
    if (!b) return false;
kpeter@648
   172
  }
kpeter@648
   173
kpeter@648
   174
  return true;
kpeter@648
   175
}
kpeter@648
   176
kpeter@648
   177
// Check the feasibility of the given potentials (dual soluiton)
kpeter@652
   178
// using the "Complementary Slackness" optimality condition
kpeter@648
   179
template < typename GR, typename LM, typename UM,
kpeter@656
   180
           typename CM, typename SM, typename FM, typename PM >
kpeter@648
   181
bool checkPotential( const GR& gr, const LM& lower, const UM& upper,
kpeter@656
   182
                     const CM& cost, const SM& supply, const FM& flow, 
kpeter@656
   183
                     const PM& pi )
kpeter@648
   184
{
kpeter@648
   185
  TEMPLATE_DIGRAPH_TYPEDEFS(GR);
kpeter@648
   186
kpeter@648
   187
  bool opt = true;
kpeter@648
   188
  for (ArcIt e(gr); opt && e != INVALID; ++e) {
kpeter@648
   189
    typename CM::Value red_cost =
kpeter@648
   190
      cost[e] + pi[gr.source(e)] - pi[gr.target(e)];
kpeter@648
   191
    opt = red_cost == 0 ||
kpeter@648
   192
          (red_cost > 0 && flow[e] == lower[e]) ||
kpeter@648
   193
          (red_cost < 0 && flow[e] == upper[e]);
kpeter@648
   194
  }
kpeter@656
   195
  
kpeter@656
   196
  for (NodeIt n(gr); opt && n != INVALID; ++n) {
kpeter@656
   197
    typename SM::Value sum = 0;
kpeter@656
   198
    for (OutArcIt e(gr, n); e != INVALID; ++e)
kpeter@656
   199
      sum += flow[e];
kpeter@656
   200
    for (InArcIt e(gr, n); e != INVALID; ++e)
kpeter@656
   201
      sum -= flow[e];
kpeter@656
   202
    opt = (sum == supply[n]) || (pi[n] == 0);
kpeter@656
   203
  }
kpeter@656
   204
  
kpeter@648
   205
  return opt;
kpeter@648
   206
}
kpeter@648
   207
kpeter@648
   208
// Run a minimum cost flow algorithm and check the results
kpeter@648
   209
template < typename MCF, typename GR,
kpeter@648
   210
           typename LM, typename UM,
kpeter@648
   211
           typename CM, typename SM >
kpeter@648
   212
void checkMcf( const MCF& mcf, bool mcf_result,
kpeter@648
   213
               const GR& gr, const LM& lower, const UM& upper,
kpeter@648
   214
               const CM& cost, const SM& supply,
kpeter@648
   215
               bool result, typename CM::Value total,
kpeter@656
   216
               const std::string &test_id = "",
kpeter@656
   217
               ProblemType type = EQ )
kpeter@648
   218
{
kpeter@648
   219
  check(mcf_result == result, "Wrong result " + test_id);
kpeter@648
   220
  if (result) {
kpeter@656
   221
    check(checkFlow(gr, lower, upper, supply, mcf.flowMap(), type),
kpeter@648
   222
          "The flow is not feasible " + test_id);
kpeter@648
   223
    check(mcf.totalCost() == total, "The flow is not optimal " + test_id);
kpeter@656
   224
    check(checkPotential(gr, lower, upper, cost, supply, mcf.flowMap(),
kpeter@648
   225
                         mcf.potentialMap()),
kpeter@648
   226
          "Wrong potentials " + test_id);
kpeter@648
   227
  }
kpeter@648
   228
}
kpeter@648
   229
kpeter@648
   230
int main()
kpeter@648
   231
{
kpeter@648
   232
  // Check the interfaces
kpeter@648
   233
  {
kpeter@654
   234
    typedef int Flow;
kpeter@654
   235
    typedef int Cost;
kpeter@652
   236
    // TODO: This typedef should be enabled if the standard maps are
kpeter@652
   237
    // reference maps in the graph concepts (See #190).
kpeter@652
   238
/**/
kpeter@648
   239
    //typedef concepts::Digraph GR;
kpeter@648
   240
    typedef ListDigraph GR;
kpeter@652
   241
/**/
kpeter@654
   242
    checkConcept< McfClassConcept<GR, Flow, Cost>,
kpeter@654
   243
                  NetworkSimplex<GR, Flow, Cost> >();
kpeter@648
   244
  }
kpeter@648
   245
kpeter@648
   246
  // Run various MCF tests
kpeter@648
   247
  typedef ListDigraph Digraph;
kpeter@648
   248
  DIGRAPH_TYPEDEFS(ListDigraph);
kpeter@648
   249
kpeter@648
   250
  // Read the test digraph
kpeter@648
   251
  Digraph gr;
kpeter@648
   252
  Digraph::ArcMap<int> c(gr), l1(gr), l2(gr), u(gr);
kpeter@656
   253
  Digraph::NodeMap<int> s1(gr), s2(gr), s3(gr), s4(gr), s5(gr);
kpeter@652
   254
  ConstMap<Arc, int> cc(1), cu(std::numeric_limits<int>::max());
kpeter@648
   255
  Node v, w;
kpeter@648
   256
kpeter@648
   257
  std::istringstream input(test_lgf);
kpeter@648
   258
  DigraphReader<Digraph>(gr, input)
kpeter@648
   259
    .arcMap("cost", c)
kpeter@648
   260
    .arcMap("cap", u)
kpeter@648
   261
    .arcMap("low1", l1)
kpeter@648
   262
    .arcMap("low2", l2)
kpeter@648
   263
    .nodeMap("sup1", s1)
kpeter@648
   264
    .nodeMap("sup2", s2)
kpeter@648
   265
    .nodeMap("sup3", s3)
kpeter@656
   266
    .nodeMap("sup4", s4)
kpeter@656
   267
    .nodeMap("sup5", s5)
kpeter@648
   268
    .node("source", v)
kpeter@648
   269
    .node("target", w)
kpeter@648
   270
    .run();
kpeter@648
   271
kpeter@652
   272
  // A. Test NetworkSimplex with the default pivot rule
kpeter@648
   273
  {
kpeter@653
   274
    NetworkSimplex<Digraph> mcf(gr);
kpeter@648
   275
kpeter@656
   276
    // Check the equality form
kpeter@653
   277
    mcf.upperMap(u).costMap(c);
kpeter@653
   278
    checkMcf(mcf, mcf.supplyMap(s1).run(),
kpeter@652
   279
             gr, l1, u, c, s1, true,  5240, "#A1");
kpeter@653
   280
    checkMcf(mcf, mcf.stSupply(v, w, 27).run(),
kpeter@652
   281
             gr, l1, u, c, s2, true,  7620, "#A2");
kpeter@653
   282
    mcf.lowerMap(l2);
kpeter@653
   283
    checkMcf(mcf, mcf.supplyMap(s1).run(),
kpeter@652
   284
             gr, l2, u, c, s1, true,  5970, "#A3");
kpeter@653
   285
    checkMcf(mcf, mcf.stSupply(v, w, 27).run(),
kpeter@652
   286
             gr, l2, u, c, s2, true,  8010, "#A4");
kpeter@653
   287
    mcf.reset();
kpeter@653
   288
    checkMcf(mcf, mcf.supplyMap(s1).run(),
kpeter@652
   289
             gr, l1, cu, cc, s1, true,  74, "#A5");
kpeter@653
   290
    checkMcf(mcf, mcf.lowerMap(l2).stSupply(v, w, 27).run(),
kpeter@652
   291
             gr, l2, cu, cc, s2, true,  94, "#A6");
kpeter@653
   292
    mcf.reset();
kpeter@653
   293
    checkMcf(mcf, mcf.run(),
kpeter@652
   294
             gr, l1, cu, cc, s3, true,   0, "#A7");
kpeter@653
   295
    checkMcf(mcf, mcf.boundMaps(l2, u).run(),
kpeter@652
   296
             gr, l2, u, cc, s3, false,   0, "#A8");
kpeter@656
   297
kpeter@656
   298
    // Check the GEQ form
kpeter@656
   299
    mcf.reset().upperMap(u).costMap(c).supplyMap(s4);
kpeter@656
   300
    checkMcf(mcf, mcf.run(),
kpeter@656
   301
             gr, l1, u, c, s4, true,  3530, "#A9", GEQ);
kpeter@656
   302
    mcf.problemType(mcf.GEQ);
kpeter@656
   303
    checkMcf(mcf, mcf.lowerMap(l2).run(),
kpeter@656
   304
             gr, l2, u, c, s4, true,  4540, "#A10", GEQ);
kpeter@656
   305
    mcf.problemType(mcf.CARRY_SUPPLIES).supplyMap(s5);
kpeter@656
   306
    checkMcf(mcf, mcf.run(),
kpeter@656
   307
             gr, l2, u, c, s5, false,    0, "#A11", GEQ);
kpeter@656
   308
kpeter@656
   309
    // Check the LEQ form
kpeter@656
   310
    mcf.reset().problemType(mcf.LEQ);
kpeter@656
   311
    mcf.upperMap(u).costMap(c).supplyMap(s5);
kpeter@656
   312
    checkMcf(mcf, mcf.run(),
kpeter@656
   313
             gr, l1, u, c, s5, true,  5080, "#A12", LEQ);
kpeter@656
   314
    checkMcf(mcf, mcf.lowerMap(l2).run(),
kpeter@656
   315
             gr, l2, u, c, s5, true,  5930, "#A13", LEQ);
kpeter@656
   316
    mcf.problemType(mcf.SATISFY_DEMANDS).supplyMap(s4);
kpeter@656
   317
    checkMcf(mcf, mcf.run(),
kpeter@656
   318
             gr, l2, u, c, s4, false,    0, "#A14", LEQ);
kpeter@648
   319
  }
kpeter@648
   320
kpeter@652
   321
  // B. Test NetworkSimplex with each pivot rule
kpeter@648
   322
  {
kpeter@653
   323
    NetworkSimplex<Digraph> mcf(gr);
kpeter@653
   324
    mcf.supplyMap(s1).costMap(c).capacityMap(u).lowerMap(l2);
kpeter@648
   325
kpeter@653
   326
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::FIRST_ELIGIBLE),
kpeter@652
   327
             gr, l2, u, c, s1, true,  5970, "#B1");
kpeter@653
   328
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::BEST_ELIGIBLE),
kpeter@652
   329
             gr, l2, u, c, s1, true,  5970, "#B2");
kpeter@653
   330
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::BLOCK_SEARCH),
kpeter@652
   331
             gr, l2, u, c, s1, true,  5970, "#B3");
kpeter@653
   332
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::CANDIDATE_LIST),
kpeter@652
   333
             gr, l2, u, c, s1, true,  5970, "#B4");
kpeter@653
   334
    checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::ALTERING_LIST),
kpeter@652
   335
             gr, l2, u, c, s1, true,  5970, "#B5");
kpeter@648
   336
  }
kpeter@648
   337
kpeter@648
   338
  return 0;
kpeter@648
   339
}