test/min_cost_flow_test.cc
author Peter Kovacs <kpeter@inf.elte.hu>
Wed, 25 Mar 2009 15:58:44 +0100
changeset 652 5232721b3f14
parent 648 e8349c6f12ca
child 653 c7d160f73d52
permissions -rw-r--r--
Rework the interface of NetworkSimplex (#234)

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