31 #if !defined(__PORTABLE_PLATFORM__)
32 #include "absl/synchronization/notification.h"
33 #include "google/protobuf/text_format.h"
36 #endif // __PORTABLE_PLATFORM__
38 #include "absl/container/flat_hash_set.h"
39 #include "absl/memory/memory.h"
40 #include "absl/status/status.h"
41 #include "absl/strings/str_cat.h"
42 #include "absl/strings/str_format.h"
43 #include "absl/strings/str_join.h"
44 #include "absl/synchronization/mutex.h"
45 #include "glog/vlog_is_on.h"
89 "Prefix filename for all dumped files");
91 cp_model_dump_models,
false,
92 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
93 "protos (original model, presolved model, mapping model) in text "
95 "'FLAGS_cp_model_dump_prefix'{model|presolved_model|mapping_model}.pbtxt.");
98 "DEBUG ONLY. When set to true, solve will dump all "
99 "lns models proto in text format to "
100 "'FLAGS_cp_model_dump_prefix'lns_xxx.pbtxt.");
103 "DEBUG ONLY. If true, the final response of each solve will be "
104 "dumped to 'FLAGS_cp_model_dump_prefix'response.pbtxt");
107 "This is interpreted as a text SatParameters proto. The "
108 "specified fields will override the normal ones for all solves.");
112 "If non-empty, a proof in DRAT format will be written to this file. "
113 "This will only be used for pure-SAT problems.");
116 "If true, a proof in DRAT format will be stored in memory and "
117 "checked if the problem is UNSAT. This will only be used for "
118 "pure-SAT problems.");
120 DEFINE_double(max_drat_time_in_seconds, std::numeric_limits<double>::infinity(),
121 "Maximum time in seconds to check the DRAT proof. This will only "
122 "be used is the drat_check flag is enabled.");
125 "When true, all intermediate solutions found by the solver will be "
126 "checked. This can be expensive, therefore it is off by default.");
134 std::string Summarize(
const std::string&
input) {
137 return absl::StrCat(
input.substr(0, half),
" ... ",
148 std::map<std::string, int> num_constraints_by_name;
149 std::map<std::string, int> num_reif_constraints_by_name;
150 std::map<std::string, int> name_to_num_literals;
151 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
156 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
157 if (
ct.linear().vars_size() == 1)
name +=
"1";
158 if (
ct.linear().vars_size() == 2)
name +=
"2";
159 if (
ct.linear().vars_size() == 3)
name +=
"3";
160 if (
ct.linear().vars_size() > 3)
name +=
"N";
163 num_constraints_by_name[
name]++;
164 if (!
ct.enforcement_literal().empty()) {
165 num_reif_constraints_by_name[
name]++;
170 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
171 name_to_num_literals[
name] +=
ct.bool_or().literals().size();
172 }
else if (
ct.constraint_case() ==
173 ConstraintProto::ConstraintCase::kBoolAnd) {
174 name_to_num_literals[
name] +=
ct.bool_and().literals().size();
175 }
else if (
ct.constraint_case() ==
176 ConstraintProto::ConstraintCase::kAtMostOne) {
177 name_to_num_literals[
name] +=
ct.at_most_one().literals().size();
181 int num_constants = 0;
182 std::set<int64> constant_values;
183 std::map<Domain, int> num_vars_per_domains;
185 if (
var.domain_size() == 2 &&
var.domain(0) ==
var.domain(1)) {
187 constant_values.insert(
var.domain(0));
195 absl::StrAppend(&result,
"Optimization model '",
model_proto.name(),
198 absl::StrAppend(&result,
"Satisfaction model '",
model_proto.name(),
202 for (
const DecisionStrategyProto& strategy :
model_proto.search_strategy()) {
204 &result,
"Search strategy: on ", strategy.variables_size(),
206 ProtoEnumToString<DecisionStrategyProto::VariableSelectionStrategy>(
207 strategy.variable_selection_strategy()),
209 ProtoEnumToString<DecisionStrategyProto::DomainReductionStrategy>(
210 strategy.domain_reduction_strategy()),
214 const std::string objective_string =
216 ? absl::StrCat(
" (",
model_proto.objective().vars_size(),
219 absl::StrAppend(&result,
"#Variables: ",
model_proto.variables_size(),
220 objective_string,
"\n");
221 if (num_vars_per_domains.size() < 100) {
222 for (
const auto& entry : num_vars_per_domains) {
223 const std::string temp = absl::StrCat(
" - ", entry.second,
" in ",
224 entry.first.ToString(),
"\n");
225 absl::StrAppend(&result, Summarize(temp));
228 int64 max_complexity = 0;
231 for (
const auto& entry : num_vars_per_domains) {
234 max_complexity =
std::max(max_complexity,
235 static_cast<int64>(entry.first.NumIntervals()));
237 absl::StrAppend(&result,
" - ", num_vars_per_domains.size(),
238 " different domains in [",
min,
",",
max,
239 "] with a largest complexity of ", max_complexity,
".\n");
242 if (num_constants > 0) {
243 const std::string temp =
244 absl::StrCat(
" - ", num_constants,
" constants in {",
245 absl::StrJoin(constant_values,
","),
"} \n");
246 absl::StrAppend(&result, Summarize(temp));
249 std::vector<std::string> constraints;
250 constraints.reserve(num_constraints_by_name.size());
251 for (
const auto& entry : num_constraints_by_name) {
252 const std::string&
name = entry.first;
253 constraints.push_back(absl::StrCat(
"#",
name,
": ", entry.second));
255 absl::StrAppend(&constraints.back(),
256 " (#enforced: ", num_reif_constraints_by_name[
name],
")");
259 absl::StrAppend(&constraints.back(),
260 " (#literals: ", name_to_num_literals[
name],
")");
263 std::sort(constraints.begin(), constraints.end());
264 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
270 bool has_objective) {
272 absl::StrAppend(&result,
"CpSolverResponse:");
273 absl::StrAppend(&result,
"\nstatus: ",
274 ProtoEnumToString<CpSolverStatus>(
response.status()));
277 absl::StrAppendFormat(&result,
"\nobjective: %.9g",
279 absl::StrAppendFormat(&result,
"\nbest_bound: %.9g",
282 absl::StrAppend(&result,
"\nobjective: NA");
283 absl::StrAppend(&result,
"\nbest_bound: NA");
286 absl::StrAppend(&result,
"\nbooleans: ",
response.num_booleans());
287 absl::StrAppend(&result,
"\nconflicts: ",
response.num_conflicts());
288 absl::StrAppend(&result,
"\nbranches: ",
response.num_branches());
292 absl::StrAppend(&result,
293 "\npropagations: ",
response.num_binary_propagations());
295 &result,
"\ninteger_propagations: ",
response.num_integer_propagations());
296 absl::StrAppend(&result,
"\nwalltime: ",
response.wall_time());
297 absl::StrAppend(&result,
"\nusertime: ",
response.user_time());
298 absl::StrAppend(&result,
299 "\ndeterministic_time: ",
response.deterministic_time());
300 absl::StrAppend(&result,
"\nprimal_integral: ",
response.primal_integral());
301 absl::StrAppend(&result,
"\n");
307 void FillSolutionInResponse(
const CpModelProto&
model_proto,
const Model&
model,
310 response->clear_solution_lower_bounds();
311 response->clear_solution_upper_bounds();
313 auto* mapping =
model.Get<CpModelMapping>();
315 auto* integer_trail =
model.Get<IntegerTrail>();
317 std::vector<int64> solution;
318 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
319 if (mapping->IsInteger(i)) {
320 const IntegerVariable
var = mapping->Integer(i);
321 if (integer_trail->IsCurrentlyIgnored(
var)) {
334 DCHECK(mapping->IsBoolean(i));
336 if (trail->Assignment().LiteralIsAssigned(
literal)) {
345 if (!solution.empty()) {
346 if (
DEBUG_MODE || FLAGS_cp_model_check_intermediate_solutions) {
354 const auto& assignment = trail->Assignment();
355 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
356 if (mapping->IsBoolean(i)) {
357 if (assignment.VariableIsAssigned(mapping->Literal(i).Variable())) {
362 response->add_solution_lower_bounds(0);
363 response->add_solution_upper_bounds(1);
366 response->add_solution_lower_bounds(
368 response->add_solution_upper_bounds(
377 IntegerVariable GetOrCreateVariableWithTightBound(
378 const std::vector<std::pair<IntegerVariable, int64>>& terms, Model*
model) {
380 if (terms.size() == 1 && terms.front().second == 1) {
381 return terms.front().first;
383 if (terms.size() == 1 && terms.front().second == -1) {
389 for (
const std::pair<IntegerVariable, int64> var_coeff : terms) {
392 const int64 coeff = var_coeff.second;
393 const int64 prod1 = min_domain * coeff;
394 const int64 prod2 = max_domain * coeff;
401 IntegerVariable GetOrCreateVariableGreaterOrEqualToSumOf(
402 const std::vector<std::pair<IntegerVariable, int64>>& terms, Model*
model) {
404 if (terms.size() == 1 && terms.front().second == 1) {
405 return terms.front().first;
407 if (terms.size() == 1 && terms.front().second == -1) {
412 const IntegerVariable new_var =
413 GetOrCreateVariableWithTightBound(terms,
model);
414 std::vector<IntegerVariable> vars;
415 std::vector<int64> coeffs;
416 for (
const auto& term : terms) {
417 vars.push_back(term.first);
418 coeffs.push_back(term.second);
420 vars.push_back(new_var);
421 coeffs.push_back(-1);
426 void TryToAddCutGenerators(
const CpModelProto&
model_proto,
427 const ConstraintProto&
ct, Model* m,
428 LinearRelaxation* relaxation) {
429 const int linearization_level =
430 m->GetOrCreate<SatParameters>()->linearization_level();
431 auto* mapping = m->GetOrCreate<CpModelMapping>();
432 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit &&
433 linearization_level > 1) {
434 std::vector<int> tails(
ct.circuit().tails().begin(),
435 ct.circuit().tails().end());
436 std::vector<int> heads(
ct.circuit().heads().begin(),
437 ct.circuit().heads().end());
438 std::vector<Literal> literals = mapping->Literals(
ct.circuit().literals());
439 const int num_nodes =
ReindexArcs(&tails, &heads, &literals);
441 relaxation->cut_generators.push_back(
445 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kRoutes &&
446 linearization_level > 1) {
447 std::vector<int> tails(
ct.routes().tails().begin(),
448 ct.routes().tails().end());
449 std::vector<int> heads(
ct.routes().heads().begin(),
450 ct.routes().heads().end());
451 std::vector<Literal> literals = mapping->Literals(
ct.routes().literals());
454 for (
int i = 0; i <
ct.routes().tails_size(); ++i) {
455 num_nodes =
std::max(num_nodes, 1 +
ct.routes().tails(i));
456 num_nodes =
std::max(num_nodes, 1 +
ct.routes().heads(i));
458 if (
ct.routes().demands().empty() ||
ct.routes().capacity() == 0) {
459 relaxation->cut_generators.push_back(
463 const std::vector<int64> demands(
ct.routes().demands().begin(),
464 ct.routes().demands().end());
465 relaxation->cut_generators.push_back(
467 ct.routes().capacity(), m));
470 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kIntProd) {
472 if (
ct.int_prod().vars_size() != 2)
return;
476 IntegerVariable z = mapping->Integer(
ct.int_prod().target());
477 IntegerVariable x = mapping->Integer(
ct.int_prod().vars(0));
478 IntegerVariable y = mapping->Integer(
ct.int_prod().vars(1));
480 IntegerTrail*
const integer_trail = m->GetOrCreate<IntegerTrail>();
481 IntegerValue x_lb = integer_trail->LowerBound(x);
482 IntegerValue x_ub = integer_trail->UpperBound(x);
483 IntegerValue y_lb = integer_trail->LowerBound(y);
484 IntegerValue y_ub = integer_trail->UpperBound(y);
488 if (x_lb < 0 && x_ub > 0)
return;
498 if (x_lb < 0 && x_ub > 0)
return;
499 if (y_lb < 0 && y_ub > 0)
return;
512 relaxation->cut_generators.push_back(
516 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kAllDiff) {
517 if (linearization_level < 2)
return;
519 const int num_vars =
ct.all_diff().vars_size();
520 if (num_vars <= m->GetOrCreate<SatParameters>()->max_all_diff_cut_size()) {
521 std::vector<IntegerVariable> vars =
522 mapping->Integers(
ct.all_diff().vars());
523 relaxation->cut_generators.push_back(
528 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCumulative) {
529 if (linearization_level < 2)
return;
531 std::vector<IntegerVariable> demands =
532 mapping->Integers(
ct.cumulative().demands());
533 std::vector<IntervalVariable> intervals =
534 mapping->Intervals(
ct.cumulative().intervals());
536 mapping->Integer(
ct.cumulative().capacity());
537 relaxation->cut_generators.push_back(
541 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinMax) {
542 if (!m->GetOrCreate<SatParameters>()->add_lin_max_cuts())
return;
546 if (
ct.lin_max().target().vars_size() != 1)
return;
547 if (
ct.lin_max().target().coeffs(0) != 1)
return;
549 const IntegerVariable target =
550 mapping->Integer(
ct.lin_max().target().vars(0));
551 std::vector<LinearExpression> exprs;
552 exprs.reserve(
ct.lin_max().exprs_size());
553 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
562 const std::vector<IntegerVariable> z_vars =
565 if (linearization_level >= 2) {
566 relaxation->cut_generators.push_back(
574 LinearRelaxation ComputeLinearRelaxation(
const CpModelProto&
model_proto,
575 int linearization_level, Model* m) {
576 LinearRelaxation relaxation;
579 absl::flat_hash_set<int> used_integer_variable;
581 auto* mapping = m->GetOrCreate<CpModelMapping>();
582 auto* encoder = m->GetOrCreate<IntegerEncoder>();
583 auto* trail = m->GetOrCreate<Trail>();
586 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit) {
587 for (
const int ref :
ct.circuit().literals()) {
588 const Literal l = mapping->Literal(ref);
603 for (
const int literal_ref : refs.literals) {
605 if (trail->Assignment().LiteralIsAssigned(
literal)) {
623 int num_full_encoding_relaxations = 0;
624 int num_partial_encoding_relaxations = 0;
625 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
626 if (mapping->IsBoolean(i))
continue;
628 const IntegerVariable
var = mapping->Integer(i);
639 if (encoder->VariableIsFullyEncoded(
var)) {
641 ++num_full_encoding_relaxations;
652 const int old = relaxation.linear_constraints.size();
654 if (relaxation.linear_constraints.size() > old) {
655 ++num_partial_encoding_relaxations;
661 m->GetOrCreate<BinaryImplicationGraph>()->TransformIntoMaxCliques(
662 &relaxation.at_most_ones);
663 for (
const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
664 if (at_most_one.empty())
continue;
666 for (
const Literal
literal : at_most_one) {
669 const bool unused ABSL_ATTRIBUTE_UNUSED =
670 lc.AddLiteralTerm(
literal, IntegerValue(1));
672 relaxation.linear_constraints.push_back(lc.Build());
678 for (
int i = 0; i < relaxation.linear_constraints.size(); ++i) {
679 if (relaxation.linear_constraints[i].vars.size() <= 1)
continue;
680 std::swap(relaxation.linear_constraints[new_size++],
681 relaxation.linear_constraints[i]);
683 relaxation.linear_constraints.resize(new_size);
686 VLOG(3) <<
"num_full_encoding_relaxations: " << num_full_encoding_relaxations;
687 VLOG(3) <<
"num_partial_encoding_relaxations: "
688 << num_partial_encoding_relaxations;
689 VLOG(3) << relaxation.linear_constraints.size()
690 <<
" constraints in the LP relaxation.";
691 VLOG(3) << relaxation.cut_generators.size() <<
" cuts generators.";
696 IntegerVariable AddLPConstraints(
const CpModelProto&
model_proto,
697 int linearization_level, Model* m) {
698 const LinearRelaxation relaxation =
699 ComputeLinearRelaxation(
model_proto, linearization_level, m);
707 const int num_lp_constraints = relaxation.linear_constraints.size();
708 const int num_lp_cut_generators = relaxation.cut_generators.size();
709 const int num_integer_variables =
710 m->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
711 ConnectedComponents<int, int> components;
712 components.Init(num_lp_constraints + num_lp_cut_generators +
713 num_integer_variables);
714 auto get_constraint_index = [](
int ct_index) {
return ct_index; };
715 auto get_cut_generator_index = [num_lp_constraints](
int cut_index) {
716 return num_lp_constraints + cut_index;
718 auto get_var_index = [num_lp_constraints,
719 num_lp_cut_generators](IntegerVariable
var) {
720 return num_lp_constraints + num_lp_cut_generators +
var.value();
722 for (
int i = 0; i < num_lp_constraints; i++) {
723 for (
const IntegerVariable
var : relaxation.linear_constraints[i].vars) {
724 components.AddArc(get_constraint_index(i), get_var_index(
var));
727 for (
int i = 0; i < num_lp_cut_generators; ++i) {
728 for (
const IntegerVariable
var : relaxation.cut_generators[i].vars) {
729 components.AddArc(get_cut_generator_index(i), get_var_index(
var));
733 std::map<int, int> components_to_size;
734 for (
int i = 0; i < num_lp_constraints; i++) {
735 const int id = components.GetClassRepresentative(get_constraint_index(i));
736 components_to_size[id] += 1;
738 for (
int i = 0; i < num_lp_cut_generators; i++) {
740 components.GetClassRepresentative(get_cut_generator_index(i));
741 components_to_size[id] += 1;
749 auto* mapping = m->GetOrCreate<CpModelMapping>();
750 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
751 const IntegerVariable
var =
753 const int id = components.GetClassRepresentative(get_var_index(
var));
754 components_to_size[id] += 1;
758 std::map<int, LinearProgrammingConstraint*> representative_to_lp_constraint;
759 std::vector<LinearProgrammingConstraint*> lp_constraints;
760 std::map<int, std::vector<LinearConstraint>> id_to_constraints;
761 for (
int i = 0; i < num_lp_constraints; i++) {
762 const int id = components.GetClassRepresentative(get_constraint_index(i));
763 if (components_to_size[
id] <= 1)
continue;
764 id_to_constraints[id].push_back(relaxation.linear_constraints[i]);
766 auto* lp = m->Create<LinearProgrammingConstraint>();
767 representative_to_lp_constraint[id] = lp;
768 lp_constraints.push_back(lp);
773 ->AddLinearConstraint(relaxation.linear_constraints[i]);
777 for (
int i = 0; i < num_lp_cut_generators; i++) {
779 components.GetClassRepresentative(get_cut_generator_index(i));
781 auto* lp = m->Create<LinearProgrammingConstraint>();
782 representative_to_lp_constraint[id] = lp;
783 lp_constraints.push_back(lp);
785 LinearProgrammingConstraint* lp = representative_to_lp_constraint[id];
786 lp->AddCutGenerator(std::move(relaxation.cut_generators[i]));
789 const SatParameters& params = *(m->GetOrCreate<SatParameters>());
790 if (params.add_knapsack_cuts()) {
791 for (
const auto& entry : id_to_constraints) {
792 const int id = entry.first;
793 LinearProgrammingConstraint* lp =
796 id_to_constraints[
id], lp->integer_variables(), m));
801 std::map<int, std::vector<std::pair<IntegerVariable, int64>>>
802 representative_to_cp_terms;
803 std::vector<std::pair<IntegerVariable, int64>> top_level_cp_terms;
804 int num_components_containing_objective = 0;
808 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
809 const IntegerVariable
var =
812 const int id = components.GetClassRepresentative(get_var_index(
var));
814 representative_to_lp_constraint[id]->SetObjectiveCoefficient(
815 var, IntegerValue(coeff));
816 representative_to_cp_terms[id].push_back(std::make_pair(
var, coeff));
819 top_level_cp_terms.push_back(std::make_pair(
var, coeff));
823 for (
const auto& it : representative_to_cp_terms) {
824 const int id = it.first;
825 LinearProgrammingConstraint* lp =
827 const std::vector<std::pair<IntegerVariable, int64>>& terms = it.second;
828 const IntegerVariable sub_obj_var =
829 GetOrCreateVariableGreaterOrEqualToSumOf(terms, m);
830 top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
831 lp->SetMainObjectiveVariable(sub_obj_var);
832 num_components_containing_objective++;
836 const IntegerVariable main_objective_var =
838 ? GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m)
843 for (
auto* lp_constraint : lp_constraints) {
844 lp_constraint->RegisterWith(m);
845 VLOG(3) <<
"LP constraint: " << lp_constraint->DimensionString() <<
".";
848 VLOG(3) << top_level_cp_terms.size()
849 <<
" terms in the main objective linear equation ("
850 << num_components_containing_objective <<
" from LP constraints).";
851 return main_objective_var;
863 const std::function<
void(
const CpSolverResponse&
response)>& observer) {
869 #if !defined(__PORTABLE_PLATFORM__)
872 const std::string& params) {
874 if (!params.empty()) {
875 CHECK(google::protobuf::TextFormat::ParseFromString(params, &
parameters))
880 #endif // __PORTABLE_PLATFORM__
898 void RegisterVariableBoundsLevelZeroExport(
899 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
901 CHECK(shared_bounds_manager !=
nullptr);
902 int saved_trail_index = 0;
903 const auto broadcast_level_zero_bounds =
905 const std::vector<IntegerVariable>& modified_vars)
mutable {
906 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
908 std::vector<int> model_variables;
909 std::vector<int64> new_lower_bounds;
910 std::vector<int64> new_upper_bounds;
911 absl::flat_hash_set<int> visited_variables;
914 auto* integer_trail =
model->Get<IntegerTrail>();
915 for (
const IntegerVariable&
var : modified_vars) {
917 const int model_var =
918 mapping->GetProtoVariableFromIntegerVariable(positive_var);
919 if (model_var == -1 || visited_variables.contains(model_var)) {
926 visited_variables.insert(model_var);
928 integer_trail->LevelZeroLowerBound(positive_var).value();
930 integer_trail->LevelZeroUpperBound(positive_var).value();
933 model_variables.push_back(model_var);
934 new_lower_bounds.push_back(new_lb);
935 new_upper_bounds.push_back(new_ub);
939 auto* trail =
model->Get<Trail>();
940 for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
941 const Literal fixed_literal = (*trail)[saved_trail_index];
942 const int model_var = mapping->GetProtoVariableFromBooleanVariable(
943 fixed_literal.Variable());
944 if (model_var == -1 || visited_variables.contains(model_var)) {
951 visited_variables.insert(model_var);
952 model_variables.push_back(model_var);
953 if (fixed_literal.IsPositive()) {
954 new_lower_bounds.push_back(1);
955 new_upper_bounds.push_back(1);
957 new_lower_bounds.push_back(0);
958 new_upper_bounds.push_back(0);
962 if (!model_variables.empty()) {
963 shared_bounds_manager->ReportPotentialNewBounds(
969 if (!
model->Get<SatParameters>()->interleave_search()) {
970 shared_bounds_manager->Synchronize();
974 model->GetOrCreate<GenericLiteralWatcher>()
975 ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
981 void RegisterVariableBoundsLevelZeroImport(
982 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
984 CHECK(shared_bounds_manager !=
nullptr);
985 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
986 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
987 const int id = shared_bounds_manager->RegisterNewId();
989 const auto& import_level_zero_bounds = [&
model_proto, shared_bounds_manager,
990 model, integer_trail, id, mapping]() {
991 std::vector<int> model_variables;
992 std::vector<int64> new_lower_bounds;
993 std::vector<int64> new_upper_bounds;
994 shared_bounds_manager->GetChangedBounds(
995 id, &model_variables, &new_lower_bounds, &new_upper_bounds);
996 bool new_bounds_have_been_imported =
false;
997 for (
int i = 0; i < model_variables.size(); ++i) {
998 const int model_var = model_variables[i];
1001 if (!mapping->IsInteger(model_var))
continue;
1002 const IntegerVariable
var = mapping->Integer(model_var);
1003 const IntegerValue new_lb(new_lower_bounds[i]);
1004 const IntegerValue new_ub(new_upper_bounds[i]);
1005 const IntegerValue old_lb = integer_trail->LowerBound(
var);
1006 const IntegerValue old_ub = integer_trail->UpperBound(
var);
1007 const bool changed_lb = new_lb > old_lb;
1008 const bool changed_ub = new_ub < old_ub;
1009 if (!changed_lb && !changed_ub)
continue;
1011 new_bounds_have_been_imported =
true;
1012 if (VLOG_IS_ON(3)) {
1013 const IntegerVariableProto& var_proto =
1015 const std::string& var_name =
1016 var_proto.name().empty()
1017 ? absl::StrCat(
"anonymous_var(", model_var,
")")
1019 LOG(INFO) <<
" '" <<
model->Name() <<
"' imports new bounds for "
1020 << var_name <<
": from [" << old_lb <<
", " << old_ub
1021 <<
"] to [" << new_lb <<
", " << new_ub <<
"]";
1035 if (new_bounds_have_been_imported &&
1036 !
model->GetOrCreate<SatSolver>()->FinishPropagation()) {
1041 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1042 import_level_zero_bounds);
1047 void RegisterObjectiveBestBoundExport(
1048 IntegerVariable objective_var,
1049 SharedResponseManager* shared_response_manager, Model*
model) {
1050 auto* integer_trail =
model->Get<IntegerTrail>();
1051 const auto broadcast_objective_lower_bound =
1052 [objective_var, integer_trail, shared_response_manager,
1053 model](
const std::vector<IntegerVariable>& unused) {
1054 shared_response_manager->UpdateInnerObjectiveBounds(
1055 model->Name(), integer_trail->LevelZeroLowerBound(objective_var),
1056 integer_trail->LevelZeroUpperBound(objective_var));
1058 if (!
model->Get<SatParameters>()->interleave_search()) {
1059 shared_response_manager->Synchronize();
1062 model->GetOrCreate<GenericLiteralWatcher>()
1063 ->RegisterLevelZeroModifiedVariablesCallback(
1064 broadcast_objective_lower_bound);
1070 void RegisterObjectiveBoundsImport(
1071 SharedResponseManager* shared_response_manager, Model*
model) {
1072 auto* solver =
model->GetOrCreate<SatSolver>();
1073 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1074 auto* objective =
model->GetOrCreate<ObjectiveDefinition>();
1076 const auto import_objective_bounds = [
name, solver, integer_trail, objective,
1077 shared_response_manager]() {
1078 if (solver->AssumptionLevel() != 0)
return true;
1079 bool propagate =
false;
1081 const IntegerValue external_lb =
1082 shared_response_manager->SynchronizedInnerObjectiveLowerBound();
1083 const IntegerValue current_lb =
1084 integer_trail->LowerBound(objective->objective_var);
1085 if (external_lb > current_lb) {
1087 objective->objective_var, external_lb),
1094 const IntegerValue external_ub =
1095 shared_response_manager->SynchronizedInnerObjectiveUpperBound();
1096 const IntegerValue current_ub =
1097 integer_trail->UpperBound(objective->objective_var);
1098 if (external_ub < current_ub) {
1100 objective->objective_var, external_ub),
1107 if (!propagate)
return true;
1109 VLOG(2) <<
"'" <<
name <<
"' imports objective bounds: external ["
1110 << objective->ScaleIntegerObjective(external_lb) <<
", "
1111 << objective->ScaleIntegerObjective(external_ub) <<
"], current ["
1112 << objective->ScaleIntegerObjective(current_lb) <<
", "
1113 << objective->ScaleIntegerObjective(current_ub) <<
"]";
1115 return solver->FinishPropagation();
1118 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1119 import_objective_bounds);
1122 void LoadBaseModel(
const CpModelProto&
model_proto,
1123 SharedResponseManager* shared_response_manager,
1125 CHECK(shared_response_manager !=
nullptr);
1126 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1129 const auto unsat = [shared_response_manager, sat_solver,
model] {
1130 sat_solver->NotifyThatModelIsUnsat();
1131 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1132 absl::StrCat(
model->Name(),
" [loading]"));
1136 model->GetOrCreate<IntegerEncoder>()->DisableImplicationBetweenLiteral();
1138 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1139 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1140 const bool view_all_booleans_as_integers =
1142 (
parameters.search_branching() == SatParameters::FIXED_SEARCH &&
1144 mapping->CreateVariables(
model_proto, view_all_booleans_as_integers,
model);
1150 if (sat_solver->IsModelUnsat())
return unsat();
1156 std::set<std::string> unsupported_types;
1157 int num_ignored_constraints = 0;
1158 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1159 if (mapping->ConstraintIsAlreadyLoaded(&
ct)) {
1160 ++num_ignored_constraints;
1175 if (sat_solver->FinishPropagation()) {
1176 Trail* trail =
model->GetOrCreate<Trail>();
1177 const int old_num_fixed = trail->Index();
1178 if (trail->Index() > old_num_fixed) {
1179 VLOG(3) <<
"Constraint fixed " << trail->Index() - old_num_fixed
1184 if (sat_solver->IsModelUnsat()) {
1185 VLOG(2) <<
"UNSAT during extraction (after adding '"
1191 if (num_ignored_constraints > 0) {
1192 VLOG(3) << num_ignored_constraints <<
" constraints were skipped.";
1194 if (!unsupported_types.empty()) {
1195 VLOG(1) <<
"There is unsuported constraints types in this model: ";
1196 for (
const std::string& type : unsupported_types) {
1197 VLOG(1) <<
" - " << type;
1202 model->GetOrCreate<IntegerEncoder>()
1203 ->AddAllImplicationsBetweenAssociatedLiterals();
1204 if (!sat_solver->FinishPropagation())
return unsat();
1207 void LoadFeasibilityPump(
const CpModelProto&
model_proto,
1208 SharedResponseManager* shared_response_manager,
1210 CHECK(shared_response_manager !=
nullptr);
1214 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1215 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1216 if (
parameters.linearization_level() == 0)
return;
1219 const LinearRelaxation relaxation = ComputeLinearRelaxation(
1221 const int num_lp_constraints = relaxation.linear_constraints.size();
1222 if (num_lp_constraints == 0)
return;
1223 auto* feasibility_pump =
model->GetOrCreate<FeasibilityPump>();
1224 for (
int i = 0; i < num_lp_constraints; i++) {
1225 feasibility_pump->AddLinearConstraint(relaxation.linear_constraints[i]);
1229 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
1230 const IntegerVariable
var =
1231 mapping->Integer(
model_proto.objective().vars(i));
1233 feasibility_pump->SetObjectiveCoefficient(
var, IntegerValue(coeff));
1243 SharedResponseManager* shared_response_manager, Model*
model) {
1244 CHECK(shared_response_manager !=
nullptr);
1245 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1250 const auto unsat = [shared_response_manager, sat_solver,
model] {
1251 sat_solver->NotifyThatModelIsUnsat();
1252 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1253 absl::StrCat(
model->Name(),
" [loading]"));
1256 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1257 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1263 if (
model->Mutable<PrecedencesPropagator>() !=
nullptr &&
1264 parameters.auto_detect_greater_than_at_least_one_of()) {
1265 model->Mutable<PrecedencesPropagator>()
1266 ->AddGreaterThanAtLeastOneOfConstraints(
model);
1267 if (!sat_solver->FinishPropagation())
return unsat();
1273 if (
parameters.cp_model_probing_level() > 1) {
1275 if (
model->GetOrCreate<SatSolver>()->IsModelUnsat()) {
1278 if (!
model->GetOrCreate<BinaryImplicationGraph>()
1279 ->ComputeTransitiveReduction()) {
1292 const CpObjectiveProto& obj =
model_proto.objective();
1293 std::vector<std::pair<IntegerVariable, int64>> terms;
1294 terms.reserve(obj.vars_size());
1295 for (
int i = 0; i < obj.vars_size(); ++i) {
1297 std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i)));
1300 objective_var = GetOrCreateVariableWithTightBound(terms,
model);
1302 objective_var = GetOrCreateVariableGreaterOrEqualToSumOf(terms,
model);
1309 const CpObjectiveProto& objective_proto =
model_proto.objective();
1310 auto* objective_definition =
model->GetOrCreate<ObjectiveDefinition>();
1312 objective_definition->scaling_factor = objective_proto.scaling_factor();
1313 if (objective_definition->scaling_factor == 0.0) {
1314 objective_definition->scaling_factor = 1.0;
1316 objective_definition->offset = objective_proto.offset();
1317 objective_definition->objective_var = objective_var;
1319 const int size = objective_proto.vars_size();
1320 objective_definition->vars.resize(size);
1321 objective_definition->coeffs.resize(size);
1322 for (
int i = 0; i < objective_proto.vars_size(); ++i) {
1325 objective_definition->vars[i] = mapping->Integer(objective_proto.vars(i));
1326 objective_definition->coeffs[i] = IntegerValue(objective_proto.coeffs(i));
1329 const int ref = objective_proto.vars(i);
1330 if (mapping->IsInteger(ref)) {
1331 const IntegerVariable
var = mapping->Integer(objective_proto.vars(i));
1332 objective_definition->objective_impacting_variables.insert(
1341 const Domain automatic_domain =
1342 model->GetOrCreate<IntegerTrail>()->InitialVariableDomain(
1344 VLOG(3) <<
"Objective offset:" <<
model_proto.objective().offset()
1345 <<
" scaling_factor:" <<
model_proto.objective().scaling_factor();
1346 VLOG(3) <<
"Automatic internal objective domain: " << automatic_domain;
1347 VLOG(3) <<
"User specified internal objective domain: " << user_domain;
1349 const bool ok =
model->GetOrCreate<IntegerTrail>()->UpdateInitialDomain(
1350 objective_var, user_domain);
1352 VLOG(2) <<
"UNSAT due to the objective domain.";
1363 if (!automatic_domain.IsIncludedIn(user_domain)) {
1364 std::vector<IntegerVariable> vars;
1365 std::vector<int64> coeffs;
1366 const CpObjectiveProto& obj =
model_proto.objective();
1367 for (
int i = 0; i < obj.vars_size(); ++i) {
1368 vars.push_back(mapping->Integer(obj.vars(i)));
1369 coeffs.push_back(obj.coeffs(i));
1371 vars.push_back(objective_var);
1372 coeffs.push_back(-1);
1379 if (!sat_solver->FinishPropagation())
return unsat();
1383 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1384 shared_response_manager->UpdateInnerObjectiveBounds(
1385 "init", integer_trail->LowerBound(objective_var),
1386 integer_trail->UpperBound(objective_var));
1389 RegisterObjectiveBestBoundExport(objective_var, shared_response_manager,
1395 if (
model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1396 RegisterObjectiveBoundsImport(shared_response_manager,
model);
1402 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1403 auto* lp_dispatcher =
model->GetOrCreate<LinearProgrammingDispatcher>();
1404 auto* lp_vars =
model->GetOrCreate<LPVariables>();
1405 IntegerVariable size = integer_trail->NumIntegerVariables();
1406 for (IntegerVariable positive_var(0); positive_var < size;
1407 positive_var += 2) {
1409 lp_var.positive_var = positive_var;
1411 mapping->GetProtoVariableFromIntegerVariable(positive_var);
1414 if (lp_var.model_var >= 0) {
1415 lp_vars->vars.push_back(lp_var);
1416 lp_vars->model_vars_size =
1417 std::max(lp_vars->model_vars_size, lp_var.model_var + 1);
1422 auto* search_heuristics =
model->GetOrCreate<SearchHeuristics>();
1425 if (VLOG_IS_ON(3)) {
1426 search_heuristics->fixed_search =
1428 search_heuristics->fixed_search,
model);
1432 std::vector<BooleanOrIntegerVariable> vars;
1433 std::vector<IntegerValue> values;
1434 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1435 const int ref =
model_proto.solution_hint().vars(i);
1437 BooleanOrIntegerVariable
var;
1438 if (mapping->IsBoolean(ref)) {
1439 var.bool_var = mapping->Literal(ref).Variable();
1441 var.int_var = mapping->Integer(ref);
1443 vars.push_back(
var);
1444 values.push_back(IntegerValue(
model_proto.solution_hint().values(i)));
1452 const std::string solution_info =
model->Name();
1454 shared_response_manager]() {
1457 response.set_solution_info(solution_info);
1461 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1462 CoreBasedOptimizer* core =
1463 new CoreBasedOptimizer(objective_var, objective.vars, objective.coeffs,
1464 solution_observer,
model);
1465 model->Register<CoreBasedOptimizer>(core);
1466 model->TakeOwnership(core);
1476 void SolveLoadedCpModel(
const CpModelProto&
model_proto,
1477 SharedResponseManager* shared_response_manager,
1479 if (shared_response_manager->ProblemIsSolved())
return;
1481 const std::string& solution_info =
model->Name();
1483 &shared_response_manager]() {
1486 response.set_solution_info(solution_info);
1493 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1495 const SatParameters&
parameters = *
model->GetOrCreate<SatParameters>();
1501 solution_observer();
1502 if (!
parameters.enumerate_all_solutions())
break;
1506 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1510 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1514 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1515 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1517 std::vector<int> core_in_proto_format;
1518 for (
const Literal l : core) {
1519 core_in_proto_format.push_back(
1520 mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1521 if (!l.IsPositive()) {
1522 core_in_proto_format.back() =
NegatedRef(core_in_proto_format.back());
1525 shared_response_manager->AddUnsatCore(core_in_proto_format);
1529 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1530 const IntegerVariable objective_var = objective.objective_var;
1538 objective, solution_observer,
model);
1540 status =
model->Mutable<CoreBasedOptimizer>()->Optimize();
1545 if (
parameters.binary_search_num_conflicts() >= 0) {
1547 solution_observer,
model);
1550 objective_var, solution_observer,
model);
1558 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1565 shared_response_manager->SetStatsFromModel(
model);
1570 void QuickSolveWithHint(
const CpModelProto&
model_proto,
1571 SharedResponseManager* shared_response_manager,
1574 if (shared_response_manager->ProblemIsSolved())
return;
1578 const SatParameters saved_params = *
parameters;
1580 parameters->set_search_branching(SatParameters::HINT_SEARCH);
1587 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1591 const std::string& solution_info =
model->Name();
1595 response.set_solution_info(absl::StrCat(solution_info,
" [hint]"));
1604 const IntegerVariable objective_var =
1605 model->GetOrCreate<ObjectiveDefinition>()->objective_var;
1606 model->GetOrCreate<SatSolver>()->Backtrack(0);
1607 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
1608 if (!integer_trail->Enqueue(
1611 shared_response_manager->GetInnerObjectiveUpperBound()),
1613 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1614 absl::StrCat(solution_info,
" [hint]"));
1615 shared_response_manager->SetStatsFromModel(
model);
1625 void MinimizeL1DistanceWithHint(
const CpModelProto&
model_proto,
1626 SharedResponseManager* shared_response_manager,
1628 SharedTimeLimit* shared_time_limit,
1632 if (shared_response_manager->ProblemIsSolved())
return;
1634 auto*
parameters = local_model.GetOrCreate<SatParameters>();
1638 if (
parameters->enumerate_all_solutions())
return;
1641 const SatParameters saved_params = *
model->GetOrCreate<SatParameters>();
1648 updated_model_proto.clear_objective();
1651 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1656 const int new_var_index = updated_model_proto.variables_size();
1657 IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1662 var_proto->add_domain(min_domain);
1663 var_proto->add_domain(max_domain);
1666 ConstraintProto*
const linear_constraint_proto =
1667 updated_model_proto.add_constraints();
1668 LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
1669 linear->add_vars(new_var_index);
1670 linear->add_coeffs(1);
1671 linear->add_vars(
var);
1672 linear->add_coeffs(-1);
1673 linear->add_domain(-
value);
1674 linear->add_domain(-
value);
1677 const int abs_var_index = updated_model_proto.variables_size();
1678 IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
1679 const int64 abs_min_domain = 0;
1680 const int64 abs_max_domain =
1681 std::max(std::abs(min_domain), std::abs(max_domain));
1682 abs_var_proto->add_domain(abs_min_domain);
1683 abs_var_proto->add_domain(abs_max_domain);
1684 ConstraintProto*
const abs_constraint_proto =
1685 updated_model_proto.add_constraints();
1686 abs_constraint_proto->mutable_int_max()->set_target(abs_var_index);
1687 abs_constraint_proto->mutable_int_max()->add_vars(new_var_index);
1688 abs_constraint_proto->mutable_int_max()->add_vars(
1691 updated_model_proto.mutable_objective()->add_vars(abs_var_index);
1692 updated_model_proto.mutable_objective()->add_coeffs(1);
1695 SharedResponseManager local_response_manager(
1696 false,
parameters->enumerate_all_solutions(),
1697 &updated_model_proto,
wall_timer, shared_time_limit);
1699 local_model.Register<SharedResponseManager>(&local_response_manager);
1702 LoadCpModel(updated_model_proto, &local_response_manager, &local_model);
1705 const auto& mapping = *local_model.GetOrCreate<CpModelMapping>();
1707 mapping.Literals(updated_model_proto.assumptions()), &local_model);
1709 const std::string& solution_info =
model->Name();
1714 CpSolverResponse updated_response;
1715 FillSolutionInResponse(updated_model_proto, local_model,
1717 LOG(INFO) <<
"Found solution with repaired hint penalty = "
1721 response.set_solution_info(absl::StrCat(solution_info,
" [repaired]"));
1730 void PostsolveResponseWithFullSolver(
1731 const int64 num_variables_in_original_model, CpModelProto mapping_proto,
1740 if (mapping_proto.variables_size() == 0) {
1745 for (
int i = 0; i <
response->solution_size(); ++i) {
1746 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1747 var_proto->clear_domain();
1748 var_proto->add_domain(
response->solution(i));
1749 var_proto->add_domain(
response->solution(i));
1751 for (
int i = 0; i <
response->solution_lower_bounds_size(); ++i) {
1752 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1755 .IntersectionWith({
response->solution_lower_bounds(i),
1756 response->solution_upper_bounds(i)}),
1763 Model postsolve_model;
1765 SatParameters params;
1766 params.set_linearization_level(0);
1767 params.set_cp_model_probing_level(0);
1772 SharedTimeLimit shared_time_limit(
time_limit.get());
1773 SharedResponseManager local_response_manager(
1774 false,
false, &mapping_proto,
1776 LoadCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1777 SolveLoadedCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1778 const CpSolverResponse postsolve_response =
1779 local_response_manager.GetResponse();
1785 response->clear_solution_lower_bounds();
1786 response->clear_solution_upper_bounds();
1787 if (!postsolve_response.solution().empty()) {
1788 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1789 response->add_solution(postsolve_response.solution(i));
1792 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1793 response->add_solution_lower_bounds(
1794 postsolve_response.solution_lower_bounds(i));
1795 response->add_solution_upper_bounds(
1796 postsolve_response.solution_upper_bounds(i));
1801 void PostsolveResponseWrapper(
const SatParameters& params,
1802 const int64 num_variables_in_original_model,
1803 const CpModelProto& mapping_proto,
1804 const std::vector<int>& postsolve_mapping,
1807 if (params.cp_model_postsolve_with_full_solver()) {
1808 PostsolveResponseWithFullSolver(num_variables_in_original_model,
1809 mapping_proto, postsolve_mapping,
1818 CpSolverResponse SolvePureSatModel(
const CpModelProto&
model_proto,
1820 std::unique_ptr<SatSolver> solver(
new SatSolver());
1823 model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(
parameters);
1826 std::unique_ptr<DratProofHandler> drat_proof_handler;
1827 #if !defined(__PORTABLE_PLATFORM__)
1828 if (!FLAGS_drat_output.empty() || FLAGS_drat_check) {
1829 if (!FLAGS_drat_output.empty()) {
1832 drat_proof_handler = absl::make_unique<DratProofHandler>(
1833 false, output, FLAGS_drat_check);
1835 drat_proof_handler = absl::make_unique<DratProofHandler>();
1837 solver->SetDratProofHandler(drat_proof_handler.get());
1839 #endif // __PORTABLE_PLATFORM__
1841 auto get_literal = [](
int ref) {
1842 if (ref >= 0)
return Literal(BooleanVariable(ref),
true);
1843 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
1846 std::vector<Literal> temp;
1847 const int num_variables =
model_proto.variables_size();
1848 solver->SetNumVariables(num_variables);
1849 if (drat_proof_handler !=
nullptr) {
1854 for (
int ref = 0; ref < num_variables; ++ref) {
1856 if (domain.IsFixed()) {
1857 const Literal ref_literal =
1858 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1862 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1863 switch (
ct.constraint_case()) {
1864 case ConstraintProto::ConstraintCase::kBoolAnd: {
1865 if (
ct.enforcement_literal_size() == 0) {
1866 for (
const int ref :
ct.bool_and().literals()) {
1871 const Literal not_a =
1872 get_literal(
ct.enforcement_literal(0)).Negated();
1873 for (
const int ref :
ct.bool_and().literals()) {
1879 case ConstraintProto::ConstraintCase::kBoolOr:
1881 for (
const int ref :
ct.bool_or().literals()) {
1882 temp.push_back(get_literal(ref));
1887 LOG(FATAL) <<
"Not supported";
1892 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1893 switch (
ct.constraint_case()) {
1894 case ConstraintProto::ConstraintCase::kBoolAnd: {
1895 if (
ct.enforcement_literal_size() == 0) {
1896 for (
const int ref :
ct.bool_and().literals()) {
1897 const Literal
b = get_literal(ref);
1898 solver->AddUnitClause(
b);
1902 const Literal not_a =
1903 get_literal(
ct.enforcement_literal(0)).Negated();
1904 for (
const int ref :
ct.bool_and().literals()) {
1905 const Literal
b = get_literal(ref);
1906 solver->AddProblemClause({not_a,
b});
1911 case ConstraintProto::ConstraintCase::kBoolOr:
1913 for (
const int ref :
ct.bool_or().literals()) {
1914 temp.push_back(get_literal(ref));
1916 solver->AddProblemClause(temp);
1919 LOG(FATAL) <<
"Not supported";
1924 for (
int ref = 0; ref < num_variables; ++ref) {
1926 if (domain.Min() == domain.Max()) {
1927 const Literal ref_literal =
1928 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1929 solver->AddUnitClause(ref_literal);
1936 std::vector<bool> solution;
1938 &solution, drat_proof_handler.get());
1941 for (
int ref = 0; ref < num_variables; ++ref) {
1942 response.add_solution(solution[ref]);
1946 status = solver->SolveWithTimeLimit(
model->GetOrCreate<TimeLimit>());
1949 for (
int ref = 0; ref < num_variables; ++ref) {
1951 solver->Assignment().LiteralIsTrue(get_literal(ref)) ? 1 : 0);
1958 model->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
1959 solver->model()->GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
1968 std::vector<int64>(
response.solution().begin(),
1978 LOG(FATAL) <<
"Unexpected SatSolver::Status " << status;
1980 response.set_num_booleans(solver->NumVariables());
1981 response.set_num_branches(solver->num_branches());
1982 response.set_num_conflicts(solver->num_failures());
1983 response.set_num_binary_propagations(solver->num_propagations());
1984 response.set_num_integer_propagations(0);
1987 model->Get<TimeLimit>()->GetElapsedDeterministicTime());
1993 drat_proof_handler->
Check(FLAGS_max_drat_time_in_seconds);
1994 switch (drat_status) {
1996 LOG(INFO) <<
"DRAT status: UNKNOWN";
1999 LOG(INFO) <<
"DRAT status: VALID";
2002 LOG(ERROR) <<
"DRAT status: INVALID";
2008 LOG(INFO) <<
"DRAT wall time: " << drat_timer.
Get();
2009 }
else if (drat_proof_handler !=
nullptr) {
2012 LOG(INFO) <<
"DRAT status: NA";
2013 LOG(INFO) <<
"DRAT wall time: NA";
2014 LOG(INFO) <<
"DRAT user time: NA";
2019 #if !defined(__PORTABLE_PLATFORM__)
2022 struct SharedClasses {
2032 bool SearchIsDone() {
2033 if (
response->ProblemIsSolved())
return true;
2040 class FullProblemSolver :
public SubSolver {
2042 FullProblemSolver(
const std::string&
name,
2043 const SatParameters& local_parameters,
bool split_in_chunks,
2044 SharedClasses* shared)
2047 split_in_chunks_(split_in_chunks),
2048 local_model_(
absl::make_unique<Model>(
name)) {
2051 shared_->time_limit->UpdateLocalLimit(
2052 local_model_->GetOrCreate<TimeLimit>());
2054 if (shared->response !=
nullptr) {
2055 local_model_->Register<SharedResponseManager>(shared->response);
2058 if (shared->relaxation_solutions !=
nullptr) {
2059 local_model_->Register<SharedRelaxationSolutionRepository>(
2060 shared->relaxation_solutions);
2063 if (shared->lp_solutions !=
nullptr) {
2064 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2067 if (shared->incomplete_solutions !=
nullptr) {
2068 local_model_->Register<SharedIncompleteSolutionManager>(
2069 shared->incomplete_solutions);
2073 if (shared_->bounds !=
nullptr) {
2074 RegisterVariableBoundsLevelZeroExport(
2075 *shared_->model_proto, shared_->bounds, local_model_.get());
2076 RegisterVariableBoundsLevelZeroImport(
2077 *shared_->model_proto, shared_->bounds, local_model_.get());
2081 bool TaskIsAvailable()
override {
2082 if (shared_->SearchIsDone())
return false;
2084 absl::MutexLock mutex_lock(&mutex_);
2085 return previous_task_is_completed_;
2088 std::function<void()> GenerateTask(
int64 task_id)
override {
2090 absl::MutexLock mutex_lock(&mutex_);
2091 previous_task_is_completed_ =
false;
2094 if (solving_first_chunk_) {
2095 LoadCpModel(*shared_->model_proto, shared_->response,
2096 local_model_.get());
2097 if (local_model_->GetOrCreate<SatParameters>()->repair_hint()) {
2098 MinimizeL1DistanceWithHint(*shared_->model_proto, shared_->response,
2099 shared_->wall_timer, shared_->time_limit,
2100 local_model_.get());
2102 QuickSolveWithHint(*shared_->model_proto, shared_->response,
2103 local_model_.get());
2107 solving_first_chunk_ =
false;
2109 if (split_in_chunks_) {
2111 absl::MutexLock mutex_lock(&mutex_);
2112 previous_task_is_completed_ =
true;
2117 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2118 if (split_in_chunks_) {
2121 auto* params = local_model_->GetOrCreate<SatParameters>();
2122 params->set_max_deterministic_time(1);
2123 time_limit->ResetLimitFromParameters(*params);
2124 shared_->time_limit->UpdateLocalLimit(
time_limit);
2127 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2128 SolveLoadedCpModel(*shared_->model_proto, shared_->response,
2129 local_model_.get());
2131 absl::MutexLock mutex_lock(&mutex_);
2132 deterministic_time_since_last_synchronize_ +=
2133 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2137 if (shared_->SearchIsDone()) {
2138 shared_->time_limit->Stop();
2143 if (split_in_chunks_) {
2144 absl::MutexLock mutex_lock(&mutex_);
2145 previous_task_is_completed_ =
true;
2153 local_model_.reset();
2160 void Synchronize()
override {
2161 absl::MutexLock mutex_lock(&mutex_);
2162 deterministic_time_ += deterministic_time_since_last_synchronize_;
2163 shared_->time_limit->AdvanceDeterministicTime(
2164 deterministic_time_since_last_synchronize_);
2165 deterministic_time_since_last_synchronize_ = 0.0;
2169 SharedClasses* shared_;
2170 const bool split_in_chunks_;
2171 std::unique_ptr<Model> local_model_;
2175 bool solving_first_chunk_ =
true;
2178 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2180 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2183 class FeasibilityPumpSolver :
public SubSolver {
2185 FeasibilityPumpSolver(
const SatParameters& local_parameters,
2186 SharedClasses* shared)
2187 : SubSolver(
"feasibility_pump"),
2189 local_model_(
absl::make_unique<Model>(name_)) {
2192 shared_->time_limit->UpdateLocalLimit(
2193 local_model_->GetOrCreate<TimeLimit>());
2195 if (shared->response !=
nullptr) {
2196 local_model_->Register<SharedResponseManager>(shared->response);
2199 if (shared->relaxation_solutions !=
nullptr) {
2200 local_model_->Register<SharedRelaxationSolutionRepository>(
2201 shared->relaxation_solutions);
2204 if (shared->lp_solutions !=
nullptr) {
2205 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2208 if (shared->incomplete_solutions !=
nullptr) {
2209 local_model_->Register<SharedIncompleteSolutionManager>(
2210 shared->incomplete_solutions);
2214 if (shared_->bounds !=
nullptr) {
2215 RegisterVariableBoundsLevelZeroImport(
2216 *shared_->model_proto, shared_->bounds, local_model_.get());
2220 bool TaskIsAvailable()
override {
2221 if (shared_->SearchIsDone())
return false;
2222 absl::MutexLock mutex_lock(&mutex_);
2223 return previous_task_is_completed_;
2226 std::function<void()> GenerateTask(
int64 task_id)
override {
2229 absl::MutexLock mutex_lock(&mutex_);
2230 if (!previous_task_is_completed_)
return;
2231 previous_task_is_completed_ =
false;
2234 absl::MutexLock mutex_lock(&mutex_);
2235 if (solving_first_chunk_) {
2236 LoadFeasibilityPump(*shared_->model_proto, shared_->response,
2237 local_model_.get());
2240 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
2241 solving_first_chunk_ =
false;
2243 previous_task_is_completed_ =
true;
2248 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2249 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2250 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
2251 if (!feasibility_pump->Solve()) {
2252 shared_->response->NotifyThatImprovingProblemIsInfeasible(name_);
2256 absl::MutexLock mutex_lock(&mutex_);
2257 deterministic_time_since_last_synchronize_ +=
2258 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2262 if (shared_->SearchIsDone()) {
2263 shared_->time_limit->Stop();
2267 absl::MutexLock mutex_lock(&mutex_);
2268 previous_task_is_completed_ =
true;
2272 void Synchronize()
override {
2273 absl::MutexLock mutex_lock(&mutex_);
2274 deterministic_time_ += deterministic_time_since_last_synchronize_;
2275 shared_->time_limit->AdvanceDeterministicTime(
2276 deterministic_time_since_last_synchronize_);
2277 deterministic_time_since_last_synchronize_ = 0.0;
2281 SharedClasses* shared_;
2282 std::unique_ptr<Model> local_model_;
2288 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
2290 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2292 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2296 class LnsSolver :
public SubSolver {
2298 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
2300 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
2301 : SubSolver(generator->
name()),
2302 generator_(std::move(generator)),
2307 bool TaskIsAvailable()
override {
2308 if (shared_->SearchIsDone())
return false;
2309 return generator_->ReadyToGenerate();
2312 std::function<void()> GenerateTask(
int64 task_id)
override {
2313 return [task_id,
this]() {
2314 if (shared_->SearchIsDone())
return;
2319 const int32 low =
static_cast<int32>(task_id);
2320 const int32 high = task_id >> 32;
2321 std::seed_seq seed{low, high, parameters_.random_seed()};
2324 NeighborhoodGenerator::SolveData data;
2325 data.difficulty = generator_->difficulty();
2326 data.deterministic_limit = generator_->deterministic_limit();
2329 CpSolverResponse base_response;
2331 const SharedSolutionRepository<int64>& repo =
2332 shared_->response->SolutionsRepository();
2333 if (repo.NumSolutions() > 0) {
2335 const SharedSolutionRepository<int64>::Solution solution =
2336 repo.GetRandomBiasedSolution(&random);
2337 for (
const int64 value : solution.variable_values) {
2338 base_response.add_solution(
value);
2342 data.initial_best_objective = repo.GetSolution(0).rank;
2343 data.base_objective = solution.rank;
2352 data.initial_best_objective =
2353 shared_->response->GetInnerObjectiveUpperBound();
2354 data.base_objective = data.initial_best_objective;
2358 Neighborhood neighborhood;
2360 absl::MutexLock mutex_lock(helper_->MutableMutex());
2362 generator_->Generate(base_response, data.difficulty, &random);
2364 neighborhood.cp_model.set_name(absl::StrCat(
"lns_", task_id));
2365 if (!neighborhood.is_generated)
return;
2366 data.neighborhood_id = neighborhood.id;
2368 const double fully_solved_proportion =
2369 static_cast<double>(generator_->num_fully_solved_calls()) /
2371 std::string source_info =
name();
2372 if (!neighborhood.source_info.empty()) {
2373 absl::StrAppend(&source_info,
"_", neighborhood.source_info);
2375 const std::string solution_info = absl::StrFormat(
2376 "%s(d=%0.2f s=%i t=%0.2f p=%0.2f)", source_info, data.difficulty,
2377 task_id, data.deterministic_limit, fully_solved_proportion);
2379 SatParameters local_params(parameters_);
2380 local_params.set_max_deterministic_time(data.deterministic_limit);
2381 local_params.set_stop_after_first_solution(
false);
2382 local_params.set_log_search_progress(
false);
2383 local_params.set_cp_model_probing_level(0);
2385 if (FLAGS_cp_model_dump_lns) {
2386 const std::string
name = absl::StrCat(
2387 FLAGS_cp_model_dump_prefix, neighborhood.cp_model.name(),
".pbtxt");
2388 LOG(INFO) <<
"Dumping LNS model to '" <<
name <<
"'.";
2395 TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
2396 shared_->time_limit->UpdateLocalLimit(local_time_limit);
2398 const int64 num_neighborhood_model_vars =
2399 neighborhood.cp_model.variables_size();
2401 CpModelProto mapping_proto;
2402 std::vector<int> postsolve_mapping;
2403 PresolveOptions options;
2404 options.log_info = VLOG_IS_ON(3);
2405 options.parameters = *local_model.GetOrCreate<SatParameters>();
2406 options.time_limit = local_model.GetOrCreate<TimeLimit>();
2407 auto context = absl::make_unique<PresolveContext>(&neighborhood.cp_model,
2417 SharedResponseManager local_response_manager(
2419 &neighborhood.cp_model, shared_->wall_timer, shared_->time_limit);
2420 LoadCpModel(neighborhood.cp_model, &local_response_manager, &local_model);
2421 QuickSolveWithHint(neighborhood.cp_model, &local_response_manager,
2423 SolveLoadedCpModel(neighborhood.cp_model, &local_response_manager,
2425 CpSolverResponse local_response = local_response_manager.GetResponse();
2426 if (local_response.solution_info().empty()) {
2427 local_response.set_solution_info(solution_info);
2429 local_response.set_solution_info(
2430 absl::StrCat(local_response.solution_info(),
" ", solution_info));
2435 PostsolveResponseWrapper(local_params, num_neighborhood_model_vars,
2436 mapping_proto, postsolve_mapping,
2437 shared_->wall_timer, &local_response);
2438 data.status = local_response.status();
2439 data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
2441 if (generator_->IsRelaxationGenerator()) {
2442 bool has_feasible_solution =
false;
2445 has_feasible_solution =
true;
2449 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2450 local_response.solution_info());
2453 if (shared_->model_proto->has_objective()) {
2456 const IntegerValue current_obj_lb =
2457 shared_->response->GetInnerObjectiveLowerBound();
2459 const IntegerValue local_obj_lb =
2460 local_response_manager.GetInnerObjectiveLowerBound();
2463 neighborhood.cp_model.objective(), local_obj_lb.value());
2466 const IntegerValue new_inner_obj_lb = IntegerValue(
2468 scaled_local_obj_bound) -
2470 data.new_objective_bound = new_inner_obj_lb;
2471 data.initial_best_objective_bound = current_obj_lb;
2472 if (new_inner_obj_lb > current_obj_lb) {
2473 shared_->response->UpdateInnerObjectiveBounds(
2480 if (has_feasible_solution) {
2482 *shared_->model_proto,
2483 std::vector<int64>(local_response.solution().begin(),
2484 local_response.solution().end()))) {
2485 shared_->response->NewSolution(local_response,
2490 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2491 local_response.solution_info());
2492 shared_->time_limit->Stop();
2495 shared_->relaxation_solutions->NewRelaxationSolution(local_response);
2498 if (!local_response.solution().empty()) {
2500 *shared_->model_proto,
2501 std::vector<int64>(local_response.solution().begin(),
2502 local_response.solution().end())))
2507 data.new_objective = data.base_objective;
2511 shared_->model_proto->objective(), local_response));
2517 shared_->response->NewSolution(local_response,
2520 if (!neighborhood.is_reduced &&
2523 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2524 local_response.solution_info());
2525 shared_->time_limit->Stop();
2529 generator_->AddSolveData(data);
2532 const int total_num_calls = task_id;
2533 VLOG(2) <<
name() <<
": [difficulty: " << data.difficulty
2534 <<
", id: " << task_id
2535 <<
", deterministic_time: " << data.deterministic_time <<
" / "
2536 << data.deterministic_limit
2537 <<
", status: " << ProtoEnumToString<CpSolverStatus>(data.status)
2538 <<
", num calls: " << generator_->num_calls()
2539 <<
", UCB1 Score: " << generator_->GetUCBScore(total_num_calls)
2540 <<
", p: " << fully_solved_proportion <<
"]";
2544 void Synchronize()
override {
2545 generator_->Synchronize();
2546 const double old = deterministic_time_;
2547 deterministic_time_ = generator_->deterministic_time();
2548 shared_->time_limit->AdvanceDeterministicTime(deterministic_time_ - old);
2552 std::unique_ptr<NeighborhoodGenerator> generator_;
2553 NeighborhoodGeneratorHelper* helper_;
2554 const SatParameters parameters_;
2555 SharedClasses* shared_;
2558 void SolveCpModelParallel(
const CpModelProto&
model_proto,
2559 SharedResponseManager* shared_response_manager,
2560 SharedTimeLimit* shared_time_limit,
2562 CHECK(shared_response_manager !=
nullptr);
2563 const SatParameters&
parameters = *global_model->GetOrCreate<SatParameters>();
2564 const int num_search_workers =
parameters.num_search_workers();
2565 const bool log_search =
parameters.log_search_progress() || VLOG_IS_ON(1);
2567 <<
"Enumerating all solutions in parallel is not supported.";
2571 const int num_strategies =
2573 ? (
parameters.reduce_memory_usage_in_interleave_mode() ? 5 : 9)
2574 : num_search_workers;
2576 std::unique_ptr<SharedBoundsManager> shared_bounds_manager;
2578 shared_bounds_manager = absl::make_unique<SharedBoundsManager>(
model_proto);
2581 std::unique_ptr<SharedRelaxationSolutionRepository>
2582 shared_relaxation_solutions;
2584 shared_relaxation_solutions =
2585 absl::make_unique<SharedRelaxationSolutionRepository>(
2587 global_model->Register<SharedRelaxationSolutionRepository>(
2588 shared_relaxation_solutions.get());
2591 auto shared_lp_solutions = absl::make_unique<SharedLPSolutionRepository>(
2593 global_model->Register<SharedLPSolutionRepository>(shared_lp_solutions.get());
2597 std::unique_ptr<SharedIncompleteSolutionManager> shared_incomplete_solutions;
2598 const bool use_feasibility_pump =
parameters.use_feasibility_pump() &&
2602 if (use_feasibility_pump) {
2603 shared_incomplete_solutions =
2604 absl::make_unique<SharedIncompleteSolutionManager>();
2605 global_model->Register<SharedIncompleteSolutionManager>(
2606 shared_incomplete_solutions.get());
2609 SharedClasses shared;
2612 shared.time_limit = shared_time_limit;
2613 shared.bounds = shared_bounds_manager.get();
2614 shared.response = shared_response_manager;
2615 shared.relaxation_solutions = shared_relaxation_solutions.get();
2616 shared.lp_solutions = shared_lp_solutions.get();
2617 shared.incomplete_solutions = shared_incomplete_solutions.get();
2620 std::vector<std::unique_ptr<SubSolver>> subsolvers;
2623 subsolvers.push_back(absl::make_unique<SynchronizationPoint>(
2624 [shared_response_manager, &shared_bounds_manager,
2625 &shared_relaxation_solutions, &shared_lp_solutions]() {
2626 shared_response_manager->Synchronize();
2627 shared_response_manager->MutableSolutionsRepository()->Synchronize();
2628 if (shared_bounds_manager !=
nullptr) {
2631 if (shared_relaxation_solutions !=
nullptr) {
2634 if (shared_lp_solutions !=
nullptr) {
2635 shared_lp_solutions->Synchronize();
2644 local_params.set_stop_after_first_solution(
true);
2645 local_params.set_linearization_level(0);
2646 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2647 "first_solution", local_params,
2655 int num_non_lns_strategies = num_strategies;
2658 num_non_lns_strategies =
std::max(1, num_strategies - 1);
2661 for (
int i = 0; i < num_non_lns_strategies; ++i) {
2662 std::string worker_name;
2663 const SatParameters local_params =
2668 if (local_params.use_lns_only())
continue;
2671 if (
parameters.optimize_with_max_hs())
continue;
2673 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2674 worker_name, local_params,
2680 if (use_feasibility_pump) {
2681 subsolvers.push_back(
2682 absl::make_unique<FeasibilityPumpSolver>(
parameters, &shared));
2689 auto unique_helper = absl::make_unique<NeighborhoodGeneratorHelper>(
2691 shared_bounds_manager.get());
2692 NeighborhoodGeneratorHelper* helper = unique_helper.get();
2693 subsolvers.push_back(std::move(unique_helper));
2695 const int num_lns_strategies =
parameters.diversify_lns_params() ? 6 : 1;
2696 for (
int i = 0; i < num_lns_strategies; ++i) {
2697 std::string strategy_name;
2698 const SatParameters local_params =
2700 if (local_params.use_lns_only())
continue;
2706 subsolvers.push_back(absl::make_unique<LnsSolver>(
2707 absl::make_unique<SimpleNeighborhoodGenerator>(
2708 helper, absl::StrCat(
"rnd_lns_", strategy_name)),
2709 local_params, helper, &shared));
2710 subsolvers.push_back(absl::make_unique<LnsSolver>(
2711 absl::make_unique<VariableGraphNeighborhoodGenerator>(
2712 helper, absl::StrCat(
"var_lns_", strategy_name)),
2713 local_params, helper, &shared));
2714 subsolvers.push_back(absl::make_unique<LnsSolver>(
2715 absl::make_unique<ConstraintGraphNeighborhoodGenerator>(
2716 helper, absl::StrCat(
"cst_lns_", strategy_name)),
2717 local_params, helper, &shared));
2719 if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty()) {
2720 subsolvers.push_back(absl::make_unique<LnsSolver>(
2721 absl::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
2723 absl::StrCat(
"scheduling_time_window_lns_", strategy_name)),
2724 local_params, helper, &shared));
2725 subsolvers.push_back(absl::make_unique<LnsSolver>(
2726 absl::make_unique<SchedulingNeighborhoodGenerator>(
2727 helper, absl::StrCat(
"scheduling_random_lns_", strategy_name)),
2728 local_params, helper, &shared));
2741 subsolvers.push_back(absl::make_unique<LnsSolver>(
2742 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2743 helper, shared.response, shared.relaxation_solutions,
2744 shared.lp_solutions,
nullptr,
2745 absl::StrCat(
"rins_lns_", strategy_name)),
2746 local_params, helper, &shared));
2749 subsolvers.push_back(absl::make_unique<LnsSolver>(
2750 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2751 helper,
nullptr, shared.relaxation_solutions,
2752 shared.lp_solutions, shared.incomplete_solutions,
2753 absl::StrCat(
"rens_lns_", strategy_name)),
2754 local_params, helper, &shared));
2758 subsolvers.push_back(absl::make_unique<LnsSolver>(
2760 ConsecutiveConstraintsRelaxationNeighborhoodGenerator>(
2761 helper, absl::StrCat(
"rnd_rel_lns_", strategy_name)),
2762 local_params, helper, &shared));
2764 subsolvers.push_back(absl::make_unique<LnsSolver>(
2765 absl::make_unique<WeightedRandomRelaxationNeighborhoodGenerator>(
2766 helper, absl::StrCat(
"wgt_rel_lns_", strategy_name)),
2767 local_params, helper, &shared));
2774 subsolvers.push_back(
2775 absl::make_unique<SynchronizationPoint>([shared_response_manager]() {
2776 shared_response_manager->UpdatePrimalIntegral();
2781 std::vector<std::string> names;
2782 for (
const auto& subsolver : subsolvers) {
2783 if (!subsolver->name().empty()) names.push_back(subsolver->name());
2785 LOG(INFO) << absl::StrFormat(
2786 "*** starting Search at %.2fs with %i workers and subsolvers: [ %s ]",
2787 wall_timer->
Get(), num_search_workers, absl::StrJoin(names,
", "));
2799 #endif // __PORTABLE_PLATFORM__
2804 void AddPostsolveClauses(
const std::vector<int>& postsolve_mapping,
2805 Model*
model, CpModelProto* mapping_proto) {
2806 auto* mapping =
model->GetOrCreate<CpModelMapping>();
2807 auto* postsolve =
model->GetOrCreate<PostsolveClauses>();
2808 for (
const auto& clause : postsolve->clauses) {
2809 auto*
ct = mapping_proto->add_constraints()->mutable_bool_or();
2810 for (
const Literal l : clause) {
2811 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
2813 var = postsolve_mapping[
var];
2817 postsolve->clauses.clear();
2828 CpSolverResponse final_response;
2830 #if !defined(__PORTABLE_PLATFORM__)
2832 if (FLAGS_cp_model_dump_models) {
2833 const std::string
file =
2834 absl::StrCat(FLAGS_cp_model_dump_prefix,
"model.pbtxt");
2835 LOG(INFO) <<
"Dumping cp model proto to '" <<
file <<
"'.";
2840 if (FLAGS_cp_model_dump_response) {
2842 const std::string
file =
2843 absl::StrCat(FLAGS_cp_model_dump_prefix,
"response.pbtxt");
2844 LOG(INFO) <<
"Dumping response proto to '" <<
file <<
"'.";
2850 if (!FLAGS_cp_model_params.empty()) {
2851 SatParameters params = *
model->GetOrCreate<SatParameters>();
2852 SatParameters flag_params;
2853 CHECK(google::protobuf::TextFormat::ParseFromString(FLAGS_cp_model_params,
2855 params.MergeFrom(flag_params);
2861 if (
model->GetOrCreate<SatParameters>()->catch_sigint_signal()) {
2862 handler.
Register([&shared_time_limit]() { shared_time_limit.
Stop(); });
2864 #endif // __PORTABLE_PLATFORM__
2866 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
2867 const bool log_search = params.log_search_progress() || VLOG_IS_ON(1);
2868 LOG_IF(INFO, log_search) <<
"Parameters: " << params.ShortDebugString();
2871 absl::Cleanup<std::function<void()>> display_response_cleanup;
2873 display_response_cleanup =
2884 if (!error.empty()) {
2885 LOG_IF(INFO, log_search) << error;
2887 return final_response;
2896 if (!params.use_sat_inprocessing() && !
model_proto.has_objective() &&
2897 !
model_proto.has_solution_hint() && !params.enumerate_all_solutions() &&
2898 !params.use_lns_only() && params.num_search_workers() <= 1 &&
2900 bool is_pure_sat =
true;
2901 for (
const IntegerVariableProto&
var :
model_proto.variables()) {
2902 if (
var.domain_size() != 2 ||
var.domain(0) < 0 ||
var.domain(1) > 1) {
2903 is_pure_sat =
false;
2908 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
2909 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolOr &&
2910 ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolAnd) {
2911 is_pure_sat =
false;
2921 final_response.set_user_time(user_timer.
Get());
2922 final_response.set_deterministic_time(
2924 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
2925 if (params.fill_tightened_domains_in_response()) {
2926 *final_response.mutable_tightened_variables() =
model_proto.variables();
2928 return final_response;
2933 LOG_IF(INFO, log_search) << absl::StrFormat(
2937 CpModelProto mapping_proto;
2943 absl::make_unique<PresolveContext>(&new_cp_model_proto, &mapping_proto);
2945 bool degraded_assumptions_support =
false;
2946 if (params.num_search_workers() > 1 ||
model_proto.has_objective()) {
2953 degraded_assumptions_support =
true;
2954 context->InitializeNewDomains();
2956 if (!
context->SetLiteralToTrue(ref)) {
2958 final_response.add_sufficient_assumptions_for_infeasibility(ref);
2959 return final_response;
2966 std::function<void(CpSolverResponse *
response)> postprocess_solution;
2969 std::vector<int> postsolve_mapping;
2972 LOG(ERROR) <<
"Error while presolving, likely due to integer overflow.";
2974 return final_response;
2976 LOG_IF(INFO, log_search) <<
CpModelStats(new_cp_model_proto);
2977 if (params.cp_model_presolve()) {
2978 postprocess_solution = [&
model_proto, ¶ms, &mapping_proto,
2979 &shared_time_limit, &postsolve_mapping, &
wall_timer,
2981 AddPostsolveClauses(postsolve_mapping,
model, &mapping_proto);
2982 PostsolveResponseWrapper(params,
model_proto.variables_size(),
2983 mapping_proto, postsolve_mapping, &
wall_timer,
2985 if (!
response->solution().empty()) {
2988 std::vector<int64>(
response->solution().begin(),
2990 &mapping_proto, &postsolve_mapping))
2991 <<
"final postsolved solution";
2993 if (params.fill_tightened_domains_in_response()) {
2995 if (mapping_proto.variables().size() >=
2997 for (
int i = 0; i <
model_proto.variables().size(); ++i) {
2998 *
response->add_tightened_variables() = mapping_proto.variables(i);
3003 response->set_user_time(user_timer.Get());
3005 shared_time_limit.GetElapsedDeterministicTime());
3010 &user_timer](CpSolverResponse*
response) {
3012 const int initial_size =
model_proto.variables_size();
3013 if (
response->solution_size() > 0) {
3014 response->mutable_solution()->Truncate(initial_size);
3015 }
else if (
response->solution_lower_bounds_size() > 0) {
3016 response->mutable_solution_lower_bounds()->Truncate(initial_size);
3017 response->mutable_solution_upper_bounds()->Truncate(initial_size);
3019 if (params.fill_tightened_domains_in_response()) {
3025 shared_time_limit.GetElapsedDeterministicTime());
3033 log_search, params.enumerate_all_solutions(), &new_cp_model_proto,
3039 if (!observers.empty()) {
3042 &postprocess_solution, &shared_time_limit](
3043 const CpSolverResponse& response_of_presolved_problem) {
3044 CpSolverResponse
response = response_of_presolved_problem;
3046 if (!
response.solution().empty()) {
3047 if (
DEBUG_MODE || FLAGS_cp_model_check_intermediate_solutions) {
3054 for (
const auto& observer : observers) {
3060 #if !defined(__PORTABLE_PLATFORM__)
3061 if (FLAGS_cp_model_dump_models) {
3062 const std::string presolved_file =
3063 absl::StrCat(FLAGS_cp_model_dump_prefix,
"presolved_model.pbtxt");
3064 LOG(INFO) <<
"Dumping presolved cp model proto to '" << presolved_file
3069 const std::string mapping_file =
3070 absl::StrCat(FLAGS_cp_model_dump_prefix,
"mapping_model.pbtxt");
3071 LOG(INFO) <<
"Dumping mapping cp model proto to '" << mapping_file <<
"'.";
3074 #endif // __PORTABLE_PLATFORM__
3076 if (params.stop_after_presolve() || shared_time_limit.
LimitReached()) {
3077 int64 num_terms = 0;
3078 for (
const ConstraintProto&
ct : new_cp_model_proto.constraints()) {
3081 LOG_IF(INFO, log_search)
3082 <<
"Stopped after presolve."
3083 <<
"\nPresolvedNumVariables: " << new_cp_model_proto.variables().size()
3084 <<
"\nPresolvedNumConstraints: "
3085 << new_cp_model_proto.constraints().size()
3086 <<
"\nPresolvedNumTerms: " << num_terms;
3090 final_response = shared_response_manager.
GetResponse();
3091 postprocess_solution(&final_response);
3092 return final_response;
3096 if (params.stop_after_first_solution()) {
3098 [&shared_time_limit](
3099 const CpSolverResponse& response_of_presolved_problem) {
3100 shared_time_limit.
Stop();
3104 #if defined(__PORTABLE_PLATFORM__)
3107 #else // __PORTABLE_PLATFORM__
3108 if (params.num_search_workers() > 1 || params.interleave_search()) {
3109 SolveCpModelParallel(new_cp_model_proto, &shared_response_manager,
3111 #endif // __PORTABLE_PLATFORM__
3114 LOG(INFO) << absl::StrFormat(
"*** starting to load the model at %.2fs",
3117 LoadCpModel(new_cp_model_proto, &shared_response_manager,
model);
3120 LOG(INFO) << absl::StrFormat(
"*** starting sequential search at %.2fs",
3122 LOG(INFO) <<
"Initial num_bool: "
3125 if (params.repair_hint()) {
3126 MinimizeL1DistanceWithHint(new_cp_model_proto, &shared_response_manager,
3129 QuickSolveWithHint(new_cp_model_proto, &shared_response_manager,
model);
3131 SolveLoadedCpModel(new_cp_model_proto, &shared_response_manager,
model);
3134 final_response = shared_response_manager.
GetResponse();
3135 postprocess_solution(&final_response);
3136 if (!final_response.solution().empty()) {
3138 model_proto, std::vector<int64>(final_response.solution().begin(),
3139 final_response.solution().end())));
3141 if (degraded_assumptions_support &&
3144 *final_response.mutable_sufficient_assumptions_for_infeasibility() =
3147 return final_response;
3156 const SatParameters& params) {
3162 #if !defined(__PORTABLE_PLATFORM__)
3164 const std::string& params) {
3169 #endif // !__PORTABLE_PLATFORM__