8 #ifndef PAAL_BUDGETED_MAXIMUM_COVERAGE_HPP
9 #define PAAL_BUDGETED_MAXIMUM_COVERAGE_HPP
17 #include <boost/heap/d_ary_heap.hpp>
18 #include <boost/range/adaptor/indexed.hpp>
19 #include <boost/range/algorithm/copy.hpp>
20 #include <boost/range/algorithm_ext/iota.hpp>
21 #include <boost/range/algorithm/fill.hpp>
22 #include <boost/range/algorithm/for_each.hpp>
23 #include <boost/range/algorithm/sort.hpp>
24 #include <boost/range/combine.hpp>
33 template <
typename ElementWeight,
typename SetCost>
struct set_data_type {
35 : m_weight_of_uncovered_elements{}, m_cost{}, m_is_processed{
true } {}
36 ElementWeight m_weight_of_uncovered_elements;
47 template <
typename SetReference,
typename GetElementsOfSet,
48 typename GetWeightOfElement>
52 const int UNCOVERED = -1;
53 template <
class Budget,
class SetCost,
class SetIdToData,
class ElementWeight,
54 class SetIdToElements,
class ElementIndex,
class GetWeightOfElement,
55 typename DecreseWeight>
57 const Budget m_budget;
58 SetCost &m_cost_of_solution;
59 std::vector<int> m_selected_sets;
60 SetIdToData &m_sets_data;
61 const ElementWeight &m_weight_of_bests_solution;
62 ElementWeight &m_weight_of_covered_elements;
63 SetIdToElements m_set_id_to_elements;
64 std::vector<int> &m_covered_by;
65 std::vector<std::vector<int>> &m_sets_covering_element;
66 ElementIndex &m_get_el_index;
67 GetWeightOfElement &m_element_to_weight;
68 DecreseWeight m_decrese_weight;
73 selector(
const Budget budget, SetCost &cost_of_solution,
74 SetIdToData &sets_data,
75 const ElementWeight &weight_of_bests_solution,
76 ElementWeight &weight_of_covered_elements,
77 SetIdToElements set_id_to_elements, std::vector<int> &covered_by,
78 std::vector<std::vector<int>> &sets_covering_element,
79 ElementIndex &get_el_index, GetWeightOfElement &element_to_weight,
80 DecreseWeight decrese_weight)
81 : m_budget(budget), m_cost_of_solution(cost_of_solution),
82 m_sets_data(sets_data),
83 m_weight_of_bests_solution(weight_of_bests_solution),
84 m_weight_of_covered_elements(weight_of_covered_elements),
85 m_set_id_to_elements(set_id_to_elements), m_covered_by(covered_by),
86 m_sets_covering_element(sets_covering_element),
87 m_get_el_index(get_el_index), m_element_to_weight(element_to_weight),
88 m_decrese_weight(decrese_weight) {}
91 bool select_set_backtrack(
int selected_set_id,
bool in_reset =
false) {
92 if(!can_select(m_sets_data[selected_set_id]))
return false;
94 select_set(selected_set_id,
true);
95 if(!in_reset) m_selected_sets.push_back(selected_set_id);
100 bool select_set_greedy(
int selected_set_id) {
101 auto &select_set_data = m_sets_data[selected_set_id];
103 if(!can_select(select_set_data))
return false;
104 m_selected_sets.push_back(selected_set_id);
106 if(greedy_prune(select_set_data))
return true;
107 select_set(selected_set_id,
false);
112 void deselect_set(
int selected_set_id,
bool backtrack =
true) {
115 m_cost_of_solution -= m_sets_data[selected_set_id].m_cost;
116 for (
auto element : m_set_id_to_elements(selected_set_id)) {
117 if (covered_by(element) == selected_set_id) {
118 m_covered_by[m_get_el_index(element)] = UNCOVERED;
119 auto weight_of_element = m_element_to_weight(element);
120 cover_element(element, -weight_of_element, backtrack,
false);
123 m_selected_sets.pop_back();
126 void set_unprocessed(
127 boost::iterator_range<std::vector<int>::const_iterator>
const & set_ids) {
128 for (
auto set_id : set_ids) {
129 m_sets_data[set_id].m_is_processed =
false;
134 set_unprocessed(m_selected_sets);
135 if (m_selected_sets.size() > 0) {
136 boost::for_each(m_selected_sets, [&](
int selected_set_id) {
137 select_set_backtrack(selected_set_id,
true);
143 return m_selected_sets.size();
146 void resize(std::size_t size) {
147 return m_selected_sets.resize(size);
150 template <
typename OutputIterator>
151 void copy_to(OutputIterator out){
152 boost::copy(m_selected_sets, out);
156 void select_set(
int selected_set_id,
bool backtrack) {
157 auto &select_set_data = m_sets_data[selected_set_id];
159 m_cost_of_solution += select_set_data.m_cost;
160 for (
auto element : m_set_id_to_elements(selected_set_id)) {
161 if (covered_by(element) == UNCOVERED) {
164 m_covered_by[m_get_el_index(element)] = selected_set_id;
165 auto weight_of_element = m_element_to_weight(element);
166 cover_element(element, weight_of_element, backtrack);
175 bool greedy_prune(
const SetData & set_data) {
176 return (m_budget - m_cost_of_solution) * set_data.m_weight_of_uncovered_elements <=
177 static_cast<Budget
>(set_data.m_cost * (m_weight_of_bests_solution - m_weight_of_covered_elements));
181 bool can_select(SetData & set_data) {
182 if (set_data.m_is_processed)
return false;
183 set_data.m_is_processed =
true;
184 return static_cast<Budget
>(m_cost_of_solution + set_data.m_cost) <= m_budget &&
185 set_data.m_weight_of_uncovered_elements!= ElementWeight{};
188 template <
typename Element>
189 void cover_element(Element && el, ElementWeight weight_diff,
bool backtrack,
bool select =
true) {
190 m_weight_of_covered_elements += weight_diff;
192 m_sets_covering_element[m_get_el_index(el)]) {
193 if (m_sets_data[set_id].m_weight_of_uncovered_elements >
194 weight_diff || backtrack) {
195 m_sets_data[set_id].m_weight_of_uncovered_elements -= weight_diff;
196 if (!backtrack && !m_sets_data[set_id].m_is_processed) {
197 m_decrese_weight(set_id);
201 m_sets_data[set_id].m_is_processed =
true;
207 template <
typename Element>
208 auto covered_by(Element && el) const
209 -> decltype(m_covered_by[m_get_el_index(el)]) {
211 return m_covered_by[m_get_el_index(el)];
215 template <
class Budget,
class SetCost,
class SetIdToData,
class ElementWeight,
216 class SetIdToElements,
class ElementIndex,
class GetWeightOfElement,
217 typename DecreseWeight>
218 auto make_selector(
const Budget budget, SetCost &cost_of_solution,
219 SetIdToData &sets_data,
220 const ElementWeight &weight_of_bests_solution,
221 ElementWeight &weight_of_covered_elements,
222 SetIdToElements set_id_to_elements,
223 std::vector<int> &covered_by,
224 std::vector<std::vector<int>> &sets_covering_element,
225 ElementIndex &get_el_index,
226 GetWeightOfElement &element_to_weight,
227 DecreseWeight decrese_weight) {
228 return selector<Budget, SetCost, SetIdToData, ElementWeight,
229 SetIdToElements, ElementIndex, GetWeightOfElement,
231 budget, cost_of_solution, sets_data,
232 weight_of_bests_solution, weight_of_covered_elements,
233 set_id_to_elements, covered_by, sets_covering_element, get_el_index,
234 element_to_weight, decrese_weight);
261 template <
typename SetRange,
class GetCostOfSet,
class GetElementsOfSet,
262 class OutputIterator,
class ElementIndex,
class Budget,
265 SetRange && sets, GetCostOfSet set_to_cost,
266 GetElementsOfSet set_to_elements, OutputIterator result,
267 ElementIndex get_el_index, Budget budget,
268 GetWeightOfElement element_to_weight = GetWeightOfElement(),
269 const unsigned int initial_set_size = 3) {
271 using set_reference =
typename boost::range_reference<SetRange>::type;
272 using element_weight =
typename detail::element_weight_t<
273 set_reference, GetElementsOfSet, GetWeightOfElement>;
275 using set_id_to_data = std::vector<detail::set_data_type<element_weight, set_cost>>;
277 auto nu_sets = boost::distance(sets);
278 set_id_to_data initial_sets_data(nu_sets), sets_data(nu_sets);
279 set_cost cost_of_solution{}, cost_of_best_solution{};
280 int number_of_elements = 0;
283 for (
auto set_and_data : boost::combine(sets, initial_sets_data)) {
284 auto const & set = boost::get<0>(set_and_data);
285 auto &cost = boost::get<1>(set_and_data).m_cost;
286 cost = set_to_cost(set);
287 assert(cost != set_cost{});
288 auto const & elements = set_to_elements(set);
289 if (!boost::empty(elements)) {
290 number_of_elements = std::max(number_of_elements,
294 element_weight weight_of_covered_elements{}, weight_of_bests_solution{};
295 std::vector<int> best_solution(1);
296 std::vector<int> covered_by(
297 number_of_elements, detail::UNCOVERED);
299 std::vector<int> sets_id(nu_sets);
300 boost::iota(sets_id, 0);
301 std::vector<std::vector<int>> sets_covering_element(number_of_elements);
302 auto decreasing_density_order =
305 sets_data[x].m_weight_of_uncovered_elements, sets_data[x].m_cost);
307 using queue = boost::heap::d_ary_heap<
308 int, boost::heap::arity<3>, boost::heap::mutable_<true>,
309 boost::heap::compare<decltype(decreasing_density_order)>>;
311 queue uncovered_set_queue{ decreasing_density_order };
312 std::vector<typename queue::handle_type> set_id_to_handle(nu_sets);
315 for (
auto set : sets | boost::adaptors::indexed()) {
316 auto set_id = set.index();
317 auto &set_data = initial_sets_data[set_id];
319 for (
auto &&element : set_to_elements(set.value())) {
320 sets_covering_element[get_el_index(element)].push_back(set_id);
321 set_data.m_weight_of_uncovered_elements += element_to_weight(element);
323 if (initial_set_size ==
326 set_cost cost_of_set = set_data.m_cost;
327 if (set_data.m_weight_of_uncovered_elements >= weight_of_bests_solution &&
328 static_cast<Budget>(cost_of_set) <= budget) {
329 weight_of_bests_solution = set_data.m_weight_of_uncovered_elements;
330 best_solution[0] = set_id;
331 cost_of_best_solution = cost_of_set;
336 auto sort_sets = [&](std::vector<int>& sets_range) {
338 return sets_data[x].m_weight_of_uncovered_elements;
340 return sets_range.end();
342 auto selector = detail::make_selector
343 (budget, cost_of_solution,sets_data,
344 weight_of_bests_solution,weight_of_covered_elements,
345 [&](
int selected_set_id){
return set_to_elements(sets[selected_set_id]);},
346 covered_by,sets_covering_element,get_el_index,
348 [&](
int set_id){uncovered_set_queue.decrease(set_id_to_handle[set_id]);});
350 boost::copy(initial_sets_data, sets_data.begin());
355 boost::copy(initial_sets_data, sets_data.begin());
356 cost_of_solution = set_cost{};
357 selector.set_unprocessed(solver.get_moves());
358 boost::fill(covered_by, detail::UNCOVERED);
359 weight_of_covered_elements = element_weight{};
363 auto on_pop = [&](
int deselected_set_id) {
364 selector.deselect_set(deselected_set_id);
365 selector.set_unprocessed(solver.get_moves());
368 auto save_best_solution = [&]() {
372 if (std::make_pair(weight_of_covered_elements, cost_of_best_solution) >
373 std::make_pair(weight_of_bests_solution, cost_of_solution)) {
374 weight_of_bests_solution = weight_of_covered_elements;
375 cost_of_best_solution = cost_of_solution;
376 best_solution.resize(selector.size());
377 selector.copy_to(best_solution.begin());
380 auto greedy_phase = [&]() {
381 uncovered_set_queue.clear();
382 auto moves = solver.get_moves();
383 if(boost::empty(moves))
return;
385 for (
auto set_id : moves) {
386 set_id_to_handle[set_id] = uncovered_set_queue.push(set_id);
390 int uncovered_set_id;
392 uncovered_set_id = uncovered_set_queue.top();
393 uncovered_set_queue.pop();
394 }
while (!selector.select_set_greedy(uncovered_set_id) &&
395 !uncovered_set_queue.empty());
398 auto can_push = [&](
int candidate) {
399 if (!selector.select_set_backtrack(candidate)) {
402 if (selector.size() == initial_set_size) {
404 save_best_solution();
405 selector.resize(initial_set_size-1);
409 save_best_solution();
415 if (initial_set_size != 0) {
419 solver.solve(can_push, on_pop, sort_sets);
422 save_best_solution();
425 for (
auto set_id : best_solution) {
426 *result = *(sets.begin() + set_id);
429 return weight_of_bests_solution;
typename boost::range_value< Range >::type range_to_elem_t
for given range returns type of its element
auto budgeted_maximum_coverage(SetRange &&sets, GetCostOfSet set_to_cost, GetElementsOfSet set_to_elements, OutputIterator result, ElementIndex get_el_index, Budget budget, GetWeightOfElement element_to_weight=GetWeightOfElement(), const unsigned int initial_set_size=3)
detail
auto max_element_functor(Range &&range, Functor f)
combination of boost::max_element and boost::adaptors::transformed
auto make_functor_to_comparator(Functor functor, Compare compare=Compare())
make for functor to comparator
This file contains set of simple useful functors or functor adapters.
fraction< A, B > make_fraction(A a, B b)
make function for fraction
Implementation of fractions, which are used only for comparison purposes, and thus they can be used w...
auto make_subset_backtrack(const Elements &elements)
make subset_backtrack
typename std::decay< typename std::result_of< F >::type >::type pure_result_of_t
return pure type of function (decays const and reference)