Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
set_cover_heuristics.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstddef>
18#include <limits>
19#include <numeric>
20#include <utility>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "absl/random/random.h"
25#include "absl/types/span.h"
30
31namespace operations_research {
32
33constexpr SubsetIndex kNotFound(-1);
34static constexpr Cost kMaxPossibleCost = std::numeric_limits<Cost>::max();
35static constexpr double kInfinity = std::numeric_limits<float>::infinity();
36
37namespace {
38SubsetBoolVector MakeBoolVector(absl::Span<const SubsetIndex> focus,
39 SubsetIndex size) {
40 SubsetBoolVector result(SubsetIndex(size), false);
41 for (const SubsetIndex subset : focus) {
42 result[subset] = true;
43 }
44 return result;
45}
46} // anonymous namespace
47
48// Preprocessor.
49
51 return NextSolution(inv_->model()->all_subsets());
52}
53
54bool Preprocessor::NextSolution(absl::Span<const SubsetIndex> focus) {
55 DVLOG(1) << "Entering Preprocessor::NextSolution";
56 const SubsetIndex num_subsets(inv_->model()->num_subsets());
57 SubsetBoolVector choices(num_subsets, false);
58 const ElementIndex num_elements(inv_->model()->num_elements());
59 const SparseRowView& rows = inv_->model()->rows();
60 SubsetBoolVector in_focus = MakeBoolVector(focus, num_subsets);
61 for (const ElementIndex element : inv_->model()->ElementRange()) {
62 if (rows[element].size() == 1) {
63 const SubsetIndex subset = rows[element][RowEntryIndex(0)];
64 if (in_focus[subset] && !inv_->is_selected()[subset]) {
65 inv_->Select(subset);
66 ++num_columns_fixed_by_singleton_row_;
67 }
68 }
69 }
70 inv_->CompressTrace();
71 return true;
72}
73
74// TrivialSolutionGenerator.
75
79
81 absl::Span<const SubsetIndex> focus) {
82 const SubsetIndex num_subsets(inv_->model()->num_subsets());
83 SubsetBoolVector choices(num_subsets, false);
84 for (const SubsetIndex subset : focus) {
85 choices[subset] = true;
86 }
87 inv_->LoadSolution(choices);
88 return true;
89}
90
91// RandomSolutionGenerator.
92
96
98 const std::vector<SubsetIndex>& focus) {
99 inv_->ClearTrace();
100 std::vector<SubsetIndex> shuffled = focus;
101 std::shuffle(shuffled.begin(), shuffled.end(), absl::BitGen());
102 for (const SubsetIndex subset : shuffled) {
103 if (inv_->is_selected()[subset]) continue;
104 if (inv_->num_free_elements()[subset] != 0) {
105 inv_->Select(subset);
106 }
107 }
108 inv_->CompressTrace();
109 DCHECK(inv_->CheckConsistency());
110 return true;
111}
112
113// GreedySolutionGenerator.
114
116 return NextSolution(inv_->model()->all_subsets(),
117 inv_->model()->subset_costs());
118}
119
121 const std::vector<SubsetIndex>& focus) {
122 return NextSolution(focus, inv_->model()->subset_costs());
123}
124
126 const std::vector<SubsetIndex>& focus, const SubsetCostVector& costs) {
127 DCHECK(inv_->CheckConsistency());
128 inv_->ClearTrace();
129 SubsetCostVector elements_per_cost(costs.size(), 0.0);
130 for (const SubsetIndex subset : focus) {
131 elements_per_cost[subset] = 1.0 / costs[subset];
132 }
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) {
137 if (!inv_->is_selected()[subset] &&
138 inv_->num_free_elements()[subset] != 0) {
139 // NOMUTANTS -- reason, for C++
140 const float priority =
141 elements_per_cost[subset] * inv_->num_free_elements()[subset];
142 subset_priorities.push_back({priority, subset.value()});
143 }
144 }
145 // The priority queue maintains the maximum number of elements covered by unit
146 // of cost. We chose 16 as the arity of the heap after some testing.
147 // TODO(user): research more about the best value for Arity.
149 subset_priorities, inv_->model()->num_subsets());
150 while (!pq.IsEmpty()) {
151 const SubsetIndex best_subset(pq.TopIndex());
152 pq.Pop();
153 inv_->Select(best_subset);
154 // NOMUTANTS -- reason, for C++
155 if (inv_->num_uncovered_elements() == 0) break;
156 for (IntersectingSubsetsIterator it(*inv_->model(), best_subset);
157 !it.at_end(); ++it) {
158 const SubsetIndex subset = *it;
159 const BaseInt marginal_impact(inv_->num_free_elements()[subset]);
160 if (marginal_impact > 0) {
161 const float priority = marginal_impact * elements_per_cost[subset];
162 pq.Update({priority, subset.value()});
163 } else {
164 pq.Remove(subset.value());
165 }
166 }
167 DVLOG(1) << "Cost = " << inv_->cost()
168 << " num_uncovered_elements = " << inv_->num_uncovered_elements();
169 }
170 inv_->CompressTrace();
171 // Don't expect the queue to be empty, because of the break in the while
172 // loop.
173 DCHECK(inv_->CheckConsistency());
174 return true;
175}
176
177// ElementDegreeSolutionGenerator.
178// There is no need to use a priority queue here, as the ratios are computed
179// on-demand. Also elements are sorted based on degree once and for all and
180// moved past when the elements become already covered.
182 const SubsetIndex num_subsets(inv_->model()->num_subsets());
183 const SubsetBoolVector in_focus(num_subsets, true);
184 return NextSolution(in_focus, inv_->model()->subset_costs());
185}
186
188 absl::Span<const SubsetIndex> focus) {
189 const SubsetIndex num_subsets(inv_->model()->num_subsets());
190 const SubsetBoolVector in_focus = MakeBoolVector(focus, num_subsets);
191 return NextSolution(in_focus, inv_->model()->subset_costs());
192}
193
195 absl::Span<const SubsetIndex> focus, const SubsetCostVector& costs) {
196 const SubsetIndex num_subsets(inv_->model()->num_subsets());
197 const SubsetBoolVector in_focus = MakeBoolVector(focus, num_subsets);
198 return NextSolution(in_focus, costs);
199}
200
202 const SubsetBoolVector& in_focus, const SubsetCostVector& costs) {
203 DVLOG(1) << "Entering ElementDegreeSolutionGenerator::NextSolution";
204 DCHECK(inv_->CheckConsistency());
205 // Create the list of all the indices in the problem.
206 const BaseInt num_elements = inv_->model()->num_elements();
207 std::vector<ElementIndex> degree_sorted_elements(num_elements);
208 std::iota(degree_sorted_elements.begin(), degree_sorted_elements.end(),
209 ElementIndex(0));
210 const SparseRowView& rows = inv_->model()->rows();
211 // Sort indices by degree i.e. the size of the row corresponding to an
212 // element.
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;
217 return false;
218 });
219 for (const ElementIndex element : degree_sorted_elements) {
220 // No need to cover an element that is already covered.
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;
226 const Cost ratio = costs[subset] / inv_->num_free_elements()[subset];
227 if (ratio < min_ratio) {
228 min_ratio = ratio;
229 best_subset = subset;
230 }
231 }
232 DCHECK_NE(best_subset, SubsetIndex(-1));
233 inv_->Select(best_subset);
234 DVLOG(1) << "Cost = " << inv_->cost()
235 << " num_uncovered_elements = " << inv_->num_uncovered_elements();
236 }
237 inv_->CompressTrace();
238 DCHECK(inv_->CheckConsistency());
239 return true;
240}
241
242// SteepestSearch.
243
244void SteepestSearch::UpdatePriorities(absl::Span<const SubsetIndex>) {}
245
246bool SteepestSearch::NextSolution(int num_iterations) {
247 const SubsetIndex num_subsets(inv_->model()->num_subsets());
248 const SubsetBoolVector in_focus(num_subsets, true);
249 return NextSolution(in_focus, inv_->model()->subset_costs(), num_iterations);
250}
251
252bool SteepestSearch::NextSolution(absl::Span<const SubsetIndex> focus,
253 int num_iterations) {
254 const SubsetIndex num_subsets(inv_->model()->num_subsets());
255 const SubsetBoolVector in_focus = MakeBoolVector(focus, num_subsets);
256 return NextSolution(focus, inv_->model()->subset_costs(), num_iterations);
257}
258
259bool SteepestSearch::NextSolution(absl::Span<const SubsetIndex> focus,
260 const SubsetCostVector& costs,
261 int num_iterations) {
262 const SubsetIndex num_subsets(inv_->model()->num_subsets());
263 const SubsetBoolVector in_focus = MakeBoolVector(focus, num_subsets);
264 return NextSolution(in_focus, costs, num_iterations);
265}
266
268 const SubsetCostVector& costs,
269 int num_iterations) {
270 DCHECK(inv_->CheckConsistency());
271 DVLOG(1) << "Entering SteepestSearch::NextSolution, num_iterations = "
272 << num_iterations;
273 // Return false if inv_ contains no solution.
274 // TODO(user): This should be relaxed for partial solutions.
275 if (inv_->num_uncovered_elements() != 0) {
276 return false;
277 }
278
279 // Create priority queue with cost of using a subset, by decreasing order.
280 // Do it only for selected AND removable subsets.
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] &&
286 inv_->ComputeIsRedundant(subset)) {
287 const float delta_per_element = costs[subset];
288 subset_priorities.push_back({delta_per_element, subset.value()});
289 }
290 }
291 DVLOG(1) << "subset_priorities.size(): " << subset_priorities.size();
293 subset_priorities, inv_->model()->num_subsets());
294 for (int iteration = 0; iteration < num_iterations && !pq.IsEmpty();
295 ++iteration) {
296 const SubsetIndex best_subset(pq.TopIndex());
297 pq.Pop();
298 DCHECK(inv_->is_selected()[best_subset]);
299 DCHECK(inv_->ComputeIsRedundant(best_subset));
300 DCHECK_GT(costs[best_subset], 0.0);
301 inv_->Deselect(best_subset);
302
303 for (IntersectingSubsetsIterator it(*inv_->model(), best_subset);
304 !it.at_end(); ++it) {
305 const SubsetIndex subset = *it;
306 if (!inv_->ComputeIsRedundant(subset)) {
307 pq.Remove(subset.value());
308 }
309 }
310 DVLOG(1) << "Cost = " << inv_->cost();
311 }
312 inv_->CompressTrace();
313 // TODO(user): change this to enable working on partial solutions.
314 DCHECK_EQ(inv_->num_uncovered_elements(), 0);
315 DCHECK(inv_->CheckConsistency());
316 return true;
317}
318
319// Guided Tabu Search
320
322 const SubsetIndex num_subsets(inv_->model()->num_subsets());
323 const SubsetCostVector& subset_costs = inv_->model()->subset_costs();
324 times_penalized_.assign(num_subsets.value(), 0);
325 augmented_costs_ = subset_costs;
326 utilities_ = subset_costs;
327}
328
329namespace {
330bool FlipCoin() {
331 // TODO(user): use STL for repeatable testing.
332 return absl::Bernoulli(absl::BitGen(), 0.5);
333}
334} // namespace
335
336void GuidedTabuSearch::UpdatePenalties(absl::Span<const SubsetIndex> focus) {
337 const SubsetCostVector& subset_costs = inv_->model()->subset_costs();
338 Cost max_utility = -1.0;
339 for (const SubsetIndex subset : focus) {
340 if (inv_->is_selected()[subset]) {
341 max_utility = std::max(max_utility, utilities_[subset]);
342 }
343 }
344 const double epsilon_utility = epsilon_ * max_utility;
345 for (const SubsetIndex subset : focus) {
346 if (inv_->is_selected()[subset]) {
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];
351 const Cost cost =
352 subset_costs[subset]; // / columns[subset].size().value();
353 utilities_[subset] = cost / (1 + times_penalized);
354 augmented_costs_[subset] =
355 cost * (1 + penalty_factor_ * times_penalized);
356 }
357 }
358 }
359}
360
361bool GuidedTabuSearch::NextSolution(int num_iterations) {
362 return NextSolution(inv_->model()->all_subsets(), num_iterations);
363}
364
365bool GuidedTabuSearch::NextSolution(absl::Span<const SubsetIndex> focus,
366 int num_iterations) {
367 DCHECK(inv_->CheckConsistency());
368 DVLOG(1) << "Entering GuidedTabuSearch::NextSolution, num_iterations = "
369 << num_iterations;
370 const SubsetCostVector& subset_costs = inv_->model()->subset_costs();
371 Cost best_cost = inv_->cost();
372 SubsetBoolVector best_choices = inv_->is_selected();
373 Cost augmented_cost =
374 std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0);
375 BaseInt trace_size = inv_->trace().size();
376 for (int iteration = 0; iteration < num_iterations; ++iteration) {
377 if (inv_->trace().size() > 2 * trace_size) {
378 inv_->CompressTrace();
379 trace_size = inv_->trace().size();
380 }
381 Cost best_delta = kMaxPossibleCost;
382 SubsetIndex best_subset = kNotFound;
383 for (const SubsetIndex subset : focus) {
384 const Cost delta = augmented_costs_[subset];
385 DVLOG(1) << "Subset, " << subset.value() << ", at ,"
386 << inv_->is_selected()[subset] << ", delta =, " << delta
387 << ", best_delta =, " << best_delta;
388 if (inv_->is_selected()[subset]) {
389 // Try to remove subset from solution, if the gain from removing is
390 // worth it:
391 if (-delta < best_delta &&
392 // and it can be removed, and
393 inv_->ComputeIsRedundant(subset) &&
394 // it is not Tabu OR decreases the actual cost (aspiration):
395 (!tabu_list_.Contains(subset) ||
396 inv_->cost() - subset_costs[subset] < best_cost)) {
397 best_delta = -delta;
398 best_subset = subset;
399 }
400 } else {
401 // Try to use subset in solution, if its penalized delta is good.
402 if (delta < best_delta) {
403 // The limit kMaxPossibleCost is ill-defined,
404 // there is always a best_subset. Is it intended?
405 if (!tabu_list_.Contains(subset)) {
406 best_delta = delta;
407 best_subset = subset;
408 }
409 }
410 }
411 }
412 if (best_subset == kNotFound) { // Local minimum reached.
413 inv_->LoadSolution(best_choices);
414 return true;
415 }
416 DVLOG(1) << "Best subset, " << best_subset.value() << ", at ,"
417 << inv_->is_selected()[best_subset] << ", best_delta = ,"
418 << best_delta;
419
420 UpdatePenalties(focus);
421 tabu_list_.Add(best_subset);
422 inv_->Flip(best_subset);
423 // TODO(user): make the cost computation incremental.
424 augmented_cost =
425 std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0);
426
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();
435 best_choices = inv_->is_selected();
436 }
437 }
438 inv_->LoadSolution(best_choices);
439 inv_->CompressTrace();
440 DCHECK(inv_->CheckConsistency());
441 return true;
442}
443
444// Guided Local Search
446 const SparseColumnView& columns = inv_->model()->columns();
447 penalties_.assign(columns.size(), 0);
448 penalization_factor_ = alpha_ * inv_->cost() * 1.0 / (columns.size());
449 for (const SetCoverDecision& decision : inv_->trace()) {
450 const SubsetIndex subset = decision.subset();
451 if (inv_->is_selected()[subset]) {
452 utility_heap_.Insert(
453 {static_cast<float>(inv_->model()->subset_costs()[subset] /
454 (1 + penalties_[subset])),
455 subset.value()});
456 }
457 }
458}
459
460bool GuidedLocalSearch::NextSolution(int num_iterations) {
461 return NextSolution(inv_->model()->all_subsets(), num_iterations);
462}
463
464Cost GuidedLocalSearch::ComputeDelta(SubsetIndex subset) const {
465 float delta = (penalization_factor_ * penalties_[subset] +
466 inv_->model()->subset_costs()[subset]);
467 if (inv_->is_selected()[subset] && inv_->ComputeIsRedundant(subset)) {
468 return delta;
469 } else if (!inv_->is_selected()[subset]) {
470 return -delta;
471 }
472 return kInfinity;
473}
474
475bool GuidedLocalSearch::NextSolution(absl::Span<const SubsetIndex> focus,
476 int num_iterations) {
477 inv_->MakeFullyUpdated();
478 Cost best_cost = inv_->cost();
479 SubsetBoolVector best_choices = inv_->is_selected();
480
481 for (const SubsetIndex& subset : focus) {
482 const float delta = ComputeDelta(subset);
483 if (delta < kInfinity) {
484 priority_heap_.Insert({delta, subset.value()});
485 }
486 }
487
488 for (int iteration = 0; iteration < num_iterations; ++iteration) {
489 // Improve current solution respective to the current penalties.
490 const SubsetIndex best_subset(priority_heap_.TopIndex());
491 if (inv_->is_selected()[best_subset]) {
492 utility_heap_.Insert({0, best_subset.value()});
493 } else {
494 utility_heap_.Insert(
495 {static_cast<float>(inv_->model()->subset_costs()[best_subset] /
496 (1 + penalties_[best_subset])),
497 best_subset.value()});
498 }
499 inv_->FlipAndFullyUpdate(best_subset); // Flip the best subset.
500
501 // Getting the subset with highest utility.
502 const SubsetIndex penalized_subset(utility_heap_.TopIndex());
503 utility_heap_.Pop();
504 ++penalties_[penalized_subset];
505 utility_heap_.Insert(
506 {static_cast<float>(inv_->model()->subset_costs()[penalized_subset] /
507 (1 + penalties_[penalized_subset])),
508 penalized_subset.value()});
509
510 // Get removable subsets (Add them to the heap).
511 for (const SubsetIndex subset : inv_->new_removable_subsets()) {
512 const float delta_selected = (penalization_factor_ * penalties_[subset] +
513 inv_->model()->subset_costs()[subset]);
514 priority_heap_.Insert({delta_selected, subset.value()});
515 }
516
517 for (const SubsetIndex subset : {penalized_subset, best_subset}) {
518 const float delta = ComputeDelta(subset);
519 if (delta < kInfinity) {
520 priority_heap_.Insert({delta, subset.value()});
521 }
522 }
523
524 // Get new non removable subsets.
525 // (Delete them from the heap)
526 for (const SubsetIndex subset : inv_->new_non_removable_subsets()) {
527 priority_heap_.Remove(subset.value());
528 }
529
530 if (inv_->cost() < best_cost) {
531 best_cost = inv_->cost();
532 best_choices = inv_->is_selected();
533 }
534 }
535 inv_->LoadSolution(best_choices);
536
537 // Improve the solution by removing redundant subsets.
538 for (const SubsetIndex& subset : focus) {
539 if (inv_->is_selected()[subset] && inv_->ComputeIsRedundant(subset))
540 inv_->DeselectAndFullyUpdate(subset);
541 }
542 DCHECK_EQ(inv_->num_uncovered_elements(), 0);
543 return true;
544}
545
546namespace {
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);
552}
553} // namespace
554
555std::vector<SubsetIndex> ClearRandomSubsets(std::size_t num_subsets,
556 SetCoverInvariant* inv) {
557 return ClearRandomSubsets(inv->model()->all_subsets(), num_subsets, inv);
558}
559
560std::vector<SubsetIndex> ClearRandomSubsets(absl::Span<const SubsetIndex> focus,
561 std::size_t num_subsets,
562 SetCoverInvariant* inv) {
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) {
567 if (inv->is_selected()[subset]) {
568 chosen_indices.push_back(subset);
569 }
570 }
571 SampleSubsets(&chosen_indices, num_subsets);
572 std::size_t num_deselected = 0;
573 for (const SubsetIndex subset : chosen_indices) {
574 inv->Deselect(subset);
575 ++num_deselected;
576 for (IntersectingSubsetsIterator it(*inv->model(), subset); !it.at_end();
577 ++it) {
578 if (!inv->is_selected()[subset]) continue;
579 inv->Deselect(subset);
580 ++num_deselected;
581 }
582 // Note that num_deselected may exceed num_subsets by more than 1.
583 if (num_deselected > num_subsets) break;
584 }
585 return chosen_indices;
586}
587
588std::vector<SubsetIndex> ClearMostCoveredElements(std::size_t max_num_subsets,
589 SetCoverInvariant* inv) {
590 return ClearMostCoveredElements(inv->model()->all_subsets(), max_num_subsets,
591 inv);
592}
593
594std::vector<SubsetIndex> ClearMostCoveredElements(
595 absl::Span<const SubsetIndex> focus, std::size_t max_num_subsets,
596 SetCoverInvariant* inv) {
597 // This is the vector we will return.
598 std::vector<SubsetIndex> sampled_subsets;
599
600 const ElementToIntVector& coverage = inv->coverage();
601 const BaseInt num_subsets = inv->model()->num_subsets();
602 const SparseRowView& rows = inv->model()->rows();
603
604 // Collect the sets which have at least one element whose coverage > 1,
605 // even if those sets are not removable.
606 SubsetBoolVector subset_is_collected(num_subsets, false);
607 for (const ElementIndex element : inv->model()->ElementRange()) {
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;
612 }
613 }
614 }
615
616 // Now intersect with focus: sampled_subsets = focus ⋂ impacted_subsets.
617 // NOTE(user): this might take too long. TODO(user):find another algorithm if
618 // necessary.
619 for (const SubsetIndex subset : focus) {
620 if (subset_is_collected[subset]) {
621 sampled_subsets.push_back(subset);
622 }
623 }
624
625 // Actually *sample* sampled_subset.
626 // TODO(user): find another algorithm if necessary.
627 std::shuffle(sampled_subsets.begin(), sampled_subsets.end(), absl::BitGen());
628 sampled_subsets.resize(std::min(sampled_subsets.size(), max_num_subsets));
629
630 // Testing has shown that sorting sampled_subsets is not necessary.
631 // Now, un-select the subset in sampled_subsets.
632 for (const SubsetIndex subset : sampled_subsets) {
633 inv->Deselect(subset);
634 }
635 return sampled_subsets;
636}
637
638} // namespace operations_research
IntegerValue size
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.
void Initialize()
Initializes the Guided Local Search algorithm.
void Initialize()
Initializes the Guided Tabu Search algorithm.
bool at_end() const
Returns (true) whether the iterator is at the end.
bool NextSolution()
Returns true if a solution was found.
A helper class used to store the decisions made during a search.
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 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.
const SubsetBoolVector & is_selected() const
Returns the subset assignment vector.
SetCoverModel * model() const
Returns the weighted set covering model to which the state applies.
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.
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.
const SubsetCostVector & subset_costs() const
Vector of costs for each subset.
const SparseColumnView & columns() const
Column view of the set covering problem.
std::vector< SubsetIndex > all_subsets() const
Returns the list of indices for all the subsets in the model.
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.
void assign(size_type n, const value_type &val)
int64_t b
Definition table.cc:45
int64_t a
Definition table.cc:44
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
int64_t delta
Definition resource.cc:1709
Fractional ratio