From 6e6c4f1a7cc822b787bc4a742a3cd56238479f43 Mon Sep 17 00:00:00 2001 From: Kilian Verhetsel <kilian.verhetsel@student.uclouvain.be> Date: Tue, 29 Nov 2016 08:36:35 +0000 Subject: [PATCH] Added a solver for the Maximum Weight Independent Set problem --- Mesh/mwis.hpp | 1037 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1037 insertions(+) create mode 100644 Mesh/mwis.hpp diff --git a/Mesh/mwis.hpp b/Mesh/mwis.hpp new file mode 100644 index 0000000000..16277dbf89 --- /dev/null +++ b/Mesh/mwis.hpp @@ -0,0 +1,1037 @@ +#include "search.hpp" + +#include <boost/iterator/counting_iterator.hpp> + +#include <boost/graph/random.hpp> +#include <boost/graph/graph_traits.hpp> +#include <boost/graph/properties.hpp> +#include <boost/graph/graph_concepts.hpp> +#include <boost/graph/adjacency_list.hpp> + +#include <random> +#include <vector> +#include <functional> +#include <limits> +#include <numeric> +#include <algorithm> +#include <iostream> + +namespace mwis { + +/** + * A functor to calculate an upper bound to the maximum weight independent set + * in a graph. + * + * A Lagrangian relaxation of the problem is used: + * + * UB = min_{λ >= 0} max_{x_i \in {0, 1}} + * (\sum_{i \in V} x_iw_i) - + * \sum_{c \in C} λ_c(\sum_{i \in c} - 1) + * + * Where C is a set of cliques within the graph, such that if there is an edge + * between two nodes in the graph, a clique that contains both nodes is present + * in C. + * + * The set {{u, v} | (u, v) \in E} can always be used as the set of + * cliques. However, using larger cliques can lead to better upper bounds. + */ +template<typename Graph, typename WeightMap> +class lagrangian_bound { + 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<bool>::iterator, index_map> selected_map_type; + typedef boost::iterator_property_map< + std::vector<std::vector<size_t>>::iterator, index_map> clique_map_type; + typedef boost::iterator_property_map< + typename std::vector<weight>::iterator, index_map> weight_map_type; + + const Graph &_graph; + WeightMap _weight; + size_t _max_size; + + std::vector<std::vector<vertex>> _clique_contents; + + std::vector<weight> _lambda, _gradient; + + std::vector<std::vector<size_t>> _clique_storage; + std::vector<bool> _selected_storage; + std::vector<weight> _effective_weight_storage; + + clique_map_type _cliques; + selected_map_type _selected; + weight_map_type _effective_weight; +public: + typedef weight result_type; + + template<typename CliqueIterator> + lagrangian_bound(const Graph &graph, WeightMap weight_map, + CliqueIterator clique_begin, + CliqueIterator clique_end, + size_t max_size = std::numeric_limits<size_t>::max()): + _graph(graph), _weight(weight_map), _max_size(max_size) { + auto vs = vertices(graph); + size_t vertex_count = vs.second - vs.first; + + _clique_storage.resize(vertex_count); + _selected_storage.resize(vertex_count); + _effective_weight_storage.resize(vertex_count); + + index_map ids = boost::get(boost::vertex_index, graph); + + _cliques = make_iterator_property_map(_clique_storage.begin(), ids); + _selected = make_iterator_property_map(_selected_storage.begin(), ids); + _effective_weight = make_iterator_property_map( + _effective_weight_storage.begin(), ids); + + size_t i = 0; + for (CliqueIterator it = clique_begin; it != clique_end; it++, i++) { + std::vector<vertex> to_add; + for (const vertex &v : *it) { + to_add.push_back(v); + get(_cliques, v).push_back(i); + } + _clique_contents.push_back(to_add); + } + + _lambda.resize(_clique_contents.size()); + _gradient.resize(_clique_contents.size()); + + for (weight &v : _lambda) v = weight(0); + } + + /** + * Calculates an upper bound. This bound is always admissible, but may not be + * optimal. + * + * Vertices in [solution_begin, solution_end) are all forced to be part of the + * solution. + * + * Vertices in [rest_begin, rest_end) can be selected in any way to maximize + * the objective functions. + * + * Vertices in neither range are assumed not to be part of the solution. + */ + template<typename SolutionIterator, + typename RestIterator> + weight operator()(SolutionIterator solution_begin, + SolutionIterator solution_end, + RestIterator rest_begin, + RestIterator rest_end) { + weight best(std::numeric_limits<weight>::max()); + + for (size_t i = 0; i < _selected_storage.size(); i++) + _selected_storage[i] = false; + + for (SolutionIterator it = solution_begin; it != solution_end; it++) + put(_selected, *it, true); + + /** + * We can get away with very few steps, because the starting point is the + * optimum that was found the last time the algorithm was used, and the + * input does not usually change much between two calls (therefore, one + * would expect the optima to be close to each other). + */ + static const size_t n = 2; + for (size_t i = 0; i < n; i++) { + compute_weights(); + if (_max_size == std::numeric_limits<size_t>::max()) + select(rest_begin, rest_end); + else + select(rest_begin, rest_end, + _max_size - (size_t)std::distance(solution_begin, + solution_end)); + best = std::min(best, evaluate()); + + if (i != n - 1) { + compute_gradient(); + follow_gradient(weight(-1e-4 / 1)); + } + } + + return best; + } + + /** + * For debugging purposes, displays any inconsistencies between the graph and + * the sequence of cliques. + * + * (u, v) \in E <=> there is a clique which contains both u and v. + */ + void check_consistency() { + auto es = boost::edges(_graph); + for (auto eit = es.first; eit != es.second; eit++) { + bool found = false; + + vertex v = boost::source(*eit, _graph); + vertex w = boost::target(*eit, _graph); + + const std::vector<size_t> &cliques_v = get(_cliques, v); + const std::vector<size_t> &cliques_w = get(_cliques, w); + + for (size_t clique : cliques_v) { + if (std::find(cliques_w.begin(), cliques_w.end(), clique) != + cliques_w.end()) { + found = true; + break; + } + } + + if (!found) { + std::cout << "no shared clique between connected vertices: " << + v << " and " << w << "\n"; + } + } + + for (const std::vector<vertex> &clique : _clique_contents) { + for (vertex v : clique) { + for (vertex w : clique) { + if (v == w) continue; + + if (!edge(v, w, _graph).second) { + std::cout << "no edge between " << v << " and " << + w << "\n"; + } + } + } + } + } +private: + /** + * Calculates the gradient according to which nodes are currently selected. + */ + void compute_gradient() { + std::transform(_clique_contents.begin(), _clique_contents.end(), + _gradient.begin(), + [&](const std::vector<vertex> &vertices) { + weight sum{1}; + for (const vertex &v : vertices) { + sum -= weight(get(_selected, v) ? 1 : 0); + } + + return sum; + }); + } + + /** + * Calculates the effective weight of each node (w_i - \sum_{c, x \in c} λ_c). + */ + void compute_weights() { + for (size_t i = 0; i < _effective_weight_storage.size(); i++) { + weight result(_weight[i]); + + for (size_t clique : _clique_storage[i]) + result -= _lambda[clique]; + + _effective_weight_storage[i] = result; + } + } + + /** + * Update the Lagrange multipliers in accordance with the last computed + * gradient, multiplied by scale. + */ + void follow_gradient(weight scale) { + std::transform(_lambda.begin(), _lambda.end(), _gradient.begin(), + _lambda.begin(), [&](const weight &cur, const weight &grad) { + return std::max(weight(0), cur + scale * grad); + }); + } + + /** + * Iterates over the given range, and selects the subset of its nodes which have + * a non-negative weight, so as to maximize the objective function. + */ + template<typename Iterator> + void select(Iterator begin, Iterator end) { + for (Iterator it = begin; it != end; it++) { + vertex v = *it; + put(_selected, v, get(_effective_weight, v) >= weight(0)); + } + } + + /** + * Iterates over the given range, and selects subset of its nodes which have + * a non-negative weight, selecting at most n nodes. + */ + template<typename Iterator> + void select(Iterator begin, Iterator end, size_t n) { + n = std::min((size_t)std::distance(begin, end), n); + std::vector<vertex> largest(n); + + std::partial_sort_copy( + begin, end, + largest.begin(), largest.end(), + [&](vertex a ,vertex b) { + return get(_effective_weight, a) > get(_effective_weight, b); + }); + + for (Iterator it = begin; it != end; it++) { + vertex v = *it; + put(_selected, v, false); + } + + for (vertex v : largest) { + if (get(_effective_weight, v) <= weight(0)) + break; + put(_selected, v, true); + } + } + + + /** + * Calculates the upper bound for the current values of the Lagrange + * multipliers and the current selection of nodes. + */ + weight evaluate() { + weight result(std::accumulate(_lambda.begin(), _lambda.end(), + weight(0), std::plus<weight>())); + + for (size_t i = 0; i < _selected_storage.size(); i++) { + if (_selected_storage[i]) + result += _effective_weight_storage[i]; + } + + return result; + } +}; + +template<typename T> +struct max_bound { + typedef T result_type; + + template<typename SolutionIter, typename RestIter> + T operator()(SolutionIter solution_begin, SolutionIter solution_end, + RestIter rest_begin, RestIter rest_end) { + return std::numeric_limits<T>::max(); + } +}; + +template<typename Graph, typename WeightMap> +class state_change; + +template<typename Graph, typename WeightMap> +class clique_assignment; + +template<typename Graph, typename WeightMap> +struct state { + 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; + + const Graph &graph; + WeightMap weight_map; + + std::vector<std::vector<vertex>> cliques; + std::vector<size_t> clique_sizes; + std::set<vertex> invalid; + std::set<vertex> selectable; + std::vector<bool> assigned; + + std::vector<std::vector<size_t>> clique_map_storage; + clique_map_type clique_map; + + std::vector<vertex> solution; + weight solution_value; + + template<typename CliqueIterator> + state(const Graph &graph, + WeightMap weight_map, + CliqueIterator clique_begin, + CliqueIterator clique_end): + graph(graph), weight_map(weight_map), + cliques(clique_begin, clique_end), + clique_sizes(cliques.size()), + selectable(vertices(graph).first, vertices(graph).second), + assigned(cliques.size(), false), + solution_value(0) + { + clique_map_storage.resize(num_vertices(graph)); + index_map id_map = get(boost::vertex_index, graph); + clique_map = make_iterator_property_map( + clique_map_storage.begin(), id_map); + + for (size_t i = 0; i < cliques.size(); i++) + clique_sizes[i] = cliques[i].size(); + + size_t id = 0; + for (std::vector<vertex> &clique : cliques) { + for (vertex v : clique) + get(clique_map, v).push_back(id); + + std::sort(clique.begin(), clique.end(), [&](vertex a, vertex b) { + return get(weight_map, a) > get(weight_map, b); + }); + + id++; + } + } +}; + +template<typename Graph, typename WeightMap> +class clique_assignment { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename boost::property_traits<WeightMap>::value_type weight; + + size_t _id; + vertex _vertex; + bool _selected; + +public: + typedef state_change<Graph, WeightMap> result_type; + typedef state<Graph, WeightMap> argument_type; + + clique_assignment(size_t id): + _id(id), _selected(false) + {} + + clique_assignment(size_t id, vertex vertex_id): + _id(id), _vertex(vertex_id), _selected(true) + {} + + state_change<Graph, WeightMap> operator()( + const state<Graph, WeightMap> &state) const { + std::vector<vertex> selected_vertex, removed; + std::vector<size_t> assigned; + + weight new_weight(state.solution_value); + + if (_selected) { + selected_vertex.push_back(_vertex); + new_weight += get(state.weight_map, _vertex); + + auto edges = out_edges(_vertex, state.graph); + for (auto eit = edges.first; eit != edges.second; eit++) { + const vertex &other = target(*eit, state.graph); + if (state.invalid.find(other) == state.invalid.end()) { + removed.push_back(other); + } + } + + for (size_t i : get(state.clique_map, _vertex)) { + if (!state.assigned[i]) assigned.push_back(i); + } + } + else { + for (vertex v : state.cliques[_id]) { + if (state.invalid.find(v) == state.invalid.end()) + removed.push_back(v); + } + + assigned.push_back(_id); + } + + std::map<size_t, size_t> size_delta; + for (vertex v : removed) { + for (size_t clique : get(state.clique_map, v)) { + size_delta[clique]++; + if (size_delta[clique] == state.clique_sizes[clique]) { + if (!state.assigned[clique]) + assigned.push_back(clique); + } + } + } + + return state_change<Graph, WeightMap>( + assigned, + size_delta, + selected_vertex, removed, + state.solution_value, new_weight); + } +}; + +template<typename Graph, typename WeightMap> +class state_change { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename boost::property_traits<WeightMap>::value_type weight; + + std::vector<size_t> _assigned_id; + std::map<size_t, size_t> _size_delta; + std::vector<vertex> _selected; + std::vector<vertex> _invalidated; + weight _old_weight, _new_weight; +public: + state_change() {} + + state_change(const std::vector<size_t> &assigned_id, + const std::map<size_t, size_t> &size_delta, + const std::vector<vertex> &selected, + const std::vector<vertex> &invalidated, + weight old_weight, weight new_weight): + _assigned_id(assigned_id), + _size_delta(size_delta), + _selected(selected), _invalidated(invalidated), + _old_weight(old_weight), _new_weight(new_weight) + {} + + void apply(state<Graph, WeightMap> &state) const { + for (size_t i : _assigned_id) + state.assigned[i] = true; + + size_t old_size = state.solution.size(); + state.solution.resize(state.solution.size() + _selected.size()); + std::copy(_selected.begin(), _selected.end(), + state.solution.begin() + old_size); + + for (vertex v : _selected) + state.selectable.erase(v); + + state.solution_value = _new_weight; + + for (vertex v : _invalidated) { + state.invalid.insert(v); + state.selectable.erase(v); + } + + for (auto pair : _size_delta) + state.clique_sizes[pair.first] -= pair.second; + } + + void reverse(state<Graph, WeightMap> &state) const { + for (size_t i : _assigned_id) + state.assigned[i] = false; + + state.solution.resize(state.solution.size() - _selected.size()); + + for (vertex v : _selected) + state.selectable.insert(v); + + state.solution_value = _old_weight; + + for (vertex v : _invalidated) { + state.invalid.erase(v); + state.selectable.insert(v); + } + + for (auto pair : _size_delta) + state.clique_sizes[pair.first] += pair.second; + } +}; + +template<typename Graph, typename WeightMap> +struct visit_state { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename boost::property_traits<WeightMap>::value_type weight; + + bool quiet; + + weight best_value; + std::vector<vertex> best_solution; + + visit_state(bool quiet = true): quiet(quiet), best_value(0) {} + + void operator()(state<Graph, WeightMap> &state) { + if (best_value < state.solution_value) { + if (!quiet) { + std::cout << "new best: " << state.solution_value << + " (" << state.solution.size() << ")\n"; + } + + best_value = state.solution_value; + best_solution = state.solution; + } + } +}; + +template<typename Graph, typename WeightMap, typename UpperBound> +class successor { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename std::vector<vertex>::iterator viterator; + typedef typename boost::property_traits<WeightMap>::value_type weight; + + UpperBound &_bound; + + size_t _visited; + weight *_best_weight; + + size_t _limit; +public: + typedef clique_assignment<Graph, WeightMap> action_type; + + successor(UpperBound &bound, weight &best_weight, + size_t limit = std::numeric_limits<size_t>::max()): + _bound(bound), _visited(0), _best_weight(&best_weight), _limit(limit) + {} + + template<typename OutputIterator> + void operator()(state<Graph, WeightMap> &state, OutputIterator it) { + if (_visited > 1000 * 1000 * 10) + return; + else + _visited++; + + 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]) + return; + + std::vector<vertex> vertices; + size_t id = *clique_it; + for (vertex v : state.cliques[id]) { + if (state.invalid.find(v) == state.invalid.end()) { + vertices.push_back(v); + } + } + + if (vertices.size() > 0) { + weight max(_bound(state.solution.begin(), state.solution.end(), + state.selectable.begin(), state.selectable.end())); + if (max <= *_best_weight) + return; + } + + std::sort(vertices.begin(), vertices.end(), + [&](vertex a, vertex b) { + return vertex_regret(state, a) < vertex_regret(state, b); + }); + + for (vertex v : vertices) + *it++ = clique_assignment<Graph, WeightMap>(id, v); + + *it++ = clique_assignment<Graph, WeightMap>(id); + } + + std::tuple<int, size_t, weight> + clique_key(state<Graph, WeightMap> &state, + size_t id) { + if (state.assigned[id]) { + return std::make_tuple(1, + std::numeric_limits<size_t>::max(), + weight(0)); + } + else if (state.clique_sizes[id] == 0) { + return std::make_tuple(0, 0, weight(0)); + } + else { + vertex v = *std::find_if( + state.cliques[id].begin(), state.cliques[id].end(), [&](vertex v) { + return state.invalid.find(v) == state.invalid.end(); + }); + + return std::make_tuple(id < 1361 ? 0 : 1, + state.clique_sizes[id], + -get(state.weight_map, v)); + } + } + + weight vertex_regret(const state<Graph, WeightMap> &state, vertex v) { + weight result(-get(state.weight_map, v)); + + auto es = out_edges(v, state.graph); + for (auto eit = es.first; eit != es.second; eit++) { + vertex u = target(*eit, state.graph); + if (state.invalid.find(u) != state.invalid.end()) + result += get(state.weight_map, u); + } + + return result; + } +}; + +template<typename Graph, typename WeightMap> +class evaluator { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename boost::property_traits<WeightMap>::value_type weight; + + std::vector<std::vector<vertex>> _cliques; + size_t _limit; +public: + typedef weight result_type; + typedef state<Graph, WeightMap> argument_type; + + template<typename Iterator> + evaluator(Iterator begin, Iterator end, size_t limit): + _cliques(begin, end), _limit(limit) + {} + + weight operator()(state<Graph, WeightMap> &cur) const { + visit_state<Graph, WeightMap> visitor; + + // max_bound<weight> bound; + lagrangian_bound<Graph, WeightMap> + bound(cur.graph, cur.weight_map, _cliques.begin(), _cliques.end(), + cur.solution.size() + _limit); + successor<Graph, WeightMap, decltype(bound)> + successor(bound, visitor.best_value, cur.solution.size() + _limit); + + visitor(cur); + search::depth_limited_search(cur, visitor, successor, _limit); + + return visitor.best_value; + } +}; + +template<typename Graph, typename WeightMap> +struct lns_state { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + + const Graph &graph; + WeightMap weight_map; + + std::vector<vertex> solution; + + lns_state(const Graph &graph, WeightMap weight_map, + const std::vector<vertex> &solution): + graph(graph), weight_map(weight_map), solution(solution) + {} +}; + +template<typename Graph> +struct lns_fragment { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + + std::set<vertex> vertices; + + lns_fragment(const std::set<vertex> &vertices): + vertices(vertices) + {} +}; + +/** + * Behaves like a queue, but elements can only be inserted into the queue a + * limited amount of times. + */ +template<typename T> +class limited_queue { + std::queue<T> _queue; + size_t _size, _max_size; +public: + typedef T value_type; + typedef typename std::queue<T>::size_type size_type; + + limited_queue(size_t max_size): + _size(0), _max_size(max_size) {} + + void push(const T &t) { + if (_size != _max_size) { + _size++; + _queue.push(t); + } + } + + void pop() { _queue.pop(); } + + T &top() { return _queue.front(); } + const T &top() const { return _queue.front(); } + + size_type size() const { return _queue.size(); } + + bool empty() { return _queue.empty(); } +}; + +/** + * A visitor for graph traversal algoritms which records the target of every + * visited edge into a set. + */ +template<typename Graph, typename Tag> +class set_recorder : public boost::base_visitor<set_recorder<Graph, Tag>> { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename boost::graph_traits<Graph>::edge_descriptor edge; + + std::set<vertex> &_set; +public: + typedef Tag event_filter; + + set_recorder(std::set<vertex> &set): _set(set) {} + + void operator()(edge e, const Graph &g) { + _set.insert(boost::target(e, g)); + } +}; + +template<typename Graph, typename WeightMap> +lns_fragment<Graph> fragment_selector(const lns_state<Graph, WeightMap> &state) { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + + std::random_device dev; + std::mt19937 gen(dev()); + + vertex v = boost::random_vertex(state.graph, gen); + + size_t neighborhood_size = std::uniform_int_distribution<size_t>(50, 80)(gen); + + std::set<vertex> set; + set.insert(v); + set_recorder<Graph, boost::on_black_target> recorder(set); + + limited_queue<vertex> queue(neighborhood_size); + + boost::breadth_first_search( + state.graph, v, + boost::buffer(queue). + visitor(boost::make_bfs_visitor(recorder))); + + return lns_fragment<Graph>(set); +} + +template<typename Graph> +class lns_assignment { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + std::vector<vertex> _vertices; +public: + lns_assignment(const std::vector<vertex> &vertices): _vertices(vertices) {} + + template<typename WeightMap> + void operator()(lns_state<Graph, WeightMap> &state, + const lns_fragment<Graph> &fragment) const { + static int i = 0; + std::cout << "\rsize: " << state.solution.size() << " " << i++; + std::cout.flush(); + + state.solution.erase( + std::remove_if( + state.solution.begin(), state.solution.end(), + [&](vertex v) { + return fragment.vertices.find(v) != fragment.vertices.end(); + }), + state.solution.end()); + + std::copy(_vertices.begin(), _vertices.end(), + std::back_inserter(state.solution)); + } +}; + +template<typename Graph, typename WeightMap> +class lns_search { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename boost::property_traits<WeightMap>::value_type weight; + + std::vector<std::vector<vertex>> _cliques; +public: + typedef lns_assignment<Graph> result_type; + typedef lns_state<Graph, WeightMap> first_argument_type; + typedef lns_fragment<Graph> second_argument_type; + + template<typename Iterator> + lns_search(Iterator begin, Iterator end): _cliques(begin, end) {} + + lns_assignment<Graph> operator()(const lns_state<Graph, WeightMap> &l_state, + const lns_fragment<Graph> &fragment) const { + state<Graph, WeightMap> search_state(l_state.graph, l_state.weight_map, + _cliques.begin(), _cliques.end()); + + std::vector<vertex> selected_vertex; + std::set<vertex> removed; + std::set<size_t> assigned; + + weight new_weight(0); + + for (vertex v : l_state.solution) { + if (fragment.vertices.find(v) != fragment.vertices.end()) + continue; + + selected_vertex.push_back(v); + + new_weight += get(l_state.weight_map, v); + + auto edges = out_edges(v, l_state.graph); + for (auto eit = edges.first; eit != edges.second; eit++) { + const vertex &other = target(*eit, l_state.graph); + removed.insert(other); + } + + for (size_t i : get(search_state.clique_map, v)) + assigned.insert(i); + } + + auto vs = boost::vertices(l_state.graph); + for (auto it = vs.first; it != vs.second; it++) { + vertex v = *it; + if (fragment.vertices.find(v) == fragment.vertices.end()) + removed.insert(v); + } + + std::map<size_t, size_t> size_delta; + for (vertex v : removed) { + for (size_t clique : get(search_state.clique_map, v)) { + size_delta[clique]++; + if (size_delta[clique] == search_state.clique_sizes[clique]) { + assigned.insert(clique); + } + } + } + + std::vector<size_t> assigned_v(assigned.begin(), assigned.end()); + std::vector<vertex> removed_v(removed.begin(), removed.end()); + + state_change<Graph, WeightMap> delta( + assigned_v, size_delta, + selected_vertex, removed_v, + weight(0), new_weight); + + delta.apply(search_state); + + lagrangian_bound<Graph, WeightMap> + bound(l_state.graph, l_state.weight_map, + _cliques.begin(), _cliques.end()); + + visit_state<Graph, WeightMap> visitor; + visitor.best_solution = l_state.solution; + visitor.best_value = weight(0); + for (vertex v : visitor.best_solution) + visitor.best_value += get(l_state.weight_map, v); + + successor<Graph, WeightMap, decltype(bound)> successor( + bound, visitor.best_value); + + search::depth_first_search(search_state, visitor, successor); + + std::vector<vertex> newly_selected; + for (vertex v : visitor.best_solution) { + if (fragment.vertices.find(v) != fragment.vertices.end()) + newly_selected.push_back(v); + } + + return lns_assignment<Graph>(newly_selected); + } +}; + +template<typename Graph, typename WeightMap> +class direct_evaluator { +public: + typedef typename boost::property_traits<WeightMap>::value_type result_type; + typedef state<Graph, WeightMap> argument_type; + +private: + result_type _bound; + +public: + direct_evaluator(result_type bound): _bound(bound) {} + + result_type operator()(const argument_type &state) const { + return state.solution_value / _bound; + } +}; + +template<typename Graph, typename OutputIterator> +void find_cliques(const Graph &graph, OutputIterator out) { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + + std::set<std::pair<vertex, vertex>> visited_edges; + + auto es = edges(graph); + for (auto eit = es.first; eit != es.second; eit++) { + auto u = source(*eit, graph); + auto v = target(*eit, graph); + + if (visited_edges.find(std::make_pair(u, v)) == visited_edges.end()) { + std::vector<vertex> contents, candidates; + + auto connected_es = out_edges(u, graph); + for (auto eit = connected_es.first; eit != connected_es.second; eit++) { + auto new_v = target(*eit, graph); + if (new_v != v && visited_edges.find(std::make_pair(u, new_v)) == + visited_edges.end()) { + candidates.push_back(new_v); + } + } + + candidates.push_back(v); + + contents.push_back(u); + + while (!candidates.empty()) { + vertex v = candidates.back(); + candidates.pop_back(); + + contents.push_back(v); + + candidates.erase( + std::remove_if(candidates.begin(), candidates.end(), + [&](vertex w) { + return visited_edges.find(std::make_pair(v, w)) == + visited_edges.end(); + }), + candidates.end()); + } + + for (size_t i = 0; i < contents.size(); i++) { + for (size_t j = 0; j < i; j++) { + visited_edges.insert(std::make_pair(contents[i], contents[j])); + visited_edges.insert(std::make_pair(contents[j], contents[i])); + } + } + + *out++ = contents; + } + } +} + +} + +template<typename Graph, typename WeightMap, typename OutputIterator> +void maximum_weight_independent_set(const Graph &graph, WeightMap weight_map, + OutputIterator out) { + typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex; + typedef typename boost::property_traits<WeightMap>::value_type weight; + + std::vector<std::vector<vertex>> cliques; + mwis::find_cliques(graph, std::back_inserter(cliques)); + + mwis::lagrangian_bound<Graph, WeightMap> bound( + graph, weight_map, + cliques.begin(), cliques.end()); + + bound.check_consistency(); + + mwis::visit_state<Graph, WeightMap> visitor; + mwis::successor<Graph, WeightMap, decltype(bound)> successor( + bound, visitor.best_value); + + mwis::state<Graph, WeightMap> state( + graph, weight_map, + cliques.begin(), cliques.end()); + + // search::depth_first_search(state, visitor, successor); + // for (vertex v : visitor.best_solution) + // *out++ = v; + + auto vs = vertices(graph); + weight root_bound(bound(vs.second, vs.second, vs.first, vs.second)); + + mwis::direct_evaluator<Graph, WeightMap> direct_eval(root_bound); + + mwis::max_bound<weight> max_bound; + mwis::successor<Graph, WeightMap, decltype(max_bound)> + max_successor(max_bound, visitor.best_value); + + search::monte_carlo_tree_search(state, max_successor, direct_eval); + std::cout << "\n"; + + for (vertex v : state.solution) + *out++ = v; + + // mwis::evaluator<Graph, WeightMap> eval(cliques.begin(), cliques.end(), 5); + + // visitor(state); + // search::greedy_search(state, successor, eval); + + // mwis::lns_state<Graph, WeightMap> l_state(graph, weight_map, state.solution); + // mwis::lns_fragment<Graph> (*l_selector)(const decltype(l_state)&) = + // mwis::fragment_selector<Graph, WeightMap>; + // mwis::lns_search<Graph, WeightMap> l_search(cliques.begin(), cliques.end()); + + // search::large_neighborhood_search(l_state, l_selector, l_search, 300); + // std::cout << "\n"; + + // for (vertex v : l_state.solution) + // *out++ = v; +} -- GitLab