23#include "absl/base/thread_annotations.h"
24#include "absl/flags/flag.h"
25#include "absl/flags/parse.h"
26#include "absl/flags/usage.h"
27#include "absl/log/check.h"
28#include "absl/log/flags.h"
29#include "absl/log/initialize.h"
30#include "absl/log/log.h"
31#include "absl/strings/match.h"
32#include "absl/strings/str_format.h"
33#include "absl/strings/str_split.h"
34#include "absl/strings/string_view.h"
35#include "absl/synchronization/mutex.h"
36#include "absl/types/span.h"
37#include "google/protobuf/arena.h"
38#include "google/protobuf/text_format.h"
56 std::string,
input,
"",
57 "Required: input file of the problem to solve. Many format are supported:"
58 ".cnf (sat, max-sat, weighted max-sat), .opb (pseudo-boolean sat/optim) "
59 "and by default the CpModelProto proto (binary or text).");
62 std::string, hint_file,
"",
63 "Protobuf file containing a CpModelResponse. The solution will be used as a"
64 " hint to bootstrap the search.");
67 "Protobuf file containing a CpModelResponse. If present, the "
68 "tightened models will be used to reduce the domain of variables.");
71 "If non-empty, write the response there. By default it uses the "
72 "binary format except if the file extension is '.txt'.");
75 "Parameters for the sat solver in a text format of the "
76 "SatParameters proto, example: --params=use_conflicts:true.");
79 "If true, when we add a slack variable to reify a soft clause, we "
80 "enforce the fact that when it is true, the clause must be false.");
81ABSL_FLAG(
bool, fingerprint_intermediate_solutions,
false,
82 "Attach the fingerprint of intermediate solutions to the output.");
84 "If true, output the log in a competition format.");
86 "If true, enable interleaved workers when num_workers is 1.");
92void TryToRemoveSuffix(absl::string_view suffix, std::string* str) {
96std::string ExtractName(absl::string_view full_filename) {
99 TryToRemoveSuffix(
"gz", &filename);
100 TryToRemoveSuffix(
"txt", &filename);
101 TryToRemoveSuffix(
"pb", &filename);
102 TryToRemoveSuffix(
"pbtxt", &filename);
103 TryToRemoveSuffix(
"proto", &filename);
104 TryToRemoveSuffix(
"prototxt", &filename);
105 TryToRemoveSuffix(
"textproto", &filename);
106 TryToRemoveSuffix(
"bin", &filename);
110class LastSolutionPrinter {
113 void MaybePrintLastSolution() {
114 absl::MutexLock lock(&mutex_);
115 if (last_solution_printed_)
return;
116 last_solution_printed_ =
true;
118 if (last_solution_.empty()) {
119 std::cout <<
"s UNKNOWN" << std::endl;
121 std::cout <<
"s SATISFIABLE" << std::endl;
123 for (
int i = 0;
i < num_variables_; ++
i) {
124 if (last_solution_[
i]) {
125 absl::StrAppend(&line,
"x",
i + 1,
" ");
127 absl::StrAppend(&line,
"-x",
i + 1,
" ");
129 if (line.size() >= 75) {
130 std::cout <<
"v " << line << std::endl;
135 std::cout <<
"v " << line << std::endl;
140 void set_num_variables(
int num_variables) { num_variables_ = num_variables; }
142 void set_last_solution(absl::Span<const int64_t>
solution) {
143 absl::MutexLock lock(&mutex_);
144 if (last_solution_printed_)
return;
150 bool mark_last_solution_printed() {
151 const absl::MutexLock lock(&mutex_);
152 if (last_solution_printed_) {
155 last_solution_printed_ =
true;
160 int num_variables_ = 0;
161 std::vector<int64_t> last_solution_ ABSL_GUARDED_BY(mutex_);
162 bool last_solution_printed_ ABSL_GUARDED_BY(mutex_) =
false;
166void LogInPbCompetitionFormat(
167 int num_variables,
bool has_objective,
Model* model,
169 std::shared_ptr<LastSolutionPrinter> last_solution_printer) {
170 CHECK(last_solution_printer !=
nullptr);
171 last_solution_printer->set_num_variables(num_variables);
173 const auto log_callback = [](
const std::string& multi_line_input) {
174 if (multi_line_input.empty()) {
175 std::cout <<
"c" << std::endl;
178 const std::vector<absl::string_view> lines =
179 absl::StrSplit(multi_line_input,
'\n');
180 for (
const absl::string_view& line : lines) {
181 std::cout <<
"c " << line << std::endl;
184 model->GetOrCreate<SolverLogger>()->AddInfoLoggingCallback(log_callback);
185 parameters->set_log_to_stdout(
false);
187 const auto response_callback = [last_solution_printer](
189 std::cout <<
"o " <<
static_cast<int64_t
>(r.objective_value()) << std::endl;
190 last_solution_printer->set_last_solution(r.solution());
194 const auto final_response_callback =
195 [num_variables, has_objective,
197 if (!last_solution_printer->mark_last_solution_printed())
return;
199 switch (r->status()) {
202 std::cout <<
"s OPTIMUM FOUND " << std::endl;
204 std::cout <<
"s SATISFIABLE" << std::endl;
208 std::cout <<
"s SATISFIABLE" << std::endl;
211 std::cout <<
"s UNSATISFIABLE" << std::endl;
214 std::cout <<
"s UNSUPPORTED" << std::endl;
217 std::cout <<
"s UNKNOWN" << std::endl;
225 for (
int i = 0;
i < num_variables; ++
i) {
226 if (r->solution(
i)) {
227 absl::StrAppend(&line,
"x",
i + 1,
" ");
229 absl::StrAppend(&line,
"-x",
i + 1,
" ");
231 if (line.size() >= 75) {
232 std::cout <<
"v " << line << std::endl;
237 std::cout <<
"v " << line << std::endl;
242 final_response_callback);
247 if (parameters->num_workers() == 1) {
248 parameters->set_interleave_search(
true);
249 parameters->set_use_rins_lns(
false);
250 parameters->add_subsolvers(
"default_lp");
251 parameters->add_subsolvers(
"max_lp");
252 parameters->add_subsolvers(
"quick_restart");
253 parameters->add_subsolvers(
"core_or_no_lp");
254 parameters->set_num_violation_ls(1);
258bool LoadProblem(
const std::string& filename, absl::string_view hint_file,
261 std::shared_ptr<LastSolutionPrinter> last_solution_printer) {
262 if (absl::EndsWith(filename,
".opb") ||
263 absl::EndsWith(filename,
".opb.bz2") ||
264 absl::EndsWith(filename,
".opb.gz") || absl::EndsWith(filename,
".wbo") ||
265 absl::EndsWith(filename,
".wbo.bz2") ||
266 absl::EndsWith(filename,
".wbo.gz")) {
268 if (!reader.LoadAndValidate(filename, cp_model)) {
269 if (!reader.model_is_supported()) {
270 if (absl::GetFlag(FLAGS_competition_mode)) {
272 std::cout <<
"s UNSUPPORTED" << std::endl;
279 var->
add_domain(std::numeric_limits<int64_t>::min());
280 var->add_domain(std::numeric_limits<int64_t>::max());
288 if (absl::GetFlag(FLAGS_competition_mode)) {
289 const int num_variables =
290 reader.model_is_supported() ? reader.num_variables() : 1;
291 LogInPbCompetitionFormat(num_variables, cp_model->has_objective(), model,
292 parameters, last_solution_printer);
294 if (absl::GetFlag(FLAGS_force_interleave_search)) {
295 SetInterleavedWorkers(parameters);
297 if (parameters->num_workers() >= 2 && parameters->num_workers() <= 15) {
300 parameters->add_ignore_subsolvers(
"max_lp_sym");
301 parameters->add_extra_subsolvers(
"max_lp");
303 }
else if (absl::EndsWith(filename,
".cnf") ||
304 absl::EndsWith(filename,
".cnf.xz") ||
305 absl::EndsWith(filename,
".cnf.gz") ||
306 absl::EndsWith(filename,
".wcnf") ||
307 absl::EndsWith(filename,
".wcnf.xz") ||
308 absl::EndsWith(filename,
".wcnf.gz")) {
309 SatCnfReader reader(absl::GetFlag(FLAGS_wcnf_use_strong_slack));
310 if (!reader.Load(filename, cp_model)) {
311 LOG(FATAL) <<
"Cannot load file '" << filename <<
"'.";
318 if (!hint_file.empty()) {
321 if (!response.solution().empty()) {
322 CHECK_EQ(response.solution_size(), cp_model->variables_size())
323 <<
"The hint from the response proto is not compatible with the "
326 cp_model->clear_solution_hint();
327 for (
int i = 0;
i < cp_model->variables_size(); ++
i) {
328 cp_model->mutable_solution_hint()->add_vars(
i);
329 cp_model->mutable_solution_hint()->add_values(response.solution(
i));
332 LOG(INFO) <<
"The response proto has no solutions, ignoring.";
337 if (!domain_file.empty()) {
340 if (!response.tightened_variables().empty()) {
341 CHECK_EQ(response.tightened_variables_size(), cp_model->variables_size())
342 <<
"The tighened variables from the response proto is not "
343 "compatible with the model proto";
345 for (
int i = 0;
i < cp_model->variables_size(); ++
i) {
347 const Domain tightened_domain =
349 const Domain new_domain =
354 LOG(INFO) <<
"The response proto has no tightened variable domains, "
360 if (cp_model->name().empty()) {
361 cp_model->set_name(ExtractName(filename));
369 if (absl::GetFlag(FLAGS_input).empty()) {
370 LOG(FATAL) <<
"Please supply a data file with --input=";
375 if (!absl::GetFlag(FLAGS_params).empty()) {
376 CHECK(google::protobuf::TextFormat::MergeFromString(
377 absl::GetFlag(FLAGS_params), ¶meters))
378 << absl::GetFlag(FLAGS_params);
382 google::protobuf::Arena arena;
384 google::protobuf::Arena::Create<CpModelProto>(&arena);
385 std::shared_ptr<LastSolutionPrinter> last_solution_printer;
386 if (absl::GetFlag(FLAGS_competition_mode)) {
387 last_solution_printer = std::make_shared<LastSolutionPrinter>();
389 if (!LoadProblem(absl::GetFlag(FLAGS_input), absl::GetFlag(FLAGS_hint_file),
390 absl::GetFlag(FLAGS_domain_file), cp_model, &model,
391 ¶meters, last_solution_printer)) {
392 if (!absl::GetFlag(FLAGS_competition_mode)) {
393 LOG(FATAL) <<
"Cannot load file '" << absl::GetFlag(FLAGS_input) <<
"'.";
399 if (absl::GetFlag(FLAGS_fingerprint_intermediate_solutions)) {
403 return absl::StrFormat(
409 if (absl::GetFlag(FLAGS_competition_mode)) {
410 model.GetOrCreate<SigtermHandler>()->Register([last_solution_printer]() {
411 last_solution_printer->MaybePrintLastSolution();
418 if (!absl::GetFlag(FLAGS_output).empty()) {
419 if (absl::EndsWith(absl::GetFlag(FLAGS_output),
"txt")) {
441 "Usage: see flags.\n"
442 "This program solves a given problem with the CP-SAT solver.";
444int main(
int argc,
char** argv) {
445 absl::InitializeLog();
446 absl::SetProgramUsageMessage(
kUsage);
447 absl::ParseCommandLine(argc, argv);
448 return operations_research::sat::Run();
Domain IntersectionWith(const Domain &domain) const
void add_domain(::int64_t value)
void set_log_search_progress(bool value)
absl::Status SetBinaryProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
absl::string_view Extension(absl::string_view path)
absl::Status SetTextProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
absl::string_view Stem(absl::string_view path)
absl::string_view Basename(absl::string_view path)
constexpr uint64_t kDefaultFingerprintSeed
Default seed for fingerprints.
uint64_t FingerprintRepeatedField(const google::protobuf::RepeatedField< T > &sequence, uint64_t seed)
std::function< SatParameters(Model *)> NewSatParameters(absl::string_view params)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Serializes a Domain into the domain field of a proto.
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
std::function< void(Model *)> NewFeasibleSolutionLogCallback(const std::function< std::string(const CpSolverResponse &response)> &callback)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &callback)
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
absl::Status ReadFileToProto(absl::string_view filename, google::protobuf::Message *proto, bool allow_partial)
static int input(yyscan_t yyscanner)
int main(int argc, char **argv)
ABSL_FLAG(std::string, input, "", "Required: input file of the problem to solve. Many format are supported:" ".cnf (sat, max-sat, weighted max-sat), .opb (pseudo-boolean sat/optim) " "and by default the CpModelProto proto (binary or text).")
static const char kUsage[]