Ticket #296: ticket296.patch
| File ticket296.patch, 52.6 KB (added by , 4 years ago) |
|---|
-
lemon/multicommodity_flow.h
diff -r 86d9dd6da44d lemon/multicommodity_flow.h
a b 2 2 * 3 3 * This file is a part of LEMON, a generic C++ optimization library. 4 4 * 5 * Copyright (C) 2003-20 095 * Copyright (C) 2003-2021 6 6 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport 7 7 * (Egervary Research Group on Combinatorial Optimization, EGRES). 8 8 * … … 14 14 * express or implied, and with no claim as to its suitability for any 15 15 * purpose. 16 16 * 17 * Additional Copyright file multicommodity_flow.h: Lancaster University (2021) 17 18 */ 18 19 19 #ifndef LEMON_MULTICOMMODITY_FLOW_H 20 #define LEMON_MULTICOMMODITY_FLOW_H 20 #ifndef LEMON_MULTICOMMODITY_FLOW_H_ 21 #define LEMON_MULTICOMMODITY_FLOW_H_ 21 22 22 23 /// \ingroup approx 23 24 /// 24 25 /// \file 25 /// \brief Approximation algorithms for solving multicommodity flow problems.26 /// \brief Fleischer's algorithms for finding approximate multicommodity flows. 26 27 28 #include <algorithm> // min 29 #include <limits> // numeric_limits 30 #include <random> // random_device, mt19937 31 #include <set> 27 32 #include <vector> 28 #include <lemon/bfs.h>29 #include <lemon/dijkstra.h>30 #include <lemon/preflow.h>31 #include <lemon/adaptors.h>32 #include <lemon/tolerance.h>33 #include <lemon/path.h>34 #include <lemon/math.h>35 33 36 // 37 // TODO: 38 // 39 // - The algorithms should provide primal and dual solution as well: 40 // primal objective value, primal solution (flow), primal solution (flow) for each commodity 41 // dual objective value, dual solution 42 // Note that these algorithms are approximation methods, so the found primal and dual obj. values 43 // are not necessarily optimal, ie. the found primal obj. value could be smaller than the found 44 // dual objective value and their ratio provides a proof for the approximation factor of both 45 // the primal and the dual solution. 46 // 47 // - compute an upper bound for the optimal primal obj. value, e.g. using 48 // x_i = min ( out_cap(s_i), in_cap(t_i) ) for each commodity i 49 // the sum of x_i or the minimum of x_i/d_i ratios can be utilized. 50 // 51 // - give back as good approximation guarantee r as possible for which: 52 // dual = r*primal 53 // 54 // - revise the implementations and their API 55 // 34 #include "lemon/adaptors.h" 35 #include "lemon/bfs.h" 36 #include "lemon/concepts/maps.h" 37 #include "lemon/dijkstra.h" 38 #include "lemon/maps.h" 39 #include "lemon/math.h" 40 #include "lemon/path.h" 41 #include "lemon/preflow.h" 42 #include "lemon/static_graph.h" 43 #include "lemon/tolerance.h" 56 44 57 45 namespace lemon { 58 46 59 /// \addtogroup approx 47 /// \brief Default traits class of \ref ApproxMCF class. 48 /// 49 /// Default traits class of \ref ApproxMCF class. 50 /// \tparam GR The type of the digraph. 51 /// \tparam CAPType The type of the capacity values. 52 /// \tparam CAPMap The type of the capacity map. 53 /// \tparam V The type for the flows and costs. 54 /// It must conform to the \ref concepts::ReadMap "ReadMap" concept. 55 #ifdef DOXYGEN 56 template<typename GR, typename CAPType, typename CAPMap, typename V> 57 #else 58 template< 59 typename GR, 60 typename CAPMap = typename GR::template ArcMap<double>, 61 typename V = double> 62 #endif 63 struct ApproxMCNFDefaultTraits { 64 /// The type of the digraph 65 typedef GR Digraph; 66 /// The type of the capacity map 67 typedef CAPMap CapacityMap; 68 /// The type of the capacity map values. By default is double. 69 typedef typename CapacityMap::Value CapacityValue; 70 71 /// \brief The type used for flow values and costs. 72 /// 73 /// \c CapacityValue must be convertible to \c Value. 74 /// By default is \c double. 75 typedef V Value; 76 typedef typename Digraph::template ArcMap<Value> ValueMap; 77 78 /// The tolerance type used for internal computations 79 typedef lemon::Tolerance<Value> Tolerance; 80 81 /// \brief The path type of the decomposed flows 82 /// 83 /// It must conform to the \ref lemon::concepts::Path "Path" concept 84 typedef lemon::Path<Digraph> Path; 85 typedef typename lemon::Path<Digraph>::ArcIt PathArcIt; 86 87 /// \brief Instantiates a ValueMap. 88 /// 89 /// This function instantiates a \ref ValueMap. 90 /// \param digraph The digraph for which we would like to define 91 /// the large cost map. This can be used for flows or otherwise. 92 static ValueMap* createValueMapPtr( 93 const Digraph& digraph, 94 const Value& value = 0) { 95 return new ValueMap(digraph, value); 96 } 97 // Same as above just const 98 static const ValueMap* createValueMapConstPtr( 99 const Digraph& digraph, 100 const Value& value = 0) { 101 return new ValueMap(digraph, value); 102 } 103 }; 104 105 /// \addtogroup approx 106 /// @{ 107 /// \brief Fleischer's algorithms for the multicommodity flow problem. 108 /// 109 /// Default traits class of \ref ApproxMCF class. 110 /// \tparam GR The type of the digraph. 111 /// \tparam TR The type of the traits class. 112 #ifdef DOXYGEN 113 template<typename GR, typename TR> 114 #else 115 template< 116 typename GR, 117 typename CM = typename GR::template ArcMap<double>, 118 typename TR = ApproxMCNFDefaultTraits<GR, CM>> 119 #endif 120 class ApproxMCF { 121 public: 122 /// The type of the digraph 123 typedef typename TR::Digraph Digraph; 124 /// The type of the capacity map 125 typedef typename TR::CapacityMap CapacityMap; 126 /// The type of the capacity values 127 typedef typename TR::CapacityValue CapacityValue; 128 129 /// \brief The large cost type 130 /// 131 /// The large cost type used for internal computations. 132 /// By default is \c double. 133 typedef typename TR::Value Value; 134 /// Arc Map with values of type \ref Value 135 typedef typename TR::ValueMap ValueMap; 136 137 /// The tolerance type 138 typedef typename TR::Tolerance Tolerance; 139 140 /// \brief The path type of the found cycles 141 /// 142 /// The path type of the found cycles. 143 /// Using the \ref lemon::ApproxMCNFDefaultTraits "default traits class", 144 /// it is \ref lemon::Path "Path<Digraph>". 145 typedef typename TR::Path Path; 146 /// The arc iterator for paths. 147 typedef typename TR::PathArcIt PathArcIt; 148 149 /// The \ref lemon::ApproxMCNFDefaultTraits "traits class" of the 150 /// algorithm 151 typedef TR Traits; 152 153 /// Types of approximation type. 154 enum ApproxType { 155 /// Maximum flow using Fleischer's algorithm. 156 FLEISCHER_MAX, 157 /// Maximum concurrent flow using Fleischer's algorithm. 158 FLEISCHER_MAX_CONCURRENT, 159 /// Minimum cost concurrent flow using binary search with cost-bounded 160 /// \ref FLEISCHER_MAX_CONCURRENT 161 BINARY_SEARCH_MIN_COST 162 }; 163 164 private: 165 struct PathData { 166 Path path; 167 // The amount of flow through the path 168 Value value; 169 // The minimum capacity along all edges in the path 170 CapacityValue min_cap; 171 // The actual cost of the path 172 Value cost; 173 int hash; 174 175 PathData() {} 176 PathData(const Path& p, Value v, CapacityValue m, Value c, int h = 0) 177 : path(p), value(v), min_cap(m), cost(c), hash(h) {} 178 }; 179 180 int hash(const Path& path) const { 181 int h = path.length(); 182 for (PathArcIt a(path); a != INVALID; ++a) 183 h = (h << 4) ^ (h >> 28) ^ _graph.id(a); 184 return h; 185 } 186 187 // Matrix indexed by commodity with all the paths found. i.e. [i][p] where i 188 // is the commodity index and p is the path index. 189 typedef std::vector<std::vector<PathData>> PathMatrix; 190 // Vector indexed by commodity with \ref ValueMap pointers. 191 typedef std::vector<ValueMap*> ValueMapVector; 192 typedef std::vector<const ValueMap*> ConstValueMapVector; 193 194 TEMPLATE_DIGRAPH_TYPEDEFS(Digraph); 195 196 const Digraph& _graph; 197 const int _num_nodes; 198 const int _num_arcs; 199 // Input capacity map 200 const CapacityMap& _cap; 201 202 // The number of commodities 203 int _num_commodities; 204 // The source nodes for each commodity 205 std::vector<Node> _source; 206 // The target nodes for each commodity 207 std::vector<Node> _target; 208 // The demands for each commodity (may be 0, or undefined). 209 std::vector<CapacityValue> _demand; 210 // The cost for each commodity (may be 0, or undefined) 211 // See \ref ValueMapVector. 212 ConstValueMapVector _cost; 213 214 /** 215 * Algorithm parameters 216 */ 217 // type of approximation algorithm. See \ref ApproxType. 218 ApproxType _approx_type; 219 // Stopping criterion 220 Value _epsilon; 221 // Whether the internal heuristics will be applied at the end of the 222 // algorithms 223 bool _heuristic; 224 // Whether there is feasible unsplitable flow 225 bool _feasible_unsplit; 226 // Objective function of the primal problem 227 Value _obj; 228 // Objective function of the dual problem 229 Value _dual_obj; 230 bool _local_cost; 231 // 232 Value _delta; 233 // Expected maximum number of iterations 234 Value _max_iter; 235 // 236 Value _limit; 237 // Number of iterations 238 int _iter; 239 // Tolerance function. See \ref lemon::Tolerance. 240 Tolerance _tol; 241 242 // For the BINARY_SEARCH_MIN_COST case 243 // Cost of the final solution. 244 Value _total_cost; 245 // Cost bounding factor. 246 Value _phi; 247 // Cost bound. 248 Value _cost_bound; 249 250 // The aggregated flow per arc over all commodities 251 ValueMap _total_flow; 252 // The value of the length per arc (the dual solution). 253 ValueMap _len; 254 // The flow for each commodity. See \ref ValueMapVector. 255 ValueMapVector _flow; 256 // The paths found for different commodities. See \ref PathMatrix. 257 PathMatrix _paths; 258 259 // Solution Attributes 260 std::vector<Path> _final_paths; 261 std::vector<Value> _final_flows; 262 std::vector<Value> _final_costs; 263 264 public: 265 /// \brief Default constructor 266 ApproxMCF(const Digraph& gr, const CapacityMap& cp) 267 : _graph(gr), 268 _num_nodes(countNodes(_graph)), 269 _num_arcs(countArcs(_graph)), 270 _cap(cp), 271 _num_commodities(0), 272 _feasible_unsplit(true), 273 _obj(0), 274 _dual_obj(0), 275 _local_cost(true), 276 _iter(0), 277 _cost_bound(0), 278 _total_flow(_graph), 279 _len(_graph) {} 280 281 ~ApproxMCF() { 282 for (int i = 0; i < _num_commodities; ++i) { 283 delete _flow[i]; 284 _flow[i] = nullptr; 285 if (_local_cost) { 286 delete _cost[i]; 287 _cost[i] = nullptr; 288 } 289 } 290 _flow.clear(); 291 _cost.clear(); 292 } 293 294 /// \name Parameters 295 /// The parameters of the algorithm can be specified using these 296 /// functions. 297 60 298 /// @{ 61 299 62 /// \brief Implementation of an approximation algorithm for finding 63 /// a maximum multicommodity flow. 300 /// \brief Set single commodity parameters. 64 301 /// 65 /// \ref MaxMulticommodityFlow implements Lisa Fleischer's algorithm 66 /// for finding a maximum multicommodity flow. 302 /// \param s The source node. 303 /// \param t The target node. 304 /// \param d The required amount of flow from node \c s to node \c t 305 /// (i.e. the supply of \c s and the demand of \c t). 306 /// \param cost_map The cost arc map. 67 307 /// 68 /// \tparam Digraph The type of the digraph the algorithm runs on. 69 /// \tparam CAP The type of the capacity map. 70 /// The default map type is \ref concepts::Digraph::ArcMap 71 /// "GR::ArcMap<double>". 72 /// \tparam V The value type used in the algorithm. 73 /// The default value type is \c CAP::Value. 74 #ifdef DOXYGEN 75 template < typename GR, 76 typename CAP, 77 typename V > 78 #else 79 template < typename GR, 80 typename CAP = typename GR::template ArcMap<double>, 81 typename V = typename CAP::Value > 82 #endif 83 class MaxMulticommodityFlow 84 { 85 TEMPLATE_DIGRAPH_TYPEDEFS(GR); 86 87 typedef SimplePath<GR> SPath; 88 typedef typename SPath::ArcIt PathArcIt; 89 90 public: 91 92 /// The type of the digraph the algorithm runs on. 93 typedef GR Digraph; 94 /// The type of the capacity map. 95 typedef CAP CapacityMap; 96 /// The value type of the algorithm. 97 typedef V Value; 98 99 #ifdef DOXYGEN 100 /// The type of the flow map (primal solution). 101 typedef Digraph::ArcMap<Value> FlowMap; 102 /// The type of the length map (dual soluiton). 103 typedef Digraph::ArcMap<Value> LengthMap; 104 #else 105 /// The type of the flow map (primal solution). 106 typedef typename Digraph::template ArcMap<Value> FlowMap; 107 /// The type of the length map (dual soluiton). 108 typedef typename Digraph::template ArcMap<Value> LengthMap; 109 #endif 110 111 private: 112 113 // The digraph the algorithm runs on 114 const Digraph &_g; 115 // The capacity map 116 const CapacityMap &_cap; 117 118 // The number of commdities 119 int _k; 120 // The source nodes for each commodity 121 std::vector<Node> _s; 122 // The sink nodes for each commodity 123 std::vector<Node> _t; 124 // The weight of each commodity 125 // std::vector<Weight> _w; 308 /// \return <tt>(*this)</tt> 309 ApproxMCF& addCommodity( 310 const Node& s, 311 const Node& t, 312 const CapacityValue& d, 313 const ValueMap& cost_map) { 314 _source.push_back(s); 315 _target.push_back(t); 316 if (d > 0) 317 _demand.push_back(d); 318 _local_cost = false; 319 _cost.push_back(&cost_map); 320 ++_num_commodities; 321 return *this; 322 } 126 323 127 // The flow map (the primal solution). 128 FlowMap _flow; 129 // The length map (the dual solution). 130 LengthMap _len; 131 132 struct PathData { 133 SPath path; 134 Value value; 135 Value min_cap; 136 int hash; 137 138 PathData() {} 139 PathData(const SPath& p, Value v, Value mc, int h = 0) : 140 path(p), value(v), min_cap(mc), hash(h) {} 141 }; 142 143 public: 144 145 /// \brief Constructor. 146 /// 147 /// Constructor. 148 /// 149 /// \param digraph The digraph the algorithm runs on. 150 /// \param capacity The capacities of the edges. 151 MaxMulticommodityFlow( const Digraph &digraph, 152 const CapacityMap &capacity ) : 153 _g(digraph), _cap(capacity), _k(0), 154 _flow(_g), _len(_g) 155 {} 324 /// @overload no cost map, optional demand 325 ApproxMCF& 326 addCommodity(const Node& s, const Node& t, const CapacityValue& d = 0) { 327 const ValueMap* cost_map_ptr = Traits::createValueMapConstPtr(_graph); 328 addCommodity(s, t, d, *cost_map_ptr); 329 _local_cost = true; 330 return *this; 331 } 156 332 157 /// \brief Constructor. 158 /// 159 /// Constructor. 160 /// 161 /// \param gr The digraph the algorithm runs on. 162 /// \param capacity The capacities of the edges. 163 /// \param k The number of commodities. 164 /// \param sources A vector containing the source nodes 165 /// (its size must be at least \c k). 166 /// \param sinks A vector containing the sink nodes 167 /// (its size must be at least \c k). 168 MaxMulticommodityFlow( const Digraph &gr, 169 const CapacityMap &capacity, 170 int k, 171 const std::vector<Node>& sources, 172 const std::vector<Node>& sinks ) : 173 _g(gr), _cap(capacity), 174 _k(k), _s(sources), _t(sinks), 175 _flow(_g), _len(_g) 176 { 177 _s.resize(k); 178 _t.resize(k); 179 } 333 /// @} 180 334 181 /// Destructor. 182 ~MaxMulticommodityFlow() {} 335 /// \name Execution control 183 336 184 /// \brief Add a new commodity. 185 /// 186 /// This function adds a new commodity with the given source and 187 /// sink nodes. 188 void addCommodity(const Node& s, const Node& t) { 189 ++_k; 190 _s.push_back(s); 191 _t.push_back(t); 192 } 193 194 /// \name Execution control 195 196 /// @{ 337 /// @{ 197 338 198 /// \brief Run the algorithm for finding a maximum multicommodity flow. 199 /// 200 /// This function runs the algorithm for finding a maximum multicommodity 201 /// flow. 202 /// \param r The approximation factor. It must be greater than 1. 203 /// \return The effective approximation factor (at least 1), i.e. the 204 /// ratio of the values of the found dual and primal solutions. 205 /// Therefore both the primal and dual solutions are r-approximate. 206 /// 207 /// \note The smaller parameter \c r is, the slower the algorithm runs, 208 /// but it might find better solution. According to our tests, using 209 /// the default value the algorithm performs well and it usually manage 210 /// to find the exact optimum due to heuristic improvements. 211 Value run(Value r = 2.0, Value opt = 0) { 212 213 #define MMF_FLEISCHER_IMPR 214 #define MMF_HEURISTIC_IMPR 215 //#define MMF_EXP_UPDATE 216 //#define MMF_DEBUG_OUTPUT 217 218 // Initialize epsilon and delta parameters 219 Tolerance<Value> ftol, ctol; 220 if (!ftol.less(1.0, r)) return 0; 221 #ifdef MMF_FLEISCHER_IMPR 222 #ifdef MMF_EXP_UPDATE 223 Value epsilon = (r + 1 - 2 * pow(r, 0.5)) / (r - 1); 224 #else 225 Value epsilon = (2 * r + 1 - pow(8 * r + 1, 0.5)) / (2 * r); 226 #endif 227 #else 228 #ifdef MMF_EXP_UPDATE 229 Value epsilon = (2 * r + 1 - pow(8 * r + 1, 0.5)) / (2 * r); 230 #else 231 Value epsilon = 1 - pow(r, -0.5); 232 #endif 233 #endif 234 235 #ifdef MMF_DEBUG_OUTPUT 236 std::cout << "r = " << r << "; epsilon = " << epsilon << "\n"; 237 #endif 238 239 int node_num = countNodes(_g); 240 Value delta = pow(1 + epsilon, 1 - 1/epsilon) / 241 pow(node_num, 1/epsilon); 242 ctol.epsilon(delta * 0.0001); 243 244 // Initialize flow values and lengths 245 for (ArcIt a(_g); a != INVALID; ++a) { 246 _flow[a] = 0; 247 _len[a] = delta; 248 } 249 250 // Check commodities 251 for (int i = 0; i < _k; ++i) { 252 if (_s[i] == _t[i] || !bfs(_g).run(_s[i], _t[i])) { 253 _s[i] = _s[_k - 1]; 254 _t[i] = _t[_k - 1]; 255 --_k; 256 } 257 } 258 if (_k <= 0) return 0; 259 339 /// \brief Run the algorithm for finding a multicommodity flow. 340 /// 341 /// This function runs the algorithm for finding a maximum/minimum cost 342 /// multicommodity flow. 343 /// 344 /// \param at The type of approximation algorithm to use. @see \ref 345 /// ApproxMCF::ApproxType. 346 /// \param heuristic Whether the internal heuristics will be applied at the 347 // end of Fleischer's algorithm. 348 /// \param epsilon The stopping criterion. Must be positive and larger than 349 /// 0. The smaller the value, the more accurate the result, but the longer the 350 /// execution time. By default is 0.1. 351 void run( 352 const ApproxType& at = FLEISCHER_MAX, 353 const bool& heuristic = true, 354 const Value& epsilon = 0.1) { 355 _approx_type = at; 356 _epsilon = epsilon; 357 _heuristic = heuristic; 358 check(); 359 switch (_approx_type) { 360 case FLEISCHER_MAX: 361 runFleischerMax(); 362 break; 363 case BINARY_SEARCH_MIN_COST: 364 runBinarySearchMinCost(); 365 break; 366 case FLEISCHER_MAX_CONCURRENT: 367 runFleischerMaxConcurrent(); 368 break; 369 } 370 } 260 371 261 // Successive improvement along shortest paths 262 std::vector<std::vector<PathData> > paths(_k); 263 #ifdef MMF_FLEISCHER_IMPR 264 Value limit = delta * (1 + epsilon); 265 int i = 0; 266 #else 267 std::vector<SPath> curr_paths(_k); 268 #endif 269 SPath curr_path; 270 Value curr_dist; 271 int iter = 0; 272 while (true) { 273 #ifdef MMF_FLEISCHER_IMPR 274 // Find the shortest path for the i-th commodity 275 // (w.r.t the current length function) 276 dijkstra(_g, _len).path(curr_path).dist(curr_dist) 277 .run(_s[i], _t[i]); 278 279 // Check if the path can be used and increase i or the 280 // length limit if necessary 281 if (!ctol.less(curr_dist, limit)) { 282 if (++i == _k) { 283 if (!ctol.less(limit, 1.0)) break; 284 i = 0; 285 limit *= (1 + epsilon); 286 if (ctol.less(1.0, limit)) limit = 1.0; 287 } 288 continue; 289 } 290 #else 291 // Find the shortest path 292 Value min_dist = 1.0; 293 int i = -1; 294 for (int j = 0; j < _k; ++j) { 295 if ( !dijkstra(_g, _len).path(curr_paths[j]).dist(curr_dist). 296 run(_s[j], _t[j]) ) { 297 std::cerr << "\n\nERROR!!! "<<j<<":"<<_k<<"\n\n"; 298 } 299 if (curr_dist < min_dist) { 300 min_dist = curr_dist; 301 i = j; 302 } 303 } 304 if (i < 0) break; 305 curr_path = curr_paths[i]; 306 #endif 372 /// @} 373 374 private: 375 // Initialize flow values and lengths 376 void init() { 377 _iter = 0; 378 _obj = 0; 379 _dual_obj = 0; 380 // Allocate paths 381 _paths.resize(_num_commodities); 382 // Solution paths 383 _final_paths.resize(_num_commodities); 384 _final_flows.resize(_num_commodities); 385 _final_costs.resize(_num_commodities); 307 386 308 ++iter; 309 310 // Check if the path is used before 311 int h = hash(curr_path); 312 int j; 313 for (j = 0; j < int(paths[i].size()); ++j) { 314 bool b = (paths[i][j].hash == h) && 315 (paths[i][j].path.length() == curr_path.length()); 316 if (b) { 317 for (PathArcIt a1(paths[i][j].path), a2(curr_path); 318 b && a1 != INVALID && a2 != INVALID; ++a1, ++a2) 319 b = _g.id(a1) == _g.id(a2); 320 if (b) break; 321 } 322 } 387 for (int i = 0; i < _num_commodities; ++i) 388 _paths[i].clear(); 389 _delta = 390 pow(1 + _epsilon, 1 - 1 / _epsilon) / pow(_num_nodes, 1 / _epsilon); 391 _tol.epsilon(_delta * 1e-5); 392 _limit = _delta * (1 + _epsilon); 323 393 324 // Set/modify the flow value for the found path 325 Value gamma; 326 if (j == int(paths[i].size())) { 327 gamma = std::numeric_limits<Value>::infinity(); 328 for (PathArcIt a(curr_path); a != INVALID; ++a) { 329 if (_cap[a] < gamma) gamma = _cap[a]; 330 } 331 paths[i].push_back(PathData(curr_path, gamma, gamma, h)); 332 } else { 333 gamma = paths[i][j].min_cap; 334 paths[i][j].value += gamma; 335 } 336 337 // Modify the arc lengths along the current path 338 #ifdef MMF_EXP_UPDATE 339 for (PathArcIt a(curr_path); a != INVALID; ++a) 340 _len[a] *= exp(epsilon * gamma / _cap[a]); 341 #else 342 for (PathArcIt a(curr_path); a != INVALID; ++a) 343 _len[a] *= 1 + epsilon * gamma / _cap[a]; 344 #endif 345 } 346 347 // Setting flow values 348 for (int i = 0; i < _k; ++i) { 349 for (int j = 0; j < int(paths[i].size()); ++j) { 350 Value val = paths[i][j].value; 351 for (PathArcIt a(paths[i][j].path); a != INVALID; ++a) 352 _flow[a] += val; 394 // Allocate/reset flow per commodity and arc 395 _flow.resize(_num_commodities); 396 for (int i = 0; i < _num_commodities; ++i) 397 if (!_flow[i]) { 398 _flow[i] = Traits::createValueMapPtr(_graph); 399 } else { 400 for (ArcIt a(_graph); a != INVALID; ++a) { 401 (*_flow[i])[a] = 0; 353 402 } 354 403 } 355 404 356 // Scaling the solution 357 Value lambda = 0; 358 for (ArcIt a(_g); a != INVALID; ++a) { 359 if (_flow[a] / _cap[a] > lambda) lambda = _flow[a] / _cap[a]; 405 // Initialise length and flow map 406 for (ArcIt a(_graph); a != INVALID; ++a) { 407 _len[a] = _delta; 408 _total_flow[a] = 0; 409 } 410 411 // Initialise phi for cost bounding 412 if (_approx_type == BINARY_SEARCH_MIN_COST && _cost_bound > 0) 413 _phi = _delta / _cost_bound; 414 else 415 _phi = 0; 416 } 417 418 // Preprocessing checks to ensure that: 419 // 420 // 1. Commodities are well-defined 421 // 2. Input sizes are consistent 422 // 3. Concurrent case: Feasible split flow exists for each commodity that 423 // satisfies demand. In the case this doesn't pass, will throw an exception. 424 // 425 // TODO(): Group commodities by source, so we only have to solve the shortest 426 // path once for each group (and length values). 427 void check() { 428 LEMON_ASSERT(_tol.positive(_epsilon), "Epsilon must be greater than 0"); 429 430 // Remove commodities with source = sink or unreachable 431 for (int i = 0; i < _num_commodities; ++i) { 432 if (_source[i] == _target[i] || 433 !bfs(_graph).run(_source[i], _target[i])) { 434 _source[i] = _source[_num_commodities - 1]; 435 _target[i] = _target[_num_commodities - 1]; 436 --_num_commodities; 437 } 438 } 439 440 const int& s_size = _source.size(); 441 const int& t_size = _target.size(); 442 const int& d_size = _demand.size(); 443 444 switch (_approx_type) { 445 case FLEISCHER_MAX: { 446 LEMON_ASSERT(_tol.positive(_num_commodities), "No commodities"); 447 // Check consistency of vector sizes 448 LEMON_ASSERT( 449 (_num_commodities == s_size && s_size == t_size && 450 !_tol.positive(d_size)), 451 "All vectors should be of size equal to the number of commodities, " 452 "and have no demand."); 453 break; 360 454 } 361 if (ftol.positive(lambda)) { 362 lambda = 1 / lambda; 363 for (ArcIt a(_g); a != INVALID; ++a) _flow[a] *= lambda; 364 for (int i = 0; i < _k; ++i) { 365 for (int j = 0; j < int(paths[i].size()); ++j) 366 paths[i][j].value *= lambda; 455 case FLEISCHER_MAX_CONCURRENT: 456 case BINARY_SEARCH_MIN_COST: { 457 // Check consistency of vector sizes 458 LEMON_ASSERT( 459 (_num_commodities == s_size && s_size == t_size && 460 s_size == d_size), 461 "All vectors should be of size equal to the number of commodities"); 462 // Remove commodities with 0 demand. 463 for (int i = 0; i < _num_commodities; ++i) { 464 if (!_tol.positive(_demand[i])) { 465 _source[i] = _source[_num_commodities - 1]; 466 _target[i] = _target[_num_commodities - 1]; 467 --_num_commodities; 468 } 469 } 470 LEMON_ASSERT(_tol.positive(_num_commodities), "No commodities"); 471 472 // Check if feasible routing of demand exists for each commodity 473 for (int i = 0; i < _num_commodities; ++i) { 474 // Only check commodity if the demand in non-zero 475 // Check if feasible split flow exists 476 // Total capacity outgoing at the source 477 CapacityValue total_cap_source = 0; 478 CapacityValue max_cap_source = 0; 479 for (OutArcIt a(_graph, _source[i]); a != INVALID; ++a) { 480 total_cap_source += _cap[a]; 481 if (max_cap_source < _cap[a]) 482 max_cap_source = _cap[a]; 483 } 484 485 // Total capacity incoming at the target 486 CapacityValue total_cap_target = 0; 487 CapacityValue max_cap_target = 0; 488 for (InArcIt a(_graph, _target[i]); a != INVALID; ++a) { 489 total_cap_target += _cap[a]; 490 if (max_cap_target < _cap[a]) 491 max_cap_target = _cap[a]; 492 } 493 494 // Check if enough capacity to satisfy the demand 495 LEMON_ASSERT( 496 (total_cap_source >= _demand[i] && 497 total_cap_target >= _demand[i]), 498 "No feasible (split) flow to satisfy demand for commodity" + 499 std::to_string(i)); 500 501 // Check if feasible unsplittable flow exists that satisfies demand 502 if (max_cap_source < _demand[i] || max_cap_target < _demand[i]) 503 _feasible_unsplit = false; 367 504 } 368 505 } 506 } 507 } 369 508 370 #ifdef MMF_DEBUG_OUTPUT 371 Value value1 = 0; 372 for (int i = 0; i < _k; ++i) { 373 for (InArcIt a(_g, _t[i]); a != INVALID; ++a) value1 += _flow[a]; 374 for (OutArcIt a(_g, _t[i]); a != INVALID; ++a) value1 -= _flow[a]; 509 // Run standard max concurrent algorithm iteratively updating the budget, or 510 // cost bound, a la binary search to obtain an approximation to the 511 // multicommodity flow minimum cost 512 void runBinarySearchMinCost() { 513 // Run standard max concurrent 514 runFleischerMaxConcurrent(); 515 516 Value lower_bound = 0, upper_bound = getTotalCost(); 517 518 // Best solution attributes 519 Value curr_best_cost = std::numeric_limits<Value>::infinity(); 520 ValueMapVector curr_best_flow; 521 522 while ((upper_bound - lower_bound) / upper_bound > _epsilon && 523 upper_bound >= lower_bound) { 524 // Run Max Concurrent algorithm with cost bound 525 _cost_bound = (upper_bound + lower_bound) / 2; 526 runFleischerMaxConcurrent(); 527 // Update upper/lower bounds 528 if (_total_cost < _cost_bound) 529 upper_bound = _cost_bound; 530 else 531 lower_bound = _cost_bound; 532 // Save current solution if appropriate 533 if (_total_cost < curr_best_cost) { 534 curr_best_cost = _total_cost; 535 curr_best_flow = _flow; 375 536 } 376 Value max_opt = value1 / (1 - epsilon); 537 } 538 // Update flow 539 _total_cost = curr_best_cost; 540 _flow = curr_best_flow; 541 } 377 542 378 std::cout << "\n"; 379 std::cout << "Computation finished. Iterations: " << iter << "\n"; 380 std::cout << "Solution value: " << value1; 381 if (opt > 0) std::cout << " \t" << value1 / opt * 100 << "%"; 382 std::cout << std::endl; 383 #else 384 opt = opt; // avoid warning 385 #endif 543 void runFleischerMaxConcurrent() { 544 // Successive improvement along shortest paths 545 init(); 546 Path curr_path; 547 Value curr_dist; 548 while (true) { 549 // Find the shortest path for the i-th commodity 550 // (w.r.t the current length function) 551 for (int i = 0; i < _num_commodities; ++i) { 552 // Route demand 553 CapacityValue d_i = _demand[i]; 554 while (true) { // while (phase) 555 dijkstra(_graph, _len) 556 .path(curr_path) 557 .dist(curr_dist) 558 .run(_source[i], _target[i]); 559 // Get 560 const CapacityValue& routed_flow = updatePaths(curr_path, i, d_i); 561 // Update demand 562 d_i -= routed_flow; 563 updateLen(curr_path, routed_flow, i); 564 updateDualObjective(); 565 if (d_i <= 0 || !_tol.less(_dual_obj, 1)) 566 break; 567 } // end while (phase) 568 } 569 ++_iter; 570 updateDualObjective(); 571 if (!_tol.less(_dual_obj, 1)) 572 break; 573 } // end while (main) 574 575 // Clean up 576 scaleFinalFlowsMaxConcurrent(); 577 578 if (_heuristic) 579 randomisedRounding(); 580 581 setFlowPerCommodity(); 582 setFinalCost(); 583 saveFinalPaths(); 584 } 585 586 void runFleischerMax() { 587 // Successive improvement along shortest paths 588 int i = 0; 589 Path curr_path; 590 Value curr_dist; 591 init(); 592 while (true) { // start while 593 dijkstra(_graph, _len) 594 .path(curr_path) 595 .dist(curr_dist) 596 .run(_source[i], _target[i]); 386 597 387 #ifdef MMF_DEBUG_OUTPUT 388 Value tmp = 0; 389 for (ArcIt a(_g); a != INVALID; ++a) 390 tmp += _cap[a] * _len[a]; 391 std::cout << "Final dual: " << tmp << "\n"; 392 #endif 598 // Check if the path can be used and increase i or the length limit if 599 // necessary 600 if (!_tol.less(curr_dist, _limit)) { 601 if (++i == _num_commodities) { 602 if (!_tol.less(_limit, 1.0)) 603 break; 604 i = 0; 605 _limit *= (1 + _epsilon); 606 if (_tol.less(1.0, _limit)) 607 _limit = 1.0; 608 } 609 continue; 610 } 611 const CapacityValue& routed_flow = updatePaths(curr_path, i); 612 // Update dual values 613 updateLen(curr_path, routed_flow, i); 614 ++_iter; 615 } // end while 616 617 scaleFinalFlowsMaxFlow(); 618 619 if (_heuristic) { 620 runHeurMax1(); 621 runHeurMax2(); 622 } 623 624 setFlowPerCommodity(); 625 626 // Get dual objective 627 _dual_obj = getDualObjective(); 628 // Get objective value 629 _obj = 0; 630 for (int i = 0; i < _num_commodities; ++i) { 631 for (InArcIt a(_graph, _target[i]); a != INVALID; ++a) 632 _obj += (*_flow[i])[a]; 633 for (OutArcIt a(_graph, _target[i]); a != INVALID; ++a) 634 _obj -= (*_flow[i])[a]; 635 } 636 } 393 637 394 #ifdef MMF_HEURISTIC_IMPR 395 // Heuristic improvement 1: improve the solution along one of the 396 // found paths if it is possible 397 int opi = 0; 398 for (int i = 0; i < _k; ++i) { 399 for (int j = 0; j < int(paths[i].size()); ++j) { 400 Value d = std::numeric_limits<Value>::infinity(); 401 for (PathArcIt a(paths[i][j].path); a != INVALID; ++a) 402 if (_cap[a] - _flow[a] < d) d = _cap[a] - _flow[a]; 403 if (ftol.positive(d)) { 404 paths[i][j].value += d; 405 for (PathArcIt a(paths[i][j].path); a != INVALID; ++a) 406 _flow[a] += d; 407 ++opi; 638 // Randomised rounding - if flow is split over multiple paths, randomised 639 // rounding randomly rounds up the largest proportion of flow/cost. 640 // 641 // Only runs if flow can be routed without splitting. 642 // If costs are given, to make expensive paths less likely to occur, we use, 643 // for each path j a value[j] = flow / cost for the discrete distribution. 644 // This distribution samples path j with a probability value[j] / sum values 645 void randomisedRounding() { 646 // Only works if flow can be routed without splitting. 647 if (!_feasible_unsplit) 648 return; 649 for (int i = 0; i < _num_commodities; ++i) { 650 const int& p_size = static_cast<int>(_paths[i].size()); 651 Value total_flow_i = 0; 652 std::vector<Value> values(p_size, 0); 653 654 for (int j = 0; j < p_size; ++j) { 655 const Value& val = _paths[i][j].value; 656 const Value& cost = _paths[i][j].cost; 657 total_flow_i += val; 658 values[j] = val; 659 if (_tol.positive(cost)) 660 values[j] /= cost; 661 } 662 // Get random index wrt values[j] / sum of values 663 int chosen_p_idx; 664 std::random_device rd; 665 std::mt19937 gen(rd()); 666 std::discrete_distribution<> d(values.begin(), values.end()); 667 chosen_p_idx = d(gen); 668 // Check routing all demand through chosen_p_idx remains capacity feasible 669 bool capacity_feasible = true; 670 // cost is all demand routed through here 671 for (PathArcIt a(_paths[i][chosen_p_idx].path); a != INVALID; ++a) { 672 if (_total_flow[a] - _paths[i][chosen_p_idx].value + _demand[i] > 673 _cap[a]) { 674 capacity_feasible = false; 675 break; 676 } 677 } 678 if (capacity_feasible) { 679 for (int j = 0; j < p_size; ++j) { 680 // Update total flows and path flow 681 if (j == chosen_p_idx) { // path j routes demand 682 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) { 683 _total_flow[a] = _total_flow[a] - _paths[i][j].value + _demand[i]; 684 } 685 _paths[i][j].value = _demand[i]; 686 } else { // path j flow is 0 687 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) { 688 _total_flow[a] = _total_flow[a] - _paths[i][j].value; 689 } 690 _paths[i][j].value = 0; 408 691 } 409 692 } 410 693 } 694 } 695 } 411 696 412 // Heuristic improvement 2: improve the solution along new paths 413 // if it is possible 414 int npi = 0; 415 BoolArcMap filter(_g); 416 for (ArcIt a(_g); a != INVALID; ++a) 417 filter[a] = ftol.positive(_cap[a] - _flow[a]); 418 FilterArcs<const Digraph, BoolArcMap> sub_graph(_g, filter); 419 SPath p; 420 for (int i = 0; i < _k; ++i) { 421 while (bfs(sub_graph).path(p).run(_s[i], _t[i])) { 422 Value d = std::numeric_limits<Value>::infinity(); 423 for (PathArcIt a(p); a != INVALID; ++a) 424 if (_cap[a] - _flow[a] < d) d = _cap[a] - _flow[a]; 425 paths[i].push_back(PathData(p, d, d)); 426 for (PathArcIt a(p); a != INVALID; ++a) { 427 _flow[a] += d; 428 filter[a] = ftol.positive(_cap[a] - _flow[a]); 429 } 430 ++npi; 697 // Heuristic improvement 1 (for FLEISCHER_MAX only). 698 // 699 // Attempts to improve the solution along one of the found paths if it is 700 // possible 701 void runHeurMax1() { 702 for (int i = 0; i < _num_commodities; ++i) { 703 for (int j = 0; j < static_cast<int>(_paths[i].size()); ++j) { 704 CapacityValue d = std::numeric_limits<CapacityValue>::infinity(); 705 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) 706 if (_cap[a] - _total_flow[a] < d) 707 d = _cap[a] - _total_flow[a]; 708 if (_tol.positive(d)) { 709 _paths[i][j].value += d; 710 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) 711 _total_flow[a] += d; 712 } 713 } 714 } 715 } 716 717 // Heuristic improvement 2 (for FLEISCHER_MAX only). 718 // 719 // Attempts to improve the solution along new paths 720 void runHeurMax2() { 721 BoolArcMap filter(_graph); 722 for (ArcIt a(_graph); a != INVALID; ++a) 723 filter[a] = _tol.positive(_cap[a] - _total_flow[a]); 724 FilterArcs<const Digraph, BoolArcMap> sub_graph(_graph, filter); 725 Path p; 726 for (int i = 0; i < _num_commodities; ++i) { 727 while (bfs(sub_graph).path(p).run(_source[i], _target[i])) { 728 CapacityValue d = std::numeric_limits<CapacityValue>::infinity(); 729 for (PathArcIt a(p); a != INVALID; ++a) 730 if (_cap[a] - _total_flow[a] < d) 731 d = _cap[a] - _total_flow[a]; 732 _paths[i].push_back(PathData(p, d, d, 0, 0)); 733 for (PathArcIt a(p); a != INVALID; ++a) { 734 _total_flow[a] += d; 735 filter[a] = _tol.positive(_cap[a] - _total_flow[a]); 431 736 } 432 737 } 433 #endif 434 435 #ifdef MMF_DEBUG_OUTPUT 436 Value value2 = 0; 437 for (int i = 0; i < _k; ++i) { 438 for (InArcIt a(_g, _t[i]); a != INVALID; ++a) value2 += _flow[a]; 439 for (OutArcIt a(_g, _t[i]); a != INVALID; ++a) value2 -= _flow[a]; 440 } 441 epsilon = 1 - value2 / max_opt; 738 } 739 } 442 740 443 #ifdef MMF_HEURISTIC_IMPR 444 std::cout << "\nHeuristics applied. Old impr: " << opi << " new impr: " << npi << "\n"; 445 #endif 446 std::cout << "Solution value: " << value2; 447 if (opt > 0) std::cout << " \t" << value2 / opt * 100 << "%"; 448 std::cout << std::endl << std::endl; 449 #endif 741 // Update paths for current commodity and return flow routed 742 Value updatePaths( 743 const Path& curr_path, 744 const int& i, 745 const CapacityValue& d_i = 746 std::numeric_limits<CapacityValue>::infinity()) { 747 int h = hash(curr_path); 748 int j; // path index 749 for (j = 0; j < static_cast<int>(_paths[i].size()); ++j) { 750 bool b = (_paths[i][j].hash == h) && 751 (_paths[i][j].path.length() == curr_path.length()); 752 if (b) { 753 for (PathArcIt a1(_paths[i][j].path), a2(curr_path); 754 b && a1 != INVALID && a2 != INVALID; 755 ++a1, ++a2) 756 b = _graph.id(a1) == _graph.id(a2); 757 if (b) 758 break; 759 } 760 } 761 const bool path_seen = !(j == static_cast<int>(_paths[i].size())); 450 762 451 // TODO: better return value 452 return r; 763 // Set/modify the flow value for the found path 764 Value routed_flow = 0; 765 CapacityValue min_cap; 766 if (!path_seen) { 767 // Add new path 768 Value path_cost = 0; 769 min_cap = std::numeric_limits<CapacityValue>::infinity(); 770 for (PathArcIt a(curr_path); a != INVALID; ++a) { 771 path_cost += (*_cost[i])[a]; 772 if (_cap[a] < min_cap) 773 min_cap = _cap[a]; 774 } 775 routed_flow = std::min(min_cap, d_i); 776 // Save path 777 _paths[i].push_back( 778 PathData(curr_path, routed_flow, min_cap, path_cost, h)); 779 } else { 780 // Update path flow 781 min_cap = _paths[i][j].min_cap; 782 routed_flow = std::min(min_cap, d_i); 783 _paths[i][j].value += routed_flow; 453 784 } 785 return routed_flow; 786 } 454 787 455 /// @} 788 // Modify the arc lengths along the current path 789 void updateLen(const Path& p, const Value& f, const int& i) { 790 Value path_cost = 0; 791 for (PathArcIt a(p); a != INVALID; ++a) { 792 const Value& c_a = (*_cost[i])[a]; 793 _len[a] *= 1 + _epsilon * f / _cap[a] + c_a * _phi; 794 path_cost += c_a; 795 } 796 // Update phi if needed 797 if (_tol.positive(_phi)) 798 _phi *= (1 + (_epsilon * path_cost * f) / _cost_bound); 799 } 456 800 457 private: 801 void updateDualObjective() { 802 _dual_obj = 0; 803 for (ArcIt a(_graph); a != INVALID; ++a) 804 _dual_obj += _cap[a] * _len[a]; 805 } 458 806 459 int hash(const SPath& path) { 460 int h = path.length(); 461 for (PathArcIt a(path); a != INVALID; ++a) 462 h = (h<<4) ^ (h>>28) ^ _g.id(a); 463 return h; 807 void setFinalCost() { 808 _total_cost = 0; 809 for (int i = 0; i < _num_commodities; ++i) { 810 for (int j = 0; j < static_cast<int>((_paths[i].size())); ++j) { 811 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) 812 _total_cost += (*_cost[i])[a] * (*_flow[i])[a]; 813 } 464 814 } 815 } 465 816 466 public: 467 468 /// \name Query Functions 469 /// The result of the algorithm can be obtained using these 470 /// functions.\n 471 /// \ref run() must be called before using them. 472 473 /// @{ 817 // Scale flows in the \ref FLEISCHER_MAX_CONCURRENT. 818 // Flows are scaled by the number of iterations. 819 void scaleFinalFlowsMaxConcurrent() { 820 int scaler = _iter; 821 for (int i = 0; i < _num_commodities; ++i) { 822 for (int j = 0; j < static_cast<int>(_paths[i].size()); ++j) { 823 _paths[i][j].value /= scaler; 824 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) { 825 _total_flow[a] += _paths[i][j].value; 826 } 827 } 828 } 829 _obj = (_iter) / scaler; 830 } 474 831 475 /// \brief Return the flow value on the given arc. 476 /// 477 /// This function returns the flow value on the given arc. 478 /// 479 /// \pre \ref run() must be called before using this function. 480 Value flow(const Arc& arc) const { 481 return _flow[arc]; 832 // Update total flow map 833 void updateTotalFlow() { 834 for (int i = 0; i < _num_commodities; ++i) { 835 for (int j = 0; j < static_cast<int>(_paths[i].size()); ++j) { 836 CapacityValue val = _paths[i][j].value; 837 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) 838 _total_flow[a] += val; 839 } 482 840 } 841 } 483 842 484 /// \brief Return a const reference to an arc map storing the 485 /// found flow. 486 /// 487 /// This function returns a const reference to an arc map storing 488 /// the found flow. 489 /// 490 /// \pre \ref run() must be called before using this function. 491 const FlowMap& flowMap() const { 492 return _flow; 843 // Scale flows per commodity for the FLEISCHER_MAX case. 844 // scaler = max {total_flow_a / cap_a} such that all flows are capacity 845 // feasible 846 void scaleFinalFlowsMaxFlow() { 847 updateTotalFlow(); 848 Value scaler = 0; 849 for (ArcIt a(_graph); a != INVALID; ++a) { 850 if (_total_flow[a] / _cap[a] > scaler) 851 scaler = _total_flow[a] / _cap[a]; 852 } 853 if (_tol.positive(scaler)) { 854 for (ArcIt a(_graph); a != INVALID; ++a) { 855 _total_flow[a] /= scaler; 856 } 857 for (int i = 0; i < _num_commodities; ++i) { 858 for (int j = 0; j < static_cast<int>(_paths[i].size()); ++j) 859 _paths[i][j].value /= scaler; 860 } 493 861 } 862 } 863 864 // Set flow decomposed solution for exposure 865 void setFlowPerCommodity() { 866 // Setting flow values using saved paths 867 for (int i = 0; i < _num_commodities; ++i) { 868 for (int j = 0; j < static_cast<int>(_paths[i].size()); ++j) { 869 Value val = _paths[i][j].value; 870 for (PathArcIt a(_paths[i][j].path); a != INVALID; ++a) 871 (*_flow[i])[a] += val; 872 } 873 } 874 } 875 876 void saveFinalPaths() { 877 for (int i = 0; i < _num_commodities; ++i) { 878 const int& p_size = static_cast<int>(_paths[i].size()); 879 bool saved_path = false; 880 Value path_flow = 0; 881 Value path_cost = std::numeric_limits<Value>::infinity(); 882 Path path; 494 883 495 /// \brief Return the length of the given arc (the dual value). 496 /// 497 /// This function returns the length of the given arc (the dual value). 498 /// 499 /// \pre \ref run() must be called before using this function. 500 Value length(const Arc& arc) const { 501 return _len[arc]; 884 for (int j = 0; j < p_size; ++j) { 885 const Value& v = _paths[i][j].value; 886 const Value& c = _paths[i][j].cost; 887 if (_tol.positive(v)) { 888 if (saved_path) 889 std::cout << "[WARNING] multiple paths with positive flow" 890 << std::endl; 891 // Keep path with largest flow 892 if (path_flow < v && c < path_cost) { 893 path = _paths[i][j].path; 894 path_flow = v; 895 path_cost = c; 896 saved_path = true; 897 } 898 } 899 } 900 _final_paths[i] = path; 901 _final_flows[i] = path_flow; 902 _final_costs[i] = path_cost; 502 903 } 904 } 905 906 public: 907 const Value& flow(const int& k, const Arc& arc) const { 908 return (*_flow[k])[arc]; 909 } 503 910 504 /// \brief Return a const reference to an arc map storing the 505 /// found lengths (the dual solution). 506 /// 507 /// This function returns a const reference to an arc map storing 508 /// the found lengths (the dual solution). 509 /// 510 /// \pre \ref run() must be called before using this function. 511 const LengthMap& lengthMap() const { 512 return _len; 513 } 911 const Value& flow(const int& k, const int& arc_id) const { 912 return (*_flow[k])[_graph.arcFromId(arc_id)]; 913 } 914 915 const Value& length(const Arc& arc) const { return _len[arc]; } 916 917 const ValueMap& lengthMap() const { return _len; } 918 919 const Value& getObjective() const { return _obj; } 920 921 const Value& getDualObjective() const { return _dual_obj; } 922 923 const Value& getTotalCost() const { return _total_cost; } 514 924 515 /// @}925 const ValueMap& getFlowMap(const int& k) const { return *_flow[k]; } 516 926 517 }; //class MaxMulticommodityFlow 927 const Path& getPath(const int& k) const { return _final_paths[k]; } 928 929 const Value& getPathCost(const int& k) const { return _final_costs[k]; } 518 930 519 ///@} 931 const Value& getPathFlow(const int& k) const { return _final_flows[k]; } 932 }; // namespace lemon 933 /// @} 520 934 521 } //namespace lemon935 } // namespace lemon 522 936 523 #endif //LEMON_MULTICOMMODITY_FLOW_H937 #endif // LEMON_MULTICOMMODITY_FLOW_H_ -
new file test/multicommodity_flow_test.cc
diff -r 86d9dd6da44d test/multicommodity_flow_test.cc
- + 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-2021 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 <lemon/concepts/digraph.h> 20 #include <lemon/concepts/maps.h> 21 #include <lemon/lgf_reader.h> 22 #include <lemon/multicommodity_flow.h> 23 #include <lemon/smart_graph.h> 24 25 #include <iostream> 26 #include <sstream> 27 28 #include "test/test_tools.h" 29 30 char test_lgf[] = 31 "@nodes\n" 32 "label\n" 33 "0\n" 34 "1\n" 35 "2\n" 36 "3\n" 37 "@arcs\n" 38 " label cap cost solution_1 solution_2 solution_1_max " 39 "solution_2_max\n" 40 "0 1 0 10.0 1.0 10.0 0.0 10.0 0.0\n" 41 "0 2 1 10.0 1.0 0.0 10.0 8.3 1.7\n" 42 "1 3 2 10.0 1.0 10.0 0.0 10.0 0.0\n" 43 "2 3 3 10.0 1000.0 0.0 0.0 8.3 0.0\n" 44 "@attributes\n" 45 "source_1 0\n" 46 "target_1 3\n" 47 "source_2 0\n" 48 "target_2 2\n"; 49 50 using namespace lemon; 51 52 // Returns string for test-naming 53 template<typename GR, typename A = typename GR::Arc> 54 std::string getEdgeName(const GR& graph, const A& edge) { 55 return std::to_string(graph.id(graph.source(edge))) + "->" + 56 std::to_string(graph.id(graph.target(edge))); 57 } 58 59 template<class Digraph> 60 void checkApproxMCF() { 61 TEMPLATE_DIGRAPH_TYPEDEFS(Digraph); 62 63 Digraph graph; 64 Node s1, t1, s2, t2; 65 typedef typename Digraph::template ArcMap<double> DoubleMap; 66 DoubleMap cap(graph), cost(graph), sol1(graph), sol2(graph), sol1_max(graph), 67 sol2_max(graph); 68 Tolerance<double> tol; 69 tol.epsilon(1e-1); // 1 decimal place 70 71 std::istringstream input(test_lgf); 72 DigraphReader<Digraph>(graph, input) 73 .arcMap("cap", cap) 74 .arcMap("cost", cost) 75 .arcMap("solution_1", sol1) // flow in the solution for commodity 1 76 .arcMap("solution_2", sol2) // flow in the solution for commodity 1 77 .arcMap( 78 "solution_1_max", 79 sol1_max) // flow in the solution for commodity (max-flow) 1 80 .arcMap( 81 "solution_2_max", 82 sol2_max) // flow in the solution for commodity (max-flow) 1 83 .node("source_1", s1) // source node commodity 1 84 .node("target_1", t1) // target node commodity 1 85 .node("source_2", s2) // source node commodity 2 86 .node("target_2", t2) // target node commodity 2 87 .run(); 88 89 // Max 90 { 91 ApproxMCF<Digraph> max(graph, cap); 92 max.addCommodity(s1, t1).addCommodity(s2, t2).run(); 93 for (ArcIt e(graph); e != INVALID; ++e) { 94 const std::string test_name = 95 "[FLEISCHER_MAX] flow on " + getEdgeName<Digraph>(graph, e); 96 // Check flow for commodity 1 (index 0) to 1 decimal place 97 check( 98 !tol.different(max.flow(0, e), sol1_max[e]), 99 test_name + " commodity 1"); 100 // Check flow for commodity 2 (index 1) to 1 decimal place 101 check( 102 !tol.different(max.flow(1, e), sol2_max[e]), 103 test_name + " commodity 2"); 104 } 105 } 106 107 // Max Concurrent 108 { 109 ApproxMCF<Digraph> max(graph, cap); 110 max.addCommodity(s1, t1, 10.0) 111 .addCommodity(s2, t2, 10.0) 112 .run(ApproxMCF<Digraph>::FLEISCHER_MAX_CONCURRENT); 113 for (ArcIt e(graph); e != INVALID; ++e) { 114 const std::string test_name = "[FLEISCHER_MAX_CONCURRENT] flow on " + 115 getEdgeName<Digraph>(graph, e); 116 // Check commodity 1 (index 0) 117 check(max.flow(0, e) == sol1[e], test_name + " commodity 1"); 118 // Check commodity 2 (index 1) 119 check(max.flow(1, e) == sol2[e], test_name + " commodity 2"); 120 } 121 } 122 123 // Min cost 124 { 125 ApproxMCF<Digraph> max(graph, cap); 126 max.addCommodity(s1, t1, 10.0, cost) 127 .addCommodity(s2, t2, 10.0, cost) 128 .run(ApproxMCF<Digraph>::BINARY_SEARCH_MIN_COST); 129 for (ArcIt e(graph); e != INVALID; ++e) { 130 const std::string test_name = 131 "[BINARY_SEARCH_MIN_COST] flow on " + getEdgeName<Digraph>(graph, e); 132 // Check commodity 1 (index 0) 133 check(max.flow(0, e) == sol1[e], test_name + " commodity 1"); 134 // Check commodity 2 (index 1) 135 check(max.flow(1, e) == sol2[e], test_name + " commodity 2"); 136 } 137 // Check total cost 138 check(max.getTotalCost() == 30.0, "min-cost failed"); 139 } 140 141 // Max Concurrent - 3 commodities 142 { 143 ApproxMCF<Digraph> max(graph, cap); 144 max.addCommodity(s1, t1, 5.0, cost) 145 .addCommodity(s1, t2, 10.0, cost) 146 .addCommodity(s1, t1, 5.0, cost) 147 .run(ApproxMCF<Digraph>::FLEISCHER_MAX_CONCURRENT); 148 149 const DoubleMap& flow = max.getFlowMap(0); 150 check( 151 max.getTotalCost() == 30.0, 152 "3 commodities FLEISCHER_MAX_CONCURRENT failed"); 153 } 154 } 155 156 template<class Digraph> 157 void checkApproxMCFDisconnected() { 158 TEMPLATE_DIGRAPH_TYPEDEFS(Digraph); 159 160 Digraph graph; 161 typedef typename Digraph::template ArcMap<double> DoubleMap; 162 DoubleMap cap(graph), cost(graph), sol1(graph), sol2(graph); 163 164 Node n0 = graph.addNode(); 165 Node n1 = graph.addNode(); 166 Node n2 = graph.addNode(); 167 Node n3 = graph.addNode(); 168 169 Arc a; 170 a = graph.addArc(n0, n1); 171 cap[a] = 10.0; 172 cost[a] = 1.0; 173 sol1[a] = 10.0; 174 sol2[a] = 0.0; 175 176 a = graph.addArc(n2, n3); 177 cap[a] = 10.0; 178 cost[a] = 1.0; 179 sol1[a] = 0.0; 180 sol2[a] = 10.0; 181 182 // Max 183 { 184 ApproxMCF<Digraph> max(graph, cap); 185 max.addCommodity(n0, n1).addCommodity(n2, n3).run(); 186 for (ArcIt e(graph); e != INVALID; ++e) { 187 const std::string test_name = 188 "[FLEISCHER_MAX] flow on " + getEdgeName<Digraph>(graph, e); 189 // Check commodity 1 190 check(max.flow(0, e) == sol1[e], test_name + " commodity 1"); 191 // Check commodity 2 192 check(max.flow(1, e) == sol2[e], test_name + " commodity 2"); 193 } 194 } 195 // Max Concurrent 196 { 197 ApproxMCF<Digraph> max(graph, cap); 198 max.addCommodity(n0, n1, 10.0, cost) 199 .addCommodity(n2, n3, 10.0, cost) 200 .run(ApproxMCF<Digraph>::FLEISCHER_MAX_CONCURRENT); 201 for (ArcIt e(graph); e != INVALID; ++e) { 202 const std::string test_name = "[FLEISCHER_MAX_CONCURRENT] flow on " + 203 getEdgeName<Digraph>(graph, e); 204 // Check commodity 1 205 check(max.flow(0, e) == sol1[e], test_name + " commodity 1"); 206 // Check commodity 2 207 check(max.flow(1, e) == sol2[e], test_name + " commodity 2"); 208 } 209 } 210 } 211 212 int main() { 213 checkApproxMCF<SmartDigraph>(); 214 checkApproxMCF<ListDigraph>(); 215 checkApproxMCFDisconnected<SmartDigraph>(); 216 checkApproxMCFDisconnected<ListDigraph>(); 217 }

