Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
set_cover_invariant.h
Go to the documentation of this file.
1// Copyright 2010-2025 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
14#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_INVARIANT_H_
15#define OR_TOOLS_ALGORITHMS_SET_COVER_INVARIANT_H_
16
17#include <tuple>
18#include <vector>
19
20#include "absl/log/check.h"
21#include "absl/types/span.h"
22#include "ortools/algorithms/set_cover.pb.h"
24
25namespace operations_research {
26
27// A helper class used to store the decisions made during a search.
29 public:
30 SetCoverDecision() : decision_(0) {}
31
32 SetCoverDecision(SubsetIndex subset, bool value) {
33 static_assert(sizeof(subset) == sizeof(decision_));
34 DCHECK_GE(subset.value(), 0);
35 decision_ = value ? subset.value() : ~subset.value();
36 }
37
38 SubsetIndex subset() const {
39 return SubsetIndex(decision_ >= 0 ? decision_ : ~decision_);
40 }
41
42 bool decision() const { return decision_ >= 0; }
43
44 private:
45 BaseInt decision_;
46};
47
48// SetCoverInvariant does the bookkeeping for a solution to the
49// SetCoverModel passed as argument.
50// The state of a SetCoverInvariant instance is uniquely defined by a
51// SubsetBoolVector representing whether a subset is selected in the solution
52// or not.
53//
54// See https://cs.brown.edu/research/pubs/pdfs/1999/Michel-1999-LML.pdf
55// for an explanation of the terminology.
56//
57// A SetCoverInvariant is (relatively) small:
58// is_selected_: a partial solution, vector of Booleans of size #subsets.
59// From this, the following can be computed:
60// coverage_ : number of times an element is covered;
61// num_free_elements_: number of elements in a subset that are uncovered.
62// num_non_overcovered_elements_: the number of elements of a subset that
63// are covered 1 time or less (not overcovered) in the current solution;
64// is_redundant_, whether a subset can be removed from the solution.
65// is_redundant_[subset] == (num_non_overcovered_elements_[subet] == 0).
67 public:
68 // The consistency level of the invariant.
69 // The values denote the level of consistency of the invariant.
70 // There is an order between the levels, and the invariant is consistent at
71 // level k if it is consistent at all levels lower than k.
72 // The consistency level that is the most natural is to use kFreeAndUncovered,
73 // since it enables to implement most heuristics.
74 // kCostAndCoverage is used by LazyElementDegree, a fast greedy heuristic.
75 // kRedundancy is used for GuidedLocalSearch, because knowing whether a
76 // subset is redundant incrementally is faster than recomputing the
77 // information over and over again.
78 // Below, the quantities that are maintained at each level are listed.
79 enum class ConsistencyLevel {
80 kInconsistent = 0, // The invariant is not consistent.
81 kCostAndCoverage, // cost_ and coverage_.
82 kFreeAndUncovered, // num_free_elements_ and num_uncovered_elements_.
83 kRedundancy // is_redundant_ and num_non_overcovered_elements_.
84 };
85
86 // Constructs an empty weighted set covering solver state.
87 // The model may not change after the invariant was built.
88 explicit SetCoverInvariant(SetCoverModel* m) : model_(m) { Initialize(); }
89
90 // Initializes the solver once the data is set. The model cannot be changed
91 // afterwards.
92 void Initialize();
93
94 // Clears the invariant. Also called by Initialize.
95 void Clear();
96
97 // Returns the weighted set covering model to which the state applies.
98 SetCoverModel* model() const { return model_; }
99
100 const SetCoverModel* const_model() const { return model_; }
101
102 // Returns the cost of current solution.
103 Cost cost() const { return cost_; }
104
105 // Returns the number of uncovered elements.
106 BaseInt num_uncovered_elements() const { return num_uncovered_elements_; }
107
108 // Returns the subset assignment vector.
109 const SubsetBoolVector& is_selected() const { return is_selected_; }
110
111 // Returns vector containing the number of elements in each subset that are
112 // not covered in the current solution.
114 return num_free_elements_;
115 }
116
117 // Returns the vector of numbers of free or exactly covered elements for
118 // each subset.
120 return num_non_overcovered_elements_;
121 }
122 // Returns vector containing number of subsets covering each element.
123 const ElementToIntVector& coverage() const { return coverage_; }
124
125 // Returns a vector containing the number of subsets within `focus` covering
126 // each element. Subsets that are without `focus` are not considered.
128 absl::Span<const SubsetIndex> focus) const;
129
130 // Returns vector of Booleans telling whether each subset can be removed from
131 // the solution.
132 const SubsetBoolVector& is_redundant() const { return is_redundant_; }
133
134 // Returns the vector of the decisions which have led to the current solution.
135 const std::vector<SetCoverDecision>& trace() const { return trace_; }
136
137 // Clears the trace.
138 void ClearTrace() { trace_.clear(); }
139
140 // Clear the removability information.
142 newly_removable_subsets_.clear();
143 newly_non_removable_subsets_.clear();
144 }
145
146 // Returns the subsets that become removable after the last update.
147 const std::vector<SubsetIndex>& newly_removable_subsets() const {
148 return newly_removable_subsets_;
149 }
150
151 // Returns the subsets that become non removable after the last update.
152 const std::vector<SubsetIndex>& newly_non_removable_subsets() const {
153 return newly_non_removable_subsets_;
154 }
155
156 // Compresses the trace so that:
157 // - each subset appears only once,
158 // - there are only "positive" decisions.
159 // This trace is equivalent to the original trace in the sense that the cost
160 // and the covered elements are the same.
161 // This can be used to recover the solution by indices after local search.
162 void CompressTrace();
163
164 // Loads the solution and recomputes the data in the invariant.
166
167 // Checks the consistency of the invariant at the given consistency level.
168 bool CheckConsistency(ConsistencyLevel consistency) const;
169
170 // Recomputes the invariant to the given consistency level.
171 void Recompute(ConsistencyLevel target_consistency);
172
173 // Returns true if the subset is redundant within the current solution, i.e.
174 // when all its elements are already covered twice. Note that the set need
175 // not be selected for this to happen.
176 // TODO(user): Implement this using AVX-512?
177 bool ComputeIsRedundant(SubsetIndex subset) const;
178
179 // Computes the number of free (uncovered) elements in the given subset.
180 BaseInt ComputeNumFreeElements(SubsetIndex subset) const;
181
182 // Includes subset in the solution by setting is_selected_[subset] to true
183 // without updating the invariant. Only updates the cost and the coverage.
184 // TODO(user): Merge with Select. Introduce consistency levels and maybe split
185 // the invariant into three.
186 void SelectNoUpdate(SubsetIndex subset);
187
188 // Flips is_selected_[subset] to its negation, by calling Select or Deselect
189 // depending on value. Updates the invariant incrementally to the given
190 // consistency level.
191 void Flip(SubsetIndex subset, ConsistencyLevel consistency);
192
193 // Includes subset in the solution by setting is_selected_[subset] to true
194 // and incrementally updating the invariant to the given consistency level.
195 void Select(SubsetIndex subset, ConsistencyLevel consistency);
196
197 // Excludes subset from the solution by setting is_selected_[subset] to false
198 // and incrementally updating the invariant to the given consistency level.
199 void Deselect(SubsetIndex subset, ConsistencyLevel consistency);
200
201 // Returns the current solution as a proto.
202 SetCoverSolutionResponse ExportSolutionAsProto() const;
203
204 // Imports the solution from a proto.
205 void ImportSolutionFromProto(const SetCoverSolutionResponse& message);
206
207 private:
208 // Computes the cost and the coverage vector for the given choices.
209 // Temporarily uses |E| BaseInts.
210 std::tuple<Cost, ElementToIntVector> ComputeCostAndCoverage(
211 const SubsetBoolVector& choices) const;
212
213 // Computes the global number of uncovered elements and the
214 // vector containing the number of free elements for each subset from
215 // a coverage vector.
216 // Temporarily uses |S| BaseInts.
217 std::tuple<BaseInt, // Number of uncovered elements,
218 SubsetToIntVector> // Vector of number of free elements.
219 ComputeNumUncoveredAndFreeElements(const ElementToIntVector& cvrg) const;
220
221 // Computes the vector containing the number of non-overcovered elements per
222 // subset and the Boolean vector telling whether a subset is redundant w.r.t.
223 // the current solution.
224 // Temporarily uses |S| BaseInts.
225 std::tuple<SubsetToIntVector, // Number of non-overcovered elements,
226 SubsetBoolVector> // Redundancy for each of the subsets.
227 ComputeRedundancyInfo(const ElementToIntVector& cvrg) const;
228
229 // Returns true if the current consistency level consistency_ is lower than
230 // cheched_consistency and the desired consistency is higher than
231 // cheched_consistency.
232 bool NeedToRecompute(ConsistencyLevel cheched_consistency,
233 ConsistencyLevel target_consistency);
234
235 // The weighted set covering model on which the solver is run.
236 SetCoverModel* model_;
237
238 // Current cost.
239 Cost cost_;
240
241 // The number of uncovered (or free) elements in the current solution.
242 BaseInt num_uncovered_elements_;
243
244 // Current assignment.
245 // Takes |S| bits.
246 SubsetBoolVector is_selected_;
247
248 // A trace of the decisions, i.e. a list of decisions (subset, Boolean) that
249 // lead to the current solution.
250 // Takes at most |S| BaseInts.
251 std::vector<SetCoverDecision> trace_;
252
253 // The coverage of an element is the number of used subsets which contains
254 // the said element.
255 // Takes |E| BaseInts
256 ElementToIntVector coverage_;
257
258 // A vector that for each subset gives the number of free elements, i.e.
259 // elements whose coverage is 0.
260 // problem.
261 // Takes |S| BaseInts.
262 SubsetToIntVector num_free_elements_;
263
264 // Counts the number of free or exactly covered elements, i.e. whose coverage
265 // is 0 or 1.
266 // Takes at most |S| BaseInts. (More likely a few percent of that).
267 SubsetToIntVector num_non_overcovered_elements_;
268
269 // True if the subset is redundant, i.e. can be removed from the solution
270 // without making it infeasible.
271 // Takes |S| bits.
272 SubsetBoolVector is_redundant_;
273
274 // Subsets that became removable after the last update.
275 // Takes at most |S| BaseInts. (More likely a few percent of that).
276 std::vector<SubsetIndex> newly_removable_subsets_;
277
278 // Subsets that became non removable after the last update.
279 // Takes at most |S| BaseInts. (More likely a few percent of that).
280 std::vector<SubsetIndex> newly_non_removable_subsets_;
281
282 // Denotes the consistency level of the invariant.
283 // Some algorithms may need to recompute the invariant to a higher consistency
284 // level.
285 // TODO(user): think of making the enforcement of the consistency level
286 // automatic at the constructor level of the heuristic algorithms.
287 ConsistencyLevel consistency_level_;
288};
289
290} // namespace operations_research
291#endif // OR_TOOLS_ALGORITHMS_SET_COVER_INVARIANT_H_
SetCoverDecision(SubsetIndex subset, bool value)
const SetCoverModel * const_model() const
bool ComputeIsRedundant(SubsetIndex subset) const
void Recompute(ConsistencyLevel target_consistency)
Recomputes the invariant to the given consistency level.
const SubsetBoolVector & is_redundant() const
const SubsetToIntVector & num_free_elements() const
SetCoverSolutionResponse ExportSolutionAsProto() const
Returns the current solution as a proto.
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.
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.
const SubsetToIntVector & num_coverage_le_1_elements() const
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 Flip(SubsetIndex subset, ConsistencyLevel consistency)
const std::vector< SubsetIndex > & newly_removable_subsets() const
Returns the subsets that become removable after the last update.
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.
Cost cost() const
Returns the cost of current solution.
void Deselect(SubsetIndex subset, ConsistencyLevel consistency)
void SelectNoUpdate(SubsetIndex subset)
ElementToIntVector ComputeCoverageInFocus(absl::Span< const SubsetIndex > focus) const
void Clear()
Clears the invariant. Also called by Initialize.
void ClearRemovabilityInformation()
Clear the removability information.
BaseInt num_uncovered_elements() const
Returns the number of uncovered elements.
const std::vector< SubsetIndex > & newly_non_removable_subsets() const
Returns the subsets that become non removable after the last update.
Main class for describing a weighted set-covering problem.
In SWIG mode, we don't want anything besides these top-level includes.
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
util_intops::StrongVector< SubsetIndex, BaseInt > SubsetToIntVector
util_intops::StrongVector< ElementIndex, BaseInt > ElementToIntVector
util_intops::StrongVector< SubsetIndex, bool > SubsetBoolVector
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.