From 27432d6760a9eaacd27a6700e53a0f4f598c7e0e Mon Sep 17 00:00:00 2001
From: Kilian Verhetsel <kilian.verhetsel@student.uclouvain.be>
Date: Sun, 11 Dec 2016 18:24:00 +0000
Subject: [PATCH] Use a priority queue in MWIS to find the smallest cliques,
 instead of iterating over them

An updatable priority queue is required to change the keys of elements
if the priority queue when making decisions or backtracking, hence why
std::priority_queue was not appropriate.
---
 Mesh/mwis.hpp | 100 +++++++++++++++++++++++++++++++-------------------
 1 file changed, 63 insertions(+), 37 deletions(-)

diff --git a/Mesh/mwis.hpp b/Mesh/mwis.hpp
index 0f90fa9d2d..4d90fe76c6 100644
--- a/Mesh/mwis.hpp
+++ b/Mesh/mwis.hpp
@@ -1,6 +1,6 @@
 #include "search.hpp"
 
-#include <boost/iterator/counting_iterator.hpp>
+#include <boost/heap/fibonacci_heap.hpp>
 
 #include <boost/graph/random.hpp>
 #include <boost/graph/graph_traits.hpp>
@@ -407,23 +407,48 @@ class clique_assignment;
 
 template<typename Graph, typename WeightMap>
 struct state {
+private:
+  class clique_size_comparator {
+    const state &_state;
+  public:
+    typedef std::size_t argument_type;
+    typedef std::size_t first_argument_type;
+    typedef std::size_t second_argument_type;
+    typedef bool result_type;
+
+    clique_size_comparator(state &state): _state(state) {}
+
+    bool operator()(std::size_t a, std::size_t b) const {
+      return _state.clique_sizes[a] > _state.clique_sizes[b];
+    }
+  };
+public:
   typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex;
   typedef typename boost::property_traits<WeightMap>::value_type weight;
 
   typedef typename boost::property_map<
     Graph, boost::vertex_index_t>::type index_map;
   typedef boost::iterator_property_map<
-    std::vector<std::vector<size_t>>::iterator, index_map> clique_map_type;
+    std::vector< std::vector<size_t> >::iterator, index_map> clique_map_type;
+
+  typedef boost::heap::fibonacci_heap<
+    std::size_t,
+    boost::heap::compare< clique_size_comparator > > clique_queue_type;
+  typedef typename clique_queue_type::handle_type clique_handle_type;
 
   const Graph &graph;
   WeightMap weight_map;
 
-  std::vector<std::vector<vertex>> cliques;
+  std::vector< std::vector<vertex> > cliques;
   std::vector<size_t> clique_sizes;
-   std::set<vertex> selectable;
+
+  clique_queue_type clique_queue;
+  std::vector<clique_handle_type> clique_handles;
+
+  std::set<vertex> selectable;
   std::vector<bool> assigned;
 
-  std::vector<std::vector<size_t>> clique_map_storage;
+  std::vector< std::vector<size_t> > clique_map_storage;
   clique_map_type clique_map;
 
   std::vector<vertex> solution;
@@ -437,6 +462,8 @@ struct state {
     graph(graph), weight_map(weight_map),
     cliques(clique_begin, clique_end),
     clique_sizes(cliques.size()),
+    clique_queue(clique_size_comparator(*this)),
+    clique_handles(cliques.size()),
     selectable(vertices(graph).first, vertices(graph).second),
     assigned(cliques.size(), false),
     solution_value(0)
@@ -446,8 +473,10 @@ struct state {
       clique_map = make_iterator_property_map(
         clique_map_storage.begin(), id_map);
 
-      for (size_t i = 0; i < cliques.size(); i++)
+      for (size_t i = 0; i < cliques.size(); i++) {
         clique_sizes[i] = cliques[i].size();
+        clique_handles[i] = clique_queue.push(i);
+      }
 
       size_t id = 0;
 
@@ -594,6 +623,18 @@ public:
     for (std::map<std::size_t, std::size_t>::const_iterator it = _size_delta.begin();
          it != _size_delta.end(); it++) {
       state.clique_sizes[it->first] -= it->second;
+
+      /*
+       * Priority queues (both std::priority_queue and boost::heap::*) are max
+       * priority queues by default, so even though we are using a min priority
+       * queue, "increasing" the key means moving towards the top of the queue.
+       */
+      state.clique_queue.increase(state.clique_handles[it->first]);
+    }
+
+    while (!state.clique_queue.empty() &&
+           state.assigned[state.clique_queue.top()]) {
+      state.clique_queue.pop();
     }
   }
 
@@ -614,6 +655,19 @@ public:
     for (std::map<std::size_t, std::size_t>::const_iterator it = _size_delta.begin();
          it != _size_delta.end(); it++) {
       state.clique_sizes[it->first] += it->second;
+
+      /*
+       * Priority queues (both std::priority_queue and boost::heap::*) are max
+       * priority queues by default, so even though we are using a min priority
+       * queue, "decreasing" the key means moving towards the bottom of the
+       * queue.
+       */
+      if (state.clique_sizes[it->first] == it->second) {
+        state.clique_handles[it->first] = state.clique_queue.push(it->first);
+      }
+      else {
+        state.clique_queue.decrease(state.clique_handles[it->first]);
+      }
     }
   }
 };
@@ -673,20 +727,12 @@ public:
     if (state.solution.size() == _limit)
       return;
 
-    auto clique_begin = boost::make_counting_iterator<size_t>(0);
-    auto clique_end   = boost::make_counting_iterator<size_t>(
-      state.cliques.size());
-
-    auto clique_it = std::min_element(
-      clique_begin, clique_end, [&](size_t a, size_t b) {
-        return clique_key(state, a) < clique_key(state, b);
-      });
-
-    if (clique_it == clique_end || state.assigned[*clique_it])
+    if (state.clique_queue.empty())
       return;
 
+    std::size_t id = state.clique_queue.top();
+
     std::vector<vertex> vertices;
-    size_t id = *clique_it;
 
     const std::vector<vertex> &clique = state.cliques[id];
     for (std::size_t i = 0; i < clique.size(); i++) {
@@ -714,26 +760,6 @@ public:
 
     *it++ = clique_assignment<Graph, WeightMap>(id);
   }
-
-  std::tuple<size_t, weight>
-  clique_key(state<Graph, WeightMap> &state,
-             size_t id) {
-    if (state.assigned[id]) {
-      return std::make_tuple(std::numeric_limits<size_t>::max(),
-                             weight(0));
-    }
-    else if (state.clique_sizes[id] == 0) {
-      return std::make_tuple(0, weight(0));
-    }
-    else {
-      vertex v = *std::find_if(
-        state.cliques[id].begin(), state.cliques[id].end(),
-        make_set_membership_test(state.selectable));
-
-      return std::make_tuple(state.clique_sizes[id],
-                             -get(state.weight_map, v));
-    }
-  }
 };
 
 template<typename Graph, typename WeightMap>
-- 
GitLab