21 #include "absl/flags/flag.h"
22 #include "absl/flags/usage.h"
23 #include "absl/log/flags.h"
24 #include "absl/log/initialize.h"
25 #include "absl/random/random.h"
26 #include "absl/status/status.h"
27 #include "absl/strings/match.h"
28 #include "absl/strings/numbers.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_format.h"
31 #include "absl/strings/string_view.h"
36 #include "ortools/linear_solver/linear_solver.pb.h"
41 #include "ortools/sat/boolean_problem.pb.h"
42 #include "ortools/sat/cp_model.pb.h"
51 #include "ortools/sat/sat_parameters.pb.h"
61 std::string,
input,
"",
62 "Required: input file of the problem to solve. Many format are supported:"
63 ".cnf (sat, max-sat, weighted max-sat), .opb (pseudo-boolean sat/optim) "
64 "and by default the LinearBooleanProblem proto (binary or text).");
67 std::string, output,
"",
68 "If non-empty, write the input problem as a LinearBooleanProblem proto to "
69 "this file. By default it uses the binary format except if the file "
70 "extension is '.txt'. If the problem is SAT, a satisfiable assignment is "
71 "also written to the file.");
74 "If true and the problem was solved to optimality, this output "
75 "the solution to stdout in cnf form.\n");
78 "Parameters for the sat solver in a text format of the "
79 "SatParameters proto, example: --params=use_conflicts:true.");
82 "If true, stop if the given input is invalid (duplicate literals, "
83 "out of range, zero cofficients, etc.)");
87 "If not empty, look for a solution with an objective value >= this bound.");
91 "If not empty, look for a solution with an objective value <= this bound.");
94 "If true, search the optimal solution with the Fu & Malik algo.");
97 "If true, search the optimal solution with the WPM1 algo.");
100 "If true, search the optimal solution with a linear scan and "
101 " the cardinality encoding used in qmaxsat.");
104 "If true, search the optimal solution with the core-based "
105 "cardinality encoding algo.");
108 "If true, search the optimal solution with the linear scan algo.");
111 "If positive, solve that many times the problem with a random "
112 "decision heuristic before trying to optimize it.");
115 "If true, find and exploit the eventual symmetries "
119 "Only work on pure SAT problem. If true, presolve the problem.");
121 ABSL_FLAG(
bool, probing,
false,
"If true, presolve the problem using probing.");
124 "Whether to interpret everything as a CpModelProto or "
125 "to read by default a CpModelProto.");
128 "If true, do not keep a copy of the original problem in memory."
129 "This reduce the memory usage, but disable the solution cheking at "
138 double GetScaledTrivialBestBound(
const LinearBooleanProblem& problem) {
140 const LinearObjective& objective = problem.objective();
141 for (
const int64_t
value : objective.coefficients()) {
148 LinearBooleanProblem* problem, CpModelProto* cp_model) {
149 if (absl::EndsWith(filename,
".opb") ||
150 absl::EndsWith(filename,
".opb.bz2")) {
152 if (!reader.Load(filename, problem)) {
153 LOG(FATAL) <<
"Cannot load file '" << filename <<
"'.";
155 }
else if (absl::EndsWith(filename,
".cnf") ||
156 absl::EndsWith(filename,
".cnf.gz") ||
157 absl::EndsWith(filename,
".wcnf") ||
158 absl::EndsWith(filename,
".wcnf.gz")) {
160 if (absl::GetFlag(FLAGS_fu_malik) || absl::GetFlag(FLAGS_linear_scan) ||
161 absl::GetFlag(FLAGS_wpm1) || absl::GetFlag(FLAGS_qmaxsat) ||
162 absl::GetFlag(FLAGS_core_enc)) {
163 reader.InterpretCnfAsMaxSat(
true);
165 if (absl::GetFlag(FLAGS_use_cp_model)) {
166 if (!reader.Load(filename, cp_model)) {
167 LOG(FATAL) <<
"Cannot load file '" << filename <<
"'.";
170 if (!reader.Load(filename, problem)) {
171 LOG(FATAL) <<
"Cannot load file '" << filename <<
"'.";
174 }
else if (absl::GetFlag(FLAGS_use_cp_model)) {
175 LOG(INFO) <<
"Reading a CpModelProto.";
176 *cp_model = ReadFileToProtoOrDie<CpModelProto>(filename);
178 LOG(INFO) <<
"Reading a LinearBooleanProblem.";
179 *problem = ReadFileToProtoOrDie<LinearBooleanProblem>(filename);
184 std::string SolutionString(
const LinearBooleanProblem& problem,
185 const std::vector<bool>& assignment) {
187 BooleanVariable limit(problem.original_num_variables());
189 if (
index > 0) output +=
" ";
190 absl::StrAppend(&output,
191 Literal(
index, assignment[
index.value()]).SignedValue());
200 if (absl::GetFlag(FLAGS_input).empty()) {
201 LOG(FATAL) <<
"Please supply a data file with --input=";
206 if (!absl::GetFlag(FLAGS_params).empty()) {
207 CHECK(google::protobuf::TextFormat::MergeFromString(
209 << absl::GetFlag(FLAGS_params);
213 std::unique_ptr<SatSolver> solver(
new SatSolver());
217 LinearBooleanProblem problem;
218 CpModelProto cp_model;
224 if (!absl::GetFlag(FLAGS_use_cp_model)) {
225 LOG(INFO) <<
"Converting to CpModelProto ...";
231 if (absl::GetFlag(FLAGS_use_cp_model)) {
237 if (!absl::GetFlag(FLAGS_output).empty()) {
238 if (absl::EndsWith(absl::GetFlag(FLAGS_output),
"txt")) {
255 if (absl::GetFlag(FLAGS_strict_validity)) {
258 LOG(ERROR) <<
"Invalid Boolean problem: " <<
status.message();
268 double scaled_best_bound = GetScaledTrivialBestBound(problem);
271 SatPostsolver probing_postsolver(problem.num_variables());
272 LinearBooleanProblem original_problem;
273 if (absl::GetFlag(FLAGS_probing)) {
275 original_problem = problem;
280 if (absl::GetFlag(FLAGS_reduce_memory_usage)) {
282 LOG(INFO) <<
"UNSAT when loading the problem.";
286 LOG(INFO) <<
"UNSAT when loading the problem.";
289 auto strtoint64 = [](
const std::string& word) {
291 if (!word.empty()) CHECK(absl::SimpleAtoi(word, &
value));
295 problem, !absl::GetFlag(FLAGS_lower_bound).empty(),
297 !absl::GetFlag(FLAGS_upper_bound).empty(),
300 LOG(INFO) <<
"UNSAT when setting the objective constraint.";
307 if (absl::GetFlag(FLAGS_use_symmetry)) {
308 CHECK(!absl::GetFlag(FLAGS_reduce_memory_usage)) <<
"incompatible";
309 CHECK(!absl::GetFlag(FLAGS_presolve)) <<
"incompatible";
310 LOG(INFO) <<
"Finding symmetries of the problem.";
311 std::vector<std::unique_ptr<SparsePermutation>> generators;
313 std::unique_ptr<SymmetryPropagator> propagator(
new SymmetryPropagator);
314 for (
int i = 0; i < generators.size(); ++i) {
315 propagator->AddSymmetry(std::move(generators[i]));
317 solver->AddPropagator(propagator.get());
318 solver->TakePropagatorOwnership(std::move(propagator));
322 std::vector<bool> solution;
324 if (absl::GetFlag(FLAGS_fu_malik) || absl::GetFlag(FLAGS_linear_scan) ||
325 absl::GetFlag(FLAGS_wpm1) || absl::GetFlag(FLAGS_qmaxsat) ||
326 absl::GetFlag(FLAGS_core_enc)) {
327 if (absl::GetFlag(FLAGS_randomize) > 0 &&
328 (absl::GetFlag(FLAGS_linear_scan) || absl::GetFlag(FLAGS_qmaxsat))) {
329 CHECK(!absl::GetFlag(FLAGS_reduce_memory_usage)) <<
"incompatible";
332 absl::GetFlag(FLAGS_randomize), bitgen,
333 solver.get(), &solution);
336 if (absl::GetFlag(FLAGS_qmaxsat)) {
337 solver = std::make_unique<SatSolver>();
342 }
else if (absl::GetFlag(FLAGS_core_enc)) {
344 solver.get(), &solution);
345 }
else if (absl::GetFlag(FLAGS_fu_malik)) {
347 }
else if (absl::GetFlag(FLAGS_wpm1)) {
349 }
else if (absl::GetFlag(FLAGS_linear_scan)) {
358 if (absl::GetFlag(FLAGS_presolve)) {
368 result = solver->Solve();
378 if (absl::GetFlag(FLAGS_fu_malik) || absl::GetFlag(FLAGS_linear_scan) ||
379 absl::GetFlag(FLAGS_wpm1) || absl::GetFlag(FLAGS_core_enc)) {
380 absl::PrintF(
"s OPTIMUM FOUND\n");
381 CHECK(!solution.empty());
386 if (absl::GetFlag(FLAGS_probing)) {
387 solution = probing_postsolver.PostsolveSolution(solution);
388 problem = original_problem;
391 absl::PrintF(
"s SATISFIABLE\n");
396 if (absl::GetFlag(FLAGS_output_cnf_solution)) {
397 absl::PrintF(
"v %s\n", SolutionString(problem, solution));
399 if (!absl::GetFlag(FLAGS_output).empty()) {
400 CHECK(!absl::GetFlag(FLAGS_reduce_memory_usage)) <<
"incompatible";
404 if (absl::EndsWith(absl::GetFlag(FLAGS_output),
".txt")) {
414 absl::PrintF(
"s UNSATISFIABLE\n");
421 if (solution.empty()) {
422 absl::PrintF(
"c objective: na\n");
423 absl::PrintF(
"c best bound: na\n");
426 absl::PrintF(
"c objective: %.16g\n",
428 absl::PrintF(
"c best bound: %.16g\n", scaled_best_bound);
432 absl::PrintF(
"c booleans: %d\n", solver->NumVariables());
433 absl::PrintF(
"c conflicts: %d\n", solver->num_failures());
434 absl::PrintF(
"c branches: %d\n", solver->num_branches());
435 absl::PrintF(
"c propagations: %d\n", solver->num_propagations());
437 absl::PrintF(
"c usertime: %f\n", user_timer.
Get());
438 absl::PrintF(
"c deterministic_time: %f\n", solver->deterministic_time());
448 "Usage: see flags.\n"
449 "This program solves a given problem with the CP-SAT solver.";
451 int main(
int argc,
char** argv) {
452 absl::InitializeLog();
453 absl::SetProgramUsageMessage(
kUsage);
454 absl::ParseCommandLine(argc, argv);
455 return operations_research::sat::Run();
static std::unique_ptr< TimeLimit > FromParameters(const Parameters ¶meters)
Creates a time limit object initialized from an object that provides methods max_time_in_seconds() an...
SharedResponseManager * response
ModelSharedTimeLimit * time_limit
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
absl::Status SetBinaryProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
std::tuple< int64_t, int64_t, const double > Coefficient
bool AddObjectiveConstraint(const LinearBooleanProblem &problem, bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, SatSolver *solver)
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
Creates parameters for the solver, which you can add to the model with.
double AddOffsetAndScaleObjectiveValue(const LinearBooleanProblem &problem, Coefficient v)
SatSolver::Status SolveWithCardinalityEncodingAndCore(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
void StoreAssignment(const VariablesAssignment &assignment, BooleanAssignment *output)
SatSolver::Status SolveWithLinearScan(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
SatSolver::Status SolveWithRandomParameters(LogBehavior log, const LinearBooleanProblem &problem, int num_times, absl::BitGenRef random, SatSolver *solver, std::vector< bool > *solution)
absl::Status ValidateBooleanProblem(const LinearBooleanProblem &problem)
void FindLinearBooleanProblemSymmetries(const LinearBooleanProblem &problem, std::vector< std::unique_ptr< SparsePermutation >> *generators)
std::string SatStatusString(SatSolver::Status status)
SatSolver::Status SolveWithWPM1(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
bool LoadAndConsumeBooleanProblem(LinearBooleanProblem *problem, SatSolver *solver)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
bool IsAssignmentValid(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
void ProbeAndSimplifyProblem(SatPostsolver *postsolver, LinearBooleanProblem *problem)
Coefficient ComputeObjectiveValue(const LinearBooleanProblem &problem, const std::vector< bool > &assignment)
SatSolver::Status SolveWithPresolve(std::unique_ptr< SatSolver > *solver, TimeLimit *time_limit, std::vector< bool > *solution, DratProofHandler *drat_proof_handler, SolverLogger *logger)
SatSolver::Status SolveWithFuMalik(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
CpModelProto BooleanProblemToCpModelproto(const LinearBooleanProblem &problem)
bool LoadBooleanProblem(const LinearBooleanProblem &problem, SatSolver *solver)
SatSolver::Status SolveWithCardinalityEncoding(LogBehavior log, const LinearBooleanProblem &problem, SatSolver *solver, std::vector< bool > *solution)
void ExtractAssignment(const LinearBooleanProblem &problem, const SatSolver &solver, std::vector< bool > *assignment)
Collection of objects used to extend the Constraint Solver library.
int64_t strtoint64(absl::string_view word)
static int input(yyscan_t yyscanner)
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 LinearBooleanProblem proto (binary or text).")
int main(int argc, char **argv)
static const char kUsage[]