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