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