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.
     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 Flow, typename Cost>
    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.reset()
    93              .lowerMap(lower)
    94              .upperMap(upper)
    95              .capacityMap(upper)
    96              .boundMaps(lower, upper)
    97              .costMap(cost)
    98              .supplyMap(sup)
    99              .stSupply(n, n, k)
   100              .run();
   101 
   102       const typename MCF::FlowMap &fm = mcf.flowMap();
   103       const typename MCF::PotentialMap &pm = mcf.potentialMap();
   104 
   105       v = mcf.totalCost();
   106       double x = mcf.template totalCost<double>();
   107       v = mcf.flow(a);
   108       v = mcf.potential(n);
   109       mcf.flowMap(flow);
   110       mcf.potentialMap(pot);
   111 
   112       ignore_unused_variable_warning(fm);
   113       ignore_unused_variable_warning(pm);
   114       ignore_unused_variable_warning(x);
   115     }
   116 
   117     typedef typename GR::Node Node;
   118     typedef typename GR::Arc Arc;
   119     typedef concepts::ReadMap<Node, Flow> NM;
   120     typedef concepts::ReadMap<Arc, Flow> FAM;
   121     typedef concepts::ReadMap<Arc, Cost> CAM;
   122 
   123     const GR &g;
   124     const FAM &lower;
   125     const FAM &upper;
   126     const CAM &cost;
   127     const NM &sup;
   128     const Node &n;
   129     const Arc &a;
   130     const Flow &k;
   131     Flow v;
   132     bool b;
   133 
   134     typename MCF::FlowMap &flow;
   135     typename MCF::PotentialMap &pot;
   136   };
   137 
   138 };
   139 
   140 
   141 // Check the feasibility of the given flow (primal soluiton)
   142 template < typename GR, typename LM, typename UM,
   143            typename SM, typename FM >
   144 bool checkFlow( const GR& gr, const LM& lower, const UM& upper,
   145                 const SM& supply, const FM& flow )
   146 {
   147   TEMPLATE_DIGRAPH_TYPEDEFS(GR);
   148 
   149   for (ArcIt e(gr); e != INVALID; ++e) {
   150     if (flow[e] < lower[e] || flow[e] > upper[e]) return false;
   151   }
   152 
   153   for (NodeIt n(gr); n != INVALID; ++n) {
   154     typename SM::Value sum = 0;
   155     for (OutArcIt e(gr, n); e != INVALID; ++e)
   156       sum += flow[e];
   157     for (InArcIt e(gr, n); e != INVALID; ++e)
   158       sum -= flow[e];
   159     if (sum != supply[n]) return false;
   160   }
   161 
   162   return true;
   163 }
   164 
   165 // Check the feasibility of the given potentials (dual soluiton)
   166 // using the "Complementary Slackness" optimality condition
   167 template < typename GR, typename LM, typename UM,
   168            typename CM, typename FM, typename PM >
   169 bool checkPotential( const GR& gr, const LM& lower, const UM& upper,
   170                      const CM& cost, const FM& flow, const PM& pi )
   171 {
   172   TEMPLATE_DIGRAPH_TYPEDEFS(GR);
   173 
   174   bool opt = true;
   175   for (ArcIt e(gr); opt && e != INVALID; ++e) {
   176     typename CM::Value red_cost =
   177       cost[e] + pi[gr.source(e)] - pi[gr.target(e)];
   178     opt = red_cost == 0 ||
   179           (red_cost > 0 && flow[e] == lower[e]) ||
   180           (red_cost < 0 && flow[e] == upper[e]);
   181   }
   182   return opt;
   183 }
   184 
   185 // Run a minimum cost flow algorithm and check the results
   186 template < typename MCF, typename GR,
   187            typename LM, typename UM,
   188            typename CM, typename SM >
   189 void checkMcf( const MCF& mcf, bool mcf_result,
   190                const GR& gr, const LM& lower, const UM& upper,
   191                const CM& cost, const SM& supply,
   192                bool result, typename CM::Value total,
   193                const std::string &test_id = "" )
   194 {
   195   check(mcf_result == result, "Wrong result " + test_id);
   196   if (result) {
   197     check(checkFlow(gr, lower, upper, supply, mcf.flowMap()),
   198           "The flow is not feasible " + test_id);
   199     check(mcf.totalCost() == total, "The flow is not optimal " + test_id);
   200     check(checkPotential(gr, lower, upper, cost, mcf.flowMap(),
   201                          mcf.potentialMap()),
   202           "Wrong potentials " + test_id);
   203   }
   204 }
   205 
   206 int main()
   207 {
   208   // Check the interfaces
   209   {
   210     typedef int Flow;
   211     typedef int Cost;
   212     // TODO: This typedef should be enabled if the standard maps are
   213     // reference maps in the graph concepts (See #190).
   214 /**/
   215     //typedef concepts::Digraph GR;
   216     typedef ListDigraph GR;
   217 /**/
   218     checkConcept< McfClassConcept<GR, Flow, Cost>,
   219                   NetworkSimplex<GR, Flow, Cost> >();
   220   }
   221 
   222   // Run various MCF tests
   223   typedef ListDigraph Digraph;
   224   DIGRAPH_TYPEDEFS(ListDigraph);
   225 
   226   // Read the test digraph
   227   Digraph gr;
   228   Digraph::ArcMap<int> c(gr), l1(gr), l2(gr), u(gr);
   229   Digraph::NodeMap<int> s1(gr), s2(gr), s3(gr);
   230   ConstMap<Arc, int> cc(1), cu(std::numeric_limits<int>::max());
   231   Node v, w;
   232 
   233   std::istringstream input(test_lgf);
   234   DigraphReader<Digraph>(gr, input)
   235     .arcMap("cost", c)
   236     .arcMap("cap", u)
   237     .arcMap("low1", l1)
   238     .arcMap("low2", l2)
   239     .nodeMap("sup1", s1)
   240     .nodeMap("sup2", s2)
   241     .nodeMap("sup3", s3)
   242     .node("source", v)
   243     .node("target", w)
   244     .run();
   245 
   246   // A. Test NetworkSimplex with the default pivot rule
   247   {
   248     NetworkSimplex<Digraph> mcf(gr);
   249 
   250     mcf.upperMap(u).costMap(c);
   251     checkMcf(mcf, mcf.supplyMap(s1).run(),
   252              gr, l1, u, c, s1, true,  5240, "#A1");
   253     checkMcf(mcf, mcf.stSupply(v, w, 27).run(),
   254              gr, l1, u, c, s2, true,  7620, "#A2");
   255     mcf.lowerMap(l2);
   256     checkMcf(mcf, mcf.supplyMap(s1).run(),
   257              gr, l2, u, c, s1, true,  5970, "#A3");
   258     checkMcf(mcf, mcf.stSupply(v, w, 27).run(),
   259              gr, l2, u, c, s2, true,  8010, "#A4");
   260     mcf.reset();
   261     checkMcf(mcf, mcf.supplyMap(s1).run(),
   262              gr, l1, cu, cc, s1, true,  74, "#A5");
   263     checkMcf(mcf, mcf.lowerMap(l2).stSupply(v, w, 27).run(),
   264              gr, l2, cu, cc, s2, true,  94, "#A6");
   265     mcf.reset();
   266     checkMcf(mcf, mcf.run(),
   267              gr, l1, cu, cc, s3, true,   0, "#A7");
   268     checkMcf(mcf, mcf.boundMaps(l2, u).run(),
   269              gr, l2, u, cc, s3, false,   0, "#A8");
   270   }
   271 
   272   // B. Test NetworkSimplex with each pivot rule
   273   {
   274     NetworkSimplex<Digraph> mcf(gr);
   275     mcf.supplyMap(s1).costMap(c).capacityMap(u).lowerMap(l2);
   276 
   277     checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::FIRST_ELIGIBLE),
   278              gr, l2, u, c, s1, true,  5970, "#B1");
   279     checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::BEST_ELIGIBLE),
   280              gr, l2, u, c, s1, true,  5970, "#B2");
   281     checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::BLOCK_SEARCH),
   282              gr, l2, u, c, s1, true,  5970, "#B3");
   283     checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::CANDIDATE_LIST),
   284              gr, l2, u, c, s1, true,  5970, "#B4");
   285     checkMcf(mcf, mcf.run(NetworkSimplex<Digraph>::ALTERING_LIST),
   286              gr, l2, u, c, s1, true,  5970, "#B5");
   287   }
   288 
   289   return 0;
   290 }