23#include "absl/log/check.h"
24#include "absl/random/random.h"
25#include "absl/types/span.h"
35static constexpr double kInfinity = std::numeric_limits<float>::infinity();
41 for (
const SubsetIndex subset : focus) {
42 result[subset] =
true;
55 DVLOG(1) <<
"Entering Preprocessor::NextSolution";
62 if (rows[element].
size() == 1) {
63 const SubsetIndex subset = rows[element][RowEntryIndex(0)];
64 if (in_focus[subset] && !inv_->
is_selected()[subset]) {
66 ++num_columns_fixed_by_singleton_row_;
81 absl::Span<const SubsetIndex> focus) {
84 for (
const SubsetIndex subset : focus) {
85 choices[subset] =
true;
98 const std::vector<SubsetIndex>& focus) {
100 std::vector<SubsetIndex> shuffled = focus;
101 std::shuffle(shuffled.begin(), shuffled.end(), absl::BitGen());
102 for (
const SubsetIndex subset : shuffled) {
121 const std::vector<SubsetIndex>& focus) {
130 for (
const SubsetIndex subset : focus) {
131 elements_per_cost[subset] = 1.0 / costs[subset];
133 std::vector<std::pair<float, SubsetIndex::ValueType>> subset_priorities;
134 DVLOG(1) <<
"focus.size(): " << focus.size();
135 subset_priorities.reserve(focus.size());
136 for (
const SubsetIndex subset : focus) {
140 const float priority =
142 subset_priorities.push_back({priority, subset.value()});
151 const SubsetIndex best_subset(pq.
TopIndex());
153 inv_->
Select(best_subset);
158 const SubsetIndex subset = *it;
160 if (marginal_impact > 0) {
161 const float priority = marginal_impact * elements_per_cost[subset];
162 pq.
Update({priority, subset.value()});
164 pq.
Remove(subset.value());
167 DVLOG(1) <<
"Cost = " << inv_->
cost()
188 absl::Span<const SubsetIndex> focus) {
203 DVLOG(1) <<
"Entering ElementDegreeSolutionGenerator::NextSolution";
207 std::vector<ElementIndex> degree_sorted_elements(num_elements);
208 std::iota(degree_sorted_elements.begin(), degree_sorted_elements.end(),
213 std::sort(degree_sorted_elements.begin(), degree_sorted_elements.end(),
214 [&rows](
const ElementIndex
a,
const ElementIndex
b) {
215 if (rows[a].size() < rows[b].size()) return true;
216 if (rows[a].size() == rows[b].size()) return a < b;
219 for (
const ElementIndex element : degree_sorted_elements) {
221 if (inv_->
coverage()[element] != 0)
continue;
222 Cost min_ratio = std::numeric_limits<Cost>::max();
223 SubsetIndex best_subset(-1);
224 for (
const SubsetIndex subset : rows[element]) {
225 if (!in_focus[subset])
continue;
227 if (
ratio < min_ratio) {
229 best_subset = subset;
232 DCHECK_NE(best_subset, SubsetIndex(-1));
233 inv_->
Select(best_subset);
234 DVLOG(1) <<
"Cost = " << inv_->
cost()
244void SteepestSearch::UpdatePriorities(absl::Span<const SubsetIndex>) {}
253 int num_iterations) {
261 int num_iterations) {
269 int num_iterations) {
271 DVLOG(1) <<
"Entering SteepestSearch::NextSolution, num_iterations = "
281 std::vector<std::pair<float, SubsetIndex::ValueType>> subset_priorities;
282 subset_priorities.reserve(in_focus.size());
283 for (
const SetCoverDecision& decision : inv_->
trace()) {
284 const SubsetIndex subset = decision.subset();
285 if (in_focus[subset] && inv_->
is_selected()[subset] &&
287 const float delta_per_element = costs[subset];
288 subset_priorities.push_back({delta_per_element, subset.value()});
291 DVLOG(1) <<
"subset_priorities.size(): " << subset_priorities.size();
294 for (
int iteration = 0; iteration < num_iterations && !pq.IsEmpty();
296 const SubsetIndex best_subset(pq.TopIndex());
300 DCHECK_GT(costs[best_subset], 0.0);
303 for (IntersectingSubsetsIterator it(*inv_->
model(), best_subset);
304 !it.at_end(); ++it) {
305 const SubsetIndex subset = *it;
307 pq.Remove(subset.value());
310 DVLOG(1) <<
"Cost = " << inv_->
cost();
324 times_penalized_.
assign(num_subsets.value(), 0);
325 augmented_costs_ = subset_costs;
326 utilities_ = subset_costs;
332 return absl::Bernoulli(absl::BitGen(), 0.5);
336void GuidedTabuSearch::UpdatePenalties(absl::Span<const SubsetIndex> focus) {
338 Cost max_utility = -1.0;
339 for (
const SubsetIndex subset : focus) {
341 max_utility = std::max(max_utility, utilities_[subset]);
344 const double epsilon_utility = epsilon_ * max_utility;
345 for (
const SubsetIndex subset : focus) {
347 const double utility = utilities_[subset];
348 if ((max_utility - utility <= epsilon_utility) && FlipCoin()) {
349 ++times_penalized_[subset];
350 const int times_penalized = times_penalized_[subset];
352 subset_costs[subset];
353 utilities_[subset] = cost / (1 + times_penalized);
354 augmented_costs_[subset] =
355 cost * (1 + penalty_factor_ * times_penalized);
366 int num_iterations) {
368 DVLOG(1) <<
"Entering GuidedTabuSearch::NextSolution, num_iterations = "
373 Cost augmented_cost =
374 std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0);
376 for (
int iteration = 0; iteration < num_iterations; ++iteration) {
377 if (inv_->
trace().size() > 2 * trace_size) {
379 trace_size = inv_->
trace().size();
383 for (
const SubsetIndex subset : focus) {
384 const Cost delta = augmented_costs_[subset];
385 DVLOG(1) <<
"Subset, " << subset.value() <<
", at ,"
387 <<
", best_delta =, " << best_delta;
391 if (-
delta < best_delta &&
396 inv_->
cost() - subset_costs[subset] < best_cost)) {
398 best_subset = subset;
402 if (
delta < best_delta) {
407 best_subset = subset;
416 DVLOG(1) <<
"Best subset, " << best_subset.value() <<
", at ,"
417 << inv_->
is_selected()[best_subset] <<
", best_delta = ,"
420 UpdatePenalties(focus);
421 tabu_list_.
Add(best_subset);
422 inv_->
Flip(best_subset);
425 std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0);
427 DVLOG(1) <<
"Iteration, " << iteration <<
", current cost = ,"
428 << inv_->
cost() <<
", best cost = ," << best_cost
429 <<
", penalized cost = ," << augmented_cost;
430 if (inv_->
cost() < best_cost) {
431 LOG(INFO) <<
"Updated best cost, " <<
"Iteration, " << iteration
432 <<
", current cost = ," << inv_->
cost() <<
", best cost = ,"
433 << best_cost <<
", penalized cost = ," << augmented_cost;
434 best_cost = inv_->
cost();
447 penalties_.
assign(columns.size(), 0);
448 penalization_factor_ = alpha_ * inv_->
cost() * 1.0 / (columns.size());
450 const SubsetIndex subset = decision.subset();
453 {
static_cast<float>(inv_->
model()->subset_costs()[subset] /
454 (1 + penalties_[subset])),
464Cost GuidedLocalSearch::ComputeDelta(SubsetIndex subset)
const {
465 float delta = (penalization_factor_ * penalties_[subset] +
466 inv_->
model()->subset_costs()[subset]);
476 int num_iterations) {
481 for (
const SubsetIndex& subset : focus) {
482 const float delta = ComputeDelta(subset);
483 if (
delta < kInfinity) {
488 for (
int iteration = 0; iteration < num_iterations; ++iteration) {
490 const SubsetIndex best_subset(priority_heap_.
TopIndex());
492 utility_heap_.
Insert({0, best_subset.value()});
495 {
static_cast<float>(inv_->
model()->subset_costs()[best_subset] /
496 (1 + penalties_[best_subset])),
497 best_subset.value()});
502 const SubsetIndex penalized_subset(utility_heap_.
TopIndex());
504 ++penalties_[penalized_subset];
506 {
static_cast<float>(inv_->
model()->subset_costs()[penalized_subset] /
507 (1 + penalties_[penalized_subset])),
508 penalized_subset.value()});
512 const float delta_selected = (penalization_factor_ * penalties_[subset] +
513 inv_->
model()->subset_costs()[subset]);
514 priority_heap_.
Insert({delta_selected, subset.value()});
517 for (
const SubsetIndex subset : {penalized_subset, best_subset}) {
518 const float delta = ComputeDelta(subset);
519 if (
delta < kInfinity) {
527 priority_heap_.
Remove(subset.value());
530 if (inv_->
cost() < best_cost) {
531 best_cost = inv_->
cost();
538 for (
const SubsetIndex& subset : focus) {
547void SampleSubsets(std::vector<SubsetIndex>* list, std::size_t num_subsets) {
548 num_subsets = std::min(num_subsets, list->size());
549 CHECK_GE(num_subsets, 0);
550 std::shuffle(list->begin(), list->end(), absl::BitGen());
551 list->resize(num_subsets);
561 std::size_t num_subsets,
563 num_subsets = std::min(num_subsets, focus.size());
564 CHECK_GE(num_subsets, 0);
565 std::vector<SubsetIndex> chosen_indices;
566 for (
const SubsetIndex subset : focus) {
568 chosen_indices.push_back(subset);
571 SampleSubsets(&chosen_indices, num_subsets);
572 std::size_t num_deselected = 0;
573 for (
const SubsetIndex subset : chosen_indices) {
583 if (num_deselected > num_subsets)
break;
585 return chosen_indices;
595 absl::Span<const SubsetIndex> focus, std::size_t max_num_subsets,
598 std::vector<SubsetIndex> sampled_subsets;
608 if (coverage[element] <= 1)
continue;
609 for (
const SubsetIndex subset : rows[element]) {
610 if (inv->
is_selected()[subset] && !subset_is_collected[subset]) {
611 subset_is_collected[subset] =
true;
619 for (
const SubsetIndex subset : focus) {
620 if (subset_is_collected[subset]) {
621 sampled_subsets.push_back(subset);
627 std::shuffle(sampled_subsets.begin(), sampled_subsets.end(), absl::BitGen());
628 sampled_subsets.resize(std::min(sampled_subsets.size(), max_num_subsets));
632 for (
const SubsetIndex subset : sampled_subsets) {
635 return sampled_subsets;
void Update(Aggregate element)
Change the value of an element.
bool IsEmpty() const
True iff the heap is empty.
void Insert(Aggregate element)
Insert an element into the heap.
bool NextSolution()
GreedySolutionGenerator.
void Initialize()
Initializes the Guided Local Search algorithm.
bool NextSolution(int num_iterations)
void Initialize()
Initializes the Guided Tabu Search algorithm.
bool NextSolution(int num_iterations)
bool at_end() const
Returns (true) whether the iterator is at the end.
bool NextSolution()
Preprocessor.
bool NextSolution()
Returns true if a solution was found.
A helper class used to store the decisions made during a search.
void Flip(SubsetIndex subset)
bool ComputeIsRedundant(SubsetIndex subset) const
const SubsetToIntVector & num_free_elements() const
const std::vector< SetCoverDecision > & trace() const
Returns the vector of the decisions which has led to the current solution.
void Select(SubsetIndex subset)
void LoadSolution(const SubsetBoolVector &solution)
Loads the solution and recomputes the data in the invariant.
const std::vector< SubsetIndex > & new_non_removable_subsets() const
Returns the subsets that become non removable after the last update.
void FlipAndFullyUpdate(SubsetIndex subset)
const SubsetBoolVector & is_selected() const
Returns the subset assignment vector.
SetCoverModel * model() const
Returns the weighted set covering model to which the state applies.
bool CheckConsistency() const
void Deselect(SubsetIndex subset)
void DeselectAndFullyUpdate(SubsetIndex subset)
const ElementToIntVector & coverage() const
Returns vector containing number of subsets covering each element.
const std::vector< SubsetIndex > & new_removable_subsets() const
Returns the subsets that become removable after the last update.
void ClearTrace()
Clears the trace.
Cost cost() const
Returns the cost of current solution.
BaseInt num_uncovered_elements() const
Returns the number of uncovered elements.
util_intops::StrongIntRange< ElementIndex > ElementRange() const
const SparseRowView & rows() const
Row view of the set covering problem.
BaseInt num_elements() const
const SubsetCostVector & subset_costs() const
Vector of costs for each subset.
const SparseColumnView & columns() const
Column view of the set covering problem.
BaseInt num_subsets() const
std::vector< SubsetIndex > all_subsets() const
Returns the list of indices for all the subsets in the model.
bool NextSolution(int num_iterations)
bool Contains(T t) const
Returns true if t is in the array. This is O(size), but small.
void Add(T t)
Adds t to the array. When the end of the array is reached, re-start at 0.
bool NextSolution()
TrivialSolutionGenerator.
void assign(size_type n, const value_type &val)
In SWIG mode, we don't want anything besides these top-level includes.
constexpr SubsetIndex kNotFound(-1)
std::vector< SubsetIndex > ClearMostCoveredElements(std::size_t max_num_subsets, SetCoverInvariant *inv)
static constexpr double kInfinity
static constexpr Cost kMaxPossibleCost
std::vector< SubsetIndex > ClearRandomSubsets(std::size_t num_subsets, SetCoverInvariant *inv)
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
util_intops::StrongVector< SubsetIndex, Cost, CostAllocator > SubsetCostVector