# HG changeset patch # User kpeter # Date 1203305412 0 # Node ID a9758ea1f01c4b11a1631fbc1dda11279ef8ed5d # Parent 303d5cb61e8cf2fc72e673bc32261ef480b1df12 Improvements in CycleCanceling. Main changes: - Use function parameter instead of #define commands to select negative cycle detection method. - Change the name of private members to start with "_". - Change the name of function parameters not to start with "_". - Remove unnecessary documentation for private members. - Doc improvements. diff -r 303d5cb61e8c -r a9758ea1f01c lemon/cycle_canceling.h --- a/lemon/cycle_canceling.h Fri Feb 08 11:58:32 2008 +0000 +++ b/lemon/cycle_canceling.h Mon Feb 18 03:30:12 2008 +0000 @@ -22,37 +22,15 @@ /// \ingroup min_cost_flow /// /// \file -/// \brief A cycle-canceling algorithm for finding a minimum cost flow. +/// \brief Cycle-canceling algorithm for finding a minimum cost flow. #include #include +#include + #include - -/// \brief The used cycle-canceling method. -#define LIMITED_CYCLE_CANCELING -//#define MIN_MEAN_CYCLE_CANCELING - -#ifdef LIMITED_CYCLE_CANCELING - #include - // The maximum number of iterations for the first execution of the - // Bellman-Ford algorithm. It should be at least 2. - #define STARTING_LIMIT 2 - // The iteration limit for the Bellman-Ford algorithm is multiplied by - // ALPHA_MUL / ALPHA_DIV in every round. - // ALPHA_MUL / ALPHA_DIV must be greater than 1. - #define ALPHA_MUL 3 - #define ALPHA_DIV 2 - -//#define _ONLY_ONE_CYCLE_ -//#define _NO_BACK_STEP_ -#endif - -#ifdef MIN_MEAN_CYCLE_CANCELING - #include - #include -#endif - -//#define _DEBUG_ITER_ +#include +#include namespace lemon { @@ -65,33 +43,40 @@ /// \ref CycleCanceling implements a cycle-canceling algorithm for /// finding a minimum cost flow. /// - /// \param Graph The directed graph type the algorithm runs on. - /// \param LowerMap The type of the lower bound map. - /// \param CapacityMap The type of the capacity (upper bound) map. - /// \param CostMap The type of the cost (length) map. - /// \param SupplyMap The type of the supply map. + /// \tparam Graph The directed graph type the algorithm runs on. + /// \tparam LowerMap The type of the lower bound map. + /// \tparam CapacityMap The type of the capacity (upper bound) map. + /// \tparam CostMap The type of the cost (length) map. + /// \tparam SupplyMap The type of the supply map. /// /// \warning - /// - Edge capacities and costs should be non-negative integers. - /// However \c CostMap::Value should be signed type. - /// - Supply values should be signed integers. - /// - \c LowerMap::Value must be convertible to - /// \c CapacityMap::Value and \c CapacityMap::Value must be - /// convertible to \c SupplyMap::Value. + /// - Edge capacities and costs should be \e non-negative \e integers. + /// - Supply values should be \e signed \e integers. + /// - \c LowerMap::Value must be convertible to \c CapacityMap::Value. + /// - \c CapacityMap::Value and \c SupplyMap::Value must be + /// convertible to each other. + /// - All value types must be convertible to \c CostMap::Value, which + /// must be signed type. + /// + /// \note By default the \ref BellmanFord "Bellman-Ford" algorithm is + /// used for negative cycle detection with limited iteration number. + /// However \ref CycleCanceling also provides the "Minimum Mean + /// Cycle-Canceling" algorithm, which is \e strongly \e polynomial, + /// but rather slower in practice. + /// To use this version of the algorithm, call \ref run() with \c true + /// parameter. /// /// \author Peter Kovacs template < typename Graph, typename LowerMap = typename Graph::template EdgeMap, - typename CapacityMap = LowerMap, + typename CapacityMap = typename Graph::template EdgeMap, typename CostMap = typename Graph::template EdgeMap, - typename SupplyMap = typename Graph::template NodeMap - > + typename SupplyMap = typename Graph::template NodeMap > class CycleCanceling { GRAPH_TYPEDEFS(typename Graph); - typedef typename LowerMap::Value Lower; typedef typename CapacityMap::Value Capacity; typedef typename CostMap::Value Cost; typedef typename SupplyMap::Value Supply; @@ -110,180 +95,200 @@ /// The type of the flow map. typedef typename Graph::template EdgeMap FlowMap; - protected: + private: - /// Map adaptor class for handling residual edge costs. - class ResCostMap : public MapBase + /// \brief Map adaptor class for handling residual edge costs. + /// + /// \ref ResidualCostMap is a map adaptor class for handling + /// residual edge costs. + class ResidualCostMap : public MapBase { private: - const CostMap &cost_map; + const CostMap &_cost_map; public: - ResCostMap(const CostMap &_cost) : cost_map(_cost) {} + ///\e + ResidualCostMap(const CostMap &cost_map) : _cost_map(cost_map) {} + ///\e Cost operator[](const ResEdge &e) const { - return ResGraph::forward(e) ? cost_map[e] : -cost_map[e]; + return ResGraph::forward(e) ? _cost_map[e] : -_cost_map[e]; } - }; //class ResCostMap + }; //class ResidualCostMap - protected: + private: - /// The directed graph the algorithm runs on. - const Graph &graph; - /// The original lower bound map. - const LowerMap *lower; - /// The modified capacity map. - CapacityEdgeMap capacity; - /// The cost map. - const CostMap &cost; - /// The modified supply map. - SupplyNodeMap supply; - bool valid_supply; + // The maximum number of iterations for the first execution of the + // Bellman-Ford algorithm. It should be at least 2. + static const int BF_FIRST_LIMIT = 2; + // The iteration limit for the Bellman-Ford algorithm is multiplied + // by BF_ALPHA in every round. + static const double BF_ALPHA = 1.5; - /// The current flow. - FlowMap flow; - /// The residual graph. - ResGraph res_graph; - /// The residual cost map. - ResCostMap res_cost; + private: - public : + // The directed graph the algorithm runs on + const Graph &_graph; + // The original lower bound map + const LowerMap *_lower; + // The modified capacity map + CapacityEdgeMap _capacity; + // The original cost map + const CostMap &_cost; + // The modified supply map + SupplyNodeMap _supply; + bool _valid_supply; + + // Edge map of the current flow + FlowMap _flow; + + // The residual graph + ResGraph _res_graph; + // The residual cost map + ResidualCostMap _res_cost; + + public: /// \brief General constructor of the class (with lower bounds). /// /// General constructor of the class (with lower bounds). /// - /// \param _graph The directed graph the algorithm runs on. - /// \param _lower The lower bounds of the edges. - /// \param _capacity The capacities (upper bounds) of the edges. - /// \param _cost The cost (length) values of the edges. - /// \param _supply The supply values of the nodes (signed). - CycleCanceling( const Graph &_graph, - const LowerMap &_lower, - const CapacityMap &_capacity, - const CostMap &_cost, - const SupplyMap &_supply ) : - graph(_graph), lower(&_lower), capacity(_graph), cost(_cost), - supply(_graph), flow(_graph, 0), - res_graph(_graph, capacity, flow), res_cost(cost) + /// \param graph The directed graph the algorithm runs on. + /// \param lower The lower bounds of the edges. + /// \param capacity The capacities (upper bounds) of the edges. + /// \param cost The cost (length) values of the edges. + /// \param supply The supply values of the nodes (signed). + CycleCanceling( const Graph &graph, + const LowerMap &lower, + const CapacityMap &capacity, + const CostMap &cost, + const SupplyMap &supply ) : + _graph(graph), _lower(&lower), _capacity(graph), _cost(cost), + _supply(graph), _flow(graph, 0), + _res_graph(graph, _capacity, _flow), _res_cost(_cost) { // Removing non-zero lower bounds - capacity = subMap(_capacity, _lower); + _capacity = subMap(capacity, lower); Supply sum = 0; - for (NodeIt n(graph); n != INVALID; ++n) { - Supply s = _supply[n]; - for (InEdgeIt e(graph, n); e != INVALID; ++e) - s += _lower[e]; - for (OutEdgeIt e(graph, n); e != INVALID; ++e) - s -= _lower[e]; - sum += (supply[n] = s); + for (NodeIt n(_graph); n != INVALID; ++n) { + Supply s = supply[n]; + for (InEdgeIt e(_graph, n); e != INVALID; ++e) + s += lower[e]; + for (OutEdgeIt e(_graph, n); e != INVALID; ++e) + s -= lower[e]; + sum += (_supply[n] = s); } - valid_supply = sum == 0; + _valid_supply = sum == 0; } /// \brief General constructor of the class (without lower bounds). /// /// General constructor of the class (without lower bounds). /// - /// \param _graph The directed graph the algorithm runs on. - /// \param _capacity The capacities (upper bounds) of the edges. - /// \param _cost The cost (length) values of the edges. - /// \param _supply The supply values of the nodes (signed). - CycleCanceling( const Graph &_graph, - const CapacityMap &_capacity, - const CostMap &_cost, - const SupplyMap &_supply ) : - graph(_graph), lower(NULL), capacity(_capacity), cost(_cost), - supply(_supply), flow(_graph, 0), - res_graph(_graph, capacity, flow), res_cost(cost) + /// \param graph The directed graph the algorithm runs on. + /// \param capacity The capacities (upper bounds) of the edges. + /// \param cost The cost (length) values of the edges. + /// \param supply The supply values of the nodes (signed). + CycleCanceling( const Graph &graph, + const CapacityMap &capacity, + const CostMap &cost, + const SupplyMap &supply ) : + _graph(graph), _lower(NULL), _capacity(capacity), _cost(cost), + _supply(supply), _flow(graph, 0), + _res_graph(graph, _capacity, _flow), _res_cost(_cost) { // Checking the sum of supply values Supply sum = 0; - for (NodeIt n(graph); n != INVALID; ++n) sum += supply[n]; - valid_supply = sum == 0; + for (NodeIt n(_graph); n != INVALID; ++n) sum += _supply[n]; + _valid_supply = sum == 0; } /// \brief Simple constructor of the class (with lower bounds). /// /// Simple constructor of the class (with lower bounds). /// - /// \param _graph The directed graph the algorithm runs on. - /// \param _lower The lower bounds of the edges. - /// \param _capacity The capacities (upper bounds) of the edges. - /// \param _cost The cost (length) values of the edges. - /// \param _s The source node. - /// \param _t The target node. - /// \param _flow_value The required amount of flow from node \c _s - /// to node \c _t (i.e. the supply of \c _s and the demand of \c _t). - CycleCanceling( const Graph &_graph, - const LowerMap &_lower, - const CapacityMap &_capacity, - const CostMap &_cost, - Node _s, Node _t, - Supply _flow_value ) : - graph(_graph), lower(&_lower), capacity(_graph), cost(_cost), - supply(_graph), flow(_graph, 0), - res_graph(_graph, capacity, flow), res_cost(cost) + /// \param graph The directed graph the algorithm runs on. + /// \param lower The lower bounds of the edges. + /// \param capacity The capacities (upper bounds) of the edges. + /// \param cost The cost (length) values of the edges. + /// \param s The source node. + /// \param t The target node. + /// \param flow_value The required amount of flow from node \c s + /// to node \c t (i.e. the supply of \c s and the demand of \c t). + CycleCanceling( const Graph &graph, + const LowerMap &lower, + const CapacityMap &capacity, + const CostMap &cost, + Node s, Node t, + Supply flow_value ) : + _graph(graph), _lower(&lower), _capacity(graph), _cost(cost), + _supply(graph), _flow(graph, 0), + _res_graph(graph, _capacity, _flow), _res_cost(_cost) { // Removing non-zero lower bounds - capacity = subMap(_capacity, _lower); - for (NodeIt n(graph); n != INVALID; ++n) { - Supply s = 0; - if (n == _s) s = _flow_value; - if (n == _t) s = -_flow_value; - for (InEdgeIt e(graph, n); e != INVALID; ++e) - s += _lower[e]; - for (OutEdgeIt e(graph, n); e != INVALID; ++e) - s -= _lower[e]; - supply[n] = s; + _capacity = subMap(capacity, lower); + for (NodeIt n(_graph); n != INVALID; ++n) { + Supply sum = 0; + if (n == s) sum = flow_value; + if (n == t) sum = -flow_value; + for (InEdgeIt e(_graph, n); e != INVALID; ++e) + sum += lower[e]; + for (OutEdgeIt e(_graph, n); e != INVALID; ++e) + sum -= lower[e]; + _supply[n] = sum; } - valid_supply = true; + _valid_supply = true; } /// \brief Simple constructor of the class (without lower bounds). /// /// Simple constructor of the class (without lower bounds). /// - /// \param _graph The directed graph the algorithm runs on. - /// \param _capacity The capacities (upper bounds) of the edges. - /// \param _cost The cost (length) values of the edges. - /// \param _s The source node. - /// \param _t The target node. - /// \param _flow_value The required amount of flow from node \c _s - /// to node \c _t (i.e. the supply of \c _s and the demand of \c _t). - CycleCanceling( const Graph &_graph, - const CapacityMap &_capacity, - const CostMap &_cost, - Node _s, Node _t, - Supply _flow_value ) : - graph(_graph), lower(NULL), capacity(_capacity), cost(_cost), - supply(_graph, 0), flow(_graph, 0), - res_graph(_graph, capacity, flow), res_cost(cost) + /// \param graph The directed graph the algorithm runs on. + /// \param capacity The capacities (upper bounds) of the edges. + /// \param cost The cost (length) values of the edges. + /// \param s The source node. + /// \param t The target node. + /// \param flow_value The required amount of flow from node \c s + /// to node \c t (i.e. the supply of \c s and the demand of \c t). + CycleCanceling( const Graph &graph, + const CapacityMap &capacity, + const CostMap &cost, + Node s, Node t, + Supply flow_value ) : + _graph(graph), _lower(NULL), _capacity(capacity), _cost(cost), + _supply(graph, 0), _flow(graph, 0), + _res_graph(graph, _capacity, _flow), _res_cost(_cost) { - supply[_s] = _flow_value; - supply[_t] = -_flow_value; - valid_supply = true; + _supply[s] = flow_value; + _supply[t] = -flow_value; + _valid_supply = true; } /// \brief Runs the algorithm. /// /// Runs the algorithm. /// + /// \param min_mean_cc Set this parameter to \c true to run the + /// "Minimum Mean Cycle-Canceling" algorithm, which is strongly + /// polynomial, but rather slower in practice. + /// /// \return \c true if a feasible flow can be found. - bool run() { - return init() && start(); + bool run(bool min_mean_cc = false) { + return init() && start(min_mean_cc); } - /// \brief Returns a const reference to the flow map. + /// \brief Returns a const reference to the edge map storing the + /// found flow. /// - /// Returns a const reference to the flow map. + /// Returns a const reference to the edge map storing the found flow. /// /// \pre \ref run() must be called before using this function. const FlowMap& flowMap() const { - return flow; + return _flow; } /// \brief Returns the total cost of the found flow. @@ -294,57 +299,54 @@ /// \pre \ref run() must be called before using this function. Cost totalCost() const { Cost c = 0; - for (EdgeIt e(graph); e != INVALID; ++e) - c += flow[e] * cost[e]; + for (EdgeIt e(_graph); e != INVALID; ++e) + c += _flow[e] * _cost[e]; return c; } - protected: + private: /// Initializes the algorithm. bool init() { - // Checking the sum of supply values - Supply sum = 0; - for (NodeIt n(graph); n != INVALID; ++n) sum += supply[n]; - if (sum != 0) return false; + if (!_valid_supply) return false; - // Finding a feasible flow + // Finding a feasible flow using Circulation Circulation< Graph, ConstMap, CapacityEdgeMap, SupplyMap > - circulation( graph, constMap((Capacity)0), capacity, - supply ); - circulation.flowMap(flow); - return circulation.run(); + circulation( _graph, constMap((Capacity)0), _capacity, + _supply ); + return circulation.flowMap(_flow).run(); } -#ifdef LIMITED_CYCLE_CANCELING - /// \brief Executes a cycle-canceling algorithm using - /// \ref Bellman-Ford algorithm with limited iteration count. + bool start(bool min_mean_cc) { + if (min_mean_cc) + return startMinMean(); + else + return start(); + } + + /// \brief Executes the algorithm using \ref BellmanFord. + /// + /// Executes the algorithm using the \ref BellmanFord + /// "Bellman-Ford" algorithm for negative cycle detection with + /// successively larger limit for the number of iterations. bool start() { - typename BellmanFord::PredMap pred(res_graph); - typename ResGraph::template NodeMap visited(res_graph); + typename BellmanFord::PredMap pred(_res_graph); + typename ResGraph::template NodeMap visited(_res_graph); std::vector cycle; - int node_num = countNodes(graph); + int node_num = countNodes(_graph); -#ifdef _DEBUG_ITER_ - int cycle_num = 0; -#endif - int length_bound = STARTING_LIMIT; + int length_bound = BF_FIRST_LIMIT; bool optimal = false; while (!optimal) { - BellmanFord bf(res_graph, res_cost); + BellmanFord bf(_res_graph, _res_cost); bf.predMap(pred); bf.init(0); int iter_num = 0; bool cycle_found = false; while (!cycle_found) { -#ifdef _NO_BACK_STEP_ - int curr_iter_num = length_bound <= node_num ? - length_bound - iter_num : node_num - iter_num; -#else int curr_iter_num = iter_num + length_bound <= node_num ? length_bound : node_num - iter_num; -#endif iter_num += curr_iter_num; int real_iter_num = curr_iter_num; for (int i = 0; i < curr_iter_num; ++i) { @@ -358,18 +360,18 @@ break; } else { // Searching for node disjoint negative cycles - for (ResNodeIt n(res_graph); n != INVALID; ++n) + for (ResNodeIt n(_res_graph); n != INVALID; ++n) visited[n] = 0; int id = 0; - for (ResNodeIt n(res_graph); n != INVALID; ++n) { + for (ResNodeIt n(_res_graph); n != INVALID; ++n) { if (visited[n] > 0) continue; visited[n] = ++id; ResNode u = pred[n] == INVALID ? - INVALID : res_graph.source(pred[n]); + INVALID : _res_graph.source(pred[n]); while (u != INVALID && visited[u] == 0) { visited[u] = id; u = pred[u] == INVALID ? - INVALID : res_graph.source(pred[u]); + INVALID : _res_graph.source(pred[u]); } if (u != INVALID && visited[u] == id) { // Finding the negative cycle @@ -377,62 +379,45 @@ cycle.clear(); ResEdge e = pred[u]; cycle.push_back(e); - Capacity d = res_graph.rescap(e); - while (res_graph.source(e) != u) { - cycle.push_back(e = pred[res_graph.source(e)]); - if (res_graph.rescap(e) < d) - d = res_graph.rescap(e); + Capacity d = _res_graph.rescap(e); + while (_res_graph.source(e) != u) { + cycle.push_back(e = pred[_res_graph.source(e)]); + if (_res_graph.rescap(e) < d) + d = _res_graph.rescap(e); } -#ifdef _DEBUG_ITER_ - ++cycle_num; -#endif + // Augmenting along the cycle - for (int i = 0; i < cycle.size(); ++i) - res_graph.augment(cycle[i], d); -#ifdef _ONLY_ONE_CYCLE_ - break; -#endif + for (int i = 0; i < int(cycle.size()); ++i) + _res_graph.augment(cycle[i], d); } } } if (!cycle_found) - length_bound = length_bound * ALPHA_MUL / ALPHA_DIV; + length_bound = int(length_bound * BF_ALPHA); } } -#ifdef _DEBUG_ITER_ - std::cout << "Limited cycle-canceling algorithm finished. " - << "Found " << cycle_num << " negative cycles." - << std::endl; -#endif - // Handling non-zero lower bounds - if (lower) { - for (EdgeIt e(graph); e != INVALID; ++e) - flow[e] += (*lower)[e]; + if (_lower) { + for (EdgeIt e(_graph); e != INVALID; ++e) + _flow[e] += (*_lower)[e]; } return true; } -#endif -#ifdef MIN_MEAN_CYCLE_CANCELING - /// \brief Executes the minimum mean cycle-canceling algorithm - /// using \ref MinMeanCycle. - bool start() { + /// \brief Executes the algorithm using \ref MinMeanCycle. + /// + /// Executes the algorithm using \ref MinMeanCycle for negative + /// cycle detection. + bool startMinMean() { typedef Path ResPath; - MinMeanCycle mmc(res_graph, res_cost); + MinMeanCycle mmc(_res_graph, _res_cost); ResPath cycle; -#ifdef _DEBUG_ITER_ - int cycle_num = 0; -#endif mmc.cyclePath(cycle).init(); if (mmc.findMinMean()) { while (mmc.cycleLength() < 0) { -#ifdef _DEBUG_ITER_ - ++cycle_num; -#endif // Finding the cycle mmc.findCycle(); @@ -440,13 +425,13 @@ // along the cycle Capacity delta = 0; for (typename ResPath::EdgeIt e(cycle); e != INVALID; ++e) { - if (delta == 0 || res_graph.rescap(e) < delta) - delta = res_graph.rescap(e); + if (delta == 0 || _res_graph.rescap(e) < delta) + delta = _res_graph.rescap(e); } // Augmenting along the cycle for (typename ResPath::EdgeIt e(cycle); e != INVALID; ++e) - res_graph.augment(e, delta); + _res_graph.augment(e, delta); // Finding the minimum cycle mean for the modified residual // graph @@ -455,20 +440,13 @@ } } -#ifdef _DEBUG_ITER_ - std::cout << "Minimum mean cycle-canceling algorithm finished. " - << "Found " << cycle_num << " negative cycles." - << std::endl; -#endif - // Handling non-zero lower bounds - if (lower) { - for (EdgeIt e(graph); e != INVALID; ++e) - flow[e] += (*lower)[e]; + if (_lower) { + for (EdgeIt e(_graph); e != INVALID; ++e) + _flow[e] += (*_lower)[e]; } return true; } -#endif }; //class CycleCanceling