24#include "absl/base/casts.h"
25#include "absl/log/check.h"
26#include "absl/numeric/bits.h"
27#include "absl/random/distributions.h"
28#include "absl/random/random.h"
29#include "absl/types/span.h"
39static constexpr double kInfinity = std::numeric_limits<float>::infinity();
45 for (
const SubsetIndex subset : focus) {
46 result[subset] =
true;
61 absl::Span<const SubsetIndex> focus) {
62 const SubsetIndex num_subsets(inv_->model()->num_subsets());
64 for (
const SubsetIndex subset : focus) {
65 choices[subset] =
true;
67 inv_->LoadSolution(choices);
68 inv_->Recompute(CL::kCostAndCoverage);
79 const std::vector<SubsetIndex>& focus) {
81 std::vector<SubsetIndex> shuffled = focus;
82 std::shuffle(shuffled.begin(), shuffled.end(), absl::BitGen());
83 for (
const SubsetIndex subset : shuffled) {
84 if (inv_->is_selected()[subset])
continue;
85 if (inv_->num_free_elements()[subset] != 0) {
86 inv_->Select(subset, CL::kFreeAndUncovered);
89 inv_->CompressTrace();
90 DCHECK(inv_->CheckConsistency(CL::kFreeAndUncovered));
98 inv_->model()->subset_costs());
102 absl::Span<const SubsetIndex> focus) {
103 return NextSolution(focus, inv_->model()->subset_costs());
108 DCHECK(inv_->CheckConsistency(CL::kCostAndCoverage));
109 inv_->Recompute(CL::kFreeAndUncovered);
111 std::vector<std::pair<float, SubsetIndex::ValueType>> subset_priorities;
112 DVLOG(1) <<
"focus.size(): " << focus.size();
113 subset_priorities.reserve(focus.size());
114 for (
const SubsetIndex subset : focus) {
115 if (!inv_->is_selected()[subset] &&
116 inv_->num_free_elements()[subset] != 0) {
118 const float priority = inv_->num_free_elements()[subset] / costs[subset];
119 subset_priorities.push_back({priority, subset.value()});
126 subset_priorities, inv_->model()->num_subsets());
128 const SubsetIndex best_subset(pq.
TopIndex());
130 inv_->Select(best_subset, CL::kFreeAndUncovered);
132 if (inv_->num_uncovered_elements() == 0)
break;
135 const SubsetIndex subset = *it;
136 const BaseInt marginal_impact(inv_->num_free_elements()[subset]);
137 if (marginal_impact > 0) {
138 const float priority = marginal_impact / costs[subset];
139 pq.
Update({priority, subset.value()});
141 pq.
Remove(subset.value());
144 DVLOG(1) <<
"Cost = " << inv_->cost()
145 <<
" num_uncovered_elements = " << inv_->num_uncovered_elements();
147 inv_->CompressTrace();
149 DCHECK(inv_->CheckConsistency(CL::kFreeAndUncovered));
155class ComputationUsefulnessStats {
162 is_active_(is_active),
163 num_ratio_computations_(),
164 num_useless_computations_(),
167 BaseInt num_subsets = inv_->model()->num_subsets();
168 num_ratio_computations_.assign(num_subsets, 0);
169 num_useless_computations_.assign(num_subsets, 0);
175 void Update(SubsetIndex subset,
BaseInt new_num_free_elements) {
177 if (new_num_free_elements == num_free_elements_[subset]) {
178 ++num_useless_computations_[subset];
180 ++num_ratio_computations_[subset];
181 num_free_elements_[subset] = new_num_free_elements;
188 BaseInt num_subsets_considered = 0;
190 BaseInt num_wasted_ratio_updates = 0;
191 for (
const SubsetIndex subset : inv_->model()->SubsetRange()) {
192 if (num_ratio_computations_[subset] > 0) {
193 ++num_subsets_considered;
194 if (num_ratio_computations_[subset] > 1) {
195 num_ratio_updates += num_ratio_computations_[subset] - 1;
198 num_wasted_ratio_updates += num_useless_computations_[subset];
200 LOG(INFO) <<
"num_subsets_considered = " << num_subsets_considered;
201 LOG(INFO) <<
"num_ratio_updates = " << num_ratio_updates;
202 LOG(INFO) <<
"num_wasted_ratio_updates = " << num_wasted_ratio_updates;
208 const SetCoverInvariant* inv_;
244uint32_t RawBits(uint32_t x) {
return x; }
245uint32_t RawBits(
int x) {
return absl::bit_cast<uint32_t>(x); }
246uint32_t RawBits(
float x) {
return absl::bit_cast<uint32_t>(x); }
247uint64_t RawBits(uint64_t x) {
return x; }
248uint64_t RawBits(int64_t x) {
return absl::bit_cast<uint64_t>(x); }
249uint64_t RawBits(
double x) {
return absl::bit_cast<uint64_t>(x); }
251inline uint32_t Bucket(uint32_t x, uint32_t shift, uint32_t radix) {
252 DCHECK_EQ(0, radix & (radix - 1));
255 return (RawBits(x) >> shift) & (radix - 1);
259int NumBitsToRepresent(T value) {
260 DCHECK_LE(absl::countl_zero(RawBits(value)),
sizeof(T) * CHAR_BIT);
261 return sizeof(T) * CHAR_BIT - absl::countl_zero(RawBits(value));
264template <
typename Key,
typename Counter>
265void UpdateCounters(uint32_t radix,
int shift, std::vector<Key>& keys,
266 std::vector<Counter>& counts) {
267 std::fill(counts.begin(), counts.end(), 0);
268 DCHECK_EQ(counts[0], 0);
269 DCHECK_EQ(0, radix & (radix - 1));
270 const auto num_keys = keys.size();
271 for (int64_t i = 0; i < num_keys; ++i) {
272 ++counts[Bucket(keys[i], shift, radix)];
276 for (uint64_t i = 1; i < radix; ++i) {
277 counts[i] += counts[i - 1];
281template <
typename Key,
typename Payload,
typename Counter>
282void IncreasingCountingSort(uint32_t radix,
int shift, std::vector<Key>& keys,
283 std::vector<Payload>& payloads,
284 std::vector<Key>& scratch_keys,
285 std::vector<Payload>& scratch_payloads,
286 std::vector<Counter>& counts) {
287 DCHECK_EQ(0, radix & (radix - 1));
288 UpdateCounters(radix, shift, keys, counts);
289 const auto num_keys = keys.size();
291 for (int64_t i = num_keys - 1; i >= 0; --i) {
292 Counter c = --counts[Bucket(keys[i], shift, radix)];
293 scratch_keys[c] = keys[i];
294 scratch_payloads[c] = payloads[i];
296 std::swap(keys, scratch_keys);
297 std::swap(payloads, scratch_payloads);
301template <
typename Key,
typename Payload>
302void RadixSort(
int radix_log, std::vector<Key>& keys,
303 std::vector<Payload>& payloads, Key , Key max_key) {
307 const int range_log = internal::NumBitsToRepresent(max_key);
308 DCHECK_EQ(internal::NumBitsToRepresent(0), 0);
309 DCHECK_LE(internal::NumBitsToRepresent(std::numeric_limits<Key>::max()),
310 std::numeric_limits<Key>::digits);
311 const int radix = 1 << radix_log;
312 std::vector<uint32_t> counters(radix, 0);
313 std::vector<Key> scratch_keys(keys.size());
314 std::vector<Payload> scratch_payloads(payloads.size());
315 for (
int shift = 0; shift < range_log; shift += radix_log) {
316 DCHECK_LE(1 << shift, max_key);
317 internal::IncreasingCountingSort(radix, shift, keys, payloads, scratch_keys,
318 scratch_payloads, counters);
323std::vector<ElementIndex> GetUncoveredElementsSortedByDegree(
325 const BaseInt num_elements = inv->model()->num_elements();
326 std::vector<ElementIndex> degree_sorted_elements;
327 degree_sorted_elements.reserve(num_elements);
328 std::vector<BaseInt> keys;
329 keys.reserve(num_elements);
332 for (
const ElementIndex element : inv->model()->ElementRange()) {
334 if (inv->coverage()[element] != 0)
continue;
335 degree_sorted_elements.push_back(element);
336 const BaseInt size = rows[element].size();
337 max_degree = std::max(max_degree, size);
338 keys.push_back(size);
340 RadixSort(11, keys, degree_sorted_elements, 1, max_degree);
343 for (
const auto key : keys) {
344 DCHECK_LE(prev_key, key);
348 return degree_sorted_elements;
357 return c1 * n2 - n1 * c2;
366 const SubsetIndex num_subsets(inv_->model()->num_subsets());
368 return NextSolution(in_focus, inv_->model()->subset_costs());
372 absl::Span<const SubsetIndex> focus) {
373 const SubsetIndex num_subsets(inv_->model()->num_subsets());
375 return NextSolution(in_focus, inv_->model()->subset_costs());
380 const SubsetIndex num_subsets(inv_->model()->num_subsets());
387 DVLOG(1) <<
"Entering ElementDegreeSolutionGenerator::NextSolution";
390 std::vector<ElementIndex> degree_sorted_elements =
391 GetUncoveredElementsSortedByDegree(inv_);
392 ComputationUsefulnessStats stats(inv_,
false);
394 for (
const ElementIndex element : degree_sorted_elements) {
396 if (inv_->
coverage()[element] != 0)
continue;
397 SubsetIndex best_subset(-1);
398 Cost best_subset_cost = 0.0;
399 BaseInt best_subset_num_free_elts = 0;
400 for (
const SubsetIndex subset : rows[element]) {
401 if (!in_focus[subset])
continue;
403 stats.Update(subset, num_free_elements);
404 const Cost det = Determinant(costs[subset], num_free_elements,
405 best_subset_cost, best_subset_num_free_elts);
414 (det == 0 && num_free_elements > best_subset_num_free_elts)) {
415 best_subset = subset;
416 best_subset_cost = costs[subset];
417 best_subset_num_free_elts = num_free_elements;
420 if (best_subset.value() == -1) {
421 LOG(WARNING) <<
"Best subset not found. Algorithmic error or invalid "
425 DCHECK_NE(best_subset.value(), -1);
426 inv_->Select(best_subset, CL::kFreeAndUncovered);
427 DVLOG(1) <<
"Cost = " << inv_->cost()
428 <<
" num_uncovered_elements = " << inv_->num_uncovered_elements();
430 inv_->CompressTrace();
432 DCHECK(inv_->CheckConsistency(CL::kFreeAndUncovered));
441 const SubsetIndex num_subsets(inv_->model()->num_subsets());
443 return NextSolution(in_focus, inv_->model()->subset_costs());
447 absl::Span<const SubsetIndex> focus) {
448 const SubsetIndex num_subsets(inv_->model()->num_subsets());
450 return NextSolution(in_focus, inv_->model()->subset_costs());
455 const SubsetIndex num_subsets(inv_->model()->num_subsets());
462 DVLOG(1) <<
"Entering LazyElementDegreeSolutionGenerator::NextSolution";
465 std::vector<ElementIndex> degree_sorted_elements =
466 GetUncoveredElementsSortedByDegree(inv_);
469 ComputationUsefulnessStats stats(inv_,
false);
470 for (
const ElementIndex element : degree_sorted_elements) {
472 if (inv_->
coverage()[element] != 0)
continue;
473 SubsetIndex best_subset(-1);
474 Cost best_subset_cost = 0.0;
475 BaseInt best_subset_num_free_elts = 0;
476 for (
const SubsetIndex subset : rows[element]) {
477 if (!in_focus[subset])
continue;
478 const Cost filtering_det =
479 Determinant(costs[subset], columns[subset].size(), best_subset_cost,
480 best_subset_num_free_elts);
483 if (filtering_det > 0)
continue;
485 stats.Update(subset, num_free_elements);
486 const Cost det = Determinant(costs[subset], num_free_elements,
487 best_subset_cost, best_subset_num_free_elts);
490 (det == 0 && num_free_elements > best_subset_num_free_elts)) {
491 best_subset = subset;
492 best_subset_cost = costs[subset];
493 best_subset_num_free_elts = num_free_elements;
496 DCHECK_NE(best_subset, SubsetIndex(-1));
497 inv_->Select(best_subset, CL::kCostAndCoverage);
498 DVLOG(1) <<
"Cost = " << inv_->cost()
499 <<
" num_uncovered_elements = " << inv_->num_uncovered_elements();
501 inv_->CompressTrace();
502 DCHECK(inv_->CheckConsistency(CL::kCostAndCoverage));
509void SteepestSearch::UpdatePriorities(absl::Span<const SubsetIndex>) {}
512 const SubsetIndex num_subsets(inv_->model()->num_subsets());
514 return NextSolution(in_focus, inv_->model()->subset_costs(), num_iterations);
518 int num_iterations) {
519 const SubsetIndex num_subsets(inv_->model()->num_subsets());
521 return NextSolution(focus, inv_->model()->subset_costs(), num_iterations);
526 int num_iterations) {
527 const SubsetIndex num_subsets(inv_->model()->num_subsets());
534 int num_iterations) {
537 DVLOG(1) <<
"Entering SteepestSearch::NextSolution, num_iterations = "
547 std::vector<std::pair<float, SubsetIndex::ValueType>> subset_priorities;
548 subset_priorities.reserve(in_focus.size());
549 for (
const SetCoverDecision& decision : inv_->
trace()) {
550 const SubsetIndex subset = decision.subset();
551 if (in_focus[subset] && inv_->
is_selected()[subset] &&
553 const float delta_per_element = costs[subset];
554 subset_priorities.push_back({delta_per_element, subset.value()});
557 DVLOG(1) <<
"subset_priorities.size(): " << subset_priorities.size();
558 AdjustableKAryHeap<float, SubsetIndex::ValueType, 16, true> pq(
559 subset_priorities, inv_->model()->num_subsets());
560 for (
int iteration = 0; iteration < num_iterations && !pq.IsEmpty();
562 const SubsetIndex best_subset(pq.TopIndex());
564 DCHECK(inv_->is_selected()[best_subset]);
565 DCHECK(inv_->ComputeIsRedundant(best_subset));
566 DCHECK_GT(costs[best_subset], 0.0);
567 inv_->Deselect(best_subset, CL::kFreeAndUncovered);
569 for (IntersectingSubsetsIterator it(*inv_->model(), best_subset);
570 !it.at_end(); ++it) {
571 const SubsetIndex subset = *it;
572 if (!inv_->ComputeIsRedundant(subset)) {
573 pq.Remove(subset.value());
576 DVLOG(1) <<
"Cost = " << inv_->cost();
578 inv_->CompressTrace();
580 DCHECK_EQ(inv_->num_uncovered_elements(), 0);
581 DCHECK(inv_->CheckConsistency(CL::kFreeAndUncovered));
588 const SubsetIndex num_subsets(inv_->model()->num_subsets());
590 times_penalized_.assign(num_subsets.value(), 0);
591 augmented_costs_ = subset_costs;
592 utilities_ = subset_costs;
598 return absl::Bernoulli(absl::BitGen(), 0.5);
602void GuidedTabuSearch::UpdatePenalties(absl::Span<const SubsetIndex> focus) {
604 Cost max_utility = -1.0;
605 for (
const SubsetIndex subset : focus) {
606 if (inv_->is_selected()[subset]) {
607 max_utility = std::max(max_utility, utilities_[subset]);
610 const double epsilon_utility = epsilon_ * max_utility;
611 for (
const SubsetIndex subset : focus) {
612 if (inv_->is_selected()[subset]) {
613 const double utility = utilities_[subset];
614 if ((max_utility - utility <= epsilon_utility) && FlipCoin()) {
615 ++times_penalized_[subset];
616 const int times_penalized = times_penalized_[subset];
618 subset_costs[subset];
619 utilities_[subset] = cost / (1 + times_penalized);
620 augmented_costs_[subset] =
621 cost * (1 + penalty_factor_ * times_penalized);
628 return NextSolution(inv_->model()->all_subsets(), num_iterations);
632 int num_iterations) {
633 DCHECK(inv_->CheckConsistency(CL::kFreeAndUncovered));
634 DVLOG(1) <<
"Entering GuidedTabuSearch::NextSolution, num_iterations = "
637 Cost best_cost = inv_->cost();
639 Cost augmented_cost =
640 std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0);
641 BaseInt trace_size = inv_->trace().size();
642 for (
int iteration = 0; iteration < num_iterations; ++iteration) {
643 if (inv_->trace().size() > 2 * trace_size) {
644 inv_->CompressTrace();
645 trace_size = inv_->trace().size();
649 for (
const SubsetIndex subset : focus) {
650 const Cost delta = augmented_costs_[subset];
651 DVLOG(1) <<
"Subset, " << subset.value() <<
", at ,"
652 << inv_->is_selected()[subset] <<
", delta =, " << delta
653 <<
", best_delta =, " << best_delta;
654 if (inv_->is_selected()[subset]) {
657 if (-delta < best_delta &&
659 inv_->ComputeIsRedundant(subset) &&
661 (!tabu_list_.Contains(subset) ||
662 inv_->cost() - subset_costs[subset] < best_cost)) {
664 best_subset = subset;
668 if (delta < best_delta) {
671 if (!tabu_list_.Contains(subset)) {
673 best_subset = subset;
679 inv_->LoadSolution(best_choices);
682 DVLOG(1) <<
"Best subset, " << best_subset.value() <<
", at ,"
683 << inv_->is_selected()[best_subset] <<
", best_delta = ,"
686 UpdatePenalties(focus);
687 tabu_list_.Add(best_subset);
688 inv_->Flip(best_subset, CL::kFreeAndUncovered);
691 std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0);
693 DVLOG(1) <<
"Iteration, " << iteration <<
", current cost = ,"
694 << inv_->cost() <<
", best cost = ," << best_cost
695 <<
", penalized cost = ," << augmented_cost;
696 if (inv_->cost() < best_cost) {
697 LOG(INFO) <<
"Updated best cost, " <<
"Iteration, " << iteration
698 <<
", current cost = ," << inv_->cost() <<
", best cost = ,"
699 << best_cost <<
", penalized cost = ," << augmented_cost;
700 best_cost = inv_->cost();
701 best_choices = inv_->is_selected();
704 inv_->LoadSolution(best_choices);
705 inv_->CompressTrace();
706 DCHECK(inv_->CheckConsistency(CL::kFreeAndUncovered));
713 penalties_.
assign(columns.size(), 0);
714 penalization_factor_ = alpha_ * inv_->cost() * 1.0 / (columns.size());
716 const SubsetIndex subset = decision.subset();
717 if (inv_->is_selected()[subset]) {
718 utility_heap_.Insert(
719 {
static_cast<float>(inv_->model()->subset_costs()[subset] /
720 (1 + penalties_[subset])),
727 return NextSolution(inv_->model()->all_subsets(), num_iterations);
730Cost GuidedLocalSearch::ComputeDelta(SubsetIndex subset)
const {
731 const float delta = (penalization_factor_ * penalties_[subset] +
732 inv_->
model()->subset_costs()[subset]);
742 int num_iterations) {
743 inv_->Recompute(CL::kRedundancy);
744 Cost best_cost = inv_->cost();
747 for (
const SubsetIndex& subset : focus) {
748 const float delta = ComputeDelta(subset);
750 priority_heap_.Insert({delta, subset.value()});
754 for (
int iteration = 0;
755 !priority_heap_.IsEmpty() && iteration < num_iterations; ++iteration) {
757 const SubsetIndex best_subset(priority_heap_.TopIndex());
758 if (inv_->is_selected()[best_subset]) {
759 utility_heap_.Insert({0, best_subset.value()});
761 utility_heap_.Insert(
762 {
static_cast<float>(inv_->model()->subset_costs()[best_subset] /
763 (1 + penalties_[best_subset])),
764 best_subset.value()});
766 inv_->Flip(best_subset, CL::kRedundancy);
767 DCHECK(!utility_heap_.IsEmpty());
771 const SubsetIndex penalized_subset(utility_heap_.TopIndex());
773 ++penalties_[penalized_subset];
774 utility_heap_.Insert(
775 {
static_cast<float>(inv_->model()->subset_costs()[penalized_subset] /
776 (1 + penalties_[penalized_subset])),
777 penalized_subset.value()});
778 DCHECK(!utility_heap_.IsEmpty());
781 for (
const SubsetIndex subset : inv_->newly_removable_subsets()) {
782 const float delta_selected = (penalization_factor_ * penalties_[subset] +
783 inv_->model()->subset_costs()[subset]);
784 priority_heap_.Insert({delta_selected, subset.value()});
786 DCHECK(!priority_heap_.IsEmpty());
788 for (
const SubsetIndex subset : {penalized_subset, best_subset}) {
789 const float delta = ComputeDelta(subset);
791 priority_heap_.Insert({delta, subset.value()});
794 DCHECK(!priority_heap_.IsEmpty());
799 for (
const SubsetIndex subset : inv_->newly_non_removable_subsets()) {
800 priority_heap_.Remove(subset.value());
803 if (inv_->cost() < best_cost) {
804 best_cost = inv_->cost();
805 best_choices = inv_->is_selected();
808 inv_->LoadSolution(best_choices);
811 for (
const SubsetIndex& subset : focus) {
812 if (inv_->is_selected()[subset] && inv_->ComputeIsRedundant(subset))
813 inv_->Deselect(subset, CL::kRedundancy);
815 DCHECK_EQ(inv_->num_uncovered_elements(), 0);
820void SampleSubsets(std::vector<SubsetIndex>* list,
BaseInt num_subsets) {
821 num_subsets = std::min<BaseInt>(num_subsets, list->size());
822 CHECK_GE(num_subsets, 0);
823 std::shuffle(list->begin(), list->end(), absl::BitGen());
824 list->resize(num_subsets);
836 num_subsets = std::min<BaseInt>(num_subsets, focus.size());
837 CHECK_GE(num_subsets, 0);
838 std::vector<SubsetIndex> chosen_indices;
839 for (
const SubsetIndex subset : focus) {
841 chosen_indices.push_back(subset);
844 SampleSubsets(&chosen_indices, num_subsets);
846 for (
const SubsetIndex subset : chosen_indices) {
847 inv->
Deselect(subset, CL::kCostAndCoverage);
852 inv->
Deselect(subset, CL::kCostAndCoverage);
856 if (num_deselected > num_subsets)
break;
858 return chosen_indices;
868 absl::Span<const SubsetIndex> focus,
BaseInt max_num_subsets,
871 std::vector<SubsetIndex> sampled_subsets;
881 if (coverage[element] <= 1)
continue;
882 for (
const SubsetIndex subset : rows[element]) {
883 if (inv->
is_selected()[subset] && !subset_is_collected[subset]) {
884 subset_is_collected[subset] =
true;
892 for (
const SubsetIndex subset : focus) {
893 if (subset_is_collected[subset]) {
894 sampled_subsets.push_back(subset);
900 std::shuffle(sampled_subsets.begin(), sampled_subsets.end(), absl::BitGen());
901 sampled_subsets.resize(
902 std::min<BaseInt>(sampled_subsets.size(), max_num_subsets));
906 for (
const SubsetIndex subset : sampled_subsets) {
907 inv->
Deselect(subset, CL::kCostAndCoverage);
909 return sampled_subsets;
void Update(Aggregate element)
Change the value of an element.
bool IsEmpty() const
True iff the heap is empty.
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()
Returns true if a solution was found.
A helper class used to store the decisions made during a search.
bool ComputeIsRedundant(SubsetIndex subset) const
void Recompute(ConsistencyLevel target_consistency)
Recomputes the invariant to the given consistency level.
const SubsetToIntVector & num_free_elements() const
const std::vector< SetCoverDecision > & trace() const
Returns the vector of the decisions which have led to the current solution.
bool CheckConsistency(ConsistencyLevel consistency) const
Checks the consistency of the invariant at the given consistency level.
const SubsetBoolVector & is_selected() const
Returns the subset assignment vector.
SetCoverModel * model() const
Returns the weighted set covering model to which the state applies.
const ElementToIntVector & coverage() const
Returns vector containing number of subsets covering each element.
BaseInt ComputeNumFreeElements(SubsetIndex subset) const
Computes the number of free (uncovered) elements in the given subset.
void Deselect(SubsetIndex subset, ConsistencyLevel consistency)
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 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 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)
static constexpr double kInfinity
util_intops::StrongVector< SubsetIndex, Cost > SubsetCostVector
SetCoverInvariant::ConsistencyLevel CL
std::vector< SubsetIndex > ClearRandomSubsets(BaseInt num_subsets, SetCoverInvariant *inv)
The consistency level is maintained up to kCostAndCoverage.
util_intops::StrongVector< SubsetIndex, BaseInt > SubsetToIntVector
static constexpr Cost kMaxPossibleCost
std::vector< SubsetIndex > ClearMostCoveredElements(BaseInt max_num_subsets, SetCoverInvariant *inv)
The consistency level is maintained up to kCostAndCoverage.
util_intops::StrongVector< ElementIndex, BaseInt > ElementToIntVector
util_intops::StrongVector< SubsetIndex, bool > SubsetBoolVector
util_intops::StrongVector< SubsetIndex, SparseColumn > SparseColumnView
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
void RadixSort(absl::Span< T > values)
num_free_elements_[subset]
util_intops::StrongVector< ElementIndex, SparseRow > SparseRowView