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"
90 "Prefix filename for all dumped files");
93 "Prefix filename for all dumped files");
96 cp_model_dump_models,
false,
97 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
98 "protos (original model, presolved model, mapping model) in text "
100 "'FLAGS_cp_model_dump_prefix'{model|presolved_model|mapping_model}.pbtxt.");
103 "DEBUG ONLY. When set to true, solve will dump all "
104 "lns models proto in text format to "
105 "'FLAGS_cp_model_dump_prefix'lns_xxx.pbtxt.");
108 "DEBUG ONLY. If true, the final response of each solve will be "
109 "dumped to 'FLAGS_cp_model_dump_prefix'response.pbtxt");
112 "This is interpreted as a text SatParameters proto. The "
113 "specified fields will override the normal ones for all solves.");
117 "If non-empty, a proof in DRAT format will be written to this file. "
118 "This will only be used for pure-SAT problems.");
121 "If true, a proof in DRAT format will be stored in memory and "
122 "checked if the problem is UNSAT. This will only be used for "
123 "pure-SAT problems.");
125 DEFINE_double(max_drat_time_in_seconds, std::numeric_limits<double>::infinity(),
126 "Maximum time in seconds to check the DRAT proof. This will only "
127 "be used is the drat_check flag is enabled.");
130 "When true, all intermediate solutions found by the solver will be "
131 "checked. This can be expensive, therefore it is off by default.");
139 std::string Summarize(
const std::string&
input) {
142 return absl::StrCat(
input.substr(0, half),
" ... ",
153 std::map<std::string, int> num_constraints_by_name;
154 std::map<std::string, int> num_reif_constraints_by_name;
155 std::map<std::string, int> name_to_num_literals;
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();
186 int num_constants = 0;
187 std::set<int64> constant_values;
188 std::map<Domain, int> num_vars_per_domains;
190 if (
var.domain_size() == 2 &&
var.domain(0) ==
var.domain(1)) {
192 constant_values.insert(
var.domain(0));
200 absl::StrAppend(&result,
"Optimization model '",
model_proto.name(),
203 absl::StrAppend(&result,
"Satisfaction model '",
model_proto.name(),
207 for (
const DecisionStrategyProto& strategy :
model_proto.search_strategy()) {
209 &result,
"Search strategy: on ", strategy.variables_size(),
211 ProtoEnumToString<DecisionStrategyProto::VariableSelectionStrategy>(
212 strategy.variable_selection_strategy()),
214 ProtoEnumToString<DecisionStrategyProto::DomainReductionStrategy>(
215 strategy.domain_reduction_strategy()),
219 const std::string objective_string =
221 ? absl::StrCat(
" (",
model_proto.objective().vars_size(),
224 absl::StrAppend(&result,
"#Variables: ",
model_proto.variables_size(),
225 objective_string,
"\n");
226 if (num_vars_per_domains.size() < 100) {
227 for (
const auto& entry : num_vars_per_domains) {
228 const std::string temp = absl::StrCat(
" - ", entry.second,
" in ",
229 entry.first.ToString(),
"\n");
230 absl::StrAppend(&result, Summarize(temp));
233 int64 max_complexity = 0;
236 for (
const auto& entry : num_vars_per_domains) {
239 max_complexity =
std::max(max_complexity,
240 static_cast<int64>(entry.first.NumIntervals()));
242 absl::StrAppend(&result,
" - ", num_vars_per_domains.size(),
243 " different domains in [",
min,
",",
max,
244 "] with a largest complexity of ", max_complexity,
".\n");
247 if (num_constants > 0) {
248 const std::string temp =
249 absl::StrCat(
" - ", num_constants,
" constants in {",
250 absl::StrJoin(constant_values,
","),
"} \n");
251 absl::StrAppend(&result, Summarize(temp));
254 std::vector<std::string> constraints;
255 constraints.reserve(num_constraints_by_name.size());
256 for (
const auto& entry : num_constraints_by_name) {
257 const std::string&
name = entry.first;
258 constraints.push_back(absl::StrCat(
"#",
name,
": ", entry.second));
260 absl::StrAppend(&constraints.back(),
261 " (#enforced: ", num_reif_constraints_by_name[
name],
")");
264 absl::StrAppend(&constraints.back(),
265 " (#literals: ", name_to_num_literals[
name],
")");
268 std::sort(constraints.begin(), constraints.end());
269 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
275 bool has_objective) {
277 absl::StrAppend(&result,
"CpSolverResponse:");
278 absl::StrAppend(&result,
"\nstatus: ",
279 ProtoEnumToString<CpSolverStatus>(
response.status()));
282 absl::StrAppendFormat(&result,
"\nobjective: %.9g",
284 absl::StrAppendFormat(&result,
"\nbest_bound: %.9g",
287 absl::StrAppend(&result,
"\nobjective: NA");
288 absl::StrAppend(&result,
"\nbest_bound: NA");
291 absl::StrAppend(&result,
"\nbooleans: ",
response.num_booleans());
292 absl::StrAppend(&result,
"\nconflicts: ",
response.num_conflicts());
293 absl::StrAppend(&result,
"\nbranches: ",
response.num_branches());
297 absl::StrAppend(&result,
298 "\npropagations: ",
response.num_binary_propagations());
300 &result,
"\ninteger_propagations: ",
response.num_integer_propagations());
301 absl::StrAppend(&result,
"\nwalltime: ",
response.wall_time());
302 absl::StrAppend(&result,
"\nusertime: ",
response.user_time());
303 absl::StrAppend(&result,
304 "\ndeterministic_time: ",
response.deterministic_time());
305 absl::StrAppend(&result,
"\nprimal_integral: ",
response.primal_integral());
306 absl::StrAppend(&result,
"\n");
312 void FillSolutionInResponse(
const CpModelProto&
model_proto,
const Model&
model,
315 response->clear_solution_lower_bounds();
316 response->clear_solution_upper_bounds();
318 auto* mapping =
model.Get<CpModelMapping>();
320 auto* integer_trail =
model.Get<IntegerTrail>();
322 std::vector<int64> solution;
323 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
324 if (mapping->IsInteger(i)) {
325 const IntegerVariable
var = mapping->Integer(i);
326 if (integer_trail->IsCurrentlyIgnored(
var)) {
339 DCHECK(mapping->IsBoolean(i));
341 if (trail->Assignment().LiteralIsAssigned(
literal)) {
350 if (!solution.empty()) {
351 if (
DEBUG_MODE || FLAGS_cp_model_check_intermediate_solutions) {
359 const auto& assignment = trail->Assignment();
360 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
361 if (mapping->IsBoolean(i)) {
362 if (assignment.VariableIsAssigned(mapping->Literal(i).Variable())) {
367 response->add_solution_lower_bounds(0);
368 response->add_solution_upper_bounds(1);
371 response->add_solution_lower_bounds(
373 response->add_solution_upper_bounds(
382 IntegerVariable GetOrCreateVariableWithTightBound(
383 const std::vector<std::pair<IntegerVariable, int64>>& terms, Model*
model) {
385 if (terms.size() == 1 && terms.front().second == 1) {
386 return terms.front().first;
388 if (terms.size() == 1 && terms.front().second == -1) {
394 for (
const std::pair<IntegerVariable, int64> var_coeff : terms) {
397 const int64 coeff = var_coeff.second;
398 const int64 prod1 = min_domain * coeff;
399 const int64 prod2 = max_domain * coeff;
406 IntegerVariable GetOrCreateVariableGreaterOrEqualToSumOf(
407 const std::vector<std::pair<IntegerVariable, int64>>& terms, Model*
model) {
409 if (terms.size() == 1 && terms.front().second == 1) {
410 return terms.front().first;
412 if (terms.size() == 1 && terms.front().second == -1) {
417 const IntegerVariable new_var =
418 GetOrCreateVariableWithTightBound(terms,
model);
419 std::vector<IntegerVariable> vars;
420 std::vector<int64> coeffs;
421 for (
const auto& term : terms) {
422 vars.push_back(term.first);
423 coeffs.push_back(term.second);
425 vars.push_back(new_var);
426 coeffs.push_back(-1);
431 void TryToAddCutGenerators(
const CpModelProto&
model_proto,
432 const ConstraintProto&
ct, Model* m,
433 LinearRelaxation* relaxation) {
434 const int linearization_level =
435 m->GetOrCreate<SatParameters>()->linearization_level();
436 auto* mapping = m->GetOrCreate<CpModelMapping>();
437 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit &&
438 linearization_level > 1) {
439 std::vector<int> tails(
ct.circuit().tails().begin(),
440 ct.circuit().tails().end());
441 std::vector<int> heads(
ct.circuit().heads().begin(),
442 ct.circuit().heads().end());
443 std::vector<Literal> literals = mapping->Literals(
ct.circuit().literals());
444 const int num_nodes =
ReindexArcs(&tails, &heads, &literals);
446 relaxation->cut_generators.push_back(
450 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kRoutes &&
451 linearization_level > 1) {
452 std::vector<int> tails(
ct.routes().tails().begin(),
453 ct.routes().tails().end());
454 std::vector<int> heads(
ct.routes().heads().begin(),
455 ct.routes().heads().end());
456 std::vector<Literal> literals = mapping->Literals(
ct.routes().literals());
459 for (
int i = 0; i <
ct.routes().tails_size(); ++i) {
460 num_nodes =
std::max(num_nodes, 1 +
ct.routes().tails(i));
461 num_nodes =
std::max(num_nodes, 1 +
ct.routes().heads(i));
463 if (
ct.routes().demands().empty() ||
ct.routes().capacity() == 0) {
464 relaxation->cut_generators.push_back(
468 const std::vector<int64> demands(
ct.routes().demands().begin(),
469 ct.routes().demands().end());
470 relaxation->cut_generators.push_back(
472 ct.routes().capacity(), m));
475 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kIntProd) {
477 if (
ct.int_prod().vars_size() != 2)
return;
481 IntegerVariable z = mapping->Integer(
ct.int_prod().target());
482 IntegerVariable x = mapping->Integer(
ct.int_prod().vars(0));
483 IntegerVariable y = mapping->Integer(
ct.int_prod().vars(1));
485 IntegerTrail*
const integer_trail = m->GetOrCreate<IntegerTrail>();
486 IntegerValue x_lb = integer_trail->LowerBound(x);
487 IntegerValue x_ub = integer_trail->UpperBound(x);
488 IntegerValue y_lb = integer_trail->LowerBound(y);
489 IntegerValue y_ub = integer_trail->UpperBound(y);
493 if (x_lb < 0 && x_ub > 0)
return;
503 if (x_lb < 0 && x_ub > 0)
return;
504 if (y_lb < 0 && y_ub > 0)
return;
517 relaxation->cut_generators.push_back(
521 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kAllDiff) {
522 if (linearization_level < 2)
return;
524 const int num_vars =
ct.all_diff().vars_size();
525 if (num_vars <= m->GetOrCreate<SatParameters>()->max_all_diff_cut_size()) {
526 std::vector<IntegerVariable> vars =
527 mapping->Integers(
ct.all_diff().vars());
528 relaxation->cut_generators.push_back(
533 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCumulative) {
534 if (linearization_level < 2)
return;
537 std::vector<IntegerVariable> demands =
538 mapping->Integers(
ct.cumulative().demands());
539 std::vector<IntervalVariable> intervals =
540 mapping->Intervals(
ct.cumulative().intervals());
542 mapping->Integer(
ct.cumulative().capacity());
543 relaxation->cut_generators.push_back(
546 relaxation->cut_generators.push_back(
550 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kNoOverlap) {
551 if (linearization_level < 2)
return;
553 std::vector<IntervalVariable> intervals =
554 mapping->Intervals(
ct.no_overlap().intervals());
555 relaxation->cut_generators.push_back(
557 relaxation->cut_generators.push_back(
561 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinMax) {
562 if (!m->GetOrCreate<SatParameters>()->add_lin_max_cuts())
return;
566 if (
ct.lin_max().target().vars_size() != 1)
return;
567 if (
ct.lin_max().target().coeffs(0) != 1)
return;
569 const IntegerVariable target =
570 mapping->Integer(
ct.lin_max().target().vars(0));
571 std::vector<LinearExpression> exprs;
572 exprs.reserve(
ct.lin_max().exprs_size());
573 for (
int i = 0; i <
ct.lin_max().exprs_size(); ++i) {
582 const std::vector<IntegerVariable> z_vars =
585 if (linearization_level >= 2) {
586 relaxation->cut_generators.push_back(
594 LinearRelaxation ComputeLinearRelaxation(
const CpModelProto&
model_proto,
595 int linearization_level, Model* m) {
596 LinearRelaxation relaxation;
599 absl::flat_hash_set<int> used_integer_variable;
601 auto* mapping = m->GetOrCreate<CpModelMapping>();
602 auto* encoder = m->GetOrCreate<IntegerEncoder>();
603 auto* trail = m->GetOrCreate<Trail>();
606 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kCircuit) {
607 for (
const int ref :
ct.circuit().literals()) {
608 const Literal l = mapping->Literal(ref);
623 for (
const int literal_ref : refs.literals) {
625 if (trail->Assignment().LiteralIsAssigned(
literal)) {
643 int num_full_encoding_relaxations = 0;
644 int num_partial_encoding_relaxations = 0;
645 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
646 if (mapping->IsBoolean(i))
continue;
648 const IntegerVariable
var = mapping->Integer(i);
659 if (encoder->VariableIsFullyEncoded(
var)) {
661 ++num_full_encoding_relaxations;
672 const int old = relaxation.linear_constraints.size();
674 if (relaxation.linear_constraints.size() > old) {
675 ++num_partial_encoding_relaxations;
681 m->GetOrCreate<BinaryImplicationGraph>()->TransformIntoMaxCliques(
682 &relaxation.at_most_ones);
683 for (
const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
684 if (at_most_one.empty())
continue;
687 for (
const Literal
literal : at_most_one) {
690 const bool unused ABSL_ATTRIBUTE_UNUSED =
691 lc.AddLiteralTerm(
literal, IntegerValue(1));
693 relaxation.linear_constraints.push_back(lc.Build());
698 relaxation.at_most_ones.clear();
703 for (
int i = 0; i < relaxation.linear_constraints.size(); ++i) {
704 if (relaxation.linear_constraints[i].vars.size() <= 1)
continue;
705 std::swap(relaxation.linear_constraints[new_size++],
706 relaxation.linear_constraints[i]);
708 relaxation.linear_constraints.resize(new_size);
711 VLOG(3) <<
"num_full_encoding_relaxations: " << num_full_encoding_relaxations;
712 VLOG(3) <<
"num_partial_encoding_relaxations: "
713 << num_partial_encoding_relaxations;
714 VLOG(3) << relaxation.linear_constraints.size()
715 <<
" constraints in the LP relaxation.";
716 VLOG(3) << relaxation.cut_generators.size() <<
" cuts generators.";
721 IntegerVariable AddLPConstraints(
const CpModelProto&
model_proto,
722 int linearization_level, Model* m) {
723 const LinearRelaxation relaxation =
724 ComputeLinearRelaxation(
model_proto, linearization_level, m);
732 const int num_lp_constraints = relaxation.linear_constraints.size();
733 const int num_lp_cut_generators = relaxation.cut_generators.size();
734 const int num_integer_variables =
735 m->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
738 num_integer_variables);
739 auto get_constraint_index = [](
int ct_index) {
return ct_index; };
740 auto get_cut_generator_index = [num_lp_constraints](
int cut_index) {
741 return num_lp_constraints + cut_index;
743 auto get_var_index = [num_lp_constraints,
744 num_lp_cut_generators](IntegerVariable
var) {
745 return num_lp_constraints + num_lp_cut_generators +
var.value();
747 for (
int i = 0; i < num_lp_constraints; i++) {
748 for (
const IntegerVariable
var : relaxation.linear_constraints[i].vars) {
749 components.
AddEdge(get_constraint_index(i), get_var_index(
var));
752 for (
int i = 0; i < num_lp_cut_generators; ++i) {
753 for (
const IntegerVariable
var : relaxation.cut_generators[i].vars) {
754 components.
AddEdge(get_cut_generator_index(i), get_var_index(
var));
762 for (
const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
764 for (
const Literal
literal : at_most_one) {
767 const bool unused ABSL_ATTRIBUTE_UNUSED =
768 builder.AddLiteralTerm(
literal, IntegerValue(1));
770 LinearConstraint lc = builder.Build();
771 for (
int i = 1; i < lc.vars.size(); ++i) {
772 components.
AddEdge(get_var_index(lc.vars[0]), get_var_index(lc.vars[i]));
777 std::vector<int> component_sizes(num_components, 0);
778 const std::vector<int> index_to_component = components.
GetComponentIds();
779 for (
int i = 0; i < num_lp_constraints; i++) {
780 ++component_sizes[index_to_component[get_constraint_index(i)]];
782 for (
int i = 0; i < num_lp_cut_generators; i++) {
783 ++component_sizes[index_to_component[get_cut_generator_index(i)]];
791 auto* mapping = m->GetOrCreate<CpModelMapping>();
792 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
793 const IntegerVariable
var =
795 ++component_sizes[index_to_component[get_var_index(
var)]];
799 std::vector<LinearProgrammingConstraint*> lp_constraints(num_components,
801 std::vector<std::vector<LinearConstraint>> component_to_constraints(
803 for (
int i = 0; i < num_lp_constraints; i++) {
804 const int c = index_to_component[get_constraint_index(i)];
805 if (component_sizes[c] <= 1)
continue;
806 component_to_constraints[c].push_back(relaxation.linear_constraints[i]);
807 if (lp_constraints[c] ==
nullptr) {
808 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
811 lp_constraints[c]->AddLinearConstraint(relaxation.linear_constraints[i]);
815 for (
int i = 0; i < num_lp_cut_generators; i++) {
816 const int c = index_to_component[get_cut_generator_index(i)];
817 if (lp_constraints[c] ==
nullptr) {
818 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
820 lp_constraints[c]->AddCutGenerator(std::move(relaxation.cut_generators[i]));
824 const SatParameters& params = *(m->GetOrCreate<SatParameters>());
825 if (params.add_clique_cuts() && params.linearization_level() > 1) {
826 for (LinearProgrammingConstraint* lp : lp_constraints) {
827 if (lp ==
nullptr)
continue;
832 if (params.add_knapsack_cuts() && params.linearization_level() > 1) {
833 for (
int c = 0; c < num_components; ++c) {
834 if (component_to_constraints[c].empty())
continue;
836 component_to_constraints[c], lp_constraints[c]->integer_variables(),
842 std::vector<std::vector<std::pair<IntegerVariable, int64>>>
843 component_to_cp_terms(num_components);
844 std::vector<std::pair<IntegerVariable, int64>> top_level_cp_terms;
845 int num_components_containing_objective = 0;
849 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
850 const IntegerVariable
var =
853 const int c = index_to_component[get_var_index(
var)];
854 if (lp_constraints[c] !=
nullptr) {
855 lp_constraints[c]->SetObjectiveCoefficient(
var, IntegerValue(coeff));
856 component_to_cp_terms[c].push_back(std::make_pair(
var, coeff));
859 top_level_cp_terms.push_back(std::make_pair(
var, coeff));
863 for (
int c = 0; c < num_components; ++c) {
864 if (component_to_cp_terms[c].empty())
continue;
865 const IntegerVariable sub_obj_var =
866 GetOrCreateVariableGreaterOrEqualToSumOf(component_to_cp_terms[c], m);
867 top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
868 lp_constraints[c]->SetMainObjectiveVariable(sub_obj_var);
869 num_components_containing_objective++;
873 const IntegerVariable main_objective_var =
875 ? GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m)
880 for (LinearProgrammingConstraint* lp_constraint : lp_constraints) {
881 if (lp_constraint ==
nullptr)
continue;
882 lp_constraint->RegisterWith(m);
883 VLOG(3) <<
"LP constraint: " << lp_constraint->DimensionString() <<
".";
886 VLOG(3) << top_level_cp_terms.size()
887 <<
" terms in the main objective linear equation ("
888 << num_components_containing_objective <<
" from LP constraints).";
889 return main_objective_var;
901 const std::function<
void(
const CpSolverResponse&
response)>& observer) {
907 #if !defined(__PORTABLE_PLATFORM__)
910 const std::string& params) {
912 if (!params.empty()) {
913 CHECK(google::protobuf::TextFormat::ParseFromString(params, &
parameters))
918 #endif // __PORTABLE_PLATFORM__
936 void RegisterVariableBoundsLevelZeroExport(
937 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
939 CHECK(shared_bounds_manager !=
nullptr);
940 int saved_trail_index = 0;
941 const auto broadcast_level_zero_bounds =
943 const std::vector<IntegerVariable>& modified_vars)
mutable {
944 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
946 std::vector<int> model_variables;
947 std::vector<int64> new_lower_bounds;
948 std::vector<int64> new_upper_bounds;
949 absl::flat_hash_set<int> visited_variables;
952 auto* integer_trail =
model->Get<IntegerTrail>();
953 for (
const IntegerVariable&
var : modified_vars) {
955 const int model_var =
956 mapping->GetProtoVariableFromIntegerVariable(positive_var);
957 if (model_var == -1 || visited_variables.contains(model_var)) {
964 visited_variables.insert(model_var);
966 integer_trail->LevelZeroLowerBound(positive_var).value();
968 integer_trail->LevelZeroUpperBound(positive_var).value();
971 model_variables.push_back(model_var);
972 new_lower_bounds.push_back(new_lb);
973 new_upper_bounds.push_back(new_ub);
977 auto* trail =
model->Get<Trail>();
978 for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
979 const Literal fixed_literal = (*trail)[saved_trail_index];
980 const int model_var = mapping->GetProtoVariableFromBooleanVariable(
981 fixed_literal.Variable());
982 if (model_var == -1 || visited_variables.contains(model_var)) {
989 visited_variables.insert(model_var);
990 model_variables.push_back(model_var);
991 if (fixed_literal.IsPositive()) {
992 new_lower_bounds.push_back(1);
993 new_upper_bounds.push_back(1);
995 new_lower_bounds.push_back(0);
996 new_upper_bounds.push_back(0);
1000 if (!model_variables.empty()) {
1001 shared_bounds_manager->ReportPotentialNewBounds(
1007 if (!
model->Get<SatParameters>()->interleave_search()) {
1008 shared_bounds_manager->Synchronize();
1012 model->GetOrCreate<GenericLiteralWatcher>()
1013 ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
1019 void RegisterVariableBoundsLevelZeroImport(
1020 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
1022 CHECK(shared_bounds_manager !=
nullptr);
1023 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1024 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
1025 const int id = shared_bounds_manager->RegisterNewId();
1027 const auto& import_level_zero_bounds = [&
model_proto, shared_bounds_manager,
1028 model, integer_trail, id, mapping]() {
1029 std::vector<int> model_variables;
1030 std::vector<int64> new_lower_bounds;
1031 std::vector<int64> new_upper_bounds;
1032 shared_bounds_manager->GetChangedBounds(
1033 id, &model_variables, &new_lower_bounds, &new_upper_bounds);
1034 bool new_bounds_have_been_imported =
false;
1035 for (
int i = 0; i < model_variables.size(); ++i) {
1036 const int model_var = model_variables[i];
1039 if (!mapping->IsInteger(model_var))
continue;
1040 const IntegerVariable
var = mapping->Integer(model_var);
1041 const IntegerValue new_lb(new_lower_bounds[i]);
1042 const IntegerValue new_ub(new_upper_bounds[i]);
1043 const IntegerValue old_lb = integer_trail->LowerBound(
var);
1044 const IntegerValue old_ub = integer_trail->UpperBound(
var);
1045 const bool changed_lb = new_lb > old_lb;
1046 const bool changed_ub = new_ub < old_ub;
1047 if (!changed_lb && !changed_ub)
continue;
1049 new_bounds_have_been_imported =
true;
1050 if (VLOG_IS_ON(3)) {
1051 const IntegerVariableProto& var_proto =
1053 const std::string& var_name =
1054 var_proto.name().empty()
1055 ? absl::StrCat(
"anonymous_var(", model_var,
")")
1057 LOG(INFO) <<
" '" <<
model->Name() <<
"' imports new bounds for "
1058 << var_name <<
": from [" << old_lb <<
", " << old_ub
1059 <<
"] to [" << new_lb <<
", " << new_ub <<
"]";
1073 if (new_bounds_have_been_imported &&
1074 !
model->GetOrCreate<SatSolver>()->FinishPropagation()) {
1079 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1080 import_level_zero_bounds);
1085 void RegisterObjectiveBestBoundExport(
1086 IntegerVariable objective_var,
1087 SharedResponseManager* shared_response_manager, Model*
model) {
1088 auto* integer_trail =
model->Get<IntegerTrail>();
1089 const auto broadcast_objective_lower_bound =
1090 [objective_var, integer_trail, shared_response_manager,
1091 model](
const std::vector<IntegerVariable>& unused) {
1092 shared_response_manager->UpdateInnerObjectiveBounds(
1093 model->Name(), integer_trail->LevelZeroLowerBound(objective_var),
1094 integer_trail->LevelZeroUpperBound(objective_var));
1096 if (!
model->Get<SatParameters>()->interleave_search()) {
1097 shared_response_manager->Synchronize();
1100 model->GetOrCreate<GenericLiteralWatcher>()
1101 ->RegisterLevelZeroModifiedVariablesCallback(
1102 broadcast_objective_lower_bound);
1108 void RegisterObjectiveBoundsImport(
1109 SharedResponseManager* shared_response_manager, Model*
model) {
1110 auto* solver =
model->GetOrCreate<SatSolver>();
1111 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1112 auto* objective =
model->GetOrCreate<ObjectiveDefinition>();
1114 const auto import_objective_bounds = [
name, solver, integer_trail, objective,
1115 shared_response_manager]() {
1116 if (solver->AssumptionLevel() != 0)
return true;
1117 bool propagate =
false;
1119 const IntegerValue external_lb =
1120 shared_response_manager->SynchronizedInnerObjectiveLowerBound();
1121 const IntegerValue current_lb =
1122 integer_trail->LowerBound(objective->objective_var);
1123 if (external_lb > current_lb) {
1125 objective->objective_var, external_lb),
1132 const IntegerValue external_ub =
1133 shared_response_manager->SynchronizedInnerObjectiveUpperBound();
1134 const IntegerValue current_ub =
1135 integer_trail->UpperBound(objective->objective_var);
1136 if (external_ub < current_ub) {
1138 objective->objective_var, external_ub),
1145 if (!propagate)
return true;
1147 VLOG(2) <<
"'" <<
name <<
"' imports objective bounds: external ["
1148 << objective->ScaleIntegerObjective(external_lb) <<
", "
1149 << objective->ScaleIntegerObjective(external_ub) <<
"], current ["
1150 << objective->ScaleIntegerObjective(current_lb) <<
", "
1151 << objective->ScaleIntegerObjective(current_ub) <<
"]";
1153 return solver->FinishPropagation();
1156 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1157 import_objective_bounds);
1160 void LoadBaseModel(
const CpModelProto&
model_proto,
1161 SharedResponseManager* shared_response_manager,
1163 CHECK(shared_response_manager !=
nullptr);
1164 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1167 const auto unsat = [shared_response_manager, sat_solver,
model] {
1168 sat_solver->NotifyThatModelIsUnsat();
1169 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1170 absl::StrCat(
model->Name(),
" [loading]"));
1174 model->GetOrCreate<IntegerEncoder>()->DisableImplicationBetweenLiteral();
1176 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1177 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1178 const bool view_all_booleans_as_integers =
1180 (
parameters.search_branching() == SatParameters::FIXED_SEARCH &&
1182 mapping->CreateVariables(
model_proto, view_all_booleans_as_integers,
model);
1188 if (sat_solver->IsModelUnsat())
return unsat();
1194 std::set<std::string> unsupported_types;
1195 int num_ignored_constraints = 0;
1196 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1197 if (mapping->ConstraintIsAlreadyLoaded(&
ct)) {
1198 ++num_ignored_constraints;
1213 if (sat_solver->FinishPropagation()) {
1214 Trail* trail =
model->GetOrCreate<Trail>();
1215 const int old_num_fixed = trail->Index();
1216 if (trail->Index() > old_num_fixed) {
1217 VLOG(3) <<
"Constraint fixed " << trail->Index() - old_num_fixed
1222 if (sat_solver->IsModelUnsat()) {
1223 VLOG(2) <<
"UNSAT during extraction (after adding '"
1229 if (num_ignored_constraints > 0) {
1230 VLOG(3) << num_ignored_constraints <<
" constraints were skipped.";
1232 if (!unsupported_types.empty()) {
1233 VLOG(1) <<
"There is unsuported constraints types in this model: ";
1234 for (
const std::string& type : unsupported_types) {
1235 VLOG(1) <<
" - " << type;
1240 model->GetOrCreate<IntegerEncoder>()
1241 ->AddAllImplicationsBetweenAssociatedLiterals();
1242 if (!sat_solver->FinishPropagation())
return unsat();
1245 void LoadFeasibilityPump(
const CpModelProto&
model_proto,
1246 SharedResponseManager* shared_response_manager,
1248 CHECK(shared_response_manager !=
nullptr);
1252 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1253 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1254 if (
parameters.linearization_level() == 0)
return;
1257 const LinearRelaxation relaxation = ComputeLinearRelaxation(
1259 const int num_lp_constraints = relaxation.linear_constraints.size();
1260 if (num_lp_constraints == 0)
return;
1261 auto* feasibility_pump =
model->GetOrCreate<FeasibilityPump>();
1262 for (
int i = 0; i < num_lp_constraints; i++) {
1263 feasibility_pump->AddLinearConstraint(relaxation.linear_constraints[i]);
1267 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
1268 const IntegerVariable
var =
1269 mapping->Integer(
model_proto.objective().vars(i));
1271 feasibility_pump->SetObjectiveCoefficient(
var, IntegerValue(coeff));
1281 SharedResponseManager* shared_response_manager, Model*
model) {
1282 CHECK(shared_response_manager !=
nullptr);
1283 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1288 const auto unsat = [shared_response_manager, sat_solver,
model] {
1289 sat_solver->NotifyThatModelIsUnsat();
1290 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1291 absl::StrCat(
model->Name(),
" [loading]"));
1294 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1295 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1301 if (
model->Mutable<PrecedencesPropagator>() !=
nullptr &&
1302 parameters.auto_detect_greater_than_at_least_one_of()) {
1303 model->Mutable<PrecedencesPropagator>()
1304 ->AddGreaterThanAtLeastOneOfConstraints(
model);
1305 if (!sat_solver->FinishPropagation())
return unsat();
1311 if (
parameters.cp_model_probing_level() > 1) {
1313 if (
model->GetOrCreate<SatSolver>()->IsModelUnsat()) {
1316 if (!
model->GetOrCreate<BinaryImplicationGraph>()
1317 ->ComputeTransitiveReduction()) {
1330 const CpObjectiveProto& obj =
model_proto.objective();
1331 std::vector<std::pair<IntegerVariable, int64>> terms;
1332 terms.reserve(obj.vars_size());
1333 for (
int i = 0; i < obj.vars_size(); ++i) {
1335 std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i)));
1338 objective_var = GetOrCreateVariableWithTightBound(terms,
model);
1340 objective_var = GetOrCreateVariableGreaterOrEqualToSumOf(terms,
model);
1347 const CpObjectiveProto& objective_proto =
model_proto.objective();
1348 auto* objective_definition =
model->GetOrCreate<ObjectiveDefinition>();
1350 objective_definition->scaling_factor = objective_proto.scaling_factor();
1351 if (objective_definition->scaling_factor == 0.0) {
1352 objective_definition->scaling_factor = 1.0;
1354 objective_definition->offset = objective_proto.offset();
1355 objective_definition->objective_var = objective_var;
1357 const int size = objective_proto.vars_size();
1358 objective_definition->vars.resize(size);
1359 objective_definition->coeffs.resize(size);
1360 for (
int i = 0; i < objective_proto.vars_size(); ++i) {
1363 objective_definition->vars[i] = mapping->Integer(objective_proto.vars(i));
1364 objective_definition->coeffs[i] = IntegerValue(objective_proto.coeffs(i));
1367 const int ref = objective_proto.vars(i);
1368 if (mapping->IsInteger(ref)) {
1369 const IntegerVariable
var = mapping->Integer(objective_proto.vars(i));
1370 objective_definition->objective_impacting_variables.insert(
1379 const Domain automatic_domain =
1380 model->GetOrCreate<IntegerTrail>()->InitialVariableDomain(
1382 VLOG(3) <<
"Objective offset:" <<
model_proto.objective().offset()
1383 <<
" scaling_factor:" <<
model_proto.objective().scaling_factor();
1384 VLOG(3) <<
"Automatic internal objective domain: " << automatic_domain;
1385 VLOG(3) <<
"User specified internal objective domain: " << user_domain;
1387 const bool ok =
model->GetOrCreate<IntegerTrail>()->UpdateInitialDomain(
1388 objective_var, user_domain);
1390 VLOG(2) <<
"UNSAT due to the objective domain.";
1401 if (!automatic_domain.IsIncludedIn(user_domain)) {
1402 std::vector<IntegerVariable> vars;
1403 std::vector<int64> coeffs;
1404 const CpObjectiveProto& obj =
model_proto.objective();
1405 for (
int i = 0; i < obj.vars_size(); ++i) {
1406 vars.push_back(mapping->Integer(obj.vars(i)));
1407 coeffs.push_back(obj.coeffs(i));
1409 vars.push_back(objective_var);
1410 coeffs.push_back(-1);
1417 if (!sat_solver->FinishPropagation())
return unsat();
1421 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1422 shared_response_manager->UpdateInnerObjectiveBounds(
1423 "init", integer_trail->LowerBound(objective_var),
1424 integer_trail->UpperBound(objective_var));
1427 RegisterObjectiveBestBoundExport(objective_var, shared_response_manager,
1433 if (
model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1434 RegisterObjectiveBoundsImport(shared_response_manager,
model);
1440 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1441 auto* lp_dispatcher =
model->GetOrCreate<LinearProgrammingDispatcher>();
1442 auto* lp_vars =
model->GetOrCreate<LPVariables>();
1443 IntegerVariable size = integer_trail->NumIntegerVariables();
1444 for (IntegerVariable positive_var(0); positive_var < size;
1445 positive_var += 2) {
1447 lp_var.positive_var = positive_var;
1449 mapping->GetProtoVariableFromIntegerVariable(positive_var);
1452 if (lp_var.model_var >= 0) {
1453 lp_vars->vars.push_back(lp_var);
1454 lp_vars->model_vars_size =
1455 std::max(lp_vars->model_vars_size, lp_var.model_var + 1);
1460 auto* search_heuristics =
model->GetOrCreate<SearchHeuristics>();
1463 if (VLOG_IS_ON(3)) {
1464 search_heuristics->fixed_search =
1466 search_heuristics->fixed_search,
model);
1470 std::vector<BooleanOrIntegerVariable> vars;
1471 std::vector<IntegerValue> values;
1472 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1473 const int ref =
model_proto.solution_hint().vars(i);
1475 BooleanOrIntegerVariable
var;
1476 if (mapping->IsBoolean(ref)) {
1477 var.bool_var = mapping->Literal(ref).Variable();
1479 var.int_var = mapping->Integer(ref);
1481 vars.push_back(
var);
1482 values.push_back(IntegerValue(
model_proto.solution_hint().values(i)));
1490 const std::string solution_info =
model->Name();
1492 shared_response_manager]() {
1495 response.set_solution_info(solution_info);
1499 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1500 CoreBasedOptimizer* core =
1501 new CoreBasedOptimizer(objective_var, objective.vars, objective.coeffs,
1502 solution_observer,
model);
1503 model->Register<CoreBasedOptimizer>(core);
1504 model->TakeOwnership(core);
1514 void SolveLoadedCpModel(
const CpModelProto&
model_proto,
1515 SharedResponseManager* shared_response_manager,
1517 if (shared_response_manager->ProblemIsSolved())
return;
1519 const std::string& solution_info =
model->Name();
1521 &shared_response_manager]() {
1524 response.set_solution_info(solution_info);
1531 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1533 const SatParameters&
parameters = *
model->GetOrCreate<SatParameters>();
1539 solution_observer();
1540 if (!
parameters.enumerate_all_solutions())
break;
1544 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1548 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1552 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1553 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1555 std::vector<int> core_in_proto_format;
1556 for (
const Literal l : core) {
1557 core_in_proto_format.push_back(
1558 mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1559 if (!l.IsPositive()) {
1560 core_in_proto_format.back() =
NegatedRef(core_in_proto_format.back());
1563 shared_response_manager->AddUnsatCore(core_in_proto_format);
1567 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1568 const IntegerVariable objective_var = objective.objective_var;
1576 objective, solution_observer,
model);
1578 status =
model->Mutable<CoreBasedOptimizer>()->Optimize();
1583 if (
parameters.binary_search_num_conflicts() >= 0) {
1585 solution_observer,
model);
1588 objective_var, solution_observer,
model);
1596 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1603 shared_response_manager->SetStatsFromModel(
model);
1608 void QuickSolveWithHint(
const CpModelProto&
model_proto,
1609 SharedResponseManager* shared_response_manager,
1612 if (shared_response_manager->ProblemIsSolved())
return;
1616 const SatParameters saved_params = *
parameters;
1618 parameters->set_search_branching(SatParameters::HINT_SEARCH);
1625 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1629 const std::string& solution_info =
model->Name();
1633 response.set_solution_info(absl::StrCat(solution_info,
" [hint]"));
1642 const IntegerVariable objective_var =
1643 model->GetOrCreate<ObjectiveDefinition>()->objective_var;
1644 model->GetOrCreate<SatSolver>()->Backtrack(0);
1645 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
1646 if (!integer_trail->Enqueue(
1649 shared_response_manager->GetInnerObjectiveUpperBound()),
1651 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1652 absl::StrCat(solution_info,
" [hint]"));
1653 shared_response_manager->SetStatsFromModel(
model);
1663 void MinimizeL1DistanceWithHint(
const CpModelProto&
model_proto,
1664 SharedResponseManager* shared_response_manager,
1666 SharedTimeLimit* shared_time_limit,
1670 if (shared_response_manager->ProblemIsSolved())
return;
1672 auto*
parameters = local_model.GetOrCreate<SatParameters>();
1676 if (
parameters->enumerate_all_solutions())
return;
1679 const SatParameters saved_params = *
model->GetOrCreate<SatParameters>();
1686 updated_model_proto.clear_objective();
1689 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1694 const int new_var_index = updated_model_proto.variables_size();
1695 IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1700 var_proto->add_domain(min_domain);
1701 var_proto->add_domain(max_domain);
1704 ConstraintProto*
const linear_constraint_proto =
1705 updated_model_proto.add_constraints();
1706 LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
1707 linear->add_vars(new_var_index);
1708 linear->add_coeffs(1);
1709 linear->add_vars(
var);
1710 linear->add_coeffs(-1);
1711 linear->add_domain(-
value);
1712 linear->add_domain(-
value);
1715 const int abs_var_index = updated_model_proto.variables_size();
1716 IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
1717 const int64 abs_min_domain = 0;
1718 const int64 abs_max_domain =
1719 std::max(std::abs(min_domain), std::abs(max_domain));
1720 abs_var_proto->add_domain(abs_min_domain);
1721 abs_var_proto->add_domain(abs_max_domain);
1722 ConstraintProto*
const abs_constraint_proto =
1723 updated_model_proto.add_constraints();
1724 abs_constraint_proto->mutable_int_max()->set_target(abs_var_index);
1725 abs_constraint_proto->mutable_int_max()->add_vars(new_var_index);
1726 abs_constraint_proto->mutable_int_max()->add_vars(
1729 updated_model_proto.mutable_objective()->add_vars(abs_var_index);
1730 updated_model_proto.mutable_objective()->add_coeffs(1);
1733 SharedResponseManager local_response_manager(
1734 false,
parameters->enumerate_all_solutions(),
1735 &updated_model_proto,
wall_timer, shared_time_limit);
1737 local_model.Register<SharedResponseManager>(&local_response_manager);
1740 LoadCpModel(updated_model_proto, &local_response_manager, &local_model);
1743 const auto& mapping = *local_model.GetOrCreate<CpModelMapping>();
1745 mapping.Literals(updated_model_proto.assumptions()), &local_model);
1747 const std::string& solution_info =
model->Name();
1752 CpSolverResponse updated_response;
1753 FillSolutionInResponse(updated_model_proto, local_model,
1755 LOG(INFO) <<
"Found solution with repaired hint penalty = "
1759 response.set_solution_info(absl::StrCat(solution_info,
" [repaired]"));
1768 void PostsolveResponseWithFullSolver(
1769 const int64 num_variables_in_original_model, CpModelProto mapping_proto,
1778 if (mapping_proto.variables_size() == 0) {
1783 for (
int i = 0; i <
response->solution_size(); ++i) {
1784 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1785 var_proto->clear_domain();
1786 var_proto->add_domain(
response->solution(i));
1787 var_proto->add_domain(
response->solution(i));
1789 for (
int i = 0; i <
response->solution_lower_bounds_size(); ++i) {
1790 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1793 .IntersectionWith({
response->solution_lower_bounds(i),
1794 response->solution_upper_bounds(i)}),
1801 Model postsolve_model;
1803 SatParameters params;
1804 params.set_linearization_level(0);
1805 params.set_cp_model_probing_level(0);
1810 SharedTimeLimit shared_time_limit(
time_limit.get());
1811 SharedResponseManager local_response_manager(
1812 false,
false, &mapping_proto,
1814 LoadCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1815 SolveLoadedCpModel(mapping_proto, &local_response_manager, &postsolve_model);
1816 const CpSolverResponse postsolve_response =
1817 local_response_manager.GetResponse();
1823 response->clear_solution_lower_bounds();
1824 response->clear_solution_upper_bounds();
1825 if (!postsolve_response.solution().empty()) {
1826 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1827 response->add_solution(postsolve_response.solution(i));
1830 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1831 response->add_solution_lower_bounds(
1832 postsolve_response.solution_lower_bounds(i));
1833 response->add_solution_upper_bounds(
1834 postsolve_response.solution_upper_bounds(i));
1839 void PostsolveResponseWrapper(
const SatParameters& params,
1840 const int64 num_variables_in_original_model,
1841 const CpModelProto& mapping_proto,
1842 const std::vector<int>& postsolve_mapping,
1845 if (params.cp_model_postsolve_with_full_solver()) {
1846 PostsolveResponseWithFullSolver(num_variables_in_original_model,
1847 mapping_proto, postsolve_mapping,
1856 CpSolverResponse SolvePureSatModel(
const CpModelProto&
model_proto,
1858 std::unique_ptr<SatSolver> solver(
new SatSolver());
1861 model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(
parameters);
1864 std::unique_ptr<DratProofHandler> drat_proof_handler;
1865 #if !defined(__PORTABLE_PLATFORM__)
1866 if (!FLAGS_drat_output.empty() || FLAGS_drat_check) {
1867 if (!FLAGS_drat_output.empty()) {
1870 drat_proof_handler = absl::make_unique<DratProofHandler>(
1871 false, output, FLAGS_drat_check);
1873 drat_proof_handler = absl::make_unique<DratProofHandler>();
1875 solver->SetDratProofHandler(drat_proof_handler.get());
1877 #endif // __PORTABLE_PLATFORM__
1879 auto get_literal = [](
int ref) {
1880 if (ref >= 0)
return Literal(BooleanVariable(ref),
true);
1881 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
1884 std::vector<Literal> temp;
1885 const int num_variables =
model_proto.variables_size();
1886 solver->SetNumVariables(num_variables);
1887 if (drat_proof_handler !=
nullptr) {
1892 for (
int ref = 0; ref < num_variables; ++ref) {
1894 if (domain.IsFixed()) {
1895 const Literal ref_literal =
1896 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1900 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1901 switch (
ct.constraint_case()) {
1902 case ConstraintProto::ConstraintCase::kBoolAnd: {
1903 if (
ct.enforcement_literal_size() == 0) {
1904 for (
const int ref :
ct.bool_and().literals()) {
1909 const Literal not_a =
1910 get_literal(
ct.enforcement_literal(0)).Negated();
1911 for (
const int ref :
ct.bool_and().literals()) {
1917 case ConstraintProto::ConstraintCase::kBoolOr:
1919 for (
const int ref :
ct.bool_or().literals()) {
1920 temp.push_back(get_literal(ref));
1925 LOG(FATAL) <<
"Not supported";
1930 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1931 switch (
ct.constraint_case()) {
1932 case ConstraintProto::ConstraintCase::kBoolAnd: {
1933 if (
ct.enforcement_literal_size() == 0) {
1934 for (
const int ref :
ct.bool_and().literals()) {
1935 const Literal
b = get_literal(ref);
1936 solver->AddUnitClause(
b);
1940 const Literal not_a =
1941 get_literal(
ct.enforcement_literal(0)).Negated();
1942 for (
const int ref :
ct.bool_and().literals()) {
1943 const Literal
b = get_literal(ref);
1944 solver->AddProblemClause({not_a,
b});
1949 case ConstraintProto::ConstraintCase::kBoolOr:
1951 for (
const int ref :
ct.bool_or().literals()) {
1952 temp.push_back(get_literal(ref));
1954 solver->AddProblemClause(temp);
1957 LOG(FATAL) <<
"Not supported";
1962 for (
int ref = 0; ref < num_variables; ++ref) {
1964 if (domain.Min() == domain.Max()) {
1965 const Literal ref_literal =
1966 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1967 solver->AddUnitClause(ref_literal);
1974 std::vector<bool> solution;
1976 &solution, drat_proof_handler.get());
1979 for (
int ref = 0; ref < num_variables; ++ref) {
1980 response.add_solution(solution[ref]);
1984 status = solver->SolveWithTimeLimit(
model->GetOrCreate<TimeLimit>());
1987 for (
int ref = 0; ref < num_variables; ++ref) {
1989 solver->Assignment().LiteralIsTrue(get_literal(ref)) ? 1 : 0);
1996 model->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
1997 solver->model()->GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
2006 std::vector<int64>(
response.solution().begin(),
2016 LOG(FATAL) <<
"Unexpected SatSolver::Status " << status;
2018 response.set_num_booleans(solver->NumVariables());
2019 response.set_num_branches(solver->num_branches());
2020 response.set_num_conflicts(solver->num_failures());
2021 response.set_num_binary_propagations(solver->num_propagations());
2022 response.set_num_integer_propagations(0);
2025 model->Get<TimeLimit>()->GetElapsedDeterministicTime());
2031 drat_proof_handler->
Check(FLAGS_max_drat_time_in_seconds);
2032 switch (drat_status) {
2034 LOG(INFO) <<
"DRAT status: UNKNOWN";
2037 LOG(INFO) <<
"DRAT status: VALID";
2040 LOG(ERROR) <<
"DRAT status: INVALID";
2046 LOG(INFO) <<
"DRAT wall time: " << drat_timer.
Get();
2047 }
else if (drat_proof_handler !=
nullptr) {
2050 LOG(INFO) <<
"DRAT status: NA";
2051 LOG(INFO) <<
"DRAT wall time: NA";
2052 LOG(INFO) <<
"DRAT user time: NA";
2057 #if !defined(__PORTABLE_PLATFORM__)
2060 struct SharedClasses {
2070 bool SearchIsDone() {
2071 if (
response->ProblemIsSolved())
return true;
2078 class FullProblemSolver :
public SubSolver {
2080 FullProblemSolver(
const std::string&
name,
2081 const SatParameters& local_parameters,
bool split_in_chunks,
2082 SharedClasses* shared)
2085 split_in_chunks_(split_in_chunks),
2086 local_model_(
absl::make_unique<Model>(
name)) {
2089 shared_->time_limit->UpdateLocalLimit(
2090 local_model_->GetOrCreate<TimeLimit>());
2092 if (shared->response !=
nullptr) {
2093 local_model_->Register<SharedResponseManager>(shared->response);
2096 if (shared->relaxation_solutions !=
nullptr) {
2097 local_model_->Register<SharedRelaxationSolutionRepository>(
2098 shared->relaxation_solutions);
2101 if (shared->lp_solutions !=
nullptr) {
2102 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2105 if (shared->incomplete_solutions !=
nullptr) {
2106 local_model_->Register<SharedIncompleteSolutionManager>(
2107 shared->incomplete_solutions);
2111 if (shared_->bounds !=
nullptr) {
2112 RegisterVariableBoundsLevelZeroExport(
2113 *shared_->model_proto, shared_->bounds, local_model_.get());
2114 RegisterVariableBoundsLevelZeroImport(
2115 *shared_->model_proto, shared_->bounds, local_model_.get());
2119 bool TaskIsAvailable()
override {
2120 if (shared_->SearchIsDone())
return false;
2122 absl::MutexLock mutex_lock(&mutex_);
2123 return previous_task_is_completed_;
2126 std::function<void()> GenerateTask(
int64 task_id)
override {
2128 absl::MutexLock mutex_lock(&mutex_);
2129 previous_task_is_completed_ =
false;
2132 if (solving_first_chunk_) {
2133 LoadCpModel(*shared_->model_proto, shared_->response,
2134 local_model_.get());
2135 if (local_model_->GetOrCreate<SatParameters>()->repair_hint()) {
2136 MinimizeL1DistanceWithHint(*shared_->model_proto, shared_->response,
2137 shared_->wall_timer, shared_->time_limit,
2138 local_model_.get());
2140 QuickSolveWithHint(*shared_->model_proto, shared_->response,
2141 local_model_.get());
2145 solving_first_chunk_ =
false;
2147 if (split_in_chunks_) {
2149 absl::MutexLock mutex_lock(&mutex_);
2150 previous_task_is_completed_ =
true;
2155 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2156 if (split_in_chunks_) {
2159 auto* params = local_model_->GetOrCreate<SatParameters>();
2160 params->set_max_deterministic_time(1);
2161 time_limit->ResetLimitFromParameters(*params);
2162 shared_->time_limit->UpdateLocalLimit(
time_limit);
2165 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2166 SolveLoadedCpModel(*shared_->model_proto, shared_->response,
2167 local_model_.get());
2169 absl::MutexLock mutex_lock(&mutex_);
2170 deterministic_time_since_last_synchronize_ +=
2171 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2175 if (shared_->SearchIsDone()) {
2176 shared_->time_limit->Stop();
2181 if (split_in_chunks_) {
2182 absl::MutexLock mutex_lock(&mutex_);
2183 previous_task_is_completed_ =
true;
2191 local_model_.reset();
2198 void Synchronize()
override {
2199 absl::MutexLock mutex_lock(&mutex_);
2200 deterministic_time_ += deterministic_time_since_last_synchronize_;
2201 shared_->time_limit->AdvanceDeterministicTime(
2202 deterministic_time_since_last_synchronize_);
2203 deterministic_time_since_last_synchronize_ = 0.0;
2207 SharedClasses* shared_;
2208 const bool split_in_chunks_;
2209 std::unique_ptr<Model> local_model_;
2213 bool solving_first_chunk_ =
true;
2216 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2218 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2221 class FeasibilityPumpSolver :
public SubSolver {
2223 FeasibilityPumpSolver(
const SatParameters& local_parameters,
2224 SharedClasses* shared)
2225 : SubSolver(
"feasibility_pump"),
2227 local_model_(
absl::make_unique<Model>(name_)) {
2230 shared_->time_limit->UpdateLocalLimit(
2231 local_model_->GetOrCreate<TimeLimit>());
2233 if (shared->response !=
nullptr) {
2234 local_model_->Register<SharedResponseManager>(shared->response);
2237 if (shared->relaxation_solutions !=
nullptr) {
2238 local_model_->Register<SharedRelaxationSolutionRepository>(
2239 shared->relaxation_solutions);
2242 if (shared->lp_solutions !=
nullptr) {
2243 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2246 if (shared->incomplete_solutions !=
nullptr) {
2247 local_model_->Register<SharedIncompleteSolutionManager>(
2248 shared->incomplete_solutions);
2252 if (shared_->bounds !=
nullptr) {
2253 RegisterVariableBoundsLevelZeroImport(
2254 *shared_->model_proto, shared_->bounds, local_model_.get());
2258 bool TaskIsAvailable()
override {
2259 if (shared_->SearchIsDone())
return false;
2260 absl::MutexLock mutex_lock(&mutex_);
2261 return previous_task_is_completed_;
2264 std::function<void()> GenerateTask(
int64 task_id)
override {
2267 absl::MutexLock mutex_lock(&mutex_);
2268 if (!previous_task_is_completed_)
return;
2269 previous_task_is_completed_ =
false;
2272 absl::MutexLock mutex_lock(&mutex_);
2273 if (solving_first_chunk_) {
2274 LoadFeasibilityPump(*shared_->model_proto, shared_->response,
2275 local_model_.get());
2278 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
2279 solving_first_chunk_ =
false;
2281 previous_task_is_completed_ =
true;
2286 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2287 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2288 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
2289 if (!feasibility_pump->Solve()) {
2290 shared_->response->NotifyThatImprovingProblemIsInfeasible(name_);
2294 absl::MutexLock mutex_lock(&mutex_);
2295 deterministic_time_since_last_synchronize_ +=
2296 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2300 if (shared_->SearchIsDone()) {
2301 shared_->time_limit->Stop();
2305 absl::MutexLock mutex_lock(&mutex_);
2306 previous_task_is_completed_ =
true;
2310 void Synchronize()
override {
2311 absl::MutexLock mutex_lock(&mutex_);
2312 deterministic_time_ += deterministic_time_since_last_synchronize_;
2313 shared_->time_limit->AdvanceDeterministicTime(
2314 deterministic_time_since_last_synchronize_);
2315 deterministic_time_since_last_synchronize_ = 0.0;
2319 SharedClasses* shared_;
2320 std::unique_ptr<Model> local_model_;
2326 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
2328 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2330 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2334 class LnsSolver :
public SubSolver {
2336 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
2338 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
2339 : SubSolver(generator->
name()),
2340 generator_(std::move(generator)),
2345 bool TaskIsAvailable()
override {
2346 if (shared_->SearchIsDone())
return false;
2347 return generator_->ReadyToGenerate();
2350 std::function<void()> GenerateTask(
int64 task_id)
override {
2351 return [task_id,
this]() {
2352 if (shared_->SearchIsDone())
return;
2357 const int32 low =
static_cast<int32>(task_id);
2358 const int32 high = task_id >> 32;
2359 std::seed_seq seed{low, high, parameters_.random_seed()};
2362 NeighborhoodGenerator::SolveData data;
2363 data.difficulty = generator_->difficulty();
2364 data.deterministic_limit = generator_->deterministic_limit();
2367 CpSolverResponse base_response;
2369 const SharedSolutionRepository<int64>& repo =
2370 shared_->response->SolutionsRepository();
2371 if (repo.NumSolutions() > 0) {
2373 const SharedSolutionRepository<int64>::Solution solution =
2374 repo.GetRandomBiasedSolution(&random);
2375 for (
const int64 value : solution.variable_values) {
2376 base_response.add_solution(
value);
2380 data.initial_best_objective = repo.GetSolution(0).rank;
2381 data.base_objective = solution.rank;
2390 data.initial_best_objective =
2391 shared_->response->GetInnerObjectiveUpperBound();
2392 data.base_objective = data.initial_best_objective;
2396 Neighborhood neighborhood;
2398 absl::MutexLock mutex_lock(helper_->MutableMutex());
2400 generator_->Generate(base_response, data.difficulty, &random);
2402 neighborhood.cp_model.set_name(absl::StrCat(
"lns_", task_id));
2403 if (!neighborhood.is_generated)
return;
2404 data.neighborhood_id = neighborhood.id;
2406 const double fully_solved_proportion =
2407 static_cast<double>(generator_->num_fully_solved_calls()) /
2409 std::string source_info =
name();
2410 if (!neighborhood.source_info.empty()) {
2411 absl::StrAppend(&source_info,
"_", neighborhood.source_info);
2413 const std::string solution_info = absl::StrFormat(
2414 "%s(d=%0.2f s=%i t=%0.2f p=%0.2f)", source_info, data.difficulty,
2415 task_id, data.deterministic_limit, fully_solved_proportion);
2417 SatParameters local_params(parameters_);
2418 local_params.set_max_deterministic_time(data.deterministic_limit);
2419 local_params.set_stop_after_first_solution(
false);
2420 local_params.set_log_search_progress(
false);
2421 local_params.set_cp_model_probing_level(0);
2423 if (FLAGS_cp_model_dump_lns) {
2424 const std::string
name = absl::StrCat(
2425 FLAGS_cp_model_dump_prefix, neighborhood.cp_model.name(),
".pbtxt");
2426 LOG(INFO) <<
"Dumping LNS model to '" <<
name <<
"'.";
2433 TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
2434 shared_->time_limit->UpdateLocalLimit(local_time_limit);
2436 const int64 num_neighborhood_model_vars =
2437 neighborhood.cp_model.variables_size();
2439 CpModelProto mapping_proto;
2440 std::vector<int> postsolve_mapping;
2441 PresolveOptions options;
2442 options.log_info = VLOG_IS_ON(3);
2443 options.parameters = *local_model.GetOrCreate<SatParameters>();
2444 options.time_limit = local_model.GetOrCreate<TimeLimit>();
2445 auto context = absl::make_unique<PresolveContext>(&neighborhood.cp_model,
2455 SharedResponseManager local_response_manager(
2457 &neighborhood.cp_model, shared_->wall_timer, shared_->time_limit);
2458 LoadCpModel(neighborhood.cp_model, &local_response_manager, &local_model);
2459 QuickSolveWithHint(neighborhood.cp_model, &local_response_manager,
2461 SolveLoadedCpModel(neighborhood.cp_model, &local_response_manager,
2463 CpSolverResponse local_response = local_response_manager.GetResponse();
2464 if (local_response.solution_info().empty()) {
2465 local_response.set_solution_info(solution_info);
2467 local_response.set_solution_info(
2468 absl::StrCat(local_response.solution_info(),
" ", solution_info));
2473 PostsolveResponseWrapper(local_params, num_neighborhood_model_vars,
2474 mapping_proto, postsolve_mapping,
2475 shared_->wall_timer, &local_response);
2476 data.status = local_response.status();
2477 data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
2479 if (generator_->IsRelaxationGenerator()) {
2480 bool has_feasible_solution =
false;
2483 has_feasible_solution =
true;
2487 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2488 local_response.solution_info());
2491 if (shared_->model_proto->has_objective()) {
2494 const IntegerValue current_obj_lb =
2495 shared_->response->GetInnerObjectiveLowerBound();
2497 const IntegerValue local_obj_lb =
2498 local_response_manager.GetInnerObjectiveLowerBound();
2501 neighborhood.cp_model.objective(), local_obj_lb.value());
2504 const IntegerValue new_inner_obj_lb = IntegerValue(
2506 scaled_local_obj_bound) -
2508 data.new_objective_bound = new_inner_obj_lb;
2509 data.initial_best_objective_bound = current_obj_lb;
2510 if (new_inner_obj_lb > current_obj_lb) {
2511 shared_->response->UpdateInnerObjectiveBounds(
2518 if (has_feasible_solution) {
2520 *shared_->model_proto,
2521 std::vector<int64>(local_response.solution().begin(),
2522 local_response.solution().end()))) {
2523 shared_->response->NewSolution(local_response,
2528 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2529 local_response.solution_info());
2530 shared_->time_limit->Stop();
2533 shared_->relaxation_solutions->NewRelaxationSolution(local_response);
2536 if (!local_response.solution().empty()) {
2538 *shared_->model_proto,
2539 std::vector<int64>(local_response.solution().begin(),
2540 local_response.solution().end())))
2545 data.new_objective = data.base_objective;
2549 shared_->model_proto->objective(), local_response));
2555 shared_->response->NewSolution(local_response,
2558 if (!neighborhood.is_reduced &&
2561 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2562 local_response.solution_info());
2563 shared_->time_limit->Stop();
2567 generator_->AddSolveData(data);
2570 const int total_num_calls = task_id;
2571 VLOG(2) <<
name() <<
": [difficulty: " << data.difficulty
2572 <<
", id: " << task_id
2573 <<
", deterministic_time: " << data.deterministic_time <<
" / "
2574 << data.deterministic_limit
2575 <<
", status: " << ProtoEnumToString<CpSolverStatus>(data.status)
2576 <<
", num calls: " << generator_->num_calls()
2577 <<
", UCB1 Score: " << generator_->GetUCBScore(total_num_calls)
2578 <<
", p: " << fully_solved_proportion <<
"]";
2582 void Synchronize()
override {
2583 generator_->Synchronize();
2584 const double old = deterministic_time_;
2585 deterministic_time_ = generator_->deterministic_time();
2586 shared_->time_limit->AdvanceDeterministicTime(deterministic_time_ - old);
2590 std::unique_ptr<NeighborhoodGenerator> generator_;
2591 NeighborhoodGeneratorHelper* helper_;
2592 const SatParameters parameters_;
2593 SharedClasses* shared_;
2596 void SolveCpModelParallel(
const CpModelProto&
model_proto,
2597 SharedResponseManager* shared_response_manager,
2598 SharedTimeLimit* shared_time_limit,
2600 CHECK(shared_response_manager !=
nullptr);
2601 const SatParameters&
parameters = *global_model->GetOrCreate<SatParameters>();
2602 const int num_search_workers =
parameters.num_search_workers();
2603 const bool log_search =
parameters.log_search_progress() || VLOG_IS_ON(1);
2605 <<
"Enumerating all solutions in parallel is not supported.";
2607 std::unique_ptr<SharedBoundsManager> shared_bounds_manager;
2609 shared_bounds_manager = absl::make_unique<SharedBoundsManager>(
model_proto);
2612 std::unique_ptr<SharedRelaxationSolutionRepository>
2613 shared_relaxation_solutions;
2615 shared_relaxation_solutions =
2616 absl::make_unique<SharedRelaxationSolutionRepository>(
2618 global_model->Register<SharedRelaxationSolutionRepository>(
2619 shared_relaxation_solutions.get());
2622 auto shared_lp_solutions = absl::make_unique<SharedLPSolutionRepository>(
2624 global_model->Register<SharedLPSolutionRepository>(shared_lp_solutions.get());
2628 std::unique_ptr<SharedIncompleteSolutionManager> shared_incomplete_solutions;
2629 const bool use_feasibility_pump =
parameters.use_feasibility_pump() &&
2633 if (use_feasibility_pump) {
2634 shared_incomplete_solutions =
2635 absl::make_unique<SharedIncompleteSolutionManager>();
2636 global_model->Register<SharedIncompleteSolutionManager>(
2637 shared_incomplete_solutions.get());
2640 SharedClasses shared;
2643 shared.time_limit = shared_time_limit;
2644 shared.bounds = shared_bounds_manager.get();
2645 shared.response = shared_response_manager;
2646 shared.relaxation_solutions = shared_relaxation_solutions.get();
2647 shared.lp_solutions = shared_lp_solutions.get();
2648 shared.incomplete_solutions = shared_incomplete_solutions.get();
2651 std::vector<std::unique_ptr<SubSolver>> subsolvers;
2654 subsolvers.push_back(absl::make_unique<SynchronizationPoint>(
2655 [shared_response_manager, &shared_bounds_manager,
2656 &shared_relaxation_solutions, &shared_lp_solutions]() {
2657 shared_response_manager->Synchronize();
2658 shared_response_manager->MutableSolutionsRepository()->Synchronize();
2659 if (shared_bounds_manager !=
nullptr) {
2662 if (shared_relaxation_solutions !=
nullptr) {
2665 if (shared_lp_solutions !=
nullptr) {
2666 shared_lp_solutions->Synchronize();
2675 local_params.set_stop_after_first_solution(
true);
2676 local_params.set_linearization_level(0);
2677 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2678 "first_solution", local_params,
2684 if (
parameters.optimize_with_max_hs())
continue;
2686 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2687 local_params.name(), local_params,
2693 if (use_feasibility_pump) {
2694 subsolvers.push_back(
2695 absl::make_unique<FeasibilityPumpSolver>(
parameters, &shared));
2702 auto unique_helper = absl::make_unique<NeighborhoodGeneratorHelper>(
2704 shared_bounds_manager.get());
2705 NeighborhoodGeneratorHelper* helper = unique_helper.get();
2706 subsolvers.push_back(std::move(unique_helper));
2708 const int num_lns_strategies =
parameters.diversify_lns_params() ? 6 : 1;
2709 const std::vector<SatParameters>& lns_params =
2711 for (
const SatParameters& local_params : lns_params) {
2716 subsolvers.push_back(absl::make_unique<LnsSolver>(
2717 absl::make_unique<SimpleNeighborhoodGenerator>(
2718 helper, absl::StrCat(
"rnd_lns_", local_params.name())),
2719 local_params, helper, &shared));
2720 subsolvers.push_back(absl::make_unique<LnsSolver>(
2721 absl::make_unique<VariableGraphNeighborhoodGenerator>(
2722 helper, absl::StrCat(
"var_lns_", local_params.name())),
2723 local_params, helper, &shared));
2724 subsolvers.push_back(absl::make_unique<LnsSolver>(
2725 absl::make_unique<ConstraintGraphNeighborhoodGenerator>(
2726 helper, absl::StrCat(
"cst_lns_", local_params.name())),
2727 local_params, helper, &shared));
2729 if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty()) {
2730 subsolvers.push_back(absl::make_unique<LnsSolver>(
2731 absl::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
2732 helper, absl::StrCat(
"scheduling_time_window_lns_",
2733 local_params.name())),
2734 local_params, helper, &shared));
2735 subsolvers.push_back(absl::make_unique<LnsSolver>(
2736 absl::make_unique<SchedulingNeighborhoodGenerator>(
2738 absl::StrCat(
"scheduling_random_lns_", local_params.name())),
2739 local_params, helper, &shared));
2752 subsolvers.push_back(absl::make_unique<LnsSolver>(
2753 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2754 helper, shared.response, shared.relaxation_solutions,
2755 shared.lp_solutions,
nullptr,
2756 absl::StrCat(
"rins_lns_", local_params.name())),
2757 local_params, helper, &shared));
2760 subsolvers.push_back(absl::make_unique<LnsSolver>(
2761 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2762 helper,
nullptr, shared.relaxation_solutions,
2763 shared.lp_solutions, shared.incomplete_solutions,
2764 absl::StrCat(
"rens_lns_", local_params.name())),
2765 local_params, helper, &shared));
2769 subsolvers.push_back(absl::make_unique<LnsSolver>(
2771 ConsecutiveConstraintsRelaxationNeighborhoodGenerator>(
2772 helper, absl::StrCat(
"rnd_rel_lns_", local_params.name())),
2773 local_params, helper, &shared));
2775 subsolvers.push_back(absl::make_unique<LnsSolver>(
2776 absl::make_unique<WeightedRandomRelaxationNeighborhoodGenerator>(
2777 helper, absl::StrCat(
"wgt_rel_lns_", local_params.name())),
2778 local_params, helper, &shared));
2785 subsolvers.push_back(
2786 absl::make_unique<SynchronizationPoint>([shared_response_manager]() {
2787 shared_response_manager->UpdatePrimalIntegral();
2792 std::vector<std::string> names;
2793 for (
const auto& subsolver : subsolvers) {
2794 if (!subsolver->name().empty()) names.push_back(subsolver->name());
2796 LOG(INFO) << absl::StrFormat(
2797 "*** starting Search at %.2fs with %i workers and subsolvers: [ %s ]",
2798 wall_timer->
Get(), num_search_workers, absl::StrJoin(names,
", "));
2810 #endif // __PORTABLE_PLATFORM__
2815 void AddPostsolveClauses(
const std::vector<int>& postsolve_mapping,
2816 Model*
model, CpModelProto* mapping_proto) {
2817 auto* mapping =
model->GetOrCreate<CpModelMapping>();
2818 auto* postsolve =
model->GetOrCreate<PostsolveClauses>();
2819 for (
const auto& clause : postsolve->clauses) {
2820 auto*
ct = mapping_proto->add_constraints()->mutable_bool_or();
2821 for (
const Literal l : clause) {
2822 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
2824 var = postsolve_mapping[
var];
2828 postsolve->clauses.clear();
2839 CpSolverResponse final_response;
2841 #if !defined(__PORTABLE_PLATFORM__)
2843 if (FLAGS_cp_model_dump_models) {
2844 const std::string
file =
2845 absl::StrCat(FLAGS_cp_model_dump_prefix,
"model.pbtxt");
2846 LOG(INFO) <<
"Dumping cp model proto to '" <<
file <<
"'.";
2851 if (FLAGS_cp_model_dump_response) {
2853 const std::string
file =
2854 absl::StrCat(FLAGS_cp_model_dump_prefix,
"response.pbtxt");
2855 LOG(INFO) <<
"Dumping response proto to '" <<
file <<
"'.";
2861 if (!FLAGS_cp_model_params.empty()) {
2862 SatParameters params = *
model->GetOrCreate<SatParameters>();
2863 SatParameters flag_params;
2864 CHECK(google::protobuf::TextFormat::ParseFromString(FLAGS_cp_model_params,
2866 params.MergeFrom(flag_params);
2872 if (
model->GetOrCreate<SatParameters>()->catch_sigint_signal()) {
2873 handler.
Register([&shared_time_limit]() { shared_time_limit.
Stop(); });
2875 #endif // __PORTABLE_PLATFORM__
2877 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
2878 const bool log_search = params.log_search_progress() || VLOG_IS_ON(1);
2879 LOG_IF(INFO, log_search) <<
"Parameters: " << params.ShortDebugString();
2882 absl::Cleanup<std::function<void()>> display_response_cleanup;
2884 display_response_cleanup =
2895 if (!error.empty()) {
2896 LOG_IF(INFO, log_search) << error;
2898 return final_response;
2907 if (!params.use_sat_inprocessing() && !
model_proto.has_objective() &&
2908 !
model_proto.has_solution_hint() && !params.enumerate_all_solutions() &&
2909 !params.use_lns_only() && params.num_search_workers() <= 1 &&
2911 bool is_pure_sat =
true;
2912 for (
const IntegerVariableProto&
var :
model_proto.variables()) {
2913 if (
var.domain_size() != 2 ||
var.domain(0) < 0 ||
var.domain(1) > 1) {
2914 is_pure_sat =
false;
2919 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
2920 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolOr &&
2921 ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolAnd) {
2922 is_pure_sat =
false;
2932 final_response.set_user_time(user_timer.
Get());
2933 final_response.set_deterministic_time(
2935 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
2936 if (params.fill_tightened_domains_in_response()) {
2937 *final_response.mutable_tightened_variables() =
model_proto.variables();
2939 return final_response;
2944 LOG_IF(INFO, log_search) << absl::StrFormat(
2948 CpModelProto mapping_proto;
2954 absl::make_unique<PresolveContext>(&new_cp_model_proto, &mapping_proto);
2956 bool degraded_assumptions_support =
false;
2957 if (params.num_search_workers() > 1 ||
model_proto.has_objective()) {
2964 degraded_assumptions_support =
true;
2965 context->InitializeNewDomains();
2967 if (!
context->SetLiteralToTrue(ref)) {
2969 final_response.add_sufficient_assumptions_for_infeasibility(ref);
2970 return final_response;
2977 std::function<void(CpSolverResponse *
response)> postprocess_solution;
2980 std::vector<int> postsolve_mapping;
2983 LOG(ERROR) <<
"Error while presolving, likely due to integer overflow.";
2985 return final_response;
2987 LOG_IF(INFO, log_search) <<
CpModelStats(new_cp_model_proto);
2988 if (params.cp_model_presolve()) {
2989 postprocess_solution = [&
model_proto, ¶ms, &mapping_proto,
2990 &shared_time_limit, &postsolve_mapping, &
wall_timer,
2992 AddPostsolveClauses(postsolve_mapping,
model, &mapping_proto);
2993 PostsolveResponseWrapper(params,
model_proto.variables_size(),
2994 mapping_proto, postsolve_mapping, &
wall_timer,
2996 if (!
response->solution().empty()) {
2999 std::vector<int64>(
response->solution().begin(),
3001 &mapping_proto, &postsolve_mapping))
3002 <<
"final postsolved solution";
3004 if (params.fill_tightened_domains_in_response()) {
3006 if (mapping_proto.variables().size() >=
3008 for (
int i = 0; i <
model_proto.variables().size(); ++i) {
3009 *
response->add_tightened_variables() = mapping_proto.variables(i);
3014 response->set_user_time(user_timer.Get());
3016 shared_time_limit.GetElapsedDeterministicTime());
3021 &user_timer](CpSolverResponse*
response) {
3023 const int initial_size =
model_proto.variables_size();
3024 if (
response->solution_size() > 0) {
3025 response->mutable_solution()->Truncate(initial_size);
3026 }
else if (
response->solution_lower_bounds_size() > 0) {
3027 response->mutable_solution_lower_bounds()->Truncate(initial_size);
3028 response->mutable_solution_upper_bounds()->Truncate(initial_size);
3030 if (params.fill_tightened_domains_in_response()) {
3036 shared_time_limit.GetElapsedDeterministicTime());
3044 log_search, params.enumerate_all_solutions(), &new_cp_model_proto,
3050 if (!observers.empty()) {
3053 &postprocess_solution, &shared_time_limit](
3054 const CpSolverResponse& response_of_presolved_problem) {
3055 CpSolverResponse
response = response_of_presolved_problem;
3057 if (!
response.solution().empty()) {
3058 if (
DEBUG_MODE || FLAGS_cp_model_check_intermediate_solutions) {
3065 for (
const auto& observer : observers) {
3071 #if !defined(__PORTABLE_PLATFORM__)
3072 if (FLAGS_cp_model_dump_models) {
3073 const std::string presolved_file =
3074 absl::StrCat(FLAGS_cp_model_dump_prefix,
"presolved_model.pbtxt");
3075 LOG(INFO) <<
"Dumping presolved cp model proto to '" << presolved_file
3080 const std::string mapping_file =
3081 absl::StrCat(FLAGS_cp_model_dump_prefix,
"mapping_model.pbtxt");
3082 LOG(INFO) <<
"Dumping mapping cp model proto to '" << mapping_file <<
"'.";
3085 #endif // __PORTABLE_PLATFORM__
3087 if (params.stop_after_presolve() || shared_time_limit.
LimitReached()) {
3088 int64 num_terms = 0;
3089 for (
const ConstraintProto&
ct : new_cp_model_proto.constraints()) {
3092 LOG_IF(INFO, log_search)
3093 <<
"Stopped after presolve."
3094 <<
"\nPresolvedNumVariables: " << new_cp_model_proto.variables().size()
3095 <<
"\nPresolvedNumConstraints: "
3096 << new_cp_model_proto.constraints().size()
3097 <<
"\nPresolvedNumTerms: " << num_terms;
3101 final_response = shared_response_manager.
GetResponse();
3102 postprocess_solution(&final_response);
3103 return final_response;
3107 if (params.stop_after_first_solution()) {
3109 [&shared_time_limit](
3110 const CpSolverResponse& response_of_presolved_problem) {
3111 shared_time_limit.
Stop();
3115 #if defined(__PORTABLE_PLATFORM__)
3118 #else // __PORTABLE_PLATFORM__
3119 if (params.num_search_workers() > 1 || params.interleave_search()) {
3120 SolveCpModelParallel(new_cp_model_proto, &shared_response_manager,
3122 #endif // __PORTABLE_PLATFORM__
3125 LOG(INFO) << absl::StrFormat(
"*** starting to load the model at %.2fs",
3128 LoadCpModel(new_cp_model_proto, &shared_response_manager,
model);
3131 LOG(INFO) << absl::StrFormat(
"*** starting sequential search at %.2fs",
3133 LOG(INFO) <<
"Initial num_bool: "
3136 if (params.repair_hint()) {
3137 MinimizeL1DistanceWithHint(new_cp_model_proto, &shared_response_manager,
3140 QuickSolveWithHint(new_cp_model_proto, &shared_response_manager,
model);
3142 SolveLoadedCpModel(new_cp_model_proto, &shared_response_manager,
model);
3145 final_response = shared_response_manager.
GetResponse();
3146 postprocess_solution(&final_response);
3147 if (!final_response.solution().empty()) {
3149 model_proto, std::vector<int64>(final_response.solution().begin(),
3150 final_response.solution().end())));
3152 if (degraded_assumptions_support &&
3155 *final_response.mutable_sufficient_assumptions_for_infeasibility() =
3158 return final_response;
3167 const SatParameters& params) {
3173 #if !defined(__PORTABLE_PLATFORM__)
3175 const std::string& params) {
3180 #endif // !__PORTABLE_PLATFORM__