21#include "absl/log/check.h"
22#include "absl/types/span.h"
35 DCHECK(model_->ComputeFeasibility());
36 model_->CreateSparseRowView();
43 const BaseInt num_subsets = model_->num_subsets();
44 const BaseInt num_elements = model_->num_elements();
46 is_selected_.assign(num_subsets,
false);
47 num_free_elements_.assign(num_subsets, 0);
48 num_non_overcovered_elements_.assign(num_subsets, 0);
49 is_redundant_.assign(num_subsets,
false);
52 for (
const SubsetIndex subset : model_->SubsetRange()) {
53 num_free_elements_[subset] = columns[subset].size();
54 num_non_overcovered_elements_[subset] = columns[subset].size();
57 coverage_.assign(num_elements, 0);
62 newly_removable_subsets_.clear();
63 newly_non_removable_subsets_.clear();
65 num_uncovered_elements_ = num_elements;
66 consistency_level_ = CL::kRedundancy;
70 CHECK(consistency <= CL::kRedundancy);
71 if (consistency == CL::kInconsistent) {
74 auto [cst, cvrg] = ComputeCostAndCoverage(is_selected_);
76 for (
const ElementIndex element : model_->ElementRange()) {
77 CHECK_EQ(cvrg[element], coverage_[element]);
79 if (consistency == CL::kCostAndCoverage) {
82 auto [num_uncvrd_elts, num_free_elts] =
83 ComputeNumUncoveredAndFreeElements(coverage_);
84 for (
const SubsetIndex subset : model_->SubsetRange()) {
85 CHECK_EQ(num_free_elts[subset], num_free_elements_[subset]);
87 if (consistency == CL::kFreeAndUncovered) {
90 auto [num_non_ovrcvrd_elts, is_rdndnt] = ComputeRedundancyInfo(coverage_);
91 for (
const SubsetIndex subset : model_->SubsetRange()) {
92 CHECK_EQ(is_rdndnt[subset], is_redundant_[subset]);
93 CHECK_EQ(is_rdndnt[subset], num_non_ovrcvrd_elts[subset] == 0);
95 if (consistency == CL::kRedundancy) {
98 LOG(FATAL) <<
"Consistency level not supported: "
99 <<
static_cast<int>(consistency);
107 SubsetIndex subset(0);
114 consistency_level_ = CL::kInconsistent;
118bool SetCoverInvariant::NeedToRecompute(ConsistencyLevel cheched_consistency,
119 ConsistencyLevel target_consistency) {
120 return consistency_level_ < cheched_consistency &&
121 cheched_consistency <= target_consistency;
125 CHECK(target_consistency >= CL::kCostAndCoverage);
126 CHECK(target_consistency <= CL::kRedundancy);
128 if (NeedToRecompute(CL::kCostAndCoverage, target_consistency)) {
129 std::tie(cost_, coverage_) = ComputeCostAndCoverage(is_selected_);
131 if (NeedToRecompute(CL::kFreeAndUncovered, target_consistency)) {
132 std::tie(num_uncovered_elements_, num_free_elements_) =
133 ComputeNumUncoveredAndFreeElements(coverage_);
135 if (NeedToRecompute(CL::kRedundancy, target_consistency)) {
136 std::tie(num_non_overcovered_elements_, is_redundant_) =
137 ComputeRedundancyInfo(coverage_);
139 consistency_level_ = target_consistency;
164std::tuple<Cost, ElementToIntVector> SetCoverInvariant::ComputeCostAndCoverage(
172 SubsetIndex subset(0);
173 for (
const bool b : choices) {
175 cst += subset_costs[subset];
176 for (
const ElementIndex element : columns[subset]) {
186 const absl::Span<const SubsetIndex> focus)
const {
188 for (
const SubsetIndex subset : focus) {
189 if (is_selected_[subset]) {
190 for (
const ElementIndex element : model_->columns()[subset]) {
198std::tuple<BaseInt, SubsetToIntVector>
199SetCoverInvariant::ComputeNumUncoveredAndFreeElements(
208 for (
const SubsetIndex subset : model_->
SubsetRange()) {
209 num_free_elts[subset] = columns[subset].size();
213 for (
const ElementIndex element : model_->
ElementRange()) {
214 if (cvrg[element] >= 1) {
216 for (
const SubsetIndex subset : rows[element]) {
217 --num_free_elts[subset];
221 return {num_uncvrd_elts, num_free_elts};
224std::tuple<SubsetToIntVector, SubsetBoolVector>
226 const BaseInt num_subsets(model_->num_subsets());
232 for (
const SubsetIndex subset : model_->SubsetRange()) {
233 num_cvrg_le_1_elts[subset] = columns[subset].size();
237 for (
const ElementIndex element : model_->ElementRange()) {
238 if (cvrg[element] >= 2) {
239 for (
const SubsetIndex subset : rows[element]) {
240 --num_cvrg_le_1_elts[subset];
241 if (num_cvrg_le_1_elts[subset] == 0) {
242 is_rdndnt[subset] =
true;
247 return {num_cvrg_le_1_elts, is_rdndnt};
255 const SubsetIndex num_subsets(model_->num_subsets());
257 for (
BaseInt i = 0; i < trace_.size(); ++i) {
258 const SubsetIndex subset(trace_[i].subset());
259 last_value_seen[subset] = trace_[i].decision();
262 for (
BaseInt i = 0; i < trace_.size(); ++i) {
263 const SubsetIndex subset(trace_[i].subset());
264 if (last_value_seen[subset]) {
265 last_value_seen[subset] =
false;
274 if (consistency_level_ >= CL::kRedundancy) {
275 return is_redundant_[subset];
277 if (is_selected_[subset]) {
278 for (
const ElementIndex element : model_->columns()[subset]) {
279 if (coverage_[element] <= 1) {
284 for (
const ElementIndex element : model_->columns()[subset]) {
285 if (coverage_[element] == 0) {
295 for (
const ElementIndex element : model_->columns()[subset]) {
296 if (coverage_[element] != 0) {
305 if (!is_selected_[subset]) {
306 Select(subset, target_consistency);
308 Deselect(subset, target_consistency);
314 const bool update_redundancy_info = target_consistency >= CL::kRedundancy;
315 if (update_redundancy_info) {
318 consistency_level_ = std::min(consistency_level_, target_consistency);
319 DVLOG(1) <<
"Selecting subset " << subset;
320 DCHECK(!is_selected_[subset]);
323 is_selected_[subset] =
true;
325 cost_ += subset_costs[subset];
329 if (target_consistency == CL::kCostAndCoverage) {
330 for (
const ElementIndex element : columns[subset]) {
331 ++coverage_[element];
335 for (
const ElementIndex element : columns[subset]) {
336 if (coverage_[element] == 0) {
338 --num_uncovered_elements_;
339 for (
const SubsetIndex impacted_subset : rows[element]) {
340 --num_free_elements_[impacted_subset];
342 }
else if (update_redundancy_info && coverage_[element] == 1) {
344 for (
const SubsetIndex impacted_subset : rows[element]) {
345 --num_non_overcovered_elements_[impacted_subset];
346 if (num_non_overcovered_elements_[impacted_subset] == 0) {
350 DCHECK(!is_redundant_[impacted_subset]);
351 if (is_selected_[impacted_subset]) {
352 newly_removable_subsets_.push_back(impacted_subset);
354 is_redundant_[impacted_subset] =
true;
361 ++coverage_[element];
363 if (update_redundancy_info) {
364 if (is_redundant_[subset]) {
365 newly_removable_subsets_.push_back(subset);
367 newly_non_removable_subsets_.push_back(subset);
376 const bool update_redundancy_info = target_consistency >= CL::kRedundancy;
377 if (update_redundancy_info) {
380 consistency_level_ = std::min(consistency_level_, target_consistency);
381 DVLOG(1) <<
"Deselecting subset " << subset;
383 DCHECK(is_selected_[subset]);
384 DCHECK_EQ(num_free_elements_[subset], 0);
386 is_selected_[subset] =
false;
388 cost_ -= subset_costs[subset];
392 if (target_consistency == CL::kCostAndCoverage) {
393 for (
const ElementIndex element : columns[subset]) {
394 --coverage_[element];
398 for (
const ElementIndex element : columns[subset]) {
401 --coverage_[element];
402 if (coverage_[element] == 0) {
404 ++num_uncovered_elements_;
405 for (
const SubsetIndex impacted_subset : rows[element]) {
406 ++num_free_elements_[impacted_subset];
408 }
else if (update_redundancy_info && coverage_[element] == 1) {
410 for (
const SubsetIndex impacted_subset : rows[element]) {
411 if (num_non_overcovered_elements_[impacted_subset] == 0) {
414 DCHECK(is_redundant_[impacted_subset]);
415 if (is_selected_[impacted_subset]) {
416 newly_non_removable_subsets_.push_back(impacted_subset);
418 is_redundant_[impacted_subset] =
false;
420 ++num_non_overcovered_elements_[impacted_subset];
431 SetCoverSolutionResponse message;
432 message.set_num_subsets(is_selected_.size());
433 Cost lower_bound = std::numeric_limits<Cost>::max();
434 for (
const SubsetIndex subset : model_->SubsetRange()) {
435 if (is_selected_[subset]) {
436 message.add_subset(subset.value());
438 lower_bound = std::min(model_->subset_costs()[subset], lower_bound);
440 message.set_cost(cost_);
441 message.set_cost_lower_bound(lower_bound);
446 const SetCoverSolutionResponse& message) {
447 is_selected_.resize(SubsetIndex(message.num_subsets()),
false);
448 for (
auto s : message.subset()) {
449 is_selected_[SubsetIndex(s)] =
true;
static bool AlmostEquals(const T x, const T y)
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
SetCoverSolutionResponse ExportSolutionAsProto() const
Returns the current solution as a proto.
bool CheckConsistency(ConsistencyLevel consistency) const
Checks the consistency of the invariant at the given consistency level.
void Select(SubsetIndex subset, ConsistencyLevel consistency)
void LoadSolution(const SubsetBoolVector &solution)
Loads the solution and recomputes the data in the invariant.
void ImportSolutionFromProto(const SetCoverSolutionResponse &message)
Imports the solution from a proto.
void Flip(SubsetIndex subset, ConsistencyLevel consistency)
const ElementToIntVector & coverage() const
Returns vector containing number of subsets covering each element.
void ClearTrace()
Clears the trace.
BaseInt ComputeNumFreeElements(SubsetIndex subset) const
Computes the number of free (uncovered) elements in the given subset.
Cost cost() const
Returns the cost of current solution.
void Deselect(SubsetIndex subset, ConsistencyLevel consistency)
ElementToIntVector ComputeCoverageInFocus(absl::Span< const SubsetIndex > focus) const
void Clear()
Clears the invariant. Also called by Initialize.
void ClearRemovabilityInformation()
Clear the removability information.
util_intops::StrongIntRange< ElementIndex > ElementRange() const
const SparseRowView & rows() const
Row view of the set covering problem.
BaseInt num_elements() const
util_intops::StrongIntRange< SubsetIndex > SubsetRange() const
Access to the ranges of subsets and elements.
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
In SWIG mode, we don't want anything besides these top-level includes.
util_intops::StrongVector< SubsetIndex, Cost > SubsetCostVector
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
SetCoverInvariant::ConsistencyLevel CL
util_intops::StrongVector< SubsetIndex, BaseInt > SubsetToIntVector
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%.
util_intops::StrongVector< ElementIndex, SparseRow > SparseRowView
trees with all degrees equal w the current value of w