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"
89 ABSL_FLAG(std::string, cp_model_dump_prefix,
".\\",
90 "Prefix filename for all dumped files");
92 ABSL_FLAG(std::string, cp_model_dump_prefix,
"/tmp/",
93 "Prefix filename for all dumped files");
96 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
97 "protos (original model, presolved model, mapping model) in text "
98 "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|"
99 "mapping_model}.pbtxt.");
102 "DEBUG ONLY. When set to true, solve will dump all "
103 "lns models proto in text format to "
104 "'FLAGS_cp_model_dump_prefix'lns_xxx.pbtxt.");
107 "DEBUG ONLY. If true, the final response of each solve will be "
108 "dumped to 'FLAGS_cp_model_dump_prefix'response.pbtxt");
111 "This is interpreted as a text SatParameters proto. The "
112 "specified fields will override the normal ones for all solves.");
115 "If non-empty, a proof in DRAT format will be written to this file. "
116 "This will only be used for pure-SAT problems.");
119 "If true, a proof in DRAT format will be stored in memory and "
120 "checked if the problem is UNSAT. This will only be used for "
121 "pure-SAT problems.");
124 std::numeric_limits<double>::infinity(),
125 "Maximum time in seconds to check the DRAT proof. This will only "
126 "be used is the drat_check flag is enabled.");
128 ABSL_FLAG(
bool, cp_model_check_intermediate_solutions,
false,
129 "When true, all intermediate solutions found by the solver will be "
130 "checked. This can be expensive, therefore it is off by default.");
138 std::string Summarize(
const std::string&
input) {
141 return absl::StrCat(
input.substr(0, half),
" ... ",
152 std::map<std::string, int> num_constraints_by_name;
153 std::map<std::string, int> num_reif_constraints_by_name;
154 std::map<std::string, int> name_to_num_literals;
155 std::map<std::string, int> name_to_num_terms;
156 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
161 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
162 if (
ct.linear().vars_size() == 1)
name +=
"1";
163 if (
ct.linear().vars_size() == 2)
name +=
"2";
164 if (
ct.linear().vars_size() == 3)
name +=
"3";
165 if (
ct.linear().vars_size() > 3)
name +=
"N";
168 num_constraints_by_name[
name]++;
169 if (!
ct.enforcement_literal().empty()) {
170 num_reif_constraints_by_name[
name]++;
175 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
176 name_to_num_literals[
name] +=
ct.bool_or().literals().size();
177 }
else if (
ct.constraint_case() ==
178 ConstraintProto::ConstraintCase::kBoolAnd) {
179 name_to_num_literals[
name] +=
ct.bool_and().literals().size();
180 }
else if (
ct.constraint_case() ==
181 ConstraintProto::ConstraintCase::kAtMostOne) {
182 name_to_num_literals[
name] +=
ct.at_most_one().literals().size();
185 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
186 ct.linear().vars_size() > 3) {
187 name_to_num_terms[
name] +=
ct.linear().vars_size();
191 int num_constants = 0;
192 std::set<int64> constant_values;
193 std::map<Domain, int> num_vars_per_domains;
195 if (
var.domain_size() == 2 &&
var.domain(0) ==
var.domain(1)) {
197 constant_values.insert(
var.domain(0));
205 absl::StrAppend(&result,
"Optimization model '",
model_proto.name(),
208 absl::StrAppend(&result,
"Satisfaction model '",
model_proto.name(),
212 for (
const DecisionStrategyProto& strategy :
model_proto.search_strategy()) {
214 &result,
"Search strategy: on ", strategy.variables_size(),
216 ProtoEnumToString<DecisionStrategyProto::VariableSelectionStrategy>(
217 strategy.variable_selection_strategy()),
219 ProtoEnumToString<DecisionStrategyProto::DomainReductionStrategy>(
220 strategy.domain_reduction_strategy()),
224 const std::string objective_string =
226 ? absl::StrCat(
" (",
model_proto.objective().vars_size(),
229 absl::StrAppend(&result,
"#Variables: ",
model_proto.variables_size(),
230 objective_string,
"\n");
231 if (num_vars_per_domains.size() < 100) {
232 for (
const auto& entry : num_vars_per_domains) {
233 const std::string temp = absl::StrCat(
" - ", entry.second,
" in ",
234 entry.first.ToString(),
"\n");
235 absl::StrAppend(&result, Summarize(temp));
238 int64 max_complexity = 0;
241 for (
const auto& entry : num_vars_per_domains) {
244 max_complexity =
std::max(max_complexity,
245 static_cast<int64>(entry.first.NumIntervals()));
247 absl::StrAppend(&result,
" - ", num_vars_per_domains.size(),
248 " different domains in [",
min,
",",
max,
249 "] with a largest complexity of ", max_complexity,
".\n");
252 if (num_constants > 0) {
253 const std::string temp =
254 absl::StrCat(
" - ", num_constants,
" constants in {",
255 absl::StrJoin(constant_values,
","),
"} \n");
256 absl::StrAppend(&result, Summarize(temp));
259 std::vector<std::string> constraints;
260 constraints.reserve(num_constraints_by_name.size());
261 for (
const auto& entry : num_constraints_by_name) {
262 const std::string&
name = entry.first;
263 constraints.push_back(absl::StrCat(
"#",
name,
": ", entry.second));
265 absl::StrAppend(&constraints.back(),
266 " (#enforced: ", num_reif_constraints_by_name[
name],
")");
269 absl::StrAppend(&constraints.back(),
270 " (#literals: ", name_to_num_literals[
name],
")");
273 absl::StrAppend(&constraints.back(),
274 " (#terms: ", name_to_num_terms[
name],
")");
277 std::sort(constraints.begin(), constraints.end());
278 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
284 bool has_objective) {
286 absl::StrAppend(&result,
"CpSolverResponse:");
287 absl::StrAppend(&result,
"\nstatus: ",
288 ProtoEnumToString<CpSolverStatus>(
response.status()));
291 absl::StrAppendFormat(&result,
"\nobjective: %.16g",
293 absl::StrAppendFormat(&result,
"\nbest_bound: %.16g",
296 absl::StrAppend(&result,
"\nobjective: NA");
297 absl::StrAppend(&result,
"\nbest_bound: NA");
300 absl::StrAppend(&result,
"\nbooleans: ",
response.num_booleans());
301 absl::StrAppend(&result,
"\nconflicts: ",
response.num_conflicts());
302 absl::StrAppend(&result,
"\nbranches: ",
response.num_branches());
306 absl::StrAppend(&result,
307 "\npropagations: ",
response.num_binary_propagations());
309 &result,
"\ninteger_propagations: ",
response.num_integer_propagations());
311 absl::StrAppend(&result,
"\nrestarts: ",
response.num_restarts());
312 absl::StrAppend(&result,
"\nlp_iterations: ",
response.num_lp_iterations());
313 absl::StrAppend(&result,
"\nwalltime: ",
response.wall_time());
314 absl::StrAppend(&result,
"\nusertime: ",
response.user_time());
315 absl::StrAppend(&result,
316 "\ndeterministic_time: ",
response.deterministic_time());
317 absl::StrAppend(&result,
"\nprimal_integral: ",
response.primal_integral());
318 absl::StrAppend(&result,
"\n");
324 void FillSolutionInResponse(
const CpModelProto&
model_proto,
const Model&
model,
327 response->clear_solution_lower_bounds();
328 response->clear_solution_upper_bounds();
330 auto* mapping =
model.Get<CpModelMapping>();
332 auto* integer_trail =
model.Get<IntegerTrail>();
334 std::vector<int64> solution;
335 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
336 if (mapping->IsInteger(i)) {
337 const IntegerVariable
var = mapping->Integer(i);
338 if (integer_trail->IsCurrentlyIgnored(
var)) {
351 DCHECK(mapping->IsBoolean(i));
353 if (trail->Assignment().LiteralIsAssigned(
literal)) {
362 if (!solution.empty()) {
364 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
372 const auto& assignment = trail->Assignment();
373 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
374 if (mapping->IsBoolean(i)) {
375 if (assignment.VariableIsAssigned(mapping->Literal(i).Variable())) {
380 response->add_solution_lower_bounds(0);
381 response->add_solution_upper_bounds(1);
384 response->add_solution_lower_bounds(
386 response->add_solution_upper_bounds(
395 IntegerVariable GetOrCreateVariableWithTightBound(
396 const std::vector<std::pair<IntegerVariable, int64>>& terms, Model*
model) {
398 if (terms.size() == 1 && terms.front().second == 1) {
399 return terms.front().first;
401 if (terms.size() == 1 && terms.front().second == -1) {
407 for (
const std::pair<IntegerVariable, int64> var_coeff : terms) {
410 const int64 coeff = var_coeff.second;
411 const int64 prod1 = min_domain * coeff;
412 const int64 prod2 = max_domain * coeff;
419 IntegerVariable GetOrCreateVariableGreaterOrEqualToSumOf(
420 const std::vector<std::pair<IntegerVariable, int64>>& terms, Model*
model) {
422 if (terms.size() == 1 && terms.front().second == 1) {
423 return terms.front().first;
425 if (terms.size() == 1 && terms.front().second == -1) {
430 const IntegerVariable new_var =
431 GetOrCreateVariableWithTightBound(terms,
model);
432 std::vector<IntegerVariable> vars;
433 std::vector<int64> coeffs;
434 for (
const auto& term : terms) {
435 vars.push_back(term.first);
436 coeffs.push_back(term.second);
438 vars.push_back(new_var);
439 coeffs.push_back(-1);
444 void TryToAddCutGenerators(
const CpModelProto&
model_proto,
445 const ConstraintProto&
ct, Model* m,
446 LinearRelaxation* relaxation) {
447 const int linearization_level =
448 m->GetOrCreate<SatParameters>()->linearization_level();
449 auto* mapping = m->GetOrCreate<CpModelMapping>();
450 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit &&
451 linearization_level > 1) {
452 std::vector<int> tails(
ct.circuit().tails().begin(),
453 ct.circuit().tails().end());
454 std::vector<int> heads(
ct.circuit().heads().begin(),
455 ct.circuit().heads().end());
456 std::vector<Literal> literals = mapping->Literals(
ct.circuit().literals());
457 const int num_nodes =
ReindexArcs(&tails, &heads, &literals);
459 relaxation->cut_generators.push_back(
463 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kRoutes &&
464 linearization_level > 1) {
465 std::vector<int> tails(
ct.routes().tails().begin(),
466 ct.routes().tails().end());
467 std::vector<int> heads(
ct.routes().heads().begin(),
468 ct.routes().heads().end());
469 std::vector<Literal> literals = mapping->Literals(
ct.routes().literals());
472 for (
int i = 0; i <
ct.routes().tails_size(); ++i) {
473 num_nodes =
std::max(num_nodes, 1 +
ct.routes().tails(i));
474 num_nodes =
std::max(num_nodes, 1 +
ct.routes().heads(i));
476 if (
ct.routes().demands().empty() ||
ct.routes().capacity() == 0) {
477 relaxation->cut_generators.push_back(
481 const std::vector<int64> demands(
ct.routes().demands().begin(),
482 ct.routes().demands().end());
483 relaxation->cut_generators.push_back(
485 ct.routes().capacity(), m));
488 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kIntProd) {
490 if (
ct.int_prod().vars_size() != 2)
return;
494 IntegerVariable z = mapping->Integer(
ct.int_prod().target());
495 IntegerVariable x = mapping->Integer(
ct.int_prod().vars(0));
496 IntegerVariable y = mapping->Integer(
ct.int_prod().vars(1));
498 IntegerTrail*
const integer_trail = m->GetOrCreate<IntegerTrail>();
499 IntegerValue x_lb = integer_trail->LowerBound(x);
500 IntegerValue x_ub = integer_trail->UpperBound(x);
501 IntegerValue y_lb = integer_trail->LowerBound(y);
502 IntegerValue y_ub = integer_trail->UpperBound(y);
506 if (x_lb < 0 && x_ub > 0)
return;
516 if (x_lb < 0 && x_ub > 0)
return;
517 if (y_lb < 0 && y_ub > 0)
return;
530 relaxation->cut_generators.push_back(
534 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kAllDiff) {
535 if (linearization_level < 2)
return;
537 const int num_vars =
ct.all_diff().vars_size();
538 if (num_vars <= m->GetOrCreate<SatParameters>()->max_all_diff_cut_size()) {
539 std::vector<IntegerVariable> vars =
540 mapping->Integers(
ct.all_diff().vars());
541 relaxation->cut_generators.push_back(
546 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCumulative) {
547 if (linearization_level < 2)
return;
550 std::vector<IntegerVariable> demands =
551 mapping->Integers(
ct.cumulative().demands());
552 std::vector<IntervalVariable> intervals =
553 mapping->Intervals(
ct.cumulative().intervals());
555 mapping->Integer(
ct.cumulative().capacity());
556 relaxation->cut_generators.push_back(
559 relaxation->cut_generators.push_back(
563 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kNoOverlap) {
564 if (linearization_level < 2)
return;
566 std::vector<IntervalVariable> intervals =
567 mapping->Intervals(
ct.no_overlap().intervals());
568 relaxation->cut_generators.push_back(
570 relaxation->cut_generators.push_back(
574 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinMax) {
575 if (!m->GetOrCreate<SatParameters>()->add_lin_max_cuts())
return;
579 if (
ct.lin_max().target().vars_size() != 1)
return;
580 if (
ct.lin_max().target().coeffs(0) != 1)
return;
582 const IntegerVariable target =
583 mapping->Integer(
ct.lin_max().target().vars(0));
584 std::vector<LinearExpression> exprs;
585 exprs.reserve(
ct.lin_max().exprs_size());
586 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
595 const std::vector<IntegerVariable> z_vars =
598 if (linearization_level >= 2) {
599 relaxation->cut_generators.push_back(
607 LinearRelaxation ComputeLinearRelaxation(
const CpModelProto&
model_proto,
608 int linearization_level, Model* m) {
609 LinearRelaxation relaxation;
612 absl::flat_hash_set<int> used_integer_variable;
614 auto* mapping = m->GetOrCreate<CpModelMapping>();
615 auto* encoder = m->GetOrCreate<IntegerEncoder>();
616 auto* trail = m->GetOrCreate<Trail>();
619 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit) {
620 for (
const int ref :
ct.circuit().literals()) {
621 const Literal l = mapping->Literal(ref);
636 for (
const int literal_ref : refs.literals) {
638 if (trail->Assignment().LiteralIsAssigned(
literal)) {
656 int num_full_encoding_relaxations = 0;
657 int num_partial_encoding_relaxations = 0;
658 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
659 if (mapping->IsBoolean(i))
continue;
661 const IntegerVariable
var = mapping->Integer(i);
672 if (encoder->VariableIsFullyEncoded(
var)) {
674 ++num_full_encoding_relaxations;
685 const int old = relaxation.linear_constraints.size();
687 if (relaxation.linear_constraints.size() > old) {
688 ++num_partial_encoding_relaxations;
694 m->GetOrCreate<BinaryImplicationGraph>()->TransformIntoMaxCliques(
695 &relaxation.at_most_ones);
696 for (
const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
697 if (at_most_one.empty())
continue;
700 for (
const Literal
literal : at_most_one) {
703 const bool unused ABSL_ATTRIBUTE_UNUSED =
704 lc.AddLiteralTerm(
literal, IntegerValue(1));
706 relaxation.linear_constraints.push_back(lc.Build());
711 relaxation.at_most_ones.clear();
716 for (
int i = 0; i < relaxation.linear_constraints.size(); ++i) {
717 if (relaxation.linear_constraints[i].vars.size() <= 1)
continue;
718 std::swap(relaxation.linear_constraints[new_size++],
719 relaxation.linear_constraints[i]);
721 relaxation.linear_constraints.resize(new_size);
724 VLOG(3) <<
"num_full_encoding_relaxations: " << num_full_encoding_relaxations;
725 VLOG(3) <<
"num_partial_encoding_relaxations: "
726 << num_partial_encoding_relaxations;
727 VLOG(3) << relaxation.linear_constraints.size()
728 <<
" constraints in the LP relaxation.";
729 VLOG(3) << relaxation.cut_generators.size() <<
" cuts generators.";
734 IntegerVariable AddLPConstraints(
const CpModelProto&
model_proto,
735 int linearization_level, Model* m) {
736 const LinearRelaxation relaxation =
737 ComputeLinearRelaxation(
model_proto, linearization_level, m);
745 const int num_lp_constraints = relaxation.linear_constraints.size();
746 const int num_lp_cut_generators = relaxation.cut_generators.size();
747 const int num_integer_variables =
748 m->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
751 num_integer_variables);
752 auto get_constraint_index = [](
int ct_index) {
return ct_index; };
753 auto get_cut_generator_index = [num_lp_constraints](
int cut_index) {
754 return num_lp_constraints + cut_index;
756 auto get_var_index = [num_lp_constraints,
757 num_lp_cut_generators](IntegerVariable
var) {
758 return num_lp_constraints + num_lp_cut_generators +
var.value();
760 for (
int i = 0; i < num_lp_constraints; i++) {
761 for (
const IntegerVariable
var : relaxation.linear_constraints[i].vars) {
762 components.
AddEdge(get_constraint_index(i), get_var_index(
var));
765 for (
int i = 0; i < num_lp_cut_generators; ++i) {
766 for (
const IntegerVariable
var : relaxation.cut_generators[i].vars) {
767 components.
AddEdge(get_cut_generator_index(i), get_var_index(
var));
775 for (
const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
777 for (
const Literal
literal : at_most_one) {
780 const bool unused ABSL_ATTRIBUTE_UNUSED =
781 builder.AddLiteralTerm(
literal, IntegerValue(1));
783 LinearConstraint lc = builder.Build();
784 for (
int i = 1; i < lc.vars.size(); ++i) {
785 components.
AddEdge(get_var_index(lc.vars[0]), get_var_index(lc.vars[i]));
790 std::vector<int> component_sizes(num_components, 0);
791 const std::vector<int> index_to_component = components.
GetComponentIds();
792 for (
int i = 0; i < num_lp_constraints; i++) {
793 ++component_sizes[index_to_component[get_constraint_index(i)]];
795 for (
int i = 0; i < num_lp_cut_generators; i++) {
796 ++component_sizes[index_to_component[get_cut_generator_index(i)]];
804 auto* mapping = m->GetOrCreate<CpModelMapping>();
805 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
806 const IntegerVariable
var =
808 ++component_sizes[index_to_component[get_var_index(
var)]];
812 std::vector<LinearProgrammingConstraint*> lp_constraints(num_components,
814 std::vector<std::vector<LinearConstraint>> component_to_constraints(
816 for (
int i = 0; i < num_lp_constraints; i++) {
817 const int c = index_to_component[get_constraint_index(i)];
818 if (component_sizes[c] <= 1)
continue;
819 component_to_constraints[c].push_back(relaxation.linear_constraints[i]);
820 if (lp_constraints[c] ==
nullptr) {
821 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
824 lp_constraints[c]->AddLinearConstraint(relaxation.linear_constraints[i]);
828 for (
int i = 0; i < num_lp_cut_generators; i++) {
829 const int c = index_to_component[get_cut_generator_index(i)];
830 if (lp_constraints[c] ==
nullptr) {
831 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
833 lp_constraints[c]->AddCutGenerator(std::move(relaxation.cut_generators[i]));
837 const SatParameters& params = *(m->GetOrCreate<SatParameters>());
838 if (params.add_clique_cuts() && params.linearization_level() > 1) {
839 for (LinearProgrammingConstraint* lp : lp_constraints) {
840 if (lp ==
nullptr)
continue;
845 if (params.add_knapsack_cuts() && params.linearization_level() > 1) {
846 for (
int c = 0; c < num_components; ++c) {
847 if (component_to_constraints[c].empty())
continue;
849 component_to_constraints[c], lp_constraints[c]->integer_variables(),
855 std::vector<std::vector<std::pair<IntegerVariable, int64>>>
856 component_to_cp_terms(num_components);
857 std::vector<std::pair<IntegerVariable, int64>> top_level_cp_terms;
858 int num_components_containing_objective = 0;
862 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
863 const IntegerVariable
var =
866 const int c = index_to_component[get_var_index(
var)];
867 if (lp_constraints[c] !=
nullptr) {
868 lp_constraints[c]->SetObjectiveCoefficient(
var, IntegerValue(coeff));
869 component_to_cp_terms[c].push_back(std::make_pair(
var, coeff));
872 top_level_cp_terms.push_back(std::make_pair(
var, coeff));
876 for (
int c = 0; c < num_components; ++c) {
877 if (component_to_cp_terms[c].empty())
continue;
878 const IntegerVariable sub_obj_var =
879 GetOrCreateVariableGreaterOrEqualToSumOf(component_to_cp_terms[c], m);
880 top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
881 lp_constraints[c]->SetMainObjectiveVariable(sub_obj_var);
882 num_components_containing_objective++;
886 const IntegerVariable main_objective_var =
888 ? GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m)
893 for (LinearProgrammingConstraint* lp_constraint : lp_constraints) {
894 if (lp_constraint ==
nullptr)
continue;
895 lp_constraint->RegisterWith(m);
896 VLOG(3) <<
"LP constraint: " << lp_constraint->DimensionString() <<
".";
899 VLOG(3) << top_level_cp_terms.size()
900 <<
" terms in the main objective linear equation ("
901 << num_components_containing_objective <<
" from LP constraints).";
902 return main_objective_var;
914 const std::function<
void(
const CpSolverResponse&
response)>& observer) {
920 #if !defined(__PORTABLE_PLATFORM__)
923 const std::string& params) {
925 if (!params.empty()) {
926 CHECK(google::protobuf::TextFormat::ParseFromString(params, &
parameters))
931 #endif // __PORTABLE_PLATFORM__
949 void RegisterVariableBoundsLevelZeroExport(
950 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
952 CHECK(shared_bounds_manager !=
nullptr);
953 int saved_trail_index = 0;
954 const auto broadcast_level_zero_bounds =
956 const std::vector<IntegerVariable>& modified_vars)
mutable {
957 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
959 std::vector<int> model_variables;
960 std::vector<int64> new_lower_bounds;
961 std::vector<int64> new_upper_bounds;
962 absl::flat_hash_set<int> visited_variables;
965 auto* integer_trail =
model->Get<IntegerTrail>();
966 for (
const IntegerVariable&
var : modified_vars) {
968 const int model_var =
969 mapping->GetProtoVariableFromIntegerVariable(positive_var);
970 if (model_var == -1 || visited_variables.contains(model_var)) {
977 visited_variables.insert(model_var);
979 integer_trail->LevelZeroLowerBound(positive_var).value();
981 integer_trail->LevelZeroUpperBound(positive_var).value();
984 model_variables.push_back(model_var);
985 new_lower_bounds.push_back(new_lb);
986 new_upper_bounds.push_back(new_ub);
990 auto* trail =
model->Get<Trail>();
991 for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
992 const Literal fixed_literal = (*trail)[saved_trail_index];
993 const int model_var = mapping->GetProtoVariableFromBooleanVariable(
994 fixed_literal.Variable());
995 if (model_var == -1 || visited_variables.contains(model_var)) {
1002 visited_variables.insert(model_var);
1003 model_variables.push_back(model_var);
1004 if (fixed_literal.IsPositive()) {
1005 new_lower_bounds.push_back(1);
1006 new_upper_bounds.push_back(1);
1008 new_lower_bounds.push_back(0);
1009 new_upper_bounds.push_back(0);
1013 if (!model_variables.empty()) {
1014 shared_bounds_manager->ReportPotentialNewBounds(
1020 if (!
model->Get<SatParameters>()->interleave_search()) {
1021 shared_bounds_manager->Synchronize();
1025 model->GetOrCreate<GenericLiteralWatcher>()
1026 ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
1032 void RegisterVariableBoundsLevelZeroImport(
1033 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
1035 CHECK(shared_bounds_manager !=
nullptr);
1036 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1037 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
1038 const int id = shared_bounds_manager->RegisterNewId();
1040 const auto& import_level_zero_bounds = [&
model_proto, shared_bounds_manager,
1041 model, integer_trail, id, mapping]() {
1042 std::vector<int> model_variables;
1043 std::vector<int64> new_lower_bounds;
1044 std::vector<int64> new_upper_bounds;
1045 shared_bounds_manager->GetChangedBounds(
1046 id, &model_variables, &new_lower_bounds, &new_upper_bounds);
1047 bool new_bounds_have_been_imported =
false;
1048 for (
int i = 0; i < model_variables.size(); ++i) {
1049 const int model_var = model_variables[i];
1052 if (!mapping->IsInteger(model_var))
continue;
1053 const IntegerVariable
var = mapping->Integer(model_var);
1054 const IntegerValue new_lb(new_lower_bounds[i]);
1055 const IntegerValue new_ub(new_upper_bounds[i]);
1056 const IntegerValue old_lb = integer_trail->LowerBound(
var);
1057 const IntegerValue old_ub = integer_trail->UpperBound(
var);
1058 const bool changed_lb = new_lb > old_lb;
1059 const bool changed_ub = new_ub < old_ub;
1060 if (!changed_lb && !changed_ub)
continue;
1062 new_bounds_have_been_imported =
true;
1064 const IntegerVariableProto& var_proto =
1066 const std::string& var_name =
1067 var_proto.name().empty()
1068 ? absl::StrCat(
"anonymous_var(", model_var,
")")
1070 LOG(
INFO) <<
" '" <<
model->Name() <<
"' imports new bounds for "
1071 << var_name <<
": from [" << old_lb <<
", " << old_ub
1072 <<
"] to [" << new_lb <<
", " << new_ub <<
"]";
1086 if (new_bounds_have_been_imported &&
1087 !
model->GetOrCreate<SatSolver>()->FinishPropagation()) {
1092 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1093 import_level_zero_bounds);
1098 void RegisterObjectiveBestBoundExport(
1099 IntegerVariable objective_var,
1100 SharedResponseManager* shared_response_manager, Model*
model) {
1101 auto* integer_trail =
model->Get<IntegerTrail>();
1102 const auto broadcast_objective_lower_bound =
1103 [objective_var, integer_trail, shared_response_manager,
1104 model](
const std::vector<IntegerVariable>& unused) {
1105 shared_response_manager->UpdateInnerObjectiveBounds(
1106 model->Name(), integer_trail->LevelZeroLowerBound(objective_var),
1107 integer_trail->LevelZeroUpperBound(objective_var));
1109 if (!
model->Get<SatParameters>()->interleave_search()) {
1110 shared_response_manager->Synchronize();
1113 model->GetOrCreate<GenericLiteralWatcher>()
1114 ->RegisterLevelZeroModifiedVariablesCallback(
1115 broadcast_objective_lower_bound);
1121 void RegisterObjectiveBoundsImport(
1122 SharedResponseManager* shared_response_manager, Model*
model) {
1123 auto* solver =
model->GetOrCreate<SatSolver>();
1124 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1125 auto* objective =
model->GetOrCreate<ObjectiveDefinition>();
1127 const auto import_objective_bounds = [
name, solver, integer_trail, objective,
1128 shared_response_manager]() {
1129 if (solver->AssumptionLevel() != 0)
return true;
1130 bool propagate =
false;
1132 const IntegerValue external_lb =
1133 shared_response_manager->SynchronizedInnerObjectiveLowerBound();
1134 const IntegerValue current_lb =
1135 integer_trail->LowerBound(objective->objective_var);
1136 if (external_lb > current_lb) {
1138 objective->objective_var, external_lb),
1145 const IntegerValue external_ub =
1146 shared_response_manager->SynchronizedInnerObjectiveUpperBound();
1147 const IntegerValue current_ub =
1148 integer_trail->UpperBound(objective->objective_var);
1149 if (external_ub < current_ub) {
1151 objective->objective_var, external_ub),
1158 if (!propagate)
return true;
1160 VLOG(2) <<
"'" <<
name <<
"' imports objective bounds: external ["
1161 << objective->ScaleIntegerObjective(external_lb) <<
", "
1162 << objective->ScaleIntegerObjective(external_ub) <<
"], current ["
1163 << objective->ScaleIntegerObjective(current_lb) <<
", "
1164 << objective->ScaleIntegerObjective(current_ub) <<
"]";
1166 return solver->FinishPropagation();
1169 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1170 import_objective_bounds);
1173 void LoadBaseModel(
const CpModelProto&
model_proto,
1174 SharedResponseManager* shared_response_manager,
1176 CHECK(shared_response_manager !=
nullptr);
1177 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1180 const auto unsat = [shared_response_manager, sat_solver,
model] {
1181 sat_solver->NotifyThatModelIsUnsat();
1182 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1183 absl::StrCat(
model->Name(),
" [loading]"));
1187 model->GetOrCreate<IntegerEncoder>()->DisableImplicationBetweenLiteral();
1189 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1190 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1191 const bool view_all_booleans_as_integers =
1193 (
parameters.search_branching() == SatParameters::FIXED_SEARCH &&
1195 mapping->CreateVariables(
model_proto, view_all_booleans_as_integers,
model);
1201 if (sat_solver->IsModelUnsat())
return unsat();
1207 std::set<std::string> unsupported_types;
1208 int num_ignored_constraints = 0;
1209 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1210 if (mapping->ConstraintIsAlreadyLoaded(&
ct)) {
1211 ++num_ignored_constraints;
1226 if (sat_solver->FinishPropagation()) {
1227 Trail* trail =
model->GetOrCreate<Trail>();
1228 const int old_num_fixed = trail->Index();
1229 if (trail->Index() > old_num_fixed) {
1230 VLOG(3) <<
"Constraint fixed " << trail->Index() - old_num_fixed
1235 if (sat_solver->IsModelUnsat()) {
1236 VLOG(2) <<
"UNSAT during extraction (after adding '"
1242 if (num_ignored_constraints > 0) {
1243 VLOG(3) << num_ignored_constraints <<
" constraints were skipped.";
1245 if (!unsupported_types.empty()) {
1246 VLOG(1) <<
"There is unsupported constraints types in this model: ";
1247 for (
const std::string& type : unsupported_types) {
1248 VLOG(1) <<
" - " << type;
1253 model->GetOrCreate<IntegerEncoder>()
1254 ->AddAllImplicationsBetweenAssociatedLiterals();
1255 if (!sat_solver->FinishPropagation())
return unsat();
1258 void LoadFeasibilityPump(
const CpModelProto&
model_proto,
1259 SharedResponseManager* shared_response_manager,
1261 CHECK(shared_response_manager !=
nullptr);
1265 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1266 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1267 if (
parameters.linearization_level() == 0)
return;
1270 const LinearRelaxation relaxation = ComputeLinearRelaxation(
1272 const int num_lp_constraints = relaxation.linear_constraints.size();
1273 if (num_lp_constraints == 0)
return;
1274 auto* feasibility_pump =
model->GetOrCreate<FeasibilityPump>();
1275 for (
int i = 0; i < num_lp_constraints; i++) {
1276 feasibility_pump->AddLinearConstraint(relaxation.linear_constraints[i]);
1280 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
1281 const IntegerVariable
var =
1282 mapping->Integer(
model_proto.objective().vars(i));
1284 feasibility_pump->SetObjectiveCoefficient(
var, IntegerValue(coeff));
1294 SharedResponseManager* shared_response_manager, Model*
model) {
1295 CHECK(shared_response_manager !=
nullptr);
1296 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1301 const auto unsat = [shared_response_manager, sat_solver,
model] {
1302 sat_solver->NotifyThatModelIsUnsat();
1303 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1304 absl::StrCat(
model->Name(),
" [loading]"));
1307 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1308 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1314 if (
model->Mutable<PrecedencesPropagator>() !=
nullptr &&
1315 parameters.auto_detect_greater_than_at_least_one_of()) {
1316 model->Mutable<PrecedencesPropagator>()
1317 ->AddGreaterThanAtLeastOneOfConstraints(
model);
1318 if (!sat_solver->FinishPropagation())
return unsat();
1324 if (
parameters.cp_model_probing_level() > 1) {
1326 if (
model->GetOrCreate<SatSolver>()->IsModelUnsat()) {
1329 if (!
model->GetOrCreate<BinaryImplicationGraph>()
1330 ->ComputeTransitiveReduction()) {
1343 const CpObjectiveProto& obj =
model_proto.objective();
1344 std::vector<std::pair<IntegerVariable, int64>> terms;
1345 terms.reserve(obj.vars_size());
1346 for (
int i = 0; i < obj.vars_size(); ++i) {
1348 std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i)));
1351 objective_var = GetOrCreateVariableWithTightBound(terms,
model);
1353 objective_var = GetOrCreateVariableGreaterOrEqualToSumOf(terms,
model);
1360 const CpObjectiveProto& objective_proto =
model_proto.objective();
1361 auto* objective_definition =
model->GetOrCreate<ObjectiveDefinition>();
1363 objective_definition->scaling_factor = objective_proto.scaling_factor();
1364 if (objective_definition->scaling_factor == 0.0) {
1365 objective_definition->scaling_factor = 1.0;
1367 objective_definition->offset = objective_proto.offset();
1368 objective_definition->objective_var = objective_var;
1370 const int size = objective_proto.vars_size();
1371 objective_definition->vars.resize(size);
1372 objective_definition->coeffs.resize(size);
1373 for (
int i = 0; i < objective_proto.vars_size(); ++i) {
1376 objective_definition->vars[i] = mapping->Integer(objective_proto.vars(i));
1377 objective_definition->coeffs[i] = IntegerValue(objective_proto.coeffs(i));
1380 const int ref = objective_proto.vars(i);
1381 if (mapping->IsInteger(ref)) {
1382 const IntegerVariable
var = mapping->Integer(objective_proto.vars(i));
1383 objective_definition->objective_impacting_variables.insert(
1389 model->TakeOwnership(
1390 new LevelZeroEquality(objective_var, objective_definition->vars,
1391 objective_definition->coeffs,
model));
1397 const Domain automatic_domain =
1398 model->GetOrCreate<IntegerTrail>()->InitialVariableDomain(
1401 <<
" scaling_factor:" <<
model_proto.objective().scaling_factor();
1402 VLOG(3) <<
"Automatic internal objective domain: " << automatic_domain;
1403 VLOG(3) <<
"User specified internal objective domain: " << user_domain;
1405 const bool ok =
model->GetOrCreate<IntegerTrail>()->UpdateInitialDomain(
1406 objective_var, user_domain);
1408 VLOG(2) <<
"UNSAT due to the objective domain.";
1419 if (!automatic_domain.IsIncludedIn(user_domain)) {
1420 std::vector<IntegerVariable> vars;
1421 std::vector<int64> coeffs;
1422 const CpObjectiveProto& obj =
model_proto.objective();
1423 for (
int i = 0; i < obj.vars_size(); ++i) {
1424 vars.push_back(mapping->Integer(obj.vars(i)));
1425 coeffs.push_back(obj.coeffs(i));
1427 vars.push_back(objective_var);
1428 coeffs.push_back(-1);
1435 if (!sat_solver->FinishPropagation())
return unsat();
1439 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1440 shared_response_manager->UpdateInnerObjectiveBounds(
1441 "init", integer_trail->LowerBound(objective_var),
1442 integer_trail->UpperBound(objective_var));
1445 RegisterObjectiveBestBoundExport(objective_var, shared_response_manager,
1451 if (
model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1452 RegisterObjectiveBoundsImport(shared_response_manager,
model);
1458 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1459 auto* lp_dispatcher =
model->GetOrCreate<LinearProgrammingDispatcher>();
1460 auto* lp_vars =
model->GetOrCreate<LPVariables>();
1461 IntegerVariable size = integer_trail->NumIntegerVariables();
1462 for (IntegerVariable positive_var(0); positive_var < size;
1463 positive_var += 2) {
1465 lp_var.positive_var = positive_var;
1467 mapping->GetProtoVariableFromIntegerVariable(positive_var);
1470 if (lp_var.model_var >= 0) {
1471 lp_vars->vars.push_back(lp_var);
1472 lp_vars->model_vars_size =
1473 std::max(lp_vars->model_vars_size, lp_var.model_var + 1);
1478 auto* search_heuristics =
model->GetOrCreate<SearchHeuristics>();
1482 search_heuristics->fixed_search =
1484 search_heuristics->fixed_search,
model);
1488 std::vector<BooleanOrIntegerVariable> vars;
1489 std::vector<IntegerValue> values;
1490 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1491 const int ref =
model_proto.solution_hint().vars(i);
1493 BooleanOrIntegerVariable
var;
1494 if (mapping->IsBoolean(ref)) {
1495 var.bool_var = mapping->Literal(ref).Variable();
1497 var.int_var = mapping->Integer(ref);
1499 vars.push_back(
var);
1500 values.push_back(IntegerValue(
model_proto.solution_hint().values(i)));
1508 const std::string solution_info =
model->Name();
1510 shared_response_manager]() {
1513 response.set_solution_info(solution_info);
1517 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1518 CoreBasedOptimizer* core =
1519 new CoreBasedOptimizer(objective_var, objective.vars, objective.coeffs,
1520 solution_observer,
model);
1521 model->Register<CoreBasedOptimizer>(core);
1522 model->TakeOwnership(core);
1532 void SolveLoadedCpModel(
const CpModelProto&
model_proto,
1533 SharedResponseManager* shared_response_manager,
1535 if (shared_response_manager->ProblemIsSolved())
return;
1537 const std::string& solution_info =
model->Name();
1539 &shared_response_manager]() {
1542 response.set_solution_info(solution_info);
1549 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1551 const SatParameters&
parameters = *
model->GetOrCreate<SatParameters>();
1557 solution_observer();
1558 if (!
parameters.enumerate_all_solutions())
break;
1562 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1566 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1570 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1571 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1573 std::vector<int> core_in_proto_format;
1574 for (
const Literal l : core) {
1575 core_in_proto_format.push_back(
1576 mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1577 if (!l.IsPositive()) {
1578 core_in_proto_format.back() =
NegatedRef(core_in_proto_format.back());
1581 shared_response_manager->AddUnsatCore(core_in_proto_format);
1585 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1586 const IntegerVariable objective_var = objective.objective_var;
1594 objective, solution_observer,
model);
1596 status =
model->Mutable<CoreBasedOptimizer>()->Optimize();
1601 if (
parameters.binary_search_num_conflicts() >= 0) {
1603 solution_observer,
model);
1606 objective_var, solution_observer,
model);
1614 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1621 shared_response_manager->SetStatsFromModel(
model);
1626 void QuickSolveWithHint(
const CpModelProto&
model_proto,
1627 SharedResponseManager* shared_response_manager,
1630 if (shared_response_manager->ProblemIsSolved())
return;
1634 const SatParameters saved_params = *
parameters;
1636 parameters->set_search_branching(SatParameters::HINT_SEARCH);
1643 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1647 const std::string& solution_info =
model->Name();
1651 response.set_solution_info(absl::StrCat(solution_info,
" [hint]"));
1660 const IntegerVariable objective_var =
1661 model->GetOrCreate<ObjectiveDefinition>()->objective_var;
1662 model->GetOrCreate<SatSolver>()->Backtrack(0);
1663 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
1664 if (!integer_trail->Enqueue(
1667 shared_response_manager->GetInnerObjectiveUpperBound()),
1669 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1670 absl::StrCat(solution_info,
" [hint]"));
1671 shared_response_manager->SetStatsFromModel(
model);
1681 void MinimizeL1DistanceWithHint(
const CpModelProto&
model_proto,
1682 SharedResponseManager* shared_response_manager,
1684 SharedTimeLimit* shared_time_limit,
1688 if (shared_response_manager->ProblemIsSolved())
return;
1690 auto*
parameters = local_model.GetOrCreate<SatParameters>();
1694 if (
parameters->enumerate_all_solutions())
return;
1697 const SatParameters saved_params = *
model->GetOrCreate<SatParameters>();
1704 updated_model_proto.clear_objective();
1707 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1712 const int new_var_index = updated_model_proto.variables_size();
1713 IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1718 var_proto->add_domain(min_domain);
1719 var_proto->add_domain(max_domain);
1722 ConstraintProto*
const linear_constraint_proto =
1723 updated_model_proto.add_constraints();
1724 LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
1725 linear->add_vars(new_var_index);
1726 linear->add_coeffs(1);
1727 linear->add_vars(
var);
1728 linear->add_coeffs(-1);
1729 linear->add_domain(-
value);
1730 linear->add_domain(-
value);
1733 const int abs_var_index = updated_model_proto.variables_size();
1734 IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
1735 const int64 abs_min_domain = 0;
1736 const int64 abs_max_domain =
1737 std::max(std::abs(min_domain), std::abs(max_domain));
1738 abs_var_proto->add_domain(abs_min_domain);
1739 abs_var_proto->add_domain(abs_max_domain);
1740 ConstraintProto*
const abs_constraint_proto =
1741 updated_model_proto.add_constraints();
1742 abs_constraint_proto->mutable_int_max()->set_target(abs_var_index);
1743 abs_constraint_proto->mutable_int_max()->add_vars(new_var_index);
1744 abs_constraint_proto->mutable_int_max()->add_vars(
1747 updated_model_proto.mutable_objective()->add_vars(abs_var_index);
1748 updated_model_proto.mutable_objective()->add_coeffs(1);
1751 SharedResponseManager local_response_manager(
1752 false,
parameters->enumerate_all_solutions(),
1753 &updated_model_proto,
wall_timer, shared_time_limit);
1755 local_model.Register<SharedResponseManager>(&local_response_manager);
1758 LoadCpModel(updated_model_proto, &local_response_manager, &local_model);
1761 const auto& mapping = *local_model.GetOrCreate<CpModelMapping>();
1763 mapping.Literals(updated_model_proto.assumptions()), &local_model);
1765 const std::string& solution_info =
model->Name();
1770 CpSolverResponse updated_response;
1771 FillSolutionInResponse(updated_model_proto, local_model,
1773 LOG(
INFO) <<
"Found solution with repaired hint penalty = "
1777 response.set_solution_info(absl::StrCat(solution_info,
" [repaired]"));
1786 void PostsolveResponseWithFullSolver(
1787 const int64 num_variables_in_original_model, CpModelProto mapping_proto,
1796 if (mapping_proto.variables_size() == 0) {
1801 for (
int i = 0; i <
response->solution_size(); ++i) {
1802 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1803 var_proto->clear_domain();
1804 var_proto->add_domain(
response->solution(i));
1805 var_proto->add_domain(
response->solution(i));
1807 for (
int i = 0; i <
response->solution_lower_bounds_size(); ++i) {
1808 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1811 .IntersectionWith({
response->solution_lower_bounds(i),
1812 response->solution_upper_bounds(i)}),
1819 Model postsolve_model;
1821 SatParameters params;
1822 params.set_linearization_level(0);
1823 params.set_cp_model_probing_level(0);
1828 SharedTimeLimit shared_time_limit(
time_limit.get());
1829 SharedResponseManager local_response_manager(
1830 false,
false, &mapping_proto,
1832 LoadCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1833 SolveLoadedCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1834 const CpSolverResponse postsolve_response =
1835 local_response_manager.GetResponse();
1841 response->clear_solution_lower_bounds();
1842 response->clear_solution_upper_bounds();
1843 if (!postsolve_response.solution().empty()) {
1844 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1845 response->add_solution(postsolve_response.solution(i));
1848 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1849 response->add_solution_lower_bounds(
1850 postsolve_response.solution_lower_bounds(i));
1851 response->add_solution_upper_bounds(
1852 postsolve_response.solution_upper_bounds(i));
1857 void PostsolveResponseWrapper(
const SatParameters& params,
1858 const int64 num_variables_in_original_model,
1859 const CpModelProto& mapping_proto,
1860 const std::vector<int>& postsolve_mapping,
1863 if (params.cp_model_postsolve_with_full_solver()) {
1864 PostsolveResponseWithFullSolver(num_variables_in_original_model,
1865 mapping_proto, postsolve_mapping,
1874 CpSolverResponse SolvePureSatModel(
const CpModelProto&
model_proto,
1876 std::unique_ptr<SatSolver> solver(
new SatSolver());
1882 std::unique_ptr<DratProofHandler> drat_proof_handler;
1883 #if !defined(__PORTABLE_PLATFORM__)
1884 if (!absl::GetFlag(FLAGS_drat_output).empty() ||
1885 absl::GetFlag(FLAGS_drat_check)) {
1886 if (!absl::GetFlag(FLAGS_drat_output).empty()) {
1890 drat_proof_handler = absl::make_unique<DratProofHandler>(
1891 false, output, absl::GetFlag(FLAGS_drat_check));
1893 drat_proof_handler = absl::make_unique<DratProofHandler>();
1895 solver->SetDratProofHandler(drat_proof_handler.get());
1897 #endif // __PORTABLE_PLATFORM__
1899 auto get_literal = [](
int ref) {
1900 if (ref >= 0)
return Literal(BooleanVariable(ref),
true);
1901 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
1904 std::vector<Literal> temp;
1905 const int num_variables =
model_proto.variables_size();
1906 solver->SetNumVariables(num_variables);
1907 if (drat_proof_handler !=
nullptr) {
1912 for (
int ref = 0; ref < num_variables; ++ref) {
1914 if (domain.IsFixed()) {
1915 const Literal ref_literal =
1916 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1920 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1921 switch (
ct.constraint_case()) {
1922 case ConstraintProto::ConstraintCase::kBoolAnd: {
1923 if (
ct.enforcement_literal_size() == 0) {
1924 for (
const int ref :
ct.bool_and().literals()) {
1929 const Literal not_a =
1930 get_literal(
ct.enforcement_literal(0)).Negated();
1931 for (
const int ref :
ct.bool_and().literals()) {
1937 case ConstraintProto::ConstraintCase::kBoolOr:
1939 for (
const int ref :
ct.bool_or().literals()) {
1940 temp.push_back(get_literal(ref));
1950 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1951 switch (
ct.constraint_case()) {
1952 case ConstraintProto::ConstraintCase::kBoolAnd: {
1953 if (
ct.enforcement_literal_size() == 0) {
1954 for (
const int ref :
ct.bool_and().literals()) {
1955 const Literal
b = get_literal(ref);
1956 solver->AddUnitClause(
b);
1960 const Literal not_a =
1961 get_literal(
ct.enforcement_literal(0)).Negated();
1962 for (
const int ref :
ct.bool_and().literals()) {
1963 const Literal
b = get_literal(ref);
1964 solver->AddProblemClause({not_a,
b});
1969 case ConstraintProto::ConstraintCase::kBoolOr:
1971 for (
const int ref :
ct.bool_or().literals()) {
1972 temp.push_back(get_literal(ref));
1974 solver->AddProblemClause(temp);
1982 for (
int ref = 0; ref < num_variables; ++ref) {
1984 if (domain.Min() == domain.Max()) {
1985 const Literal ref_literal =
1986 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1987 solver->AddUnitClause(ref_literal);
1994 std::vector<bool> solution;
1996 &solution, drat_proof_handler.get());
1999 for (
int ref = 0; ref < num_variables; ++ref) {
2000 response.add_solution(solution[ref]);
2004 status = solver->SolveWithTimeLimit(
model->GetOrCreate<
TimeLimit>());
2007 for (
int ref = 0; ref < num_variables; ++ref) {
2009 solver->Assignment().LiteralIsTrue(get_literal(ref)) ? 1 : 0);
2017 solver->model()->GetOrCreate<
TimeLimit>()->GetElapsedDeterministicTime());
2026 std::vector<int64>(
response.solution().begin(),
2036 LOG(
FATAL) <<
"Unexpected SatSolver::Status " << status;
2038 response.set_num_booleans(solver->NumVariables());
2039 response.set_num_branches(solver->num_branches());
2040 response.set_num_conflicts(solver->num_failures());
2041 response.set_num_binary_propagations(solver->num_propagations());
2042 response.set_num_integer_propagations(0);
2051 absl::GetFlag(FLAGS_max_drat_time_in_seconds));
2052 switch (drat_status) {
2054 LOG(
INFO) <<
"DRAT status: UNKNOWN";
2057 LOG(
INFO) <<
"DRAT status: VALID";
2060 LOG(
ERROR) <<
"DRAT status: INVALID";
2066 LOG(
INFO) <<
"DRAT wall time: " << drat_timer.
Get();
2067 }
else if (drat_proof_handler !=
nullptr) {
2070 LOG(
INFO) <<
"DRAT status: NA";
2071 LOG(
INFO) <<
"DRAT wall time: NA";
2072 LOG(
INFO) <<
"DRAT user time: NA";
2077 #if !defined(__PORTABLE_PLATFORM__)
2080 struct SharedClasses {
2090 bool SearchIsDone() {
2091 if (
response->ProblemIsSolved())
return true;
2098 class FullProblemSolver :
public SubSolver {
2100 FullProblemSolver(
const std::string&
name,
2101 const SatParameters& local_parameters,
bool split_in_chunks,
2102 SharedClasses* shared)
2105 split_in_chunks_(split_in_chunks),
2106 local_model_(
absl::make_unique<Model>(
name)) {
2109 shared_->time_limit->UpdateLocalLimit(
2110 local_model_->GetOrCreate<
TimeLimit>());
2112 if (shared->response !=
nullptr) {
2113 local_model_->Register<SharedResponseManager>(shared->response);
2116 if (shared->relaxation_solutions !=
nullptr) {
2117 local_model_->Register<SharedRelaxationSolutionRepository>(
2118 shared->relaxation_solutions);
2121 if (shared->lp_solutions !=
nullptr) {
2122 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2125 if (shared->incomplete_solutions !=
nullptr) {
2126 local_model_->Register<SharedIncompleteSolutionManager>(
2127 shared->incomplete_solutions);
2131 if (shared_->bounds !=
nullptr) {
2132 RegisterVariableBoundsLevelZeroExport(
2133 *shared_->model_proto, shared_->bounds, local_model_.get());
2134 RegisterVariableBoundsLevelZeroImport(
2135 *shared_->model_proto, shared_->bounds, local_model_.get());
2139 bool TaskIsAvailable()
override {
2140 if (shared_->SearchIsDone())
return false;
2142 absl::MutexLock mutex_lock(&mutex_);
2143 return previous_task_is_completed_;
2146 std::function<void()> GenerateTask(
int64 task_id)
override {
2148 absl::MutexLock mutex_lock(&mutex_);
2149 previous_task_is_completed_ =
false;
2152 if (solving_first_chunk_) {
2153 LoadCpModel(*shared_->model_proto, shared_->response,
2154 local_model_.get());
2155 if (local_model_->GetOrCreate<SatParameters>()->repair_hint()) {
2156 MinimizeL1DistanceWithHint(*shared_->model_proto, shared_->response,
2157 shared_->wall_timer, shared_->time_limit,
2158 local_model_.get());
2160 QuickSolveWithHint(*shared_->model_proto, shared_->response,
2161 local_model_.get());
2165 solving_first_chunk_ =
false;
2167 if (split_in_chunks_) {
2169 absl::MutexLock mutex_lock(&mutex_);
2170 previous_task_is_completed_ =
true;
2176 if (split_in_chunks_) {
2179 auto* params = local_model_->GetOrCreate<SatParameters>();
2180 params->set_max_deterministic_time(1);
2181 time_limit->ResetLimitFromParameters(*params);
2182 shared_->time_limit->UpdateLocalLimit(
time_limit);
2185 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2186 SolveLoadedCpModel(*shared_->model_proto, shared_->response,
2187 local_model_.get());
2189 absl::MutexLock mutex_lock(&mutex_);
2190 deterministic_time_since_last_synchronize_ +=
2191 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2195 if (shared_->SearchIsDone()) {
2196 shared_->time_limit->Stop();
2201 if (split_in_chunks_) {
2202 absl::MutexLock mutex_lock(&mutex_);
2203 previous_task_is_completed_ =
true;
2211 local_model_.reset();
2218 void Synchronize()
override {
2219 absl::MutexLock mutex_lock(&mutex_);
2220 deterministic_time_ += deterministic_time_since_last_synchronize_;
2221 shared_->time_limit->AdvanceDeterministicTime(
2222 deterministic_time_since_last_synchronize_);
2223 deterministic_time_since_last_synchronize_ = 0.0;
2227 SharedClasses* shared_;
2228 const bool split_in_chunks_;
2229 std::unique_ptr<Model> local_model_;
2233 bool solving_first_chunk_ =
true;
2236 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2238 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2241 class FeasibilityPumpSolver :
public SubSolver {
2243 FeasibilityPumpSolver(
const SatParameters& local_parameters,
2244 SharedClasses* shared)
2245 : SubSolver(
"feasibility_pump"),
2247 local_model_(
absl::make_unique<Model>(name_)) {
2250 shared_->time_limit->UpdateLocalLimit(
2251 local_model_->GetOrCreate<
TimeLimit>());
2253 if (shared->response !=
nullptr) {
2254 local_model_->Register<SharedResponseManager>(shared->response);
2257 if (shared->relaxation_solutions !=
nullptr) {
2258 local_model_->Register<SharedRelaxationSolutionRepository>(
2259 shared->relaxation_solutions);
2262 if (shared->lp_solutions !=
nullptr) {
2263 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2266 if (shared->incomplete_solutions !=
nullptr) {
2267 local_model_->Register<SharedIncompleteSolutionManager>(
2268 shared->incomplete_solutions);
2272 if (shared_->bounds !=
nullptr) {
2273 RegisterVariableBoundsLevelZeroImport(
2274 *shared_->model_proto, shared_->bounds, local_model_.get());
2278 bool TaskIsAvailable()
override {
2279 if (shared_->SearchIsDone())
return false;
2280 absl::MutexLock mutex_lock(&mutex_);
2281 return previous_task_is_completed_;
2284 std::function<void()> GenerateTask(
int64 task_id)
override {
2287 absl::MutexLock mutex_lock(&mutex_);
2288 if (!previous_task_is_completed_)
return;
2289 previous_task_is_completed_ =
false;
2292 absl::MutexLock mutex_lock(&mutex_);
2293 if (solving_first_chunk_) {
2294 LoadFeasibilityPump(*shared_->model_proto, shared_->response,
2295 local_model_.get());
2298 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
2299 solving_first_chunk_ =
false;
2301 previous_task_is_completed_ =
true;
2307 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2308 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
2309 if (!feasibility_pump->Solve()) {
2310 shared_->response->NotifyThatImprovingProblemIsInfeasible(name_);
2314 absl::MutexLock mutex_lock(&mutex_);
2315 deterministic_time_since_last_synchronize_ +=
2316 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2320 if (shared_->SearchIsDone()) {
2321 shared_->time_limit->Stop();
2325 absl::MutexLock mutex_lock(&mutex_);
2326 previous_task_is_completed_ =
true;
2330 void Synchronize()
override {
2331 absl::MutexLock mutex_lock(&mutex_);
2332 deterministic_time_ += deterministic_time_since_last_synchronize_;
2333 shared_->time_limit->AdvanceDeterministicTime(
2334 deterministic_time_since_last_synchronize_);
2335 deterministic_time_since_last_synchronize_ = 0.0;
2339 SharedClasses* shared_;
2340 std::unique_ptr<Model> local_model_;
2346 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
2348 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2350 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2354 class LnsSolver :
public SubSolver {
2356 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
2358 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
2359 : SubSolver(generator->
name()),
2360 generator_(std::move(generator)),
2365 bool TaskIsAvailable()
override {
2366 if (shared_->SearchIsDone())
return false;
2367 return generator_->ReadyToGenerate();
2370 std::function<void()> GenerateTask(
int64 task_id)
override {
2371 return [task_id,
this]() {
2372 if (shared_->SearchIsDone())
return;
2377 const int32 low =
static_cast<int32>(task_id);
2378 const int32 high = task_id >> 32;
2379 std::seed_seq seed{low, high, parameters_.random_seed()};
2382 NeighborhoodGenerator::SolveData data;
2383 data.difficulty = generator_->difficulty();
2384 data.deterministic_limit = generator_->deterministic_limit();
2387 CpSolverResponse base_response;
2389 const SharedSolutionRepository<int64>& repo =
2390 shared_->response->SolutionsRepository();
2391 if (repo.NumSolutions() > 0) {
2393 const SharedSolutionRepository<int64>::Solution solution =
2394 repo.GetRandomBiasedSolution(&random);
2395 for (
const int64 value : solution.variable_values) {
2396 base_response.add_solution(
value);
2400 data.initial_best_objective = repo.GetSolution(0).rank;
2401 data.base_objective = solution.rank;
2410 data.initial_best_objective =
2411 shared_->response->GetInnerObjectiveUpperBound();
2412 data.base_objective = data.initial_best_objective;
2416 Neighborhood neighborhood;
2418 absl::MutexLock mutex_lock(helper_->MutableMutex());
2420 generator_->Generate(base_response, data.difficulty, &random);
2422 neighborhood.cp_model.set_name(absl::StrCat(
"lns_", task_id));
2423 if (!neighborhood.is_generated)
return;
2424 data.neighborhood_id = neighborhood.id;
2426 const double fully_solved_proportion =
2427 static_cast<double>(generator_->num_fully_solved_calls()) /
2429 std::string source_info =
name();
2430 if (!neighborhood.source_info.empty()) {
2431 absl::StrAppend(&source_info,
"_", neighborhood.source_info);
2433 const std::string solution_info = absl::StrFormat(
2434 "%s(d=%0.2f s=%i t=%0.2f p=%0.2f)", source_info, data.difficulty,
2435 task_id, data.deterministic_limit, fully_solved_proportion);
2437 SatParameters local_params(parameters_);
2438 local_params.set_max_deterministic_time(data.deterministic_limit);
2439 local_params.set_stop_after_first_solution(
false);
2440 local_params.set_log_search_progress(
false);
2441 local_params.set_cp_model_probing_level(0);
2443 if (absl::GetFlag(FLAGS_cp_model_dump_lns)) {
2444 const std::string
name =
2445 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2446 neighborhood.cp_model.name(),
".pbtxt");
2447 LOG(
INFO) <<
"Dumping LNS model to '" <<
name <<
"'.";
2455 shared_->time_limit->UpdateLocalLimit(local_time_limit);
2457 const int64 num_neighborhood_model_vars =
2458 neighborhood.cp_model.variables_size();
2460 CpModelProto mapping_proto;
2461 std::vector<int> postsolve_mapping;
2462 PresolveOptions options;
2464 options.parameters = *local_model.GetOrCreate<SatParameters>();
2465 options.time_limit = local_model.GetOrCreate<
TimeLimit>();
2466 auto context = absl::make_unique<PresolveContext>(&neighborhood.cp_model,
2476 SharedResponseManager local_response_manager(
2478 &neighborhood.cp_model, shared_->wall_timer, shared_->time_limit);
2479 LoadCpModel(neighborhood.cp_model, &local_response_manager, &local_model);
2480 QuickSolveWithHint(neighborhood.cp_model, &local_response_manager,
2482 SolveLoadedCpModel(neighborhood.cp_model, &local_response_manager,
2484 CpSolverResponse local_response = local_response_manager.GetResponse();
2485 if (local_response.solution_info().empty()) {
2486 local_response.set_solution_info(solution_info);
2488 local_response.set_solution_info(
2489 absl::StrCat(local_response.solution_info(),
" ", solution_info));
2494 PostsolveResponseWrapper(local_params, num_neighborhood_model_vars,
2495 mapping_proto, postsolve_mapping,
2496 shared_->wall_timer, &local_response);
2497 data.status = local_response.status();
2498 data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
2500 if (generator_->IsRelaxationGenerator()) {
2501 bool has_feasible_solution =
false;
2504 has_feasible_solution =
true;
2508 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2509 local_response.solution_info());
2512 if (shared_->model_proto->has_objective()) {
2515 const IntegerValue current_obj_lb =
2516 shared_->response->GetInnerObjectiveLowerBound();
2518 const IntegerValue local_obj_lb =
2519 local_response_manager.GetInnerObjectiveLowerBound();
2522 neighborhood.cp_model.objective(), local_obj_lb.value());
2525 const IntegerValue new_inner_obj_lb = IntegerValue(
2527 scaled_local_obj_bound) -
2529 data.new_objective_bound = new_inner_obj_lb;
2530 data.initial_best_objective_bound = current_obj_lb;
2531 if (new_inner_obj_lb > current_obj_lb) {
2532 shared_->response->UpdateInnerObjectiveBounds(
2539 if (has_feasible_solution) {
2541 *shared_->model_proto,
2542 std::vector<int64>(local_response.solution().begin(),
2543 local_response.solution().end()))) {
2544 shared_->response->NewSolution(local_response,
2549 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2550 local_response.solution_info());
2551 shared_->time_limit->Stop();
2554 shared_->relaxation_solutions->NewRelaxationSolution(local_response);
2557 if (!local_response.solution().empty()) {
2559 *shared_->model_proto,
2560 std::vector<int64>(local_response.solution().begin(),
2561 local_response.solution().end())))
2566 data.new_objective = data.base_objective;
2570 shared_->model_proto->objective(), local_response));
2576 shared_->response->NewSolution(local_response,
2579 if (!neighborhood.is_reduced &&
2582 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2583 local_response.solution_info());
2584 shared_->time_limit->Stop();
2588 generator_->AddSolveData(data);
2591 const int total_num_calls = task_id;
2592 VLOG(2) <<
name() <<
": [difficulty: " << data.difficulty
2593 <<
", id: " << task_id
2594 <<
", deterministic_time: " << data.deterministic_time <<
" / "
2595 << data.deterministic_limit
2596 <<
", status: " << ProtoEnumToString<CpSolverStatus>(data.status)
2597 <<
", num calls: " << generator_->num_calls()
2598 <<
", UCB1 Score: " << generator_->GetUCBScore(total_num_calls)
2599 <<
", p: " << fully_solved_proportion <<
"]";
2603 void Synchronize()
override {
2604 generator_->Synchronize();
2605 const double old = deterministic_time_;
2606 deterministic_time_ = generator_->deterministic_time();
2607 shared_->time_limit->AdvanceDeterministicTime(deterministic_time_ - old);
2611 std::unique_ptr<NeighborhoodGenerator> generator_;
2612 NeighborhoodGeneratorHelper* helper_;
2613 const SatParameters parameters_;
2614 SharedClasses* shared_;
2617 void SolveCpModelParallel(
const CpModelProto&
model_proto,
2618 SharedResponseManager* shared_response_manager,
2619 SharedTimeLimit* shared_time_limit,
2621 CHECK(shared_response_manager !=
nullptr);
2622 const SatParameters&
parameters = *global_model->GetOrCreate<SatParameters>();
2623 const int num_search_workers =
parameters.num_search_workers();
2626 <<
"Enumerating all solutions in parallel is not supported.";
2628 std::unique_ptr<SharedBoundsManager> shared_bounds_manager;
2630 shared_bounds_manager = absl::make_unique<SharedBoundsManager>(
model_proto);
2633 std::unique_ptr<SharedRelaxationSolutionRepository>
2634 shared_relaxation_solutions;
2636 shared_relaxation_solutions =
2637 absl::make_unique<SharedRelaxationSolutionRepository>(
2639 global_model->Register<SharedRelaxationSolutionRepository>(
2640 shared_relaxation_solutions.get());
2643 auto shared_lp_solutions = absl::make_unique<SharedLPSolutionRepository>(
2645 global_model->Register<SharedLPSolutionRepository>(shared_lp_solutions.get());
2649 std::unique_ptr<SharedIncompleteSolutionManager> shared_incomplete_solutions;
2650 const bool use_feasibility_pump =
parameters.use_feasibility_pump() &&
2654 if (use_feasibility_pump) {
2655 shared_incomplete_solutions =
2656 absl::make_unique<SharedIncompleteSolutionManager>();
2657 global_model->Register<SharedIncompleteSolutionManager>(
2658 shared_incomplete_solutions.get());
2661 SharedClasses shared;
2664 shared.time_limit = shared_time_limit;
2665 shared.bounds = shared_bounds_manager.get();
2666 shared.response = shared_response_manager;
2667 shared.relaxation_solutions = shared_relaxation_solutions.get();
2668 shared.lp_solutions = shared_lp_solutions.get();
2669 shared.incomplete_solutions = shared_incomplete_solutions.get();
2672 std::vector<std::unique_ptr<SubSolver>> subsolvers;
2675 subsolvers.push_back(absl::make_unique<SynchronizationPoint>(
2676 [shared_response_manager, &shared_bounds_manager,
2677 &shared_relaxation_solutions, &shared_lp_solutions]() {
2678 shared_response_manager->Synchronize();
2679 shared_response_manager->MutableSolutionsRepository()->Synchronize();
2680 if (shared_bounds_manager !=
nullptr) {
2683 if (shared_relaxation_solutions !=
nullptr) {
2686 if (shared_lp_solutions !=
nullptr) {
2687 shared_lp_solutions->Synchronize();
2696 local_params.set_stop_after_first_solution(
true);
2697 local_params.set_linearization_level(0);
2698 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2699 "first_solution", local_params,
2705 if (
parameters.optimize_with_max_hs())
continue;
2707 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2708 local_params.name(), local_params,
2714 if (use_feasibility_pump) {
2715 subsolvers.push_back(
2716 absl::make_unique<FeasibilityPumpSolver>(
parameters, &shared));
2723 auto unique_helper = absl::make_unique<NeighborhoodGeneratorHelper>(
2725 shared_bounds_manager.get());
2726 NeighborhoodGeneratorHelper* helper = unique_helper.get();
2727 subsolvers.push_back(std::move(unique_helper));
2730 std::vector<SatParameters> lns_params = {
parameters};
2731 lns_params.back().set_name(
"default");
2733 std::vector<SatParameters> lns_params =
2736 for (
const SatParameters& local_params : lns_params) {
2741 subsolvers.push_back(absl::make_unique<LnsSolver>(
2742 absl::make_unique<SimpleNeighborhoodGenerator>(
2743 helper, absl::StrCat(
"rnd_lns_", local_params.name())),
2744 local_params, helper, &shared));
2745 subsolvers.push_back(absl::make_unique<LnsSolver>(
2746 absl::make_unique<VariableGraphNeighborhoodGenerator>(
2747 helper, absl::StrCat(
"var_lns_", local_params.name())),
2748 local_params, helper, &shared));
2749 subsolvers.push_back(absl::make_unique<LnsSolver>(
2750 absl::make_unique<ConstraintGraphNeighborhoodGenerator>(
2751 helper, absl::StrCat(
"cst_lns_", local_params.name())),
2752 local_params, helper, &shared));
2754 if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty()) {
2755 subsolvers.push_back(absl::make_unique<LnsSolver>(
2756 absl::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
2757 helper, absl::StrCat(
"scheduling_time_window_lns_",
2758 local_params.name())),
2759 local_params, helper, &shared));
2760 subsolvers.push_back(absl::make_unique<LnsSolver>(
2761 absl::make_unique<SchedulingNeighborhoodGenerator>(
2763 absl::StrCat(
"scheduling_random_lns_", local_params.name())),
2764 local_params, helper, &shared));
2777 subsolvers.push_back(absl::make_unique<LnsSolver>(
2778 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2779 helper, shared.response, shared.relaxation_solutions,
2780 shared.lp_solutions,
nullptr,
2781 absl::StrCat(
"rins_lns_", local_params.name())),
2782 local_params, helper, &shared));
2785 subsolvers.push_back(absl::make_unique<LnsSolver>(
2786 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2787 helper,
nullptr, shared.relaxation_solutions,
2788 shared.lp_solutions, shared.incomplete_solutions,
2789 absl::StrCat(
"rens_lns_", local_params.name())),
2790 local_params, helper, &shared));
2794 subsolvers.push_back(absl::make_unique<LnsSolver>(
2796 ConsecutiveConstraintsRelaxationNeighborhoodGenerator>(
2797 helper, absl::StrCat(
"rnd_rel_lns_", local_params.name())),
2798 local_params, helper, &shared));
2800 subsolvers.push_back(absl::make_unique<LnsSolver>(
2801 absl::make_unique<WeightedRandomRelaxationNeighborhoodGenerator>(
2802 helper, absl::StrCat(
"wgt_rel_lns_", local_params.name())),
2803 local_params, helper, &shared));
2810 subsolvers.push_back(
2811 absl::make_unique<SynchronizationPoint>([shared_response_manager]() {
2812 shared_response_manager->UpdatePrimalIntegral();
2817 std::vector<std::string> names;
2818 for (
const auto& subsolver : subsolvers) {
2819 if (!subsolver->name().empty()) names.push_back(subsolver->name());
2822 "*** starting Search at %.2fs with %i workers and subsolvers: [ %s ]",
2823 wall_timer->
Get(), num_search_workers, absl::StrJoin(names,
", "));
2835 #endif // __PORTABLE_PLATFORM__
2840 void AddPostsolveClauses(
const std::vector<int>& postsolve_mapping,
2841 Model*
model, CpModelProto* mapping_proto) {
2842 auto* mapping =
model->GetOrCreate<CpModelMapping>();
2843 auto* postsolve =
model->GetOrCreate<PostsolveClauses>();
2844 for (
const auto& clause : postsolve->clauses) {
2845 auto*
ct = mapping_proto->add_constraints()->mutable_bool_or();
2846 for (
const Literal l : clause) {
2847 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
2849 var = postsolve_mapping[
var];
2853 postsolve->clauses.clear();
2864 CpSolverResponse final_response;
2866 #if !defined(__PORTABLE_PLATFORM__)
2868 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
2869 const std::string
file =
2870 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
"model.pbtxt");
2871 LOG(
INFO) <<
"Dumping cp model proto to '" <<
file <<
"'.";
2876 if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
2877 const std::string
file = absl::StrCat(
2878 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"response.pbtxt");
2879 LOG(
INFO) <<
"Dumping response proto to '" <<
file <<
"'.";
2885 if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
2886 SatParameters params = *
model->GetOrCreate<SatParameters>();
2887 SatParameters flag_params;
2888 CHECK(google::protobuf::TextFormat::ParseFromString(
2889 absl::GetFlag(FLAGS_cp_model_params), &flag_params));
2890 params.MergeFrom(flag_params);
2896 if (
model->GetOrCreate<SatParameters>()->catch_sigint_signal()) {
2897 handler.
Register([&shared_time_limit]() { shared_time_limit.
Stop(); });
2899 #endif // __PORTABLE_PLATFORM__
2901 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
2902 const bool log_search = params.log_search_progress() ||
VLOG_IS_ON(1);
2903 LOG_IF(
INFO, log_search) <<
"Parameters: " << params.ShortDebugString();
2906 auto display_response_cleanup =
2918 if (!error.empty()) {
2921 return final_response;
2930 if (!params.use_sat_inprocessing() && !
model_proto.has_objective() &&
2931 !
model_proto.has_solution_hint() && !params.enumerate_all_solutions() &&
2932 !params.use_lns_only() && params.num_search_workers() <= 1 &&
2934 bool is_pure_sat =
true;
2935 for (
const IntegerVariableProto&
var :
model_proto.variables()) {
2936 if (
var.domain_size() != 2 ||
var.domain(0) < 0 ||
var.domain(1) > 1) {
2937 is_pure_sat =
false;
2942 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
2943 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolOr &&
2944 ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolAnd) {
2945 is_pure_sat =
false;
2955 final_response.set_user_time(user_timer.
Get());
2956 final_response.set_deterministic_time(
2958 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
2959 if (params.fill_tightened_domains_in_response()) {
2960 *final_response.mutable_tightened_variables() =
model_proto.variables();
2962 return final_response;
2971 CpModelProto mapping_proto;
2977 absl::make_unique<PresolveContext>(&new_cp_model_proto, &mapping_proto);
2979 bool degraded_assumptions_support =
false;
2980 if (params.num_search_workers() > 1 ||
model_proto.has_objective()) {
2987 degraded_assumptions_support =
true;
2988 context->InitializeNewDomains();
2990 if (!
context->SetLiteralToTrue(ref)) {
2992 final_response.add_sufficient_assumptions_for_infeasibility(ref);
2993 return final_response;
3000 std::function<void(CpSolverResponse *
response)> postprocess_solution;
3003 std::vector<int> postsolve_mapping;
3006 LOG(
ERROR) <<
"Error while presolving, likely due to integer overflow.";
3008 return final_response;
3011 if (params.cp_model_presolve()) {
3012 postprocess_solution = [&
model_proto, ¶ms, &mapping_proto,
3013 &shared_time_limit, &postsolve_mapping, &
wall_timer,
3015 AddPostsolveClauses(postsolve_mapping,
model, &mapping_proto);
3016 PostsolveResponseWrapper(params,
model_proto.variables_size(),
3017 mapping_proto, postsolve_mapping, &
wall_timer,
3019 if (!
response->solution().empty()) {
3022 std::vector<int64>(
response->solution().begin(),
3024 &mapping_proto, &postsolve_mapping))
3025 <<
"final postsolved solution";
3027 if (params.fill_tightened_domains_in_response()) {
3029 if (mapping_proto.variables().size() >=
3031 for (
int i = 0; i <
model_proto.variables().size(); ++i) {
3032 *
response->add_tightened_variables() = mapping_proto.variables(i);
3037 response->set_user_time(user_timer.Get());
3039 shared_time_limit.GetElapsedDeterministicTime());
3044 &user_timer](CpSolverResponse*
response) {
3046 const int initial_size =
model_proto.variables_size();
3047 if (
response->solution_size() > 0) {
3048 response->mutable_solution()->Truncate(initial_size);
3049 }
else if (
response->solution_lower_bounds_size() > 0) {
3050 response->mutable_solution_lower_bounds()->Truncate(initial_size);
3051 response->mutable_solution_upper_bounds()->Truncate(initial_size);
3053 if (params.fill_tightened_domains_in_response()) {
3059 shared_time_limit.GetElapsedDeterministicTime());
3067 log_search, params.enumerate_all_solutions(), &new_cp_model_proto,
3070 absl::GetFlag(FLAGS_cp_model_dump_prefix));
3074 if (!observers.empty()) {
3077 &postprocess_solution, &shared_time_limit](
3078 const CpSolverResponse& response_of_presolved_problem) {
3079 CpSolverResponse
response = response_of_presolved_problem;
3081 if (!
response.solution().empty()) {
3083 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
3090 for (
const auto& observer : observers) {
3096 #if !defined(__PORTABLE_PLATFORM__)
3097 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
3098 const std::string presolved_file = absl::StrCat(
3099 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"presolved_model.pbtxt");
3100 LOG(
INFO) <<
"Dumping presolved cp model proto to '" << presolved_file
3105 const std::string mapping_file = absl::StrCat(
3106 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"mapping_model.pbtxt");
3107 LOG(
INFO) <<
"Dumping mapping cp model proto to '" << mapping_file <<
"'.";
3110 #endif // __PORTABLE_PLATFORM__
3112 if (params.stop_after_presolve() || shared_time_limit.
LimitReached()) {
3113 int64 num_terms = 0;
3114 for (
const ConstraintProto&
ct : new_cp_model_proto.constraints()) {
3118 <<
"Stopped after presolve."
3119 <<
"\nPresolvedNumVariables: " << new_cp_model_proto.variables().size()
3120 <<
"\nPresolvedNumConstraints: "
3121 << new_cp_model_proto.constraints().size()
3122 <<
"\nPresolvedNumTerms: " << num_terms;
3126 final_response = shared_response_manager.
GetResponse();
3127 postprocess_solution(&final_response);
3128 return final_response;
3132 if (params.stop_after_first_solution()) {
3134 [&shared_time_limit](
3135 const CpSolverResponse& response_of_presolved_problem) {
3136 shared_time_limit.
Stop();
3140 #if defined(__PORTABLE_PLATFORM__)
3143 #else // __PORTABLE_PLATFORM__
3144 if (params.num_search_workers() > 1 || params.interleave_search()) {
3145 SolveCpModelParallel(new_cp_model_proto, &shared_response_manager,
3147 #endif // __PORTABLE_PLATFORM__
3150 LOG(
INFO) << absl::StrFormat(
"*** starting to load the model at %.2fs",
3153 LoadCpModel(new_cp_model_proto, &shared_response_manager,
model);
3156 LOG(
INFO) << absl::StrFormat(
"*** starting sequential search at %.2fs",
3158 LOG(
INFO) <<
"Initial num_bool: "
3161 if (params.repair_hint()) {
3162 MinimizeL1DistanceWithHint(new_cp_model_proto, &shared_response_manager,
3165 QuickSolveWithHint(new_cp_model_proto, &shared_response_manager,
model);
3167 SolveLoadedCpModel(new_cp_model_proto, &shared_response_manager,
model);
3170 final_response = shared_response_manager.
GetResponse();
3171 postprocess_solution(&final_response);
3172 if (!final_response.solution().empty()) {
3174 model_proto, std::vector<int64>(final_response.solution().begin(),
3175 final_response.solution().end())));
3177 if (degraded_assumptions_support &&
3180 *final_response.mutable_sufficient_assumptions_for_infeasibility() =
3183 return final_response;
3192 const SatParameters& params) {
3198 #if !defined(__PORTABLE_PLATFORM__)
3200 const std::string& params) {
3205 #endif // !__PORTABLE_PLATFORM__