Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
set_cover_solve.cc
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#include <string>
15#include <vector>
16
17#include "absl/flags/flag.h"
18#include "absl/log/check.h"
19#include "absl/strings/match.h"
20#include "absl/strings/str_join.h"
21#include "absl/strings/string_view.h"
22#include "absl/time/time.h"
29#include "ortools/base/timer.h"
30
31ABSL_FLAG(std::string, input, "", "REQUIRED: Input file name.");
32ABSL_FLAG(std::string, input_fmt, "",
33 "REQUIRED: Input file format. Either proto, proto_bin, OrlibRail, "
34 "OrlibScp or FimiDat.");
35
36ABSL_FLAG(std::string, hint_sol, "", "Input file name for solution.");
37ABSL_FLAG(std::string, hint_fmt, "", "Input file format for solution.");
38
39ABSL_FLAG(std::string, output, "",
40 "If non-empty, write the returned solution to the given file.");
41ABSL_FLAG(std::string, output_fmt, "",
42 "If out is non-empty, use the given format for the output.");
43ABSL_FLAG(std::string, output_model, "",
44 "If non-empty, write the set cover model to the given file. ");
45
46ABSL_FLAG(bool, generate, false, "Generate a new model from the input model.");
47ABSL_FLAG(int, num_elements_wanted, 0,
48 "Number of elements wanted in the new generated model.");
49ABSL_FLAG(int, num_subsets_wanted, 0,
50 "Number of subsets wanted in the new generated model.");
51ABSL_FLAG(float, row_scale, 1.0, "Row scale for the new generated model.");
52ABSL_FLAG(float, column_scale, 1.0,
53 "Column scale for the new generated model.");
54ABSL_FLAG(float, cost_scale, 1.0, "Cost scale for the new generated model.");
55
56ABSL_FLAG(std::string, generation, "", "First-solution generation method.");
57ABSL_FLAG(std::string, improvement, "", "Solution improvement method.");
58ABSL_FLAG(int, num_threads, 1,
59 "Number of threads to use by the underlying solver.");
60
61ABSL_FLAG(bool, solve, false, "Solve the model.");
62ABSL_FLAG(bool, stats, false, "Log stats about the model.");
63
64namespace operations_research {
66
67void LogStats(std::string name, SetCoverModel* model) {
68 LOG(INFO) << ", " << name << ", num_elements, " << model->num_elements()
69 << ", num_subsets, " << model->num_subsets();
70 LOG(INFO) << ", " << name << ", num_nonzeros, " << model->num_nonzeros()
71 << ", fill rate, " << model->FillRate();
72 LOG(INFO) << ", " << name << ", cost, "
73 << model->ComputeCostStats().DebugString();
74
75 LOG(INFO) << ", " << name << ", num_rows, " << model->num_elements()
76 << ", rows sizes, " << model->ComputeRowStats().DebugString();
77 LOG(INFO) << ", " << name << ", row size deciles, "
78 << absl::StrJoin(model->ComputeRowDeciles(), ", ");
79 LOG(INFO) << ", " << name << ", row delta byte size stats, "
81 LOG(INFO) << ", " << name << ", num_columns, " << model->num_subsets()
82 << ", columns sizes, " << model->ComputeColumnStats().DebugString();
83 LOG(INFO) << ", " << name << ", column size deciles, "
84 << absl::StrJoin(model->ComputeColumnDeciles(), ", ");
85 LOG(INFO) << ", " << name << ", column delta byte size stats, "
87 BaseInt num_singleton_rows = 0;
88 for (const ElementIndex element : model->ElementRange()) {
89 if (model->rows()[element].size() == 1) {
90 ++num_singleton_rows;
91 }
92 }
93 BaseInt num_singleton_columns = 0;
94 for (const SubsetIndex subset : model->SubsetRange()) {
95 if (model->columns()[subset].size() == 1) {
96 ++num_singleton_columns;
97 }
98 }
99 LOG(INFO) << ", " << name << ", num_singleton_rows, " << num_singleton_rows
100 << ", num_singleton_columns, " << num_singleton_columns;
101}
102
103void LogCostAndTiming(std::string name, std::string algo, double cost,
104 BaseInt solution_cardinality, const WallTimer& timer) {
105 LOG(INFO) << ", " << name << ", " << algo << ", cost, " << cost
106 << ", solution_cardinality, " << solution_cardinality << ", "
107 << absl::ToInt64Microseconds(timer.GetDuration()) << "e-6, s";
108}
109
110void LogCostAndTiming(std::string name, std::string algo,
111 const SetCoverInvariant& inv, const WallTimer& timer) {
112 LogCostAndTiming(name, algo, inv.cost(), inv.trace().size(), timer);
113}
114
124
125FileFormat ParseFileFormat(const std::string& format_name) {
126 if (format_name.empty()) {
127 return FileFormat::EMPTY;
128 } else if (absl::EqualsIgnoreCase(format_name, "OrlibScp")) {
130 } else if (absl::EqualsIgnoreCase(format_name, "OrlibRail")) {
132 } else if (absl::EqualsIgnoreCase(format_name, "FimiDat")) {
134 } else if (absl::EqualsIgnoreCase(format_name, "proto")) {
135 return FileFormat::PROTO;
136 } else if (absl::EqualsIgnoreCase(format_name, "proto_bin")) {
138 } else if (absl::EqualsIgnoreCase(format_name, "txt")) {
139 return FileFormat::TXT;
140 } else {
141 LOG(FATAL) << "Unsupported input format: " << format_name;
142 }
143}
144
145SetCoverModel ReadModel(absl::string_view input_file, FileFormat input_format) {
146 switch (input_format) {
148 return ReadOrlibScp(input_file);
150 return ReadOrlibRail(input_file);
152 return ReadFimiDat(input_file);
154 return ReadSetCoverProto(input_file, /*binary=*/false);
156 return ReadSetCoverProto(input_file, /*binary=*/true);
157 default:
158 LOG(FATAL) << "Unsupported input format: "
159 << static_cast<int>(input_format);
160 }
161}
162
163SubsetBoolVector ReadSolution(absl::string_view input_file,
164 FileFormat input_format) {
165 switch (input_format) {
166 case FileFormat::TXT:
167 return ReadSetCoverSolutionText(input_file);
169 return ReadSetCoverSolutionProto(input_file, /*binary=*/false);
171 return ReadSetCoverSolutionProto(input_file, /*binary=*/true);
172 default:
173 LOG(FATAL) << "Unsupported input format: "
174 << static_cast<int>(input_format);
175 }
176}
177
178void WriteModel(const SetCoverModel& model, const std::string& output_file,
179 FileFormat output_format) {
180 LOG(INFO) << "Writing model to " << output_file;
181 switch (output_format) {
183 WriteOrlibScp(model, output_file);
184 break;
186 WriteOrlibRail(model, output_file);
187 break;
189 WriteSetCoverProto(model, output_file, /*binary=*/false);
190 break;
192 WriteSetCoverProto(model, output_file, /*binary=*/true);
193 break;
194 default:
195 LOG(FATAL) << "Unsupported output format: "
196 << static_cast<int>(output_format);
197 }
198}
199
201 absl::string_view output_file, FileFormat output_format) {
202 switch (output_format) {
203 case FileFormat::TXT:
204 WriteSetCoverSolutionText(model, solution, output_file);
205 break;
207 WriteSetCoverSolutionProto(model, solution, output_file,
208 /*binary=*/false);
209 break;
211 WriteSetCoverSolutionProto(model, solution, output_file,
212 /*binary=*/true);
213 break;
214 default:
215 LOG(FATAL) << "Unsupported output format: "
216 << static_cast<int>(output_format);
217 }
218}
219
221 SetCoverInvariant inv(model);
222 LazyElementDegreeSolutionGenerator element_degree(&inv);
223 WallTimer timer;
224 timer.Start();
225 CHECK(element_degree.NextSolution());
226 DCHECK(inv.CheckConsistency(CL::kCostAndCoverage));
227 LogCostAndTiming(name, "LazyElementDegreeSolutionGenerator", inv.cost(),
228 inv.trace().size(), timer);
229 return inv;
230}
231
232void Run() {
233 const auto& input = absl::GetFlag(FLAGS_input);
234 const auto& input_format = ParseFileFormat(absl::GetFlag(FLAGS_input_fmt));
235 const auto& output = absl::GetFlag(FLAGS_output);
236 const auto& output_format = ParseFileFormat(absl::GetFlag(FLAGS_output_fmt));
237 if (input.empty()) {
238 LOG(FATAL) << "No input file specified.";
239 }
240 if (!input.empty() && input_format == FileFormat::EMPTY) {
241 LOG(FATAL) << "Input format cannot be empty.";
242 }
243 if (!output.empty() && output_format == FileFormat::EMPTY) {
244 LOG(FATAL) << "Output format cannot be empty.";
245 }
246 SetCoverModel model = ReadModel(input, input_format);
247 if (absl::GetFlag(FLAGS_generate)) {
248 model.CreateSparseRowView();
250 model, absl::GetFlag(FLAGS_num_elements_wanted),
251 absl::GetFlag(FLAGS_num_subsets_wanted), absl::GetFlag(FLAGS_row_scale),
252 absl::GetFlag(FLAGS_column_scale), absl::GetFlag(FLAGS_cost_scale));
253 }
254 if (!output.empty()) {
255 if (output_format == FileFormat::ORLIB_SCP) {
256 model.CreateSparseRowView();
257 }
258 WriteModel(model, output, output_format);
259 }
260 auto problem = output.empty() ? input : output;
261 if (absl::GetFlag(FLAGS_stats)) {
262 LogStats(problem, &model);
263 }
264 if (absl::GetFlag(FLAGS_solve)) {
265 LOG(INFO) << "Solving " << problem;
266 model.CreateSparseRowView();
267 SetCoverInvariant inv = RunLazyElementDegree(problem, &model);
268 }
269}
270} // namespace operations_research
271
272int main(int argc, char** argv) {
273 InitGoogle(argv[0], &argc, &argv, true);
275 return 0;
276}
absl::Duration GetDuration() const
Definition timer.h:49
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:32
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.
Cost cost() const
Returns the cost of current solution.
Main class for describing a weighted set-covering problem.
static SetCoverModel GenerateRandomModelFrom(const SetCoverModel &seed_model, BaseInt num_elements, BaseInt num_subsets, double row_scale, double column_scale, double cost_scale)
Stats ComputeColumnStats()
Computes basic statistics on columns and returns a Stats structure.
std::vector< int64_t > ComputeRowDeciles() const
Computes deciles on rows and returns a vector of deciles.
util_intops::StrongIntRange< ElementIndex > ElementRange() const
Stats ComputeCostStats()
Computes basic statistics on costs and returns a Stats structure.
Stats ComputeRowStats()
Computes basic statistics on rows and returns a Stats structure.
const SparseRowView & rows() const
Row view of the set covering problem.
int64_t num_nonzeros() const
Current number of nonzeros in the matrix.
util_intops::StrongIntRange< SubsetIndex > SubsetRange() const
Access to the ranges of subsets and elements.
std::vector< int64_t > ComputeColumnDeciles() const
Computes deciles on columns and returns a vector of deciles.
const SparseColumnView & columns() const
Column view of the set covering problem.
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
Definition init_google.h:34
In SWIG mode, we don't want anything besides these top-level includes.
SetCoverModel ReadOrlibScp(absl::string_view filename)
This is a row-based format where the elements are 1-indexed.
void WriteSetCoverProto(const SetCoverModel &model, absl::string_view filename, bool binary)
SetCoverModel ReadSetCoverProto(absl::string_view filename, bool binary)
SetCoverModel ReadModel(absl::string_view input_file, FileFormat input_format)
void WriteSetCoverSolutionProto(const SetCoverModel &model, const SubsetBoolVector &solution, absl::string_view filename, bool binary)
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
void LogStats(std::string name, SetCoverModel *model)
SetCoverInvariant::ConsistencyLevel CL
SubsetBoolVector ReadSetCoverSolutionText(absl::string_view filename)
void WriteSetCoverSolutionText(const SetCoverModel &model, const SubsetBoolVector &solution, absl::string_view filename)
SetCoverInvariant RunLazyElementDegree(std::string name, SetCoverModel *model)
FileFormat ParseFileFormat(const std::string &format_name)
SetCoverModel ReadOrlibRail(absl::string_view filename)
This is a column-based format where the elements are 1-indexed.
util_intops::StrongVector< SubsetIndex, bool > SubsetBoolVector
SubsetBoolVector ReadSolution(absl::string_view input_file, FileFormat input_format)
void WriteModel(const SetCoverModel &model, const std::string &output_file, FileFormat output_format)
void WriteOrlibScp(const SetCoverModel &model, absl::string_view filename)
void WriteOrlibRail(const SetCoverModel &model, absl::string_view filename)
Beware the fact that elements written are converted to 1-indexed.
SubsetBoolVector ReadSetCoverSolutionProto(absl::string_view filename, bool binary)
void LogCostAndTiming(std::string name, std::string algo, double cost, BaseInt solution_cardinality, const WallTimer &timer)
SetCoverModel ReadFimiDat(absl::string_view filename)
void WriteSolution(const SetCoverModel &model, const SubsetBoolVector &solution, absl::string_view output_file, FileFormat output_format)
static int input(yyscan_t yyscanner)
int main(int argc, char **argv)
ABSL_FLAG(std::string, input, "", "REQUIRED: Input file name.")