Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
set_cover_model.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 <cmath>
18#include <cstddef>
19#include <cstdint>
20#include <numeric>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/log/check.h"
26#include "ortools/algorithms/set_cover.pb.h"
28
29namespace operations_research {
30
31void SetCoverModel::UpdateAllSubsetsList() {
32 const BaseInt old_size = all_subsets_.size();
33 DCHECK_LE(old_size, num_subsets());
34 all_subsets_.resize(num_subsets());
35 for (BaseInt subset(old_size); subset < num_subsets(); ++subset) {
36 all_subsets_[subset] = SubsetIndex(subset);
37 }
38}
39
41 subset_costs_.push_back(cost);
42 columns_.push_back(SparseColumn());
43 all_subsets_.push_back(SubsetIndex(num_subsets_));
44 ++num_subsets_;
45 CHECK_EQ(columns_.size(), num_subsets());
46 CHECK_EQ(subset_costs_.size(), num_subsets());
47 CHECK_EQ(all_subsets_.size(), num_subsets());
48 row_view_is_valid_ = false;
49}
50
52 columns_.back().push_back(ElementIndex(element));
53 num_elements_ = std::max(num_elements_, element + 1);
54 // No need to update the list all_subsets_.
55 ++num_nonzeros_;
56 row_view_is_valid_ = false;
57}
58
59void SetCoverModel::AddElementToLastSubset(ElementIndex element) {
60 AddElementToLastSubset(element.value());
61}
62
64 CHECK(std::isfinite(cost));
65 DCHECK_GE(subset, 0);
66 num_subsets_ = std::max(num_subsets_, subset + 1);
67 columns_.resize(num_subsets_, SparseColumn());
68 subset_costs_.resize(num_subsets_, 0.0);
69 subset_costs_[SubsetIndex(subset)] = cost;
70 UpdateAllSubsetsList();
71 row_view_is_valid_ = false; // Probably overkill, but better safe than sorry.
72}
73
74void SetCoverModel::SetSubsetCost(SubsetIndex subset, Cost cost) {
75 SetSubsetCost(subset.value(), cost);
76}
77
79 num_subsets_ = std::max(num_subsets_, subset + 1);
80 subset_costs_.resize(num_subsets_, 0.0);
81 columns_.resize(num_subsets_, SparseColumn());
82 UpdateAllSubsetsList();
83 columns_[SubsetIndex(subset)].push_back(ElementIndex(element));
84 num_elements_ = std::max(num_elements_, element + 1);
85 ++num_nonzeros_;
86 row_view_is_valid_ = false;
87}
88
89void SetCoverModel::AddElementToSubset(ElementIndex element,
90 SubsetIndex subset) {
91 AddElementToSubset(element.value(), subset.value());
92}
93
94// Reserves num_subsets columns in the model.
96 num_subsets_ = std::max(num_subsets_, number_of_subsets);
97 columns_.resize(num_subsets_, SparseColumn());
98 subset_costs_.resize(num_subsets_, 0.0);
99}
100
101void SetCoverModel::ReserveNumSubsets(SubsetIndex num_subsets) {
103}
104
105// Reserves num_elements rows in the column indexed by subset.
107 BaseInt subset) {
108 ReserveNumSubsets(subset);
109 columns_[SubsetIndex(subset)].reserve(ColumnEntryIndex(num_elements));
110}
111
112void SetCoverModel::ReserveNumElementsInSubset(ElementIndex num_elements,
113 SubsetIndex subset) {
114 ReserveNumElementsInSubset(num_elements.value(), subset.value());
115}
116
118 if (row_view_is_valid_) {
119 return;
120 }
121 rows_.resize(num_elements_, SparseRow());
122 ElementToIntVector row_sizes(num_elements_, 0);
123 for (const SubsetIndex subset : SubsetRange()) {
124 // Sort the columns. It's not super-critical to improve performance here as
125 // this needs to be done only once.
126 std::sort(columns_[subset].begin(), columns_[subset].end());
127 for (const ElementIndex element : columns_[subset]) {
128 ++row_sizes[element];
129 }
130 }
131 for (const ElementIndex element : ElementRange()) {
132 rows_[element].reserve(RowEntryIndex(row_sizes[element]));
133 }
134 for (const SubsetIndex subset : SubsetRange()) {
135 for (const ElementIndex element : columns_[subset]) {
136 rows_[element].push_back(subset);
137 }
138 }
139 row_view_is_valid_ = true;
140}
141
143 CHECK_GT(num_elements(), 0);
144 CHECK_GT(num_subsets(), 0);
145 CHECK_EQ(columns_.size(), num_subsets());
146 CHECK_EQ(subset_costs_.size(), num_subsets());
147 CHECK_EQ(all_subsets_.size(), num_subsets());
148 ElementToIntVector coverage(num_elements_, 0);
149 for (const Cost cost : subset_costs_) {
150 CHECK_GT(cost, 0.0);
151 }
152 for (const SparseColumn& column : columns_) {
153 CHECK_GT(column.size(), 0);
154 for (const ElementIndex element : column) {
155 ++coverage[element];
156 }
157 }
158 for (const ElementIndex element : ElementRange()) {
159 CHECK_GE(coverage[element], 0);
160 if (coverage[element] == 0) {
161 return false;
162 }
163 }
164 VLOG(1) << "Max possible coverage = "
165 << *std::max_element(coverage.begin(), coverage.end());
166 for (const SubsetIndex subset : SubsetRange()) {
167 CHECK_EQ(all_subsets_[subset.value()], subset) << "subset = " << subset;
168 }
169 return true;
170}
171
173 SetCoverProto message;
174 for (const SubsetIndex subset : SubsetRange()) {
175 SetCoverProto::Subset* subset_proto = message.add_subset();
176 subset_proto->set_cost(subset_costs_[subset]);
177 std::sort(columns_[subset].begin(), columns_[subset].end());
178 for (const ElementIndex element : columns_[subset]) {
179 subset_proto->add_element(element.value());
180 }
181 }
182 return message;
183}
184
186 columns_.clear();
187 subset_costs_.clear();
188 ReserveNumSubsets(message.subset_size());
189 SubsetIndex subset_index(0);
190 for (const SetCoverProto::Subset& subset_proto : message.subset()) {
191 subset_costs_[subset_index] = subset_proto.cost();
192 if (subset_proto.element_size() > 0) {
193 columns_[subset_index].reserve(
194 ColumnEntryIndex(subset_proto.element_size()));
195 for (const BaseInt element : subset_proto.element()) {
196 columns_[subset_index].push_back(ElementIndex(element));
197 num_elements_ = std::max(num_elements_, element + 1);
198 }
199 ++subset_index;
200 }
201 }
202 UpdateAllSubsetsList();
204}
205
206namespace {
207// Returns the standard deviation of the vector v, excluding those values that
208// are zero.
209template <typename T>
210double StandardDeviation(const std::vector<T>& values) {
211 const size_t size = values.size();
212 double n = 0.0; // n is used in a calculation involving doubles.
213 double sum_of_squares = 0.0;
214 double sum = 0.0;
215 for (size_t i = 0; i < size; ++i) {
216 double sample = static_cast<double>(values[i]);
217 if (sample == 0.0) continue;
218 sum_of_squares += sample * sample;
219 sum += sample;
220 ++n;
221 }
222 return n == 0.0 ? 0.0 : sqrt((sum_of_squares - sum * sum / n) / n);
223}
224
225template <typename T>
226SetCoverModel::Stats ComputeStats(std::vector<T> sizes) {
227 SetCoverModel::Stats stats;
228 stats.min = *std::min_element(sizes.begin(), sizes.end());
229 stats.max = *std::max_element(sizes.begin(), sizes.end());
230 stats.mean = std::accumulate(sizes.begin(), sizes.end(), 0.0) / sizes.size();
231 std::nth_element(sizes.begin(), sizes.begin() + sizes.size() / 2,
232 sizes.end());
233 stats.median = sizes[sizes.size() / 2];
234 stats.stddev = StandardDeviation(sizes);
235 return stats;
236}
237
238template <typename T>
239std::vector<T> ComputeDeciles(std::vector<T> values) {
240 const int kNumDeciles = 10;
241 std::vector<T> deciles;
242 deciles.reserve(kNumDeciles);
243 for (int i = 1; i <= kNumDeciles; ++i) {
244 const size_t point = values.size() * i / kNumDeciles - 1;
245 std::nth_element(values.begin(), values.begin() + point, values.end());
246 deciles.push_back(values[point]);
247 }
248 return deciles;
249}
250} // namespace
251
253 std::vector<Cost> subset_costs(num_subsets());
254 std::copy(subset_costs_.begin(), subset_costs_.end(), subset_costs.begin());
255 return ComputeStats(std::move(subset_costs));
256}
257
259 std::vector<ssize_t> row_sizes(num_elements(), 0);
260 for (const SparseColumn& column : columns_) {
261 for (const ElementIndex element : column) {
262 ++row_sizes[element.value()];
263 }
264 }
265 return ComputeStats(std::move(row_sizes));
266}
267
269 std::vector<ssize_t> column_sizes(columns_.size());
270 for (const SubsetIndex subset : SubsetRange()) {
271 column_sizes[subset.value()] = columns_[subset].size();
272 }
273 return ComputeStats(std::move(column_sizes));
274}
275
276std::vector<ssize_t> SetCoverModel::ComputeRowDeciles() const {
277 std::vector<ssize_t> row_sizes(num_elements(), 0);
278 for (const SparseColumn& column : columns_) {
279 for (const ElementIndex element : column) {
280 ++row_sizes[element.value()];
281 }
282 }
283 return ComputeDeciles(std::move(row_sizes));
284}
285
286std::vector<ssize_t> SetCoverModel::ComputeColumnDeciles() const {
287 std::vector<ssize_t> column_sizes(columns_.size());
288 for (const SubsetIndex subset : SubsetRange()) {
289 column_sizes[subset.value()] = columns_[subset].size();
290 }
291 return ComputeDeciles(std::move(column_sizes));
292}
293
294} // namespace operations_research
IntegerValue size
void ImportModelFromProto(const SetCoverProto &message)
Imports the model from a SetCoverProto.
void ReserveNumElementsInSubset(BaseInt num_elements, BaseInt subset)
Reserves num_elements rows in the column indexed by subset.
Stats ComputeColumnStats()
Computes basic statistics on columns and returns a Stats structure.
util_intops::StrongIntRange< ElementIndex > ElementRange() const
void AddElementToLastSubset(BaseInt element)
Stats ComputeCostStats()
Computes basic statistics on costs and returns a Stats structure.
Stats ComputeRowStats()
Computes basic statistics on rows and returns a Stats structure.
std::vector< ssize_t > ComputeColumnDeciles() const
Computes deciles on columns and returns a vector of deciles.
void ReserveNumSubsets(BaseInt num_subsets)
Reserves num_subsets columns in the model.
util_intops::StrongIntRange< SubsetIndex > SubsetRange() const
Access to the ranges of subsets and elements.
void AddElementToSubset(BaseInt element, BaseInt subset)
std::vector< ssize_t > ComputeRowDeciles() const
Computes deciles on rows and returns a vector of deciles.
void SetSubsetCost(BaseInt subset, Cost cost)
const SubsetCostVector & subset_costs() const
Vector of costs for each subset.
void CreateSparseRowView()
Creates the sparse ("dual") representation of the problem.
void push_back(const value_type &val)
void reserve(size_type n)
void resize(size_type new_size)
QuadraticProgramStats ComputeStats(const ShardedQuadraticProgram &qp)
Returns a QuadraticProgramStats for a ShardedQuadraticProgram.
In SWIG mode, we don't want anything besides these top-level includes.
util_intops::StrongVector< ColumnEntryIndex, ElementIndex, ElementAllocator > SparseColumn
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
util_intops::StrongVector< RowEntryIndex, SubsetIndex, SubsetAllocator > SparseRow
int column
std::optional< int64_t > end
std::string message
Definition trace.cc:397