lemon/network_simplex.h
changeset 2622 fa2877651022
parent 2593 8eed667ea23c
child 2623 90defb96ee61
equal deleted inserted replaced
13:3dea823e213e 14:104250edf62f
    30 #include <lemon/graph_adaptor.h>
    30 #include <lemon/graph_adaptor.h>
    31 #include <lemon/graph_utils.h>
    31 #include <lemon/graph_utils.h>
    32 #include <lemon/smart_graph.h>
    32 #include <lemon/smart_graph.h>
    33 #include <lemon/math.h>
    33 #include <lemon/math.h>
    34 
    34 
       
    35 #define _DEBUG_
       
    36 
    35 namespace lemon {
    37 namespace lemon {
    36 
    38 
    37   /// \addtogroup min_cost_flow
    39   /// \addtogroup min_cost_flow
    38   /// @{
    40   /// @{
    39 
    41 
    40   /// \brief Implementation of the network simplex algorithm for
    42   /// \brief Implementation of the primal network simplex algorithm
    41   /// finding a minimum cost flow.
    43   /// for finding a minimum cost flow.
    42   ///
    44   ///
    43   /// \ref NetworkSimplex implements the network simplex algorithm for
    45   /// \ref NetworkSimplex implements the primal network simplex algorithm
    44   /// finding a minimum cost flow.
    46   /// for finding a minimum cost flow.
    45   ///
    47   ///
    46   /// \tparam Graph The directed graph type the algorithm runs on.
    48   /// \tparam Graph The directed graph type the algorithm runs on.
    47   /// \tparam LowerMap The type of the lower bound map.
    49   /// \tparam LowerMap The type of the lower bound map.
    48   /// \tparam CapacityMap The type of the capacity (upper bound) map.
    50   /// \tparam CapacityMap The type of the capacity (upper bound) map.
    49   /// \tparam CostMap The type of the cost (length) map.
    51   /// \tparam CostMap The type of the cost (length) map.
    53   /// - Edge capacities and costs should be \e non-negative \e integers.
    55   /// - Edge capacities and costs should be \e non-negative \e integers.
    54   /// - Supply values should be \e signed \e integers.
    56   /// - Supply values should be \e signed \e integers.
    55   /// - The value types of the maps should be convertible to each other.
    57   /// - The value types of the maps should be convertible to each other.
    56   /// - \c CostMap::Value must be signed type.
    58   /// - \c CostMap::Value must be signed type.
    57   ///
    59   ///
    58   /// \note \ref NetworkSimplex provides six different pivot rule
    60   /// \note \ref NetworkSimplex provides five different pivot rule
    59   /// implementations that significantly affect the efficiency of the
    61   /// implementations that significantly affect the efficiency of the
    60   /// algorithm.
    62   /// algorithm.
    61   /// By default a combined pivot rule is used, which is the fastest
    63   /// By default "Block Search" pivot rule is used, which proved to be
    62   /// implementation according to our benchmark tests.
    64   /// by far the most efficient according to our benchmark tests.
    63   /// Another pivot rule can be selected using \ref run() function
    65   /// However another pivot rule can be selected using \ref run()
    64   /// with the proper parameter.
    66   /// function with the proper parameter.
    65   ///
    67   ///
    66   /// \author Peter Kovacs
    68   /// \author Peter Kovacs
    67 
       
    68   template < typename Graph,
    69   template < typename Graph,
    69              typename LowerMap = typename Graph::template EdgeMap<int>,
    70              typename LowerMap = typename Graph::template EdgeMap<int>,
    70              typename CapacityMap = typename Graph::template EdgeMap<int>,
    71              typename CapacityMap = typename Graph::template EdgeMap<int>,
    71              typename CostMap = typename Graph::template EdgeMap<int>,
    72              typename CostMap = typename Graph::template EdgeMap<int>,
    72              typename SupplyMap = typename Graph::template NodeMap<int> >
    73              typename SupplyMap = typename Graph::template NodeMap<int> >
    87     typedef typename SGraph::template NodeMap<int> IntNodeMap;
    88     typedef typename SGraph::template NodeMap<int> IntNodeMap;
    88     typedef typename SGraph::template NodeMap<bool> BoolNodeMap;
    89     typedef typename SGraph::template NodeMap<bool> BoolNodeMap;
    89     typedef typename SGraph::template NodeMap<Node> NodeNodeMap;
    90     typedef typename SGraph::template NodeMap<Node> NodeNodeMap;
    90     typedef typename SGraph::template NodeMap<Edge> EdgeNodeMap;
    91     typedef typename SGraph::template NodeMap<Edge> EdgeNodeMap;
    91     typedef typename SGraph::template EdgeMap<int> IntEdgeMap;
    92     typedef typename SGraph::template EdgeMap<int> IntEdgeMap;
       
    93     typedef typename SGraph::template EdgeMap<bool> BoolEdgeMap;
    92 
    94 
    93     typedef typename Graph::template NodeMap<Node> NodeRefMap;
    95     typedef typename Graph::template NodeMap<Node> NodeRefMap;
    94     typedef typename Graph::template EdgeMap<Edge> EdgeRefMap;
    96     typedef typename Graph::template EdgeMap<Edge> EdgeRefMap;
       
    97 
       
    98     typedef std::vector<Edge> EdgeVector;
    95 
    99 
    96   public:
   100   public:
    97 
   101 
    98     /// The type of the flow map.
   102     /// The type of the flow map.
    99     typedef typename Graph::template EdgeMap<Capacity> FlowMap;
   103     typedef typename Graph::template EdgeMap<Capacity> FlowMap;
   105     /// Enum type to select the pivot rule used by \ref run().
   109     /// Enum type to select the pivot rule used by \ref run().
   106     enum PivotRuleEnum {
   110     enum PivotRuleEnum {
   107       FIRST_ELIGIBLE_PIVOT,
   111       FIRST_ELIGIBLE_PIVOT,
   108       BEST_ELIGIBLE_PIVOT,
   112       BEST_ELIGIBLE_PIVOT,
   109       BLOCK_SEARCH_PIVOT,
   113       BLOCK_SEARCH_PIVOT,
   110       LIMITED_SEARCH_PIVOT,
       
   111       CANDIDATE_LIST_PIVOT,
   114       CANDIDATE_LIST_PIVOT,
   112       COMBINED_PIVOT
   115       ALTERING_LIST_PIVOT
   113     };
   116     };
   114 
   117 
   115   private:
   118   private:
   116 
   119 
   117     /// \brief Map adaptor class for handling reduced edge costs.
   120     /// \brief Map adaptor class for handling reduced edge costs.
   146     /// \brief Implementation of the "First Eligible" pivot rule for the
   149     /// \brief Implementation of the "First Eligible" pivot rule for the
   147     /// \ref NetworkSimplex "network simplex" algorithm.
   150     /// \ref NetworkSimplex "network simplex" algorithm.
   148     ///
   151     ///
   149     /// This class implements the "First Eligible" pivot rule
   152     /// This class implements the "First Eligible" pivot rule
   150     /// for the \ref NetworkSimplex "network simplex" algorithm.
   153     /// for the \ref NetworkSimplex "network simplex" algorithm.
       
   154     ///
       
   155     /// For more information see \ref NetworkSimplex::run().
   151     class FirstEligiblePivotRule
   156     class FirstEligiblePivotRule
   152     {
   157     {
   153     private:
   158     private:
   154 
   159 
       
   160       // References to the NetworkSimplex class
   155       NetworkSimplex &_ns;
   161       NetworkSimplex &_ns;
   156       EdgeIt _next_edge;
   162       EdgeVector &_edges;
       
   163 
       
   164       int _next_edge;
   157 
   165 
   158     public:
   166     public:
   159 
   167 
   160       /// Constructor.
   168       /// Constructor
   161       FirstEligiblePivotRule(NetworkSimplex &ns) :
   169       FirstEligiblePivotRule(NetworkSimplex &ns, EdgeVector &edges) :
   162         _ns(ns), _next_edge(ns._graph) {}
   170         _ns(ns), _edges(edges), _next_edge(0) {}
   163 
   171 
   164       /// Finds the next entering edge.
   172       /// Find next entering edge
   165       bool findEnteringEdge() {
   173       inline bool findEnteringEdge() {
   166         for (EdgeIt e = _next_edge; e != INVALID; ++e) {
   174         Edge e;
       
   175         for (int i = _next_edge; i < int(_edges.size()); ++i) {
       
   176           e = _edges[i];
   167           if (_ns._state[e] * _ns._red_cost[e] < 0) {
   177           if (_ns._state[e] * _ns._red_cost[e] < 0) {
   168             _ns._in_edge = e;
   178             _ns._in_edge = e;
   169             _next_edge = ++e;
   179             _next_edge = i + 1;
   170             return true;
   180             return true;
   171           }
   181           }
   172         }
   182         }
   173         for (EdgeIt e(_ns._graph); e != _next_edge; ++e) {
   183         for (int i = 0; i < _next_edge; ++i) {
       
   184           e = _edges[i];
   174           if (_ns._state[e] * _ns._red_cost[e] < 0) {
   185           if (_ns._state[e] * _ns._red_cost[e] < 0) {
   175             _ns._in_edge = e;
   186             _ns._in_edge = e;
   176             _next_edge = ++e;
   187             _next_edge = i + 1;
   177             return true;
   188             return true;
   178           }
   189           }
   179         }
   190         }
   180         return false;
   191         return false;
   181       }
   192       }
   184     /// \brief Implementation of the "Best Eligible" pivot rule for the
   195     /// \brief Implementation of the "Best Eligible" pivot rule for the
   185     /// \ref NetworkSimplex "network simplex" algorithm.
   196     /// \ref NetworkSimplex "network simplex" algorithm.
   186     ///
   197     ///
   187     /// This class implements the "Best Eligible" pivot rule
   198     /// This class implements the "Best Eligible" pivot rule
   188     /// for the \ref NetworkSimplex "network simplex" algorithm.
   199     /// for the \ref NetworkSimplex "network simplex" algorithm.
       
   200     ///
       
   201     /// For more information see \ref NetworkSimplex::run().
   189     class BestEligiblePivotRule
   202     class BestEligiblePivotRule
   190     {
   203     {
   191     private:
   204     private:
   192 
   205 
       
   206       // References to the NetworkSimplex class
   193       NetworkSimplex &_ns;
   207       NetworkSimplex &_ns;
       
   208       EdgeVector &_edges;
   194 
   209 
   195     public:
   210     public:
   196 
   211 
   197       /// Constructor.
   212       /// Constructor
   198       BestEligiblePivotRule(NetworkSimplex &ns) : _ns(ns) {}
   213       BestEligiblePivotRule(NetworkSimplex &ns, EdgeVector &edges) :
   199 
   214         _ns(ns), _edges(edges) {}
   200       /// Finds the next entering edge.
   215 
   201       bool findEnteringEdge() {
   216       /// Find next entering edge
       
   217       inline bool findEnteringEdge() {
   202         Cost min = 0;
   218         Cost min = 0;
   203         for (EdgeIt e(_ns._graph); e != INVALID; ++e) {
   219         Edge e;
       
   220         for (int i = 0; i < int(_edges.size()); ++i) {
       
   221           e = _edges[i];
   204           if (_ns._state[e] * _ns._red_cost[e] < min) {
   222           if (_ns._state[e] * _ns._red_cost[e] < min) {
   205             min = _ns._state[e] * _ns._red_cost[e];
   223             min = _ns._state[e] * _ns._red_cost[e];
   206             _ns._in_edge = e;
   224             _ns._in_edge = e;
   207           }
   225           }
   208         }
   226         }
   213     /// \brief Implementation of the "Block Search" pivot rule for the
   231     /// \brief Implementation of the "Block Search" pivot rule for the
   214     /// \ref NetworkSimplex "network simplex" algorithm.
   232     /// \ref NetworkSimplex "network simplex" algorithm.
   215     ///
   233     ///
   216     /// This class implements the "Block Search" pivot rule
   234     /// This class implements the "Block Search" pivot rule
   217     /// for the \ref NetworkSimplex "network simplex" algorithm.
   235     /// for the \ref NetworkSimplex "network simplex" algorithm.
       
   236     ///
       
   237     /// For more information see \ref NetworkSimplex::run().
   218     class BlockSearchPivotRule
   238     class BlockSearchPivotRule
   219     {
   239     {
   220     private:
   240     private:
   221 
   241 
       
   242       // References to the NetworkSimplex class
   222       NetworkSimplex &_ns;
   243       NetworkSimplex &_ns;
   223       EdgeIt _next_edge, _min_edge;
   244       EdgeVector &_edges;
       
   245 
   224       int _block_size;
   246       int _block_size;
   225 
   247       int _next_edge, _min_edge;
   226       static const int MIN_BLOCK_SIZE = 10;
       
   227 
   248 
   228     public:
   249     public:
   229 
   250 
   230       /// Constructor.
   251       /// Constructor
   231       BlockSearchPivotRule(NetworkSimplex &ns) :
   252       BlockSearchPivotRule(NetworkSimplex &ns, EdgeVector &edges) :
   232         _ns(ns), _next_edge(ns._graph), _min_edge(ns._graph)
   253         _ns(ns), _edges(edges), _next_edge(0), _min_edge(0)
   233       {
   254       {
   234         _block_size = 2 * int(sqrt(countEdges(_ns._graph)));
   255         // The main parameters of the pivot rule
   235         if (_block_size < MIN_BLOCK_SIZE) _block_size = MIN_BLOCK_SIZE;
   256         const double BLOCK_SIZE_FACTOR = 2.0;
   236       }
   257         const int MIN_BLOCK_SIZE = 10;
   237 
   258 
   238       /// Finds the next entering edge.
   259         _block_size = std::max( int(BLOCK_SIZE_FACTOR * sqrt(_edges.size())),
   239       bool findEnteringEdge() {
   260                                 MIN_BLOCK_SIZE );
       
   261       }
       
   262 
       
   263       /// Find next entering edge
       
   264       inline bool findEnteringEdge() {
   240         Cost curr, min = 0;
   265         Cost curr, min = 0;
   241         int cnt = 0;
   266         Edge e;
   242         for (EdgeIt e = _next_edge; e != INVALID; ++e) {
   267         int cnt = _block_size;
       
   268         int i;
       
   269         for (i = _next_edge; i < int(_edges.size()); ++i) {
       
   270           e = _edges[i];
   243           if ((curr = _ns._state[e] * _ns._red_cost[e]) < min) {
   271           if ((curr = _ns._state[e] * _ns._red_cost[e]) < min) {
   244             min = curr;
   272             min = curr;
   245             _min_edge = e;
   273             _min_edge = i;
   246           }
   274           }
   247           if (++cnt == _block_size) {
   275           if (--cnt == 0) {
   248             if (min < 0) break;
   276             if (min < 0) break;
   249             cnt = 0;
   277             cnt = _block_size;
   250           }
   278           }
   251         }
   279         }
   252         if (min == 0) {
   280         if (min == 0 || cnt > 0) {
   253           for (EdgeIt e(_ns._graph); e != _next_edge; ++e) {
   281           for (i = 0; i < _next_edge; ++i) {
       
   282             e = _edges[i];
   254             if ((curr = _ns._state[e] * _ns._red_cost[e]) < min) {
   283             if ((curr = _ns._state[e] * _ns._red_cost[e]) < min) {
   255               min = curr;
   284               min = curr;
   256               _min_edge = e;
   285               _min_edge = i;
   257             }
   286             }
   258             if (++cnt == _block_size) {
   287             if (--cnt == 0) {
   259               if (min < 0) break;
   288               if (min < 0) break;
   260               cnt = 0;
   289               cnt = _block_size;
   261             }
   290             }
   262           }
   291           }
   263         }
   292         }
   264         _ns._in_edge = _min_edge;
   293         if (min >= 0) return false;
   265         _next_edge = ++_min_edge;
   294         _ns._in_edge = _edges[_min_edge];
   266         return min < 0;
   295         _next_edge = i;
       
   296         return true;
   267       }
   297       }
   268     }; //class BlockSearchPivotRule
   298     }; //class BlockSearchPivotRule
   269 
       
   270     /// \brief Implementation of the "Limited Search" pivot rule for the
       
   271     /// \ref NetworkSimplex "network simplex" algorithm.
       
   272     ///
       
   273     /// This class implements the "Limited Search" pivot rule
       
   274     /// for the \ref NetworkSimplex "network simplex" algorithm.
       
   275     class LimitedSearchPivotRule
       
   276     {
       
   277     private:
       
   278 
       
   279       NetworkSimplex &_ns;
       
   280       EdgeIt _next_edge, _min_edge;
       
   281       int _sample_size;
       
   282 
       
   283       static const int SAMPLE_SIZE_FACTOR = 15;
       
   284       static const int MIN_SAMPLE_SIZE = 10;
       
   285 
       
   286     public:
       
   287 
       
   288       /// Constructor.
       
   289       LimitedSearchPivotRule(NetworkSimplex &ns) :
       
   290         _ns(ns), _next_edge(ns._graph), _min_edge(ns._graph)
       
   291       {
       
   292         _sample_size = countEdges(_ns._graph) *
       
   293                        SAMPLE_SIZE_FACTOR / 10000;
       
   294         if (_sample_size < MIN_SAMPLE_SIZE)
       
   295           _sample_size = MIN_SAMPLE_SIZE;
       
   296       }
       
   297 
       
   298       /// Finds the next entering edge.
       
   299       bool findEnteringEdge() {
       
   300         Cost curr, min = 0;
       
   301         int cnt = 0;
       
   302         for (EdgeIt e = _next_edge; e != INVALID; ++e) {
       
   303           if ((curr = _ns._state[e] * _ns._red_cost[e]) < min) {
       
   304             min = curr;
       
   305             _min_edge = e;
       
   306           }
       
   307           if (curr < 0 && ++cnt == _sample_size) break;
       
   308         }
       
   309         if (min == 0) {
       
   310           for (EdgeIt e(_ns._graph); e != _next_edge; ++e) {
       
   311             if ((curr = _ns._state[e] * _ns._red_cost[e]) < min) {
       
   312               min = curr;
       
   313               _min_edge = e;
       
   314             }
       
   315             if (curr < 0 && ++cnt == _sample_size) break;
       
   316           }
       
   317         }
       
   318         _ns._in_edge = _min_edge;
       
   319         _next_edge = ++_min_edge;
       
   320         return min < 0;
       
   321       }
       
   322     }; //class LimitedSearchPivotRule
       
   323 
   299 
   324     /// \brief Implementation of the "Candidate List" pivot rule for the
   300     /// \brief Implementation of the "Candidate List" pivot rule for the
   325     /// \ref NetworkSimplex "network simplex" algorithm.
   301     /// \ref NetworkSimplex "network simplex" algorithm.
   326     ///
   302     ///
   327     /// This class implements the "Candidate List" pivot rule
   303     /// This class implements the "Candidate List" pivot rule
   328     /// for the \ref NetworkSimplex "network simplex" algorithm.
   304     /// for the \ref NetworkSimplex "network simplex" algorithm.
       
   305     ///
       
   306     /// For more information see \ref NetworkSimplex::run().
   329     class CandidateListPivotRule
   307     class CandidateListPivotRule
   330     {
   308     {
   331     private:
   309     private:
   332 
   310 
       
   311       // References to the NetworkSimplex class
   333       NetworkSimplex &_ns;
   312       NetworkSimplex &_ns;
   334 
   313       EdgeVector &_edges;
   335       // The list of candidate edges.
   314 
   336       std::vector<Edge> _candidates;
   315       EdgeVector _candidates;
   337       // The maximum length of the edge list.
   316       int _list_length, _minor_limit;
   338       int _list_length;
   317       int _curr_length, _minor_count;
   339       // The maximum number of minor iterations between two major
   318       int _next_edge, _min_edge;
   340       // itarations.
       
   341       int _minor_limit;
       
   342 
       
   343       int _minor_count;
       
   344       EdgeIt _next_edge;
       
   345 
       
   346       static const int LIST_LENGTH_FACTOR = 20;
       
   347       static const int MINOR_LIMIT_FACTOR = 10;
       
   348       static const int MIN_LIST_LENGTH = 10;
       
   349       static const int MIN_MINOR_LIMIT = 2;
       
   350 
   319 
   351     public:
   320     public:
   352 
   321 
   353       /// Constructor.
   322       /// Constructor
   354       CandidateListPivotRule(NetworkSimplex &ns) :
   323       CandidateListPivotRule(NetworkSimplex &ns, EdgeVector &edges) :
   355         _ns(ns), _next_edge(ns._graph)
   324         _ns(ns), _edges(edges), _next_edge(0), _min_edge(0)
   356       {
   325       {
   357         int edge_num = countEdges(_ns._graph);
   326         // The main parameters of the pivot rule
   358         _minor_count = 0;
   327         const double LIST_LENGTH_FACTOR = 1.0;
   359         _list_length = edge_num * LIST_LENGTH_FACTOR / 10000;
   328         const int MIN_LIST_LENGTH = 10;
   360         if (_list_length < MIN_LIST_LENGTH)
   329         const double MINOR_LIMIT_FACTOR = 0.1;
   361           _list_length = MIN_LIST_LENGTH;
   330         const int MIN_MINOR_LIMIT = 3;
   362         _minor_limit = _list_length * MINOR_LIMIT_FACTOR / 100;
   331 
   363         if (_minor_limit < MIN_MINOR_LIMIT)
   332         _list_length = std::max( int(LIST_LENGTH_FACTOR * sqrt(_edges.size())),
   364           _minor_limit = MIN_MINOR_LIMIT;
   333                                  MIN_LIST_LENGTH );
   365       }
   334         _minor_limit = std::max( int(MINOR_LIMIT_FACTOR * _list_length),
   366 
   335                                  MIN_MINOR_LIMIT );
   367       /// Finds the next entering edge.
   336         _curr_length = _minor_count = 0;
   368       bool findEnteringEdge() {
   337         _candidates.resize(_list_length);
       
   338       }
       
   339 
       
   340       /// Find next entering edge
       
   341       inline bool findEnteringEdge() {
   369         Cost min, curr;
   342         Cost min, curr;
   370         if (_minor_count < _minor_limit && _candidates.size() > 0) {
   343         if (_curr_length > 0 && _minor_count < _minor_limit) {
   371           // Minor iteration
   344           // Minor iteration: selecting the best eligible edge from
       
   345           // the current candidate list
   372           ++_minor_count;
   346           ++_minor_count;
   373           Edge e;
   347           Edge e;
   374           min = 0;
   348           min = 0;
   375           for (int i = 0; i < int(_candidates.size()); ++i) {
   349           for (int i = 0; i < _curr_length; ++i) {
   376             e = _candidates[i];
   350             e = _candidates[i];
   377             if ((curr = _ns._state[e] * _ns._red_cost[e]) < min) {
   351             curr = _ns._state[e] * _ns._red_cost[e];
   378               min = curr;
       
   379               _ns._in_edge = e;
       
   380             }
       
   381           }
       
   382           if (min < 0) return true;
       
   383         }
       
   384 
       
   385         // Major iteration
       
   386         _candidates.clear();
       
   387         EdgeIt e = _next_edge;
       
   388         min = 0;
       
   389         for ( ; e != INVALID; ++e) {
       
   390           if ((curr = _ns._state[e] * _ns._red_cost[e]) < 0) {
       
   391             _candidates.push_back(e);
       
   392             if (curr < min) {
   352             if (curr < min) {
   393               min = curr;
   353               min = curr;
   394               _ns._in_edge = e;
   354               _ns._in_edge = e;
   395             }
   355             }
   396             if (int(_candidates.size()) == _list_length) break;
   356             if (curr >= 0) {
       
   357               _candidates[i--] = _candidates[--_curr_length];
       
   358             }
   397           }
   359           }
   398         }
   360           if (min < 0) return true;
   399         if (int(_candidates.size()) < _list_length) {
   361         }
   400           for (e = EdgeIt(_ns._graph); e != _next_edge; ++e) {
   362 
       
   363         // Major iteration: building a new candidate list
       
   364         Edge e;
       
   365         min = 0;
       
   366         _curr_length = 0;
       
   367         int i;
       
   368         for (i = _next_edge; i < int(_edges.size()); ++i) {
       
   369           e = _edges[i];
       
   370           if ((curr = _ns._state[e] * _ns._red_cost[e]) < 0) {
       
   371             _candidates[_curr_length++] = e;
       
   372             if (curr < min) {
       
   373               min = curr;
       
   374               _min_edge = i;
       
   375             }
       
   376             if (_curr_length == _list_length) break;
       
   377           }
       
   378         }
       
   379         if (_curr_length < _list_length) {
       
   380           for (i = 0; i < _next_edge; ++i) {
       
   381             e = _edges[i];
   401             if ((curr = _ns._state[e] * _ns._red_cost[e]) < 0) {
   382             if ((curr = _ns._state[e] * _ns._red_cost[e]) < 0) {
   402               _candidates.push_back(e);
   383               _candidates[_curr_length++] = e;
   403               if (curr < min) {
   384               if (curr < min) {
   404                 min = curr;
   385                 min = curr;
   405                 _ns._in_edge = e;
   386                 _min_edge = i;
   406               }
   387               }
   407               if (int(_candidates.size()) == _list_length) break;
   388               if (_curr_length == _list_length) break;
   408             }
   389             }
   409           }
   390           }
   410         }
   391         }
   411         if (_candidates.size() == 0) return false;
   392         if (_curr_length == 0) return false;
   412         _minor_count = 1;
   393         _minor_count = 1;
   413         _next_edge = ++e;
   394         _ns._in_edge = _edges[_min_edge];
       
   395         _next_edge = i;
   414         return true;
   396         return true;
   415       }
   397       }
   416     }; //class CandidateListPivotRule
   398     }; //class CandidateListPivotRule
       
   399 
       
   400     /// \brief Implementation of the "Altering Candidate List" pivot rule
       
   401     /// for the \ref NetworkSimplex "network simplex" algorithm.
       
   402     ///
       
   403     /// This class implements the "Altering Candidate List" pivot rule
       
   404     /// for the \ref NetworkSimplex "network simplex" algorithm.
       
   405     ///
       
   406     /// For more information see \ref NetworkSimplex::run().
       
   407     class AlteringListPivotRule
       
   408     {
       
   409     private:
       
   410 
       
   411       // References to the NetworkSimplex class
       
   412       NetworkSimplex &_ns;
       
   413       EdgeVector &_edges;
       
   414 
       
   415       EdgeVector _candidates;
       
   416       SCostMap _cand_cost;
       
   417       int _block_size, _head_length, _curr_length;
       
   418       int _next_edge;
       
   419 
       
   420       // Functor class to compare edges during sort of the candidate list
       
   421       class SortFunc
       
   422       {
       
   423       private:
       
   424         const SCostMap &_map;
       
   425       public:
       
   426         SortFunc(const SCostMap &map) : _map(map) {}
       
   427         bool operator()(const Edge &e1, const Edge &e2) {
       
   428 	  return _map[e1] < _map[e2];
       
   429         }
       
   430       };
       
   431 
       
   432       SortFunc _sort_func;
       
   433 
       
   434     public:
       
   435 
       
   436       /// Constructor
       
   437       AlteringListPivotRule(NetworkSimplex &ns, EdgeVector &edges) :
       
   438         _ns(ns), _edges(edges), _cand_cost(_ns._graph),
       
   439         _next_edge(0), _sort_func(_cand_cost)
       
   440       {
       
   441         // The main parameters of the pivot rule
       
   442         const double BLOCK_SIZE_FACTOR = 1.0;
       
   443         const int MIN_BLOCK_SIZE = 10;
       
   444         const double HEAD_LENGTH_FACTOR = 0.1;
       
   445         const int MIN_HEAD_LENGTH = 5;
       
   446 
       
   447         _block_size = std::max( int(BLOCK_SIZE_FACTOR * sqrt(_edges.size())),
       
   448                                 MIN_BLOCK_SIZE );
       
   449         _head_length = std::max( int(HEAD_LENGTH_FACTOR * _block_size),
       
   450                                  MIN_HEAD_LENGTH );
       
   451         _candidates.resize(_head_length + _block_size);
       
   452         _curr_length = 0;
       
   453       }
       
   454 
       
   455       /// Find next entering edge
       
   456       inline bool findEnteringEdge() {
       
   457         // Checking the current candidate list
       
   458         Edge e;
       
   459         for (int idx = 0; idx < _curr_length; ++idx) {
       
   460           e = _candidates[idx];
       
   461           if ((_cand_cost[e] = _ns._state[e] * _ns._red_cost[e]) >= 0) {
       
   462             _candidates[idx--] = _candidates[--_curr_length];
       
   463           }
       
   464         }
       
   465 
       
   466         // Extending the list
       
   467         int cnt = _block_size;
       
   468         int last_edge = 0;
       
   469         int limit = _head_length;
       
   470         for (int i = _next_edge; i < int(_edges.size()); ++i) {
       
   471           e = _edges[i];
       
   472           if ((_cand_cost[e] = _ns._state[e] * _ns._red_cost[e]) < 0) {
       
   473             _candidates[_curr_length++] = e;
       
   474             last_edge = i;
       
   475           }
       
   476           if (--cnt == 0) {
       
   477             if (_curr_length > limit) break;
       
   478             limit = 0;
       
   479             cnt = _block_size;
       
   480           }
       
   481         }
       
   482         if (_curr_length <= limit) {
       
   483           for (int i = 0; i < _next_edge; ++i) {
       
   484             e = _edges[i];
       
   485             if ((_cand_cost[e] = _ns._state[e] * _ns._red_cost[e]) < 0) {
       
   486               _candidates[_curr_length++] = e;
       
   487               last_edge = i;
       
   488             }
       
   489             if (--cnt == 0) {
       
   490               if (_curr_length > limit) break;
       
   491               limit = 0;
       
   492               cnt = _block_size;
       
   493             }
       
   494           }
       
   495         }
       
   496         if (_curr_length == 0) return false;
       
   497         _next_edge = last_edge + 1;
       
   498 
       
   499         // Sorting the list partially
       
   500         EdgeVector::iterator sort_end = _candidates.begin();
       
   501         EdgeVector::iterator vector_end = _candidates.begin();
       
   502         for (int idx = 0; idx < _curr_length; ++idx) {
       
   503           ++vector_end;
       
   504           if (idx <= _head_length) ++sort_end;
       
   505         }
       
   506         partial_sort(_candidates.begin(), sort_end, vector_end, _sort_func);
       
   507 
       
   508         _ns._in_edge = _candidates[0];
       
   509         if (_curr_length > _head_length) {
       
   510           _candidates[0] = _candidates[_head_length - 1];
       
   511           _curr_length = _head_length - 1;
       
   512         } else {
       
   513           _candidates[0] = _candidates[_curr_length - 1];
       
   514           --_curr_length;
       
   515         }
       
   516 
       
   517         return true;
       
   518       }
       
   519     }; //class AlteringListPivotRule
   417 
   520 
   418   private:
   521   private:
   419 
   522 
   420     // State constants for edges
   523     // State constants for edges
   421     enum EdgeStateEnum {
   524     enum EdgeStateEnum {
   422       STATE_UPPER = -1,
   525       STATE_UPPER = -1,
   423       STATE_TREE  =  0,
   526       STATE_TREE  =  0,
   424       STATE_LOWER =  1
   527       STATE_LOWER =  1
   425     };
   528     };
   426 
       
   427     // Constant for the combined pivot rule.
       
   428     static const int COMBINED_PIVOT_MAX_DEG = 5;
       
   429 
   529 
   430   private:
   530   private:
   431 
   531 
   432     // The directed graph the algorithm runs on
   532     // The directed graph the algorithm runs on
   433     SGraph _graph;
   533     SGraph _graph;
   463     // The root node of the starting spanning tree
   563     // The root node of the starting spanning tree
   464     Node _root;
   564     Node _root;
   465 
   565 
   466     // The reduced cost map
   566     // The reduced cost map
   467     ReducedCostMap _red_cost;
   567     ReducedCostMap _red_cost;
       
   568 
       
   569     // The non-artifical edges
       
   570     EdgeVector _edges;
   468 
   571 
   469     // Members for handling the original graph
   572     // Members for handling the original graph
   470     FlowMap *_flow_result;
   573     FlowMap *_flow_result;
   471     PotentialMap *_potential_result;
   574     PotentialMap *_potential_result;
   472     bool _local_flow;
   575     bool _local_flow;
   670     ~NetworkSimplex() {
   773     ~NetworkSimplex() {
   671       if (_local_flow) delete _flow_result;
   774       if (_local_flow) delete _flow_result;
   672       if (_local_potential) delete _potential_result;
   775       if (_local_potential) delete _potential_result;
   673     }
   776     }
   674 
   777 
   675     /// \brief Sets the flow map.
   778     /// \brief Set the flow map.
   676     ///
   779     ///
   677     /// Sets the flow map.
   780     /// Set the flow map.
   678     ///
   781     ///
   679     /// \return \c (*this)
   782     /// \return \c (*this)
   680     NetworkSimplex& flowMap(FlowMap &map) {
   783     NetworkSimplex& flowMap(FlowMap &map) {
   681       if (_local_flow) {
   784       if (_local_flow) {
   682         delete _flow_result;
   785         delete _flow_result;
   684       }
   787       }
   685       _flow_result = &map;
   788       _flow_result = &map;
   686       return *this;
   789       return *this;
   687     }
   790     }
   688 
   791 
   689     /// \brief Sets the potential map.
   792     /// \brief Set the potential map.
   690     ///
   793     ///
   691     /// Sets the potential map.
   794     /// Set the potential map.
   692     ///
   795     ///
   693     /// \return \c (*this)
   796     /// \return \c (*this)
   694     NetworkSimplex& potentialMap(PotentialMap &map) {
   797     NetworkSimplex& potentialMap(PotentialMap &map) {
   695       if (_local_potential) {
   798       if (_local_potential) {
   696         delete _potential_result;
   799         delete _potential_result;
   699       _potential_result = &map;
   802       _potential_result = &map;
   700       return *this;
   803       return *this;
   701     }
   804     }
   702 
   805 
   703     /// \name Execution control
   806     /// \name Execution control
   704     /// The only way to execute the algorithm is to call the run()
       
   705     /// function.
       
   706 
   807 
   707     /// @{
   808     /// @{
   708 
   809 
   709     /// \brief Runs the algorithm.
   810     /// \brief Runs the algorithm.
   710     ///
   811     ///
   724     ///
   825     ///
   725     /// - BLOCK_SEARCH_PIVOT A specified number of edges are examined in
   826     /// - BLOCK_SEARCH_PIVOT A specified number of edges are examined in
   726     /// every iteration in a wraparound fashion and the best eligible
   827     /// every iteration in a wraparound fashion and the best eligible
   727     /// edge is selected from this block (\ref BlockSearchPivotRule).
   828     /// edge is selected from this block (\ref BlockSearchPivotRule).
   728     ///
   829     ///
   729     /// - LIMITED_SEARCH_PIVOT A specified number of eligible edges are
   830     /// - CANDIDATE_LIST_PIVOT In a major iteration a candidate list is
   730     /// examined in every iteration in a wraparound fashion and the best
   831     /// built from eligible edges in a wraparound fashion and in the
   731     /// one is selected from them (\ref LimitedSearchPivotRule).
   832     /// following minor iterations the best eligible edge is selected
   732     ///
   833     /// from this list (\ref CandidateListPivotRule).
   733     /// - CANDIDATE_LIST_PIVOT In major iterations a candidate list is
   834     ///
   734     /// built from eligible edges and it is used for edge selection in
   835     /// - ALTERING_LIST_PIVOT It is a modified version of the
   735     /// the following minor iterations (\ref CandidateListPivotRule).
   836     /// "Candidate List" pivot rule. It keeps only the several best
   736     ///
   837     /// eligible edges from the former candidate list and extends this
   737     /// - COMBINED_PIVOT This is a combined version of the two fastest
   838     /// list in every iteration (\ref AlteringListPivotRule).
   738     /// pivot rules.
   839     ///
   739     /// For rather sparse graphs \ref LimitedSearchPivotRule
   840     /// According to our comprehensive benchmark tests the "Block Search"
   740     /// "Limited Search" implementation is used, otherwise
   841     /// pivot rule proved to be by far the fastest and the most robust
   741     /// \ref BlockSearchPivotRule "Block Search" pivot rule is used.
   842     /// on various test inputs. Thus it is the default option.
   742     /// According to our benchmark tests this combined method is the
       
   743     /// most efficient.
       
   744     ///
   843     ///
   745     /// \return \c true if a feasible flow can be found.
   844     /// \return \c true if a feasible flow can be found.
   746     bool run(PivotRuleEnum pivot_rule = COMBINED_PIVOT) {
   845     bool run(PivotRuleEnum pivot_rule = BLOCK_SEARCH_PIVOT) {
   747       return init() && start(pivot_rule);
   846       return init() && start(pivot_rule);
   748     }
   847     }
   749 
   848 
   750     /// @}
   849     /// @}
   751 
   850 
   752     /// \name Query Functions
   851     /// \name Query Functions
   753     /// The result of the algorithm can be obtained using these
   852     /// The results of the algorithm can be obtained using these
   754     /// functions.
   853     /// functions.\n
   755     /// \n run() must be called before using them.
   854     /// \ref lemon::NetworkSimplex::run() "run()" must be called before
       
   855     /// using them.
   756 
   856 
   757     /// @{
   857     /// @{
   758 
   858 
   759     /// \brief Returns a const reference to the edge map storing the
   859     /// \brief Return a const reference to the edge map storing the
   760     /// found flow.
   860     /// found flow.
   761     ///
   861     ///
   762     /// Returns a const reference to the edge map storing the found flow.
   862     /// Return a const reference to the edge map storing the found flow.
   763     ///
   863     ///
   764     /// \pre \ref run() must be called before using this function.
   864     /// \pre \ref run() must be called before using this function.
   765     const FlowMap& flowMap() const {
   865     const FlowMap& flowMap() const {
   766       return *_flow_result;
   866       return *_flow_result;
   767     }
   867     }
   768 
   868 
   769     /// \brief Returns a const reference to the node map storing the
   869     /// \brief Return a const reference to the node map storing the
   770     /// found potentials (the dual solution).
   870     /// found potentials (the dual solution).
   771     ///
   871     ///
   772     /// Returns a const reference to the node map storing the found
   872     /// Return a const reference to the node map storing the found
   773     /// potentials (the dual solution).
   873     /// potentials (the dual solution).
   774     ///
   874     ///
   775     /// \pre \ref run() must be called before using this function.
   875     /// \pre \ref run() must be called before using this function.
   776     const PotentialMap& potentialMap() const {
   876     const PotentialMap& potentialMap() const {
   777       return *_potential_result;
   877       return *_potential_result;
   778     }
   878     }
   779 
   879 
   780     /// \brief Returns the flow on the given edge.
   880     /// \brief Return the flow on the given edge.
   781     ///
   881     ///
   782     /// Returns the flow on the given edge.
   882     /// Return the flow on the given edge.
   783     ///
   883     ///
   784     /// \pre \ref run() must be called before using this function.
   884     /// \pre \ref run() must be called before using this function.
   785     Capacity flow(const typename Graph::Edge& edge) const {
   885     Capacity flow(const typename Graph::Edge& edge) const {
   786       return (*_flow_result)[edge];
   886       return (*_flow_result)[edge];
   787     }
   887     }
   788 
   888 
   789     /// \brief Returns the potential of the given node.
   889     /// \brief Return the potential of the given node.
   790     ///
   890     ///
   791     /// Returns the potential of the given node.
   891     /// Return the potential of the given node.
   792     ///
   892     ///
   793     /// \pre \ref run() must be called before using this function.
   893     /// \pre \ref run() must be called before using this function.
   794     Cost potential(const typename Graph::Node& node) const {
   894     Cost potential(const typename Graph::Node& node) const {
   795       return (*_potential_result)[node];
   895       return (*_potential_result)[node];
   796     }
   896     }
   797 
   897 
   798     /// \brief Returns the total cost of the found flow.
   898     /// \brief Return the total cost of the found flow.
   799     ///
   899     ///
   800     /// Returns the total cost of the found flow. The complexity of the
   900     /// Return the total cost of the found flow. The complexity of the
   801     /// function is \f$ O(e) \f$.
   901     /// function is \f$ O(e) \f$.
   802     ///
   902     ///
   803     /// \pre \ref run() must be called before using this function.
   903     /// \pre \ref run() must be called before using this function.
   804     Cost totalCost() const {
   904     Cost totalCost() const {
   805       Cost c = 0;
   905       Cost c = 0;
   810 
   910 
   811     /// @}
   911     /// @}
   812 
   912 
   813   private:
   913   private:
   814 
   914 
   815     /// \brief Extends the underlaying graph and initializes all the
   915     /// \brief Extend the underlying graph and initialize all the
   816     /// node and edge maps.
   916     /// node and edge maps.
   817     bool init() {
   917     bool init() {
   818       if (!_valid_supply) return false;
   918       if (!_valid_supply) return false;
   819 
   919 
   820       // Initializing result maps
   920       // Initializing result maps
   825       if (!_potential_result) {
   925       if (!_potential_result) {
   826         _potential_result = new PotentialMap(_graph_ref);
   926         _potential_result = new PotentialMap(_graph_ref);
   827         _local_potential = true;
   927         _local_potential = true;
   828       }
   928       }
   829 
   929 
   830       // Initializing state and flow maps
   930       // Initializing the edge vector and the edge maps
       
   931       _edges.reserve(countEdges(_graph));
   831       for (EdgeIt e(_graph); e != INVALID; ++e) {
   932       for (EdgeIt e(_graph); e != INVALID; ++e) {
       
   933         _edges.push_back(e);
   832         _flow[e] = 0;
   934         _flow[e] = 0;
   833         _state[e] = STATE_LOWER;
   935         _state[e] = STATE_LOWER;
   834       }
   936       }
   835 
   937 
   836       // Adding an artificial root node to the graph
   938       // Adding an artificial root node to the graph
   871       _thread[last] = _root;
   973       _thread[last] = _root;
   872 
   974 
   873       return true;
   975       return true;
   874     }
   976     }
   875 
   977 
   876     /// Finds the join node.
   978     /// Find the join node.
   877     Node findJoinNode() {
   979     inline Node findJoinNode() {
   878       Node u = _graph.source(_in_edge);
   980       Node u = _graph.source(_in_edge);
   879       Node v = _graph.target(_in_edge);
   981       Node v = _graph.target(_in_edge);
   880       while (u != v) {
   982       while (u != v) {
   881         if (_depth[u] == _depth[v]) {
   983         if (_depth[u] == _depth[v]) {
   882           u = _parent[u];
   984           u = _parent[u];
   886         else v = _parent[v];
   988         else v = _parent[v];
   887       }
   989       }
   888       return u;
   990       return u;
   889     }
   991     }
   890 
   992 
   891     /// \brief Finds the leaving edge of the cycle. Returns \c true if
   993     /// \brief Find the leaving edge of the cycle.
   892     /// the leaving edge is not the same as the entering edge.
   994     /// \return \c true if the leaving edge is not the same as the
   893     bool findLeavingEdge() {
   995     /// entering edge.
       
   996     inline bool findLeavingEdge() {
   894       // Initializing first and second nodes according to the direction
   997       // Initializing first and second nodes according to the direction
   895       // of the cycle
   998       // of the cycle
   896       if (_state[_in_edge] == STATE_LOWER) {
   999       if (_state[_in_edge] == STATE_LOWER) {
   897         first  = _graph.source(_in_edge);
  1000         first  = _graph.source(_in_edge);
   898         second = _graph.target(_in_edge);
  1001         second = _graph.target(_in_edge);
   932         }
  1035         }
   933       }
  1036       }
   934       return result;
  1037       return result;
   935     }
  1038     }
   936 
  1039 
   937     /// Changes \c flow and \c state edge maps.
  1040     /// Change \c flow and \c state edge maps.
   938     void changeFlows(bool change) {
  1041     inline void changeFlows(bool change) {
   939       // Augmenting along the cycle
  1042       // Augmenting along the cycle
   940       if (delta > 0) {
  1043       if (delta > 0) {
   941         Capacity val = _state[_in_edge] * delta;
  1044         Capacity val = _state[_in_edge] * delta;
   942         _flow[_in_edge] += val;
  1045         _flow[_in_edge] += val;
   943         for (Node u = _graph.source(_in_edge); u != join; u = _parent[u]) {
  1046         for (Node u = _graph.source(_in_edge); u != join; u = _parent[u]) {
   955       } else {
  1058       } else {
   956         _state[_in_edge] = -_state[_in_edge];
  1059         _state[_in_edge] = -_state[_in_edge];
   957       }
  1060       }
   958     }
  1061     }
   959 
  1062 
   960     /// Updates \c thread and \c parent node maps.
  1063     /// Update \c thread and \c parent node maps.
   961     void updateThreadParent() {
  1064     inline void updateThreadParent() {
   962       Node u;
  1065       Node u;
   963       v_out = _parent[u_out];
  1066       v_out = _parent[u_out];
   964 
  1067 
   965       // Handling the case when join and v_out coincide
  1068       // Handling the case when join and v_out coincide
   966       bool par_first = false;
  1069       bool par_first = false;
  1014         for (u = v_out; _thread[u] != u_out; u = _thread[u]) ;
  1117         for (u = v_out; _thread[u] != u_out; u = _thread[u]) ;
  1015         _thread[u] = right;
  1118         _thread[u] = right;
  1016       }
  1119       }
  1017     }
  1120     }
  1018 
  1121 
  1019     /// Updates \c pred_edge and \c forward node maps.
  1122     /// Update \c pred_edge and \c forward node maps.
  1020     void updatePredEdge() {
  1123     inline void updatePredEdge() {
  1021       Node u = u_out, v;
  1124       Node u = u_out, v;
  1022       while (u != u_in) {
  1125       while (u != u_in) {
  1023         v = _parent[u];
  1126         v = _parent[u];
  1024         _pred_edge[u] = _pred_edge[v];
  1127         _pred_edge[u] = _pred_edge[v];
  1025         _forward[u] = !_forward[v];
  1128         _forward[u] = !_forward[v];
  1027       }
  1130       }
  1028       _pred_edge[u_in] = _in_edge;
  1131       _pred_edge[u_in] = _in_edge;
  1029       _forward[u_in] = (u_in == _graph.source(_in_edge));
  1132       _forward[u_in] = (u_in == _graph.source(_in_edge));
  1030     }
  1133     }
  1031 
  1134 
  1032     /// Updates \c depth and \c potential node maps.
  1135     /// Update \c depth and \c potential node maps.
  1033     void updateDepthPotential() {
  1136     inline void updateDepthPotential() {
  1034       _depth[u_in] = _depth[v_in] + 1;
  1137       _depth[u_in] = _depth[v_in] + 1;
  1035       _potential[u_in] = _forward[u_in] ?
  1138       _potential[u_in] = _forward[u_in] ?
  1036         _potential[v_in] - _cost[_pred_edge[u_in]] :
  1139         _potential[v_in] - _cost[_pred_edge[u_in]] :
  1037         _potential[v_in] + _cost[_pred_edge[u_in]];
  1140         _potential[v_in] + _cost[_pred_edge[u_in]];
  1038 
  1141 
  1047         if (_depth[u] <= _depth[v_in]) break;
  1150         if (_depth[u] <= _depth[v_in]) break;
  1048         u = _thread[u];
  1151         u = _thread[u];
  1049       }
  1152       }
  1050     }
  1153     }
  1051 
  1154 
  1052     /// Executes the algorithm.
  1155     /// Execute the algorithm.
  1053     bool start(PivotRuleEnum pivot_rule) {
  1156     bool start(PivotRuleEnum pivot_rule) {
       
  1157       // Selecting the pivot rule implementation
  1054       switch (pivot_rule) {
  1158       switch (pivot_rule) {
  1055         case FIRST_ELIGIBLE_PIVOT:
  1159         case FIRST_ELIGIBLE_PIVOT:
  1056           return start<FirstEligiblePivotRule>();
  1160           return start<FirstEligiblePivotRule>();
  1057         case BEST_ELIGIBLE_PIVOT:
  1161         case BEST_ELIGIBLE_PIVOT:
  1058           return start<BestEligiblePivotRule>();
  1162           return start<BestEligiblePivotRule>();
  1059         case BLOCK_SEARCH_PIVOT:
  1163         case BLOCK_SEARCH_PIVOT:
  1060           return start<BlockSearchPivotRule>();
  1164           return start<BlockSearchPivotRule>();
  1061         case LIMITED_SEARCH_PIVOT:
       
  1062           return start<LimitedSearchPivotRule>();
       
  1063         case CANDIDATE_LIST_PIVOT:
  1165         case CANDIDATE_LIST_PIVOT:
  1064           return start<CandidateListPivotRule>();
  1166           return start<CandidateListPivotRule>();
  1065         case COMBINED_PIVOT:
  1167         case ALTERING_LIST_PIVOT:
  1066           if ( countEdges(_graph) / countNodes(_graph) <=
  1168           return start<AlteringListPivotRule>();
  1067                COMBINED_PIVOT_MAX_DEG )
       
  1068             return start<LimitedSearchPivotRule>();
       
  1069           else
       
  1070             return start<BlockSearchPivotRule>();
       
  1071       }
  1169       }
  1072       return false;
  1170       return false;
  1073     }
  1171     }
  1074 
  1172 
  1075     template<class PivotRuleImplementation>
  1173     template<class PivotRuleImplementation>
  1076     bool start() {
  1174     bool start() {
  1077       PivotRuleImplementation pivot(*this);
  1175       PivotRuleImplementation pivot(*this, _edges);
  1078 
  1176 
  1079       // Executing the network simplex algorithm
  1177       // Executing the network simplex algorithm
  1080       while (pivot.findEnteringEdge()) {
  1178       while (pivot.findEnteringEdge()) {
  1081         join = findJoinNode();
  1179         join = findJoinNode();
  1082         bool change = findLeavingEdge();
  1180         bool change = findLeavingEdge();