Rework and fix the implementation of MinMeanCycle (#179)
authorPeter Kovacs <kpeter@inf.elte.hu>
Thu, 06 Aug 2009 20:12:43 +0200
changeset 80783ce7ce39f21
parent 806 d66ff32624e2
child 808 5795860737f5
Rework and fix the implementation of MinMeanCycle (#179)

- Fix the handling of the cycle means.
- Many implementation improvements:
- More efficient data storage for the strongly connected
components.
- Better handling of BFS queues.
- Merge consecutive BFS searches (perform two BFS searches
instead of three).

This version is about two times faster on average and an order of
magnitude faster if there are a lot of strongly connected components.
lemon/min_mean_cycle.h
     1.1 --- a/lemon/min_mean_cycle.h	Mon Aug 03 14:35:38 2009 +0200
     1.2 +++ b/lemon/min_mean_cycle.h	Thu Aug 06 20:12:43 2009 +0200
     1.3 @@ -74,26 +74,32 @@
     1.4      // The length of the arcs
     1.5      const LengthMap &_length;
     1.6  
     1.7 -    // The total length of the found cycle
     1.8 -    Value _cycle_length;
     1.9 -    // The number of arcs on the found cycle
    1.10 -    int _cycle_size;
    1.11 -    // The found cycle
    1.12 +    // Data for the found cycles
    1.13 +    bool _curr_found, _best_found;
    1.14 +    Value _curr_length, _best_length;
    1.15 +    int _curr_size, _best_size;
    1.16 +    Node _curr_node, _best_node;
    1.17 +
    1.18      Path *_cycle_path;
    1.19 +    bool _local_path;
    1.20  
    1.21 -    bool _local_path;
    1.22 -    bool _cycle_found;
    1.23 -    Node _cycle_node;
    1.24 +    // Internal data used by the algorithm
    1.25 +    typename Digraph::template NodeMap<Arc> _policy;
    1.26 +    typename Digraph::template NodeMap<bool> _reached;
    1.27 +    typename Digraph::template NodeMap<int> _level;
    1.28 +    typename Digraph::template NodeMap<double> _dist;
    1.29  
    1.30 -    typename Digraph::template NodeMap<bool> _reached;
    1.31 -    typename Digraph::template NodeMap<double> _dist;
    1.32 -    typename Digraph::template NodeMap<Arc> _policy;
    1.33 -
    1.34 +    // Data for storing the strongly connected components
    1.35 +    int _comp_num;
    1.36      typename Digraph::template NodeMap<int> _comp;
    1.37 -    int _comp_num;
    1.38 -
    1.39 -    std::vector<Node> _nodes;
    1.40 -    std::vector<Arc> _arcs;
    1.41 +    std::vector<std::vector<Node> > _comp_nodes;
    1.42 +    std::vector<Node>* _nodes;
    1.43 +    typename Digraph::template NodeMap<std::vector<Arc> > _in_arcs;
    1.44 +    
    1.45 +    // Queue used for BFS search
    1.46 +    std::vector<Node> _queue;
    1.47 +    int _qfront, _qback;
    1.48 +    
    1.49      Tolerance<double> _tol;
    1.50  
    1.51    public:
    1.52 @@ -106,9 +112,9 @@
    1.53      /// \param length The lengths (costs) of the arcs.
    1.54      MinMeanCycle( const Digraph &digraph,
    1.55                    const LengthMap &length ) :
    1.56 -      _gr(digraph), _length(length), _cycle_length(0), _cycle_size(-1),
    1.57 -      _cycle_path(NULL), _local_path(false), _reached(digraph),
    1.58 -      _dist(digraph), _policy(digraph), _comp(digraph)
    1.59 +      _gr(digraph), _length(length), _cycle_path(NULL), _local_path(false),
    1.60 +      _policy(digraph), _reached(digraph), _level(digraph), _dist(digraph),
    1.61 +      _comp(digraph), _in_arcs(digraph)
    1.62      {}
    1.63  
    1.64      /// Destructor.
    1.65 @@ -172,26 +178,28 @@
    1.66      ///
    1.67      /// \return \c true if a directed cycle exists in the digraph.
    1.68      bool findMinMean() {
    1.69 -      // Initialize
    1.70 -      _tol.epsilon(1e-6);
    1.71 -      if (!_cycle_path) {
    1.72 -        _local_path = true;
    1.73 -        _cycle_path = new Path;
    1.74 -      }
    1.75 -      _cycle_path->clear();
    1.76 -      _cycle_found = false;
    1.77 -
    1.78 +      // Initialize and find strongly connected components
    1.79 +      init();
    1.80 +      findComponents();
    1.81 +      
    1.82        // Find the minimum cycle mean in the components
    1.83 -      _comp_num = stronglyConnectedComponents(_gr, _comp);
    1.84        for (int comp = 0; comp < _comp_num; ++comp) {
    1.85 -        if (!initCurrentComponent(comp)) continue;
    1.86 +        // Find the minimum mean cycle in the current component
    1.87 +        if (!buildPolicyGraph(comp)) continue;
    1.88          while (true) {
    1.89 -          if (!findPolicyCycles()) break;
    1.90 -          contractPolicyGraph(comp);
    1.91 +          findPolicyCycle();
    1.92            if (!computeNodeDistances()) break;
    1.93          }
    1.94 +        // Update the best cycle (global minimum mean cycle)
    1.95 +        if ( !_best_found || (_curr_found &&
    1.96 +             _curr_length * _best_size < _best_length * _curr_size) ) {
    1.97 +          _best_found = true;
    1.98 +          _best_length = _curr_length;
    1.99 +          _best_size = _curr_size;
   1.100 +          _best_node = _curr_node;
   1.101 +        }
   1.102        }
   1.103 -      return _cycle_found;
   1.104 +      return _best_found;
   1.105      }
   1.106  
   1.107      /// \brief Find a minimum mean directed cycle.
   1.108 @@ -203,10 +211,10 @@
   1.109      ///
   1.110      /// \pre \ref findMinMean() must be called before using this function.
   1.111      bool findCycle() {
   1.112 -      if (!_cycle_found) return false;
   1.113 -      _cycle_path->addBack(_policy[_cycle_node]);
   1.114 -      for ( Node v = _cycle_node;
   1.115 -            (v = _gr.target(_policy[v])) != _cycle_node; ) {
   1.116 +      if (!_best_found) return false;
   1.117 +      _cycle_path->addBack(_policy[_best_node]);
   1.118 +      for ( Node v = _best_node;
   1.119 +            (v = _gr.target(_policy[v])) != _best_node; ) {
   1.120          _cycle_path->addBack(_policy[v]);
   1.121        }
   1.122        return true;
   1.123 @@ -225,36 +233,36 @@
   1.124      ///
   1.125      /// This function returns the total length of the found cycle.
   1.126      ///
   1.127 -    /// \pre \ref run() or \ref findCycle() must be called before
   1.128 +    /// \pre \ref run() or \ref findMinMean() must be called before
   1.129      /// using this function.
   1.130      Value cycleLength() const {
   1.131 -      return _cycle_length;
   1.132 +      return _best_length;
   1.133      }
   1.134  
   1.135      /// \brief Return the number of arcs on the found cycle.
   1.136      ///
   1.137      /// This function returns the number of arcs on the found cycle.
   1.138      ///
   1.139 -    /// \pre \ref run() or \ref findCycle() must be called before
   1.140 +    /// \pre \ref run() or \ref findMinMean() must be called before
   1.141      /// using this function.
   1.142      int cycleArcNum() const {
   1.143 -      return _cycle_size;
   1.144 +      return _best_size;
   1.145      }
   1.146  
   1.147      /// \brief Return the mean length of the found cycle.
   1.148      ///
   1.149      /// This function returns the mean length of the found cycle.
   1.150      ///
   1.151 -    /// \note <tt>mmc.cycleMean()</tt> is just a shortcut of the
   1.152 +    /// \note <tt>alg.cycleMean()</tt> is just a shortcut of the
   1.153      /// following code.
   1.154      /// \code
   1.155 -    ///   return double(mmc.cycleLength()) / mmc.cycleArcNum();
   1.156 +    ///   return static_cast<double>(alg.cycleLength()) / alg.cycleArcNum();
   1.157      /// \endcode
   1.158      ///
   1.159      /// \pre \ref run() or \ref findMinMean() must be called before
   1.160      /// using this function.
   1.161      double cycleMean() const {
   1.162 -      return double(_cycle_length) / _cycle_size;
   1.163 +      return static_cast<double>(_best_length) / _best_size;
   1.164      }
   1.165  
   1.166      /// \brief Return the found cycle.
   1.167 @@ -274,153 +282,166 @@
   1.168  
   1.169    private:
   1.170  
   1.171 -    // Initialize the internal data structures for the current strongly
   1.172 -    // connected component and create the policy graph.
   1.173 -    // The policy graph can be represented by the _policy map because
   1.174 -    // the out-degree of every node is 1.
   1.175 -    bool initCurrentComponent(int comp) {
   1.176 -      // Find the nodes of the current component
   1.177 -      _nodes.clear();
   1.178 -      for (NodeIt n(_gr); n != INVALID; ++n) {
   1.179 -        if (_comp[n] == comp) _nodes.push_back(n);
   1.180 +    // Initialize
   1.181 +    void init() {
   1.182 +      _tol.epsilon(1e-6);
   1.183 +      if (!_cycle_path) {
   1.184 +        _local_path = true;
   1.185 +        _cycle_path = new Path;
   1.186        }
   1.187 -      if (_nodes.size() <= 1) return false;
   1.188 -      // Find the arcs of the current component
   1.189 -      _arcs.clear();
   1.190 -      for (ArcIt e(_gr); e != INVALID; ++e) {
   1.191 -        if ( _comp[_gr.source(e)] == comp &&
   1.192 -             _comp[_gr.target(e)] == comp )
   1.193 -          _arcs.push_back(e);
   1.194 +      _queue.resize(countNodes(_gr));
   1.195 +      _best_found = false;
   1.196 +      _best_length = 0;
   1.197 +      _best_size = 1;
   1.198 +      _cycle_path->clear();
   1.199 +    }
   1.200 +    
   1.201 +    // Find strongly connected components and initialize _comp_nodes
   1.202 +    // and _in_arcs
   1.203 +    void findComponents() {
   1.204 +      _comp_num = stronglyConnectedComponents(_gr, _comp);
   1.205 +      _comp_nodes.resize(_comp_num);
   1.206 +      if (_comp_num == 1) {
   1.207 +        _comp_nodes[0].clear();
   1.208 +        for (NodeIt n(_gr); n != INVALID; ++n) {
   1.209 +          _comp_nodes[0].push_back(n);
   1.210 +          _in_arcs[n].clear();
   1.211 +          for (InArcIt a(_gr, n); a != INVALID; ++a) {
   1.212 +            _in_arcs[n].push_back(a);
   1.213 +          }
   1.214 +        }
   1.215 +      } else {
   1.216 +        for (int i = 0; i < _comp_num; ++i)
   1.217 +          _comp_nodes[i].clear();
   1.218 +        for (NodeIt n(_gr); n != INVALID; ++n) {
   1.219 +          int k = _comp[n];
   1.220 +          _comp_nodes[k].push_back(n);
   1.221 +          _in_arcs[n].clear();
   1.222 +          for (InArcIt a(_gr, n); a != INVALID; ++a) {
   1.223 +            if (_comp[_gr.source(a)] == k) _in_arcs[n].push_back(a);
   1.224 +          }
   1.225 +        }
   1.226        }
   1.227 -      // Initialize _reached, _dist, _policy maps
   1.228 -      for (int i = 0; i < int(_nodes.size()); ++i) {
   1.229 -        _reached[_nodes[i]] = false;
   1.230 -        _policy[_nodes[i]] = INVALID;
   1.231 +    }
   1.232 +
   1.233 +    // Build the policy graph in the given strongly connected component
   1.234 +    // (the out-degree of every node is 1)
   1.235 +    bool buildPolicyGraph(int comp) {
   1.236 +      _nodes = &(_comp_nodes[comp]);
   1.237 +      if (_nodes->size() < 1 ||
   1.238 +          (_nodes->size() == 1 && _in_arcs[(*_nodes)[0]].size() == 0)) {
   1.239 +        return false;
   1.240        }
   1.241 -      Node u; Arc e;
   1.242 -      for (int j = 0; j < int(_arcs.size()); ++j) {
   1.243 -        e = _arcs[j];
   1.244 -        u = _gr.source(e);
   1.245 -        if (!_reached[u] || _length[e] < _dist[u]) {
   1.246 -          _dist[u] = _length[e];
   1.247 -          _policy[u] = e;
   1.248 -          _reached[u] = true;
   1.249 +      for (int i = 0; i < int(_nodes->size()); ++i) {
   1.250 +        _dist[(*_nodes)[i]] = std::numeric_limits<double>::max();
   1.251 +      }
   1.252 +      Node u, v;
   1.253 +      Arc e;
   1.254 +      for (int i = 0; i < int(_nodes->size()); ++i) {
   1.255 +        v = (*_nodes)[i];
   1.256 +        for (int j = 0; j < int(_in_arcs[v].size()); ++j) {
   1.257 +          e = _in_arcs[v][j];
   1.258 +          u = _gr.source(e);
   1.259 +          if (_length[e] < _dist[u]) {
   1.260 +            _dist[u] = _length[e];
   1.261 +            _policy[u] = e;
   1.262 +          }
   1.263          }
   1.264        }
   1.265        return true;
   1.266      }
   1.267  
   1.268 -    // Find all cycles in the policy graph.
   1.269 -    // Set _cycle_found to true if a cycle is found and set
   1.270 -    // _cycle_length, _cycle_size, _cycle_node to represent the minimum
   1.271 -    // mean cycle in the policy graph.
   1.272 -    bool findPolicyCycles() {
   1.273 -      typename Digraph::template NodeMap<int> level(_gr, -1);
   1.274 -      bool curr_cycle_found = false;
   1.275 +    // Find the minimum mean cycle in the policy graph
   1.276 +    void findPolicyCycle() {
   1.277 +      for (int i = 0; i < int(_nodes->size()); ++i) {
   1.278 +        _level[(*_nodes)[i]] = -1;
   1.279 +      }
   1.280        Value clength;
   1.281        int csize;
   1.282 -      int path_cnt = 0;
   1.283        Node u, v;
   1.284 -      // Searching for cycles
   1.285 -      for (int i = 0; i < int(_nodes.size()); ++i) {
   1.286 -        if (level[_nodes[i]] < 0) {
   1.287 -          u = _nodes[i];
   1.288 -          level[u] = path_cnt;
   1.289 -          while (level[u = _gr.target(_policy[u])] < 0)
   1.290 -            level[u] = path_cnt;
   1.291 -          if (level[u] == path_cnt) {
   1.292 -            // A cycle is found
   1.293 -            curr_cycle_found = true;
   1.294 -            clength = _length[_policy[u]];
   1.295 -            csize = 1;
   1.296 -            for (v = u; (v = _gr.target(_policy[v])) != u; ) {
   1.297 -              clength += _length[_policy[v]];
   1.298 -              ++csize;
   1.299 -            }
   1.300 -            if ( !_cycle_found ||
   1.301 -                 clength * _cycle_size < _cycle_length * csize ) {
   1.302 -              _cycle_found = true;
   1.303 -              _cycle_length = clength;
   1.304 -              _cycle_size = csize;
   1.305 -              _cycle_node = u;
   1.306 -            }
   1.307 +      _curr_found = false;
   1.308 +      for (int i = 0; i < int(_nodes->size()); ++i) {
   1.309 +        u = (*_nodes)[i];
   1.310 +        if (_level[u] >= 0) continue;
   1.311 +        for (; _level[u] < 0; u = _gr.target(_policy[u])) {
   1.312 +          _level[u] = i;
   1.313 +        }
   1.314 +        if (_level[u] == i) {
   1.315 +          // A cycle is found
   1.316 +          clength = _length[_policy[u]];
   1.317 +          csize = 1;
   1.318 +          for (v = u; (v = _gr.target(_policy[v])) != u; ) {
   1.319 +            clength += _length[_policy[v]];
   1.320 +            ++csize;
   1.321            }
   1.322 -          ++path_cnt;
   1.323 -        }
   1.324 -      }
   1.325 -      return curr_cycle_found;
   1.326 -    }
   1.327 -
   1.328 -    // Contract the policy graph to be connected by cutting all cycles
   1.329 -    // except for the main cycle (i.e. the minimum mean cycle).
   1.330 -    void contractPolicyGraph(int comp) {
   1.331 -      // Find the component of the main cycle using reverse BFS search
   1.332 -      typename Digraph::template NodeMap<int> found(_gr, false);
   1.333 -      std::deque<Node> queue;
   1.334 -      queue.push_back(_cycle_node);
   1.335 -      found[_cycle_node] = true;
   1.336 -      Node u, v;
   1.337 -      while (!queue.empty()) {
   1.338 -        v = queue.front(); queue.pop_front();
   1.339 -        for (InArcIt e(_gr, v); e != INVALID; ++e) {
   1.340 -          u = _gr.source(e);
   1.341 -          if (_policy[u] == e && !found[u]) {
   1.342 -            found[u] = true;
   1.343 -            queue.push_back(u);
   1.344 -          }
   1.345 -        }
   1.346 -      }
   1.347 -      // Connect all other nodes to this component using reverse BFS search
   1.348 -      queue.clear();
   1.349 -      for (int i = 0; i < int(_nodes.size()); ++i)
   1.350 -        if (found[_nodes[i]]) queue.push_back(_nodes[i]);
   1.351 -      int found_cnt = queue.size();
   1.352 -      while (found_cnt < int(_nodes.size())) {
   1.353 -        v = queue.front(); queue.pop_front();
   1.354 -        for (InArcIt e(_gr, v); e != INVALID; ++e) {
   1.355 -          u = _gr.source(e);
   1.356 -          if (_comp[u] == comp && !found[u]) {
   1.357 -            found[u] = true;
   1.358 -            ++found_cnt;
   1.359 -            _policy[u] = e;
   1.360 -            queue.push_back(u);
   1.361 +          if ( !_curr_found ||
   1.362 +               (clength * _curr_size < _curr_length * csize) ) {
   1.363 +            _curr_found = true;
   1.364 +            _curr_length = clength;
   1.365 +            _curr_size = csize;
   1.366 +            _curr_node = u;
   1.367            }
   1.368          }
   1.369        }
   1.370      }
   1.371  
   1.372 -    // Compute node distances in the policy graph and update the
   1.373 -    // policy graph if the node distances can be improved.
   1.374 +    // Contract the policy graph and compute node distances
   1.375      bool computeNodeDistances() {
   1.376 -      // Compute node distances using reverse BFS search
   1.377 -      double cycle_mean = double(_cycle_length) / _cycle_size;
   1.378 -      typename Digraph::template NodeMap<int> found(_gr, false);
   1.379 -      std::deque<Node> queue;
   1.380 -      queue.push_back(_cycle_node);
   1.381 -      found[_cycle_node] = true;
   1.382 -      _dist[_cycle_node] = 0;
   1.383 +      // Find the component of the main cycle and compute node distances
   1.384 +      // using reverse BFS
   1.385 +      for (int i = 0; i < int(_nodes->size()); ++i) {
   1.386 +        _reached[(*_nodes)[i]] = false;
   1.387 +      }
   1.388 +      double curr_mean = double(_curr_length) / _curr_size;
   1.389 +      _qfront = _qback = 0;
   1.390 +      _queue[0] = _curr_node;
   1.391 +      _reached[_curr_node] = true;
   1.392 +      _dist[_curr_node] = 0;
   1.393        Node u, v;
   1.394 -      while (!queue.empty()) {
   1.395 -        v = queue.front(); queue.pop_front();
   1.396 -        for (InArcIt e(_gr, v); e != INVALID; ++e) {
   1.397 +      Arc e;
   1.398 +      while (_qfront <= _qback) {
   1.399 +        v = _queue[_qfront++];
   1.400 +        for (int j = 0; j < int(_in_arcs[v].size()); ++j) {
   1.401 +          e = _in_arcs[v][j];
   1.402            u = _gr.source(e);
   1.403 -          if (_policy[u] == e && !found[u]) {
   1.404 -            found[u] = true;
   1.405 -            _dist[u] = _dist[v] + _length[e] - cycle_mean;
   1.406 -            queue.push_back(u);
   1.407 +          if (_policy[u] == e && !_reached[u]) {
   1.408 +            _reached[u] = true;
   1.409 +            _dist[u] = _dist[v] + _length[e] - curr_mean;
   1.410 +            _queue[++_qback] = u;
   1.411            }
   1.412          }
   1.413        }
   1.414 -      // Improving node distances
   1.415 +
   1.416 +      // Connect all other nodes to this component and compute node
   1.417 +      // distances using reverse BFS
   1.418 +      _qfront = 0;
   1.419 +      while (_qback < int(_nodes->size())-1) {
   1.420 +        v = _queue[_qfront++];
   1.421 +        for (int j = 0; j < int(_in_arcs[v].size()); ++j) {
   1.422 +          e = _in_arcs[v][j];
   1.423 +          u = _gr.source(e);
   1.424 +          if (!_reached[u]) {
   1.425 +            _reached[u] = true;
   1.426 +            _policy[u] = e;
   1.427 +            _dist[u] = _dist[v] + _length[e] - curr_mean;
   1.428 +            _queue[++_qback] = u;
   1.429 +          }
   1.430 +        }
   1.431 +      }
   1.432 +
   1.433 +      // Improve node distances
   1.434        bool improved = false;
   1.435 -      for (int j = 0; j < int(_arcs.size()); ++j) {
   1.436 -        Arc e = _arcs[j];
   1.437 -        u = _gr.source(e); v = _gr.target(e);
   1.438 -        double delta = _dist[v] + _length[e] - cycle_mean;
   1.439 -        if (_tol.less(delta, _dist[u])) {
   1.440 -          improved = true;
   1.441 -          _dist[u] = delta;
   1.442 -          _policy[u] = e;
   1.443 +      for (int i = 0; i < int(_nodes->size()); ++i) {
   1.444 +        v = (*_nodes)[i];
   1.445 +        for (int j = 0; j < int(_in_arcs[v].size()); ++j) {
   1.446 +          e = _in_arcs[v][j];
   1.447 +          u = _gr.source(e);
   1.448 +          double delta = _dist[v] + _length[e] - curr_mean;
   1.449 +          if (_tol.less(delta, _dist[u])) {
   1.450 +            _dist[u] = delta;
   1.451 +            _policy[u] = e;
   1.452 +            improved = true;
   1.453 +          }
   1.454          }
   1.455        }
   1.456        return improved;