33 #if !defined(__PORTABLE_PLATFORM__)
38 #include "absl/base/thread_annotations.h"
39 #include "absl/container/btree_map.h"
40 #include "absl/container/btree_set.h"
41 #include "absl/container/flat_hash_set.h"
42 #include "absl/flags/flag.h"
43 #include "absl/status/status.h"
44 #include "absl/strings/str_cat.h"
45 #include "absl/strings/str_format.h"
46 #include "absl/strings/str_join.h"
47 #include "absl/strings/str_split.h"
48 #include "absl/strings/string_view.h"
49 #include "absl/synchronization/mutex.h"
50 #include "absl/types/span.h"
55 #include "ortools/sat/cp_model.pb.h"
88 #include "ortools/sat/sat_parameters.pb.h"
96 #if !defined(__PORTABLE_PLATFORM__)
104 #if defined(_MSC_VER)
105 ABSL_FLAG(std::string, cp_model_dump_prefix,
".\\",
106 "Prefix filename for all dumped files");
109 "Prefix filename for all dumped files");
112 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
113 "protos (original model, presolved model, mapping model) in text "
114 "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|"
115 "mapping_model}.pb.txt.");
118 "DEBUG ONLY, dump models in text proto instead of binary proto.");
121 "DEBUG ONLY. When set to true, solve will dump all "
122 "lns models proto in text format to "
123 "'FLAGS_cp_model_dump_prefix'lns_xxx.pb.txt.");
126 bool, cp_model_dump_problematic_lns,
false,
127 "DEBUG ONLY. Similar to --cp_model_dump_lns, but only dump fragment for "
128 "which we got an issue while validating the postsolved solution. This "
129 "allows to debug presolve issues without dumping all the models.");
132 "DEBUG ONLY. If true, the final response of each solve will be "
133 "dumped to 'FLAGS_cp_model_dump_prefix'response.pb.txt");
136 "This is interpreted as a text SatParameters proto. The "
137 "specified fields will override the normal ones for all solves.");
140 "If non-empty, a proof in DRAT format will be written to this file. "
141 "This will only be used for pure-SAT problems.");
144 "If true, a proof in DRAT format will be stored in memory and "
145 "checked if the problem is UNSAT. This will only be used for "
146 "pure-SAT problems.");
149 std::numeric_limits<double>::infinity(),
150 "Maximum time in seconds to check the DRAT proof. This will only "
151 "be used is the drat_check flag is enabled.");
153 ABSL_FLAG(
bool, cp_model_check_intermediate_solutions,
false,
154 "When true, all intermediate solutions found by the solver will be "
155 "checked. This can be expensive, therefore it is off by default.");
158 std::string, cp_model_load_debug_solution,
"",
159 "DEBUG ONLY. When this is set to a non-empty file name, "
160 "we will interpret this as an internal solution which can be used for "
161 "debugging. For instance we use it to identify wrong cuts/reasons.");
164 "If true, ignore the objective.");
165 ABSL_FLAG(
bool, cp_model_fingerprint_model,
true,
"Fingerprint the model.");
177 std::string Summarize(
const std::string&
input) {
180 return absl::StrCat(
input.substr(0, half),
" ... ",
185 void DumpModelProto(
const M&
proto,
const std::string&
name) {
186 std::string filename;
187 if (absl::GetFlag(FLAGS_cp_model_dump_text_proto)) {
188 filename = absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
name,
190 LOG(INFO) <<
"Dumping " <<
name <<
" text proto to '" << filename <<
"'.";
192 const std::string filename =
193 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
name,
".bin");
194 LOG(INFO) <<
"Dumping " <<
name <<
" binary proto to '" << filename <<
"'.";
206 absl::btree_map<std::string, int> num_constraints_by_name;
207 absl::btree_map<std::string, int> num_reif_constraints_by_name;
208 absl::btree_map<std::string, int> num_multi_reif_constraints_by_name;
209 absl::btree_map<std::string, int> name_to_num_literals;
210 absl::btree_map<std::string, int> name_to_num_terms;
211 absl::btree_map<std::string, int> name_to_num_complex_domain;
212 absl::btree_map<std::string, int> name_to_num_expressions;
214 int no_overlap_2d_num_rectangles = 0;
215 int no_overlap_2d_num_optional_rectangles = 0;
216 int no_overlap_2d_num_linear_areas = 0;
217 int no_overlap_2d_num_quadratic_areas = 0;
219 int cumulative_num_intervals = 0;
220 int cumulative_num_optional_intervals = 0;
221 int cumulative_num_variable_sizes = 0;
222 int cumulative_num_variable_demands = 0;
224 int no_overlap_num_intervals = 0;
225 int no_overlap_num_optional_intervals = 0;
226 int no_overlap_num_variable_sizes = 0;
228 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
233 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
234 if (
ct.linear().vars_size() == 0)
name +=
"0";
235 if (
ct.linear().vars_size() == 1)
name +=
"1";
236 if (
ct.linear().vars_size() == 2)
name +=
"2";
237 if (
ct.linear().vars_size() == 3)
name +=
"3";
238 if (
ct.linear().vars_size() > 3)
name +=
"N";
241 num_constraints_by_name[
name]++;
242 if (!
ct.enforcement_literal().empty()) {
243 num_reif_constraints_by_name[
name]++;
244 if (
ct.enforcement_literal().size() > 1) {
245 num_multi_reif_constraints_by_name[
name]++;
250 const IntegerVariableProto&
proto =
255 auto expression_is_fixed =
256 [&variable_is_fixed](
const LinearExpressionProto& expr) {
257 for (
const int ref : expr.vars()) {
258 if (!variable_is_fixed(ref)) {
265 auto interval_has_fixed_size = [&
model_proto, &expression_is_fixed](
int c) {
266 return expression_is_fixed(
model_proto.constraints(c).interval().size());
269 auto constraint_is_optional = [&
model_proto](
int i) {
270 return !
model_proto.constraints(i).enforcement_literal().empty();
275 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
276 name_to_num_literals[
name] +=
ct.bool_or().literals().size();
277 }
else if (
ct.constraint_case() ==
278 ConstraintProto::ConstraintCase::kBoolAnd) {
279 name_to_num_literals[
name] +=
280 ct.enforcement_literal().size() +
ct.bool_and().literals().size();
281 }
else if (
ct.constraint_case() ==
282 ConstraintProto::ConstraintCase::kAtMostOne) {
283 name_to_num_literals[
name] +=
ct.at_most_one().literals().size();
284 }
else if (
ct.constraint_case() ==
285 ConstraintProto::ConstraintCase::kExactlyOne) {
286 name_to_num_literals[
name] +=
ct.exactly_one().literals().size();
287 }
else if (
ct.constraint_case() ==
288 ConstraintProto::ConstraintCase::kLinMax) {
289 name_to_num_expressions[
name] +=
ct.lin_max().exprs().size();
290 }
else if (
ct.constraint_case() ==
291 ConstraintProto::ConstraintCase::kNoOverlap2D) {
292 const int num_boxes =
ct.no_overlap_2d().x_intervals_size();
293 no_overlap_2d_num_rectangles += num_boxes;
294 for (
int i = 0; i < num_boxes; ++i) {
295 const int x_interval =
ct.no_overlap_2d().x_intervals(i);
296 const int y_interval =
ct.no_overlap_2d().y_intervals(i);
297 if (constraint_is_optional(x_interval) ||
298 constraint_is_optional(y_interval)) {
299 no_overlap_2d_num_optional_rectangles++;
301 const int num_fixed = interval_has_fixed_size(x_interval) +
302 interval_has_fixed_size(y_interval);
303 if (num_fixed == 0) {
304 no_overlap_2d_num_quadratic_areas++;
305 }
else if (num_fixed == 1) {
306 no_overlap_2d_num_linear_areas++;
309 }
else if (
ct.constraint_case() ==
310 ConstraintProto::ConstraintCase::kNoOverlap) {
311 const int num_intervals =
ct.no_overlap().intervals_size();
312 no_overlap_num_intervals += num_intervals;
313 for (
int i = 0; i < num_intervals; ++i) {
314 const int interval =
ct.no_overlap().intervals(i);
315 if (constraint_is_optional(
interval)) {
316 no_overlap_num_optional_intervals++;
318 if (!interval_has_fixed_size(
interval)) {
319 no_overlap_num_variable_sizes++;
322 }
else if (
ct.constraint_case() ==
323 ConstraintProto::ConstraintCase::kCumulative) {
324 const int num_intervals =
ct.cumulative().intervals_size();
325 cumulative_num_intervals += num_intervals;
326 for (
int i = 0; i < num_intervals; ++i) {
327 const int interval =
ct.cumulative().intervals(i);
328 if (constraint_is_optional(
interval)) {
329 cumulative_num_optional_intervals++;
331 if (!interval_has_fixed_size(
interval)) {
332 cumulative_num_variable_sizes++;
334 if (!expression_is_fixed(
ct.cumulative().demands(i))) {
335 cumulative_num_variable_demands++;
340 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
341 ct.linear().vars_size() > 3) {
342 name_to_num_terms[
name] +=
ct.linear().vars_size();
344 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
345 ct.linear().vars_size() > 1 &&
ct.linear().domain().size() > 2) {
346 name_to_num_complex_domain[
name]++;
350 int num_constants = 0;
351 absl::btree_set<int64_t> constant_values;
352 absl::btree_map<Domain, int> num_vars_per_domains;
354 if (
var.domain_size() == 2 &&
var.domain(0) ==
var.domain(1)) {
356 constant_values.insert(
var.domain(0));
363 const std::string model_fingerprint_str =
364 (absl::GetFlag(FLAGS_cp_model_fingerprint_model))
365 ? absl::StrFormat(
" (model_fingerprint: %#x)",
371 absl::StrAppend(&result,
"optimization model '",
model_proto.name(),
372 "':", model_fingerprint_str,
"\n");
374 absl::StrAppend(&result,
"satisfaction model '",
model_proto.name(),
375 "':", model_fingerprint_str,
"\n");
378 for (
const DecisionStrategyProto& strategy :
model_proto.search_strategy()) {
380 &result,
"Search strategy: on ", strategy.variables_size(),
382 ProtoEnumToString<DecisionStrategyProto::VariableSelectionStrategy>(
383 strategy.variable_selection_strategy()),
385 ProtoEnumToString<DecisionStrategyProto::DomainReductionStrategy>(
386 strategy.domain_reduction_strategy()),
390 auto count_variables_by_type =
391 [&
model_proto](
const google::protobuf::RepeatedField<int>& vars,
392 int* num_booleans,
int* num_integers) {
393 for (
const int ref : vars) {
395 if (var_proto.domain_size() == 2 && var_proto.domain(0) == 0 &&
396 var_proto.domain(1) == 1) {
400 *num_integers = vars.size() - *num_booleans;
404 int num_boolean_variables_in_objective = 0;
405 int num_integer_variables_in_objective = 0;
407 count_variables_by_type(
model_proto.objective().vars(),
408 &num_boolean_variables_in_objective,
409 &num_integer_variables_in_objective);
412 count_variables_by_type(
model_proto.floating_point_objective().vars(),
413 &num_boolean_variables_in_objective,
414 &num_integer_variables_in_objective);
417 std::vector<std::string> obj_vars_strings;
418 if (num_boolean_variables_in_objective > 0) {
419 obj_vars_strings.push_back(
420 absl::StrCat(
"#bools:", num_boolean_variables_in_objective));
422 if (num_integer_variables_in_objective > 0) {
423 obj_vars_strings.push_back(
424 absl::StrCat(
"#ints:", num_integer_variables_in_objective));
427 const std::string objective_string =
429 ? absl::StrCat(
" (", absl::StrJoin(obj_vars_strings,
" "),
432 ? absl::StrCat(
" (", absl::StrJoin(obj_vars_strings,
" "),
433 " in floating point objective)")
435 absl::StrAppend(&result,
"#Variables: ",
model_proto.variables_size(),
436 objective_string,
"\n");
438 if (num_vars_per_domains.contains(
Domain(0, 1))) {
440 const int num_bools = num_vars_per_domains[
Domain(0, 1)];
441 const std::string temp = absl::StrCat(
" - ", num_bools,
" Booleans in ",
443 absl::StrAppend(&result, Summarize(temp));
444 num_vars_per_domains.erase(
Domain(0, 1));
446 if (num_vars_per_domains.size() < 100) {
447 for (
const auto& entry : num_vars_per_domains) {
448 const std::string temp = absl::StrCat(
" - ", entry.second,
" in ",
449 entry.first.ToString(),
"\n");
450 absl::StrAppend(&result, Summarize(temp));
453 int64_t max_complexity = 0;
456 for (
const auto& entry : num_vars_per_domains) {
460 max_complexity,
static_cast<int64_t
>(entry.first.NumIntervals()));
462 absl::StrAppend(&result,
" - ", num_vars_per_domains.size(),
463 " different domains in [",
min,
",",
max,
464 "] with a largest complexity of ", max_complexity,
".\n");
467 if (num_constants > 0) {
468 const std::string temp =
469 absl::StrCat(
" - ", num_constants,
" constants in {",
470 absl::StrJoin(constant_values,
","),
"} \n");
471 absl::StrAppend(&result, Summarize(temp));
474 std::vector<std::string> constraints;
475 constraints.reserve(num_constraints_by_name.size());
476 for (
const auto& entry : num_constraints_by_name) {
477 const std::string&
name = entry.first;
478 constraints.push_back(absl::StrCat(
"#",
name,
": ", entry.second));
479 if (num_reif_constraints_by_name.contains(
name)) {
480 if (num_multi_reif_constraints_by_name.contains(
name)) {
481 absl::StrAppend(&constraints.back(),
482 " (#enforced: ", num_reif_constraints_by_name[
name],
483 " #multi: ", num_multi_reif_constraints_by_name[
name],
486 absl::StrAppend(&constraints.back(),
487 " (#enforced: ", num_reif_constraints_by_name[
name],
491 if (name_to_num_literals.contains(
name)) {
492 absl::StrAppend(&constraints.back(),
493 " (#literals: ", name_to_num_literals[
name],
")");
495 if (name_to_num_terms.contains(
name)) {
496 absl::StrAppend(&constraints.back(),
497 " (#terms: ", name_to_num_terms[
name],
")");
499 if (name_to_num_expressions.contains(
name)) {
500 absl::StrAppend(&constraints.back(),
501 " (#expressions: ", name_to_num_expressions[
name],
")");
503 if (name_to_num_complex_domain.contains(
name)) {
504 absl::StrAppend(&constraints.back(),
505 " (#complex_domain: ", name_to_num_complex_domain[
name],
508 if (
name ==
"kNoOverlap2D") {
509 absl::StrAppend(&constraints.back(),
510 " (#rectangles: ", no_overlap_2d_num_rectangles);
511 if (no_overlap_2d_num_optional_rectangles > 0) {
512 absl::StrAppend(&constraints.back(),
513 ", #optional: ", no_overlap_2d_num_optional_rectangles);
515 if (no_overlap_2d_num_linear_areas > 0) {
516 absl::StrAppend(&constraints.back(),
517 ", #linear_areas: ", no_overlap_2d_num_linear_areas);
519 if (no_overlap_2d_num_quadratic_areas > 0) {
520 absl::StrAppend(&constraints.back(),
", #quadratic_areas: ",
521 no_overlap_2d_num_quadratic_areas);
523 absl::StrAppend(&constraints.back(),
")");
524 }
else if (
name ==
"kCumulative") {
525 absl::StrAppend(&constraints.back(),
526 " (#intervals: ", cumulative_num_intervals);
527 if (cumulative_num_optional_intervals > 0) {
528 absl::StrAppend(&constraints.back(),
529 ", #optional: ", cumulative_num_optional_intervals);
531 if (cumulative_num_variable_sizes > 0) {
532 absl::StrAppend(&constraints.back(),
533 ", #variable_sizes: ", cumulative_num_variable_sizes);
535 if (cumulative_num_variable_demands > 0) {
536 absl::StrAppend(&constraints.back(),
", #variable_demands: ",
537 cumulative_num_variable_demands);
539 absl::StrAppend(&constraints.back(),
")");
540 }
else if (
name ==
"kNoOverlap") {
541 absl::StrAppend(&constraints.back(),
542 " (#intervals: ", no_overlap_num_intervals);
543 if (no_overlap_num_optional_intervals > 0) {
544 absl::StrAppend(&constraints.back(),
545 ", #optional: ", no_overlap_num_optional_intervals);
547 if (no_overlap_num_variable_sizes > 0) {
548 absl::StrAppend(&constraints.back(),
549 ", #variable_sizes: ", no_overlap_num_variable_sizes);
551 absl::StrAppend(&constraints.back(),
")");
554 std::sort(constraints.begin(), constraints.end());
555 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
561 bool has_objective) {
563 absl::StrAppend(&result,
"CpSolverResponse summary:");
564 absl::StrAppend(&result,
"\nstatus: ",
565 ProtoEnumToString<CpSolverStatus>(
response.status()));
568 absl::StrAppendFormat(&result,
"\nobjective: %.16g",
570 absl::StrAppendFormat(&result,
"\nbest_bound: %.16g",
573 absl::StrAppend(&result,
"\nobjective: NA");
574 absl::StrAppend(&result,
"\nbest_bound: NA");
577 absl::StrAppend(&result,
"\nintegers: ",
response.num_integers());
578 absl::StrAppend(&result,
"\nbooleans: ",
response.num_booleans());
579 absl::StrAppend(&result,
"\nconflicts: ",
response.num_conflicts());
580 absl::StrAppend(&result,
"\nbranches: ",
response.num_branches());
584 absl::StrAppend(&result,
585 "\npropagations: ",
response.num_binary_propagations());
587 &result,
"\ninteger_propagations: ",
response.num_integer_propagations());
589 absl::StrAppend(&result,
"\nrestarts: ",
response.num_restarts());
590 absl::StrAppend(&result,
"\nlp_iterations: ",
response.num_lp_iterations());
591 absl::StrAppend(&result,
"\nwalltime: ",
response.wall_time());
592 absl::StrAppend(&result,
"\nusertime: ",
response.user_time());
593 absl::StrAppend(&result,
594 "\ndeterministic_time: ",
response.deterministic_time());
595 absl::StrAppend(&result,
"\ngap_integral: ",
response.gap_integral());
597 absl::StrAppendFormat(
598 &result,
"\nsolution_fingerprint: %#x",
601 absl::StrAppend(&result,
"\n");
607 #if !defined(__PORTABLE_PLATFORM__)
614 #if !defined(__PORTABLE_PLATFORM__)
615 if (absl::GetFlag(FLAGS_cp_model_load_debug_solution).empty())
return;
619 "Reading debug solution from '",
620 absl::GetFlag(FLAGS_cp_model_load_debug_solution),
"'.");
627 model->GetOrCreate<SharedResponseManager>()->LoadDebugSolution(
634 void InitializeDebugSolution(
const CpModelProto&
model_proto, Model*
model) {
635 auto* shared_response =
model->Get<SharedResponseManager>();
636 if (shared_response ==
nullptr)
return;
637 if (shared_response->DebugSolution().empty())
return;
640 DebugSolution& debug_sol = *
model->GetOrCreate<DebugSolution>();
641 debug_sol.proto_values = shared_response->DebugSolution();
644 const int num_integers =
645 model->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
646 debug_sol.ivar_has_value.assign(num_integers,
false);
647 debug_sol.ivar_values.assign(num_integers, 0);
649 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
650 for (
int i = 0; i < debug_sol.proto_values.size(); ++i) {
651 if (!mapping.IsInteger(i))
continue;
652 const IntegerVariable
var = mapping.Integer(i);
653 debug_sol.ivar_has_value[
var] =
true;
655 debug_sol.ivar_values[
var] = debug_sol.proto_values[i];
656 debug_sol.ivar_values[
NegationOf(
var)] = -debug_sol.proto_values[i];
661 auto* objective_def =
model->Get<ObjectiveDefinition>();
662 if (objective_def !=
nullptr) {
663 const IntegerVariable objective_var = objective_def->objective_var;
666 debug_sol.ivar_has_value[objective_var] =
true;
667 debug_sol.ivar_has_value[
NegationOf(objective_var)] =
true;
673 auto* encoder =
model->GetOrCreate<IntegerEncoder>();
674 const auto checker = [mapping, encoder, debug_sol,
model](
675 absl::Span<const Literal> clause,
676 absl::Span<const IntegerLiteral> integers) {
677 bool is_satisfied =
false;
680 std::vector<std::tuple<Literal, IntegerLiteral, int>> to_print;
681 for (
const Literal l : clause) {
684 const int proto_var =
685 mapping.GetProtoVariableFromBooleanVariable(l.Variable());
686 if (proto_var != -1) {
687 to_print.push_back({l, IntegerLiteral(), proto_var});
688 if (debug_sol.proto_values[proto_var] == (l.IsPositive() ? 1 : 0)) {
699 bool all_true =
true;
700 for (
const IntegerLiteral associated : encoder->GetIntegerLiterals(l)) {
701 const int proto_var = mapping.GetProtoVariableFromIntegerVariable(
703 if (proto_var == -1)
break;
704 int64_t
value = debug_sol.proto_values[proto_var];
705 to_print.push_back({l, associated, proto_var});
708 if (
value < associated.bound) {
719 for (
const IntegerLiteral i_lit : integers) {
720 const int proto_var = mapping.GetProtoVariableFromIntegerVariable(
722 if (proto_var == -1) {
727 int64_t
value = debug_sol.proto_values[proto_var];
733 if (
value >= i_lit.bound) {
739 LOG(INFO) <<
"Reason clause is not satisfied by loaded solution:";
740 LOG(INFO) <<
"Worker '" <<
model->Name() <<
"', level="
741 <<
model->GetOrCreate<SatSolver>()->CurrentDecisionLevel();
742 LOG(INFO) <<
"literals (neg): " << clause;
743 LOG(INFO) <<
"integer literals: " << integers;
744 for (
const auto [l, i_lit, proto_var] : to_print) {
745 LOG(INFO) << l <<
" " << i_lit <<
" var=" << proto_var
746 <<
" value_in_sol=" << debug_sol.proto_values[proto_var];
751 const auto lit_checker = [checker](absl::Span<const Literal> clause) {
752 return checker(clause, {});
755 model->GetOrCreate<Trail>()->RegisterDebugChecker(lit_checker);
756 model->GetOrCreate<IntegerTrail>()->RegisterDebugChecker(checker);
759 std::vector<int64_t> GetSolutionValues(
const CpModelProto&
model_proto,
760 const Model&
model) {
761 auto* mapping =
model.Get<CpModelMapping>();
762 auto* trail =
model.Get<Trail>();
764 std::vector<int64_t> solution;
765 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
766 if (mapping->IsInteger(i)) {
767 const IntegerVariable
var = mapping->Integer(i);
773 DCHECK(mapping->IsBoolean(i));
775 if (trail->Assignment().LiteralIsAssigned(
literal)) {
779 solution.push_back(0);
785 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
794 IntegerVariable GetOrCreateVariableWithTightBound(
795 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
798 if (terms.size() == 1 && terms.front().second == 1) {
799 return terms.front().first;
801 if (terms.size() == 1 && terms.front().second == -1) {
807 for (
const std::pair<IntegerVariable, int64_t>& var_coeff : terms) {
810 const int64_t coeff = var_coeff.second;
811 const int64_t prod1 = min_domain * coeff;
812 const int64_t prod2 = max_domain * coeff;
819 IntegerVariable GetOrCreateVariableLinkedToSumOf(
820 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
821 bool use_equality, Model*
model) {
823 if (terms.size() == 1 && terms.front().second == 1) {
824 return terms.front().first;
826 if (terms.size() == 1 && terms.front().second == -1) {
831 const IntegerVariable new_var =
832 GetOrCreateVariableWithTightBound(terms,
model);
835 std::vector<IntegerVariable> vars;
836 std::vector<int64_t> coeffs;
837 for (
const auto [
var, coeff] : terms) {
839 coeffs.push_back(coeff);
841 vars.push_back(new_var);
842 coeffs.push_back(-1);
845 const bool lb_required = use_equality;
846 const bool ub_required =
true;
864 IntegerVariable AddLPConstraints(
bool objective_need_to_be_tight,
874 const int num_lp_constraints = relaxation.linear_constraints.size();
875 const int num_lp_cut_generators = relaxation.cut_generators.size();
876 const int num_integer_variables =
877 m->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
881 num_integer_variables);
882 auto get_constraint_index = [](
int ct_index) {
return ct_index; };
883 auto get_cut_generator_index = [num_lp_constraints](
int cut_index) {
884 return num_lp_constraints + cut_index;
886 auto get_var_index = [num_lp_constraints,
887 num_lp_cut_generators](IntegerVariable
var) {
888 return num_lp_constraints + num_lp_cut_generators +
891 for (
int i = 0; i < num_lp_constraints; i++) {
892 for (
const IntegerVariable
var : relaxation.linear_constraints[i].vars) {
893 components.
AddEdge(get_constraint_index(i), get_var_index(
var));
896 for (
int i = 0; i < num_lp_cut_generators; ++i) {
897 for (
const IntegerVariable
var : relaxation.cut_generators[i].vars) {
898 components.
AddEdge(get_cut_generator_index(i), get_var_index(
var));
903 std::vector<int> component_sizes(num_components, 0);
904 const std::vector<int> index_to_component = components.
GetComponentIds();
905 for (
int i = 0; i < num_lp_constraints; i++) {
906 ++component_sizes[index_to_component[get_constraint_index(i)]];
908 for (
int i = 0; i < num_lp_cut_generators; i++) {
909 ++component_sizes[index_to_component[get_cut_generator_index(i)]];
913 std::vector<std::vector<IntegerVariable>> component_to_var(num_components);
914 for (IntegerVariable
var(0);
var < num_integer_variables;
var += 2) {
916 component_to_var[index_to_component[get_var_index(
var)]].push_back(
var);
924 auto* mapping = m->GetOrCreate<CpModelMapping>();
925 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
926 const IntegerVariable
var =
928 ++component_sizes[index_to_component[get_var_index(
var)]];
932 std::vector<LinearProgrammingConstraint*> lp_constraints(num_components,
934 std::vector<std::vector<LinearConstraint>> component_to_constraints(
936 for (
int i = 0; i < num_lp_constraints; i++) {
937 const int c = index_to_component[get_constraint_index(i)];
938 if (component_sizes[c] <= 1)
continue;
939 component_to_constraints[c].push_back(relaxation.linear_constraints[i]);
940 if (lp_constraints[c] ==
nullptr) {
942 new LinearProgrammingConstraint(m, component_to_var[c]);
943 m->TakeOwnership(lp_constraints[c]);
946 lp_constraints[c]->AddLinearConstraint(relaxation.linear_constraints[i]);
950 for (
int i = 0; i < num_lp_cut_generators; i++) {
951 const int c = index_to_component[get_cut_generator_index(i)];
952 if (lp_constraints[c] ==
nullptr) {
954 new LinearProgrammingConstraint(m, component_to_var[c]);
955 m->TakeOwnership(lp_constraints[c]);
957 lp_constraints[c]->AddCutGenerator(std::move(relaxation.cut_generators[i]));
961 std::vector<std::vector<std::pair<IntegerVariable, int64_t>>>
962 component_to_cp_terms(num_components);
963 std::vector<std::pair<IntegerVariable, int64_t>> top_level_cp_terms;
964 int num_components_containing_objective = 0;
968 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
969 const IntegerVariable
var =
971 const int64_t coeff =
model_proto.objective().coeffs(i);
972 const int c = index_to_component[get_var_index(
var)];
973 if (lp_constraints[c] !=
nullptr) {
974 lp_constraints[c]->SetObjectiveCoefficient(
var, IntegerValue(coeff));
975 component_to_cp_terms[c].push_back(std::make_pair(
var, coeff));
978 top_level_cp_terms.push_back(std::make_pair(
var, coeff));
982 for (
int c = 0; c < num_components; ++c) {
983 if (component_to_cp_terms[c].empty())
continue;
984 const IntegerVariable sub_obj_var = GetOrCreateVariableLinkedToSumOf(
985 component_to_cp_terms[c], objective_need_to_be_tight, m);
986 top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
987 lp_constraints[c]->SetMainObjectiveVariable(sub_obj_var);
988 num_components_containing_objective++;
992 const IntegerVariable main_objective_var =
994 ? GetOrCreateVariableLinkedToSumOf(top_level_cp_terms,
995 objective_need_to_be_tight, m)
1000 for (LinearProgrammingConstraint* lp_constraint : lp_constraints) {
1001 if (lp_constraint ==
nullptr)
continue;
1002 lp_constraint->RegisterWith(m);
1003 VLOG(3) <<
"LP constraint: " << lp_constraint->DimensionString() <<
".";
1006 VLOG(3) << top_level_cp_terms.size()
1007 <<
" terms in the main objective linear equation ("
1008 << num_components_containing_objective <<
" from LP constraints).";
1009 return main_objective_var;
1020 const std::function<
void(
const CpSolverResponse&
response)>& observer) {
1026 #if !defined(__PORTABLE_PLATFORM__)
1029 const std::string& params) {
1031 if (!params.empty()) {
1032 CHECK(google::protobuf::TextFormat::ParseFromString(params, &
parameters))
1059 void RegisterVariableBoundsLevelZeroExport(
1060 const CpModelProto& ,
1061 SharedBoundsManager* shared_bounds_manager, Model*
model) {
1062 CHECK(shared_bounds_manager !=
nullptr);
1064 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1066 auto* integer_trail =
model->Get<IntegerTrail>();
1068 int saved_trail_index = 0;
1069 std::vector<int> model_variables;
1070 std::vector<int64_t> new_lower_bounds;
1071 std::vector<int64_t> new_upper_bounds;
1072 absl::flat_hash_set<int> visited_variables;
1074 auto broadcast_level_zero_bounds =
1075 [=](
const std::vector<IntegerVariable>& modified_vars)
mutable {
1077 for (
const IntegerVariable&
var : modified_vars) {
1079 const int model_var =
1080 mapping->GetProtoVariableFromIntegerVariable(positive_var);
1082 if (model_var == -1)
continue;
1083 const auto [_, inserted] = visited_variables.insert(model_var);
1084 if (!inserted)
continue;
1086 const int64_t new_lb =
1087 integer_trail->LevelZeroLowerBound(positive_var).value();
1088 const int64_t new_ub =
1089 integer_trail->LevelZeroUpperBound(positive_var).value();
1093 model_variables.push_back(model_var);
1094 new_lower_bounds.push_back(new_lb);
1095 new_upper_bounds.push_back(new_ub);
1099 for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
1100 const Literal fixed_literal = (*trail)[saved_trail_index];
1101 const int model_var = mapping->GetProtoVariableFromBooleanVariable(
1102 fixed_literal.Variable());
1104 if (model_var == -1)
continue;
1105 const auto [_, inserted] = visited_variables.insert(model_var);
1106 if (!inserted)
continue;
1108 model_variables.push_back(model_var);
1109 if (fixed_literal.IsPositive()) {
1110 new_lower_bounds.push_back(1);
1111 new_upper_bounds.push_back(1);
1113 new_lower_bounds.push_back(0);
1114 new_upper_bounds.push_back(0);
1118 if (!model_variables.empty()) {
1119 shared_bounds_manager->ReportPotentialNewBounds(
1120 model->Name(), model_variables, new_lower_bounds,
1124 model_variables.clear();
1125 new_lower_bounds.clear();
1126 new_upper_bounds.clear();
1127 visited_variables.clear();
1130 if (!
model->Get<SatParameters>()->interleave_search()) {
1131 shared_bounds_manager->Synchronize();
1143 const IntegerVariable num_vars =
1144 model->GetOrCreate<IntegerTrail>()->NumIntegerVariables();
1145 std::vector<IntegerVariable> all_variables;
1146 all_variables.reserve(num_vars.value());
1147 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
1148 all_variables.push_back(
var);
1150 broadcast_level_zero_bounds(all_variables);
1152 model->GetOrCreate<GenericLiteralWatcher>()
1153 ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
1159 void RegisterVariableBoundsLevelZeroImport(
1160 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
1162 CHECK(shared_bounds_manager !=
nullptr);
1163 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1164 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
1165 const int id = shared_bounds_manager->RegisterNewId();
1167 const auto& import_level_zero_bounds = [&
model_proto, shared_bounds_manager,
1168 model, integer_trail, id, mapping]() {
1169 std::vector<int> model_variables;
1170 std::vector<int64_t> new_lower_bounds;
1171 std::vector<int64_t> new_upper_bounds;
1172 shared_bounds_manager->GetChangedBounds(
1173 id, &model_variables, &new_lower_bounds, &new_upper_bounds);
1174 bool new_bounds_have_been_imported =
false;
1175 for (
int i = 0; i < model_variables.size(); ++i) {
1176 const int model_var = model_variables[i];
1179 if (!mapping->IsInteger(model_var))
continue;
1180 const IntegerVariable
var = mapping->Integer(model_var);
1181 const IntegerValue new_lb(new_lower_bounds[i]);
1182 const IntegerValue new_ub(new_upper_bounds[i]);
1183 const IntegerValue old_lb = integer_trail->LowerBound(
var);
1184 const IntegerValue old_ub = integer_trail->UpperBound(
var);
1185 const bool changed_lb = new_lb > old_lb;
1186 const bool changed_ub = new_ub < old_ub;
1187 if (!changed_lb && !changed_ub)
continue;
1189 new_bounds_have_been_imported =
true;
1191 const IntegerVariableProto& var_proto =
1193 const std::string& var_name =
1194 var_proto.name().empty()
1195 ? absl::StrCat(
"anonymous_var(", model_var,
")")
1197 LOG(INFO) <<
" '" <<
model->Name() <<
"' imports new bounds for "
1198 << var_name <<
": from [" << old_lb <<
", " << old_ub
1199 <<
"] to [" << new_lb <<
", " << new_ub <<
"]";
1213 if (new_bounds_have_been_imported &&
1214 !
model->GetOrCreate<SatSolver>()->FinishPropagation()) {
1219 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1220 import_level_zero_bounds);
1225 void RegisterObjectiveBestBoundExport(
1226 IntegerVariable objective_var,
1227 SharedResponseManager* shared_response_manager, Model*
model) {
1228 auto* integer_trail =
model->Get<IntegerTrail>();
1229 const auto broadcast_objective_lower_bound =
1230 [objective_var, integer_trail, shared_response_manager,
model,
1233 const IntegerValue objective_lb =
1234 integer_trail->LevelZeroLowerBound(objective_var);
1235 if (objective_lb > best_obj_lb) {
1236 best_obj_lb = objective_lb;
1237 shared_response_manager->UpdateInnerObjectiveBounds(
1238 model->Name(), objective_lb,
1239 integer_trail->LevelZeroUpperBound(objective_var));
1241 if (!
model->Get<SatParameters>()->interleave_search()) {
1242 shared_response_manager->Synchronize();
1246 model->GetOrCreate<GenericLiteralWatcher>()
1247 ->RegisterLevelZeroModifiedVariablesCallback(
1248 broadcast_objective_lower_bound);
1254 void RegisterObjectiveBoundsImport(
1255 SharedResponseManager* shared_response_manager, Model*
model) {
1256 auto* solver =
model->GetOrCreate<SatSolver>();
1257 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1258 auto* objective =
model->GetOrCreate<ObjectiveDefinition>();
1260 const auto import_objective_bounds = [
name, solver, integer_trail, objective,
1261 shared_response_manager]() {
1262 if (solver->AssumptionLevel() != 0)
return true;
1263 bool propagate =
false;
1265 const IntegerValue external_lb =
1266 shared_response_manager->SynchronizedInnerObjectiveLowerBound();
1267 const IntegerValue current_lb =
1268 integer_trail->LowerBound(objective->objective_var);
1269 if (external_lb > current_lb) {
1271 objective->objective_var, external_lb),
1278 const IntegerValue external_ub =
1279 shared_response_manager->SynchronizedInnerObjectiveUpperBound();
1280 const IntegerValue current_ub =
1281 integer_trail->UpperBound(objective->objective_var);
1282 if (external_ub < current_ub) {
1284 objective->objective_var, external_ub),
1291 if (!propagate)
return true;
1293 VLOG(3) <<
"'" <<
name <<
"' imports objective bounds: external ["
1294 << objective->ScaleIntegerObjective(external_lb) <<
", "
1295 << objective->ScaleIntegerObjective(external_ub) <<
"], current ["
1296 << objective->ScaleIntegerObjective(current_lb) <<
", "
1297 << objective->ScaleIntegerObjective(current_ub) <<
"]";
1299 return solver->FinishPropagation();
1302 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1303 import_objective_bounds);
1308 void RegisterClausesExport(
int id, SharedClausesManager* shared_clauses_manager,
1310 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1311 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1312 const auto& share_binary_clause = [mapping, id, shared_clauses_manager](
1313 Literal l1, Literal l2) {
1315 mapping->GetProtoVariableFromBooleanVariable(l1.Variable());
1316 if (var1 == -1)
return;
1318 mapping->GetProtoVariableFromBooleanVariable(l2.Variable());
1319 if (var2 == -1)
return;
1320 const int lit1 = l1.IsPositive() ? var1 :
NegatedRef(var1);
1321 const int lit2 = l2.IsPositive() ? var2 :
NegatedRef(var2);
1322 shared_clauses_manager->AddBinaryClause(
id, lit1, lit2);
1324 sat_solver->SetShareBinaryClauseCallback(share_binary_clause);
1333 int RegisterClausesLevelZeroImport(
int id,
1334 SharedClausesManager* shared_clauses_manager,
1336 CHECK(shared_clauses_manager !=
nullptr);
1337 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
1338 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1339 const auto& import_level_zero_clauses = [shared_clauses_manager, id, mapping,
1341 std::vector<std::pair<int, int>> new_binary_clauses;
1342 shared_clauses_manager->GetUnseenBinaryClauses(
id, &new_binary_clauses);
1343 for (
const auto& [ref1, ref2] : new_binary_clauses) {
1344 const Literal l1 = mapping->Literal(ref1);
1345 const Literal l2 = mapping->Literal(ref2);
1346 if (!sat_solver->AddBinaryClause(l1, l2)) {
1352 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1353 import_level_zero_clauses);
1358 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1359 CHECK(shared_response_manager !=
nullptr);
1360 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1363 const auto unsat = [shared_response_manager, sat_solver,
model] {
1364 sat_solver->NotifyThatModelIsUnsat();
1365 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1366 absl::StrCat(
model->Name(),
" [loading]"));
1370 model->GetOrCreate<IntegerEncoder>()->DisableImplicationBetweenLiteral();
1372 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1373 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1374 const bool view_all_booleans_as_integers =
1376 (
parameters.search_branching() == SatParameters::FIXED_SEARCH &&
1402 if (sat_solver->ModelIsUnsat())
return unsat();
1408 absl::btree_set<std::string> unsupported_types;
1409 int num_ignored_constraints = 0;
1410 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1411 if (mapping->ConstraintIsAlreadyLoaded(&
ct)) {
1412 ++num_ignored_constraints;
1427 if (sat_solver->FinishPropagation()) {
1428 Trail* trail =
model->GetOrCreate<Trail>();
1429 const int old_num_fixed = trail->Index();
1430 if (trail->Index() > old_num_fixed) {
1431 VLOG(3) <<
"Constraint fixed " << trail->Index() - old_num_fixed
1436 if (sat_solver->ModelIsUnsat()) {
1437 VLOG(2) <<
"UNSAT during extraction (after adding '"
1443 if (num_ignored_constraints > 0) {
1444 VLOG(3) << num_ignored_constraints <<
" constraints were skipped.";
1446 if (!unsupported_types.empty()) {
1447 VLOG(1) <<
"There is unsupported constraints types in this model: ";
1448 for (
const std::string& type : unsupported_types) {
1449 VLOG(1) <<
" - " << type;
1454 model->GetOrCreate<IntegerEncoder>()
1455 ->AddAllImplicationsBetweenAssociatedLiterals();
1456 if (!sat_solver->FinishPropagation())
return unsat();
1458 model->GetOrCreate<ProductDetector>()->ProcessImplicationGraph(
1459 model->GetOrCreate<BinaryImplicationGraph>());
1465 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1466 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1467 if (
parameters.linearization_level() == 0)
return;
1470 const LinearRelaxation relaxation =
1472 const int num_lp_constraints = relaxation.linear_constraints.size();
1473 if (num_lp_constraints == 0)
return;
1474 auto* feasibility_pump =
model->GetOrCreate<FeasibilityPump>();
1475 for (
int i = 0; i < num_lp_constraints; i++) {
1476 feasibility_pump->AddLinearConstraint(relaxation.linear_constraints[i]);
1480 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
1481 const IntegerVariable
var =
1482 mapping->Integer(
model_proto.objective().vars(i));
1483 const int64_t coeff =
model_proto.objective().coeffs(i);
1484 feasibility_pump->SetObjectiveCoefficient(
var, IntegerValue(coeff));
1502 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1503 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1504 const auto unsat = [shared_response_manager, sat_solver,
model] {
1505 sat_solver->NotifyThatModelIsUnsat();
1506 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1507 absl::StrCat(
model->Name(),
" [loading]"));
1510 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1511 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1517 if (
model->Mutable<PrecedencesPropagator>() !=
nullptr &&
1518 parameters.auto_detect_greater_than_at_least_one_of()) {
1519 model->Mutable<PrecedencesPropagator>()
1520 ->AddGreaterThanAtLeastOneOfConstraints(
model);
1521 if (!sat_solver->FinishPropagation())
return unsat();
1527 if (
parameters.cp_model_probing_level() > 1) {
1528 Prober* prober =
model->GetOrCreate<Prober>();
1529 prober->ProbeBooleanVariables(1.0);
1530 if (!
model->GetOrCreate<BinaryImplicationGraph>()
1531 ->ComputeTransitiveReduction()) {
1535 if (sat_solver->ModelIsUnsat())
return unsat();
1539 bool objective_need_to_be_tight =
false;
1542 int64_t min_value = 0;
1543 int64_t max_value = 0;
1544 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1545 const CpObjectiveProto& obj =
model_proto.objective();
1546 for (
int i = 0; i < obj.vars_size(); ++i) {
1547 const int64_t coeff = obj.coeffs(i);
1548 const IntegerVariable
var = mapping->Integer(obj.vars(i));
1550 min_value += coeff * integer_trail->LowerBound(
var).value();
1551 max_value += coeff * integer_trail->UpperBound(
var).value();
1553 min_value += coeff * integer_trail->UpperBound(
var).value();
1554 max_value += coeff * integer_trail->LowerBound(
var).value();
1558 const Domain automatic_domain = Domain(min_value, max_value);
1559 objective_need_to_be_tight = !automatic_domain.IsIncludedIn(user_domain);
1570 const CpObjectiveProto& obj =
model_proto.objective();
1571 std::vector<std::pair<IntegerVariable, int64_t>> terms;
1572 terms.reserve(obj.vars_size());
1573 for (
int i = 0; i < obj.vars_size(); ++i) {
1575 std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i)));
1577 if (
parameters.optimize_with_core() && !objective_need_to_be_tight) {
1578 objective_var = GetOrCreateVariableWithTightBound(terms,
model);
1580 objective_var = GetOrCreateVariableLinkedToSumOf(
1581 terms, objective_need_to_be_tight,
model);
1588 const CpObjectiveProto& objective_proto =
model_proto.objective();
1589 auto* objective_definition =
model->GetOrCreate<ObjectiveDefinition>();
1591 objective_definition->scaling_factor = objective_proto.scaling_factor();
1592 if (objective_definition->scaling_factor == 0.0) {
1593 objective_definition->scaling_factor = 1.0;
1595 objective_definition->offset = objective_proto.offset();
1596 objective_definition->objective_var = objective_var;
1598 const int size = objective_proto.vars_size();
1599 objective_definition->vars.resize(size);
1600 objective_definition->coeffs.resize(size);
1601 for (
int i = 0; i < objective_proto.vars_size(); ++i) {
1604 objective_definition->vars[i] = mapping->Integer(objective_proto.vars(i));
1605 objective_definition->coeffs[i] = IntegerValue(objective_proto.coeffs(i));
1608 const int ref = objective_proto.vars(i);
1609 if (mapping->IsInteger(ref)) {
1610 const IntegerVariable
var = mapping->Integer(objective_proto.vars(i));
1611 objective_definition->objective_impacting_variables.insert(
1617 model->TakeOwnership(
1618 new LevelZeroEquality(objective_var, objective_definition->vars,
1619 objective_definition->coeffs,
model));
1624 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1626 const Domain automatic_domain =
1627 integer_trail->InitialVariableDomain(objective_var);
1629 <<
" scaling_factor:" <<
model_proto.objective().scaling_factor();
1630 VLOG(3) <<
"Automatic internal objective domain: " << automatic_domain;
1631 VLOG(3) <<
"User specified internal objective domain: " << user_domain;
1633 if (!integer_trail->UpdateInitialDomain(objective_var, user_domain)) {
1634 VLOG(2) <<
"UNSAT due to the objective domain.";
1642 "Initial num_bool: ", sat_solver->NumVariables());
1643 if (!sat_solver->FinishPropagation())
return unsat();
1647 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1648 shared_response_manager->UpdateInnerObjectiveBounds(
1649 absl::StrCat(
model->Name(),
" initial_propagation"),
1650 integer_trail->LowerBound(objective_var),
1651 integer_trail->UpperBound(objective_var));
1654 RegisterObjectiveBestBoundExport(objective_var, shared_response_manager,
1660 if (
model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1661 RegisterObjectiveBoundsImport(shared_response_manager,
model);
1667 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1668 auto* lp_dispatcher =
model->GetOrCreate<LinearProgrammingDispatcher>();
1669 auto* lp_vars =
model->GetOrCreate<LPVariables>();
1670 IntegerVariable size = integer_trail->NumIntegerVariables();
1671 for (IntegerVariable positive_var(0); positive_var < size;
1672 positive_var += 2) {
1674 lp_var.positive_var = positive_var;
1676 mapping->GetProtoVariableFromIntegerVariable(positive_var);
1677 const auto& it = lp_dispatcher->find(positive_var);
1678 lp_var.lp = it != lp_dispatcher->end() ? it->second :
nullptr;
1680 if (lp_var.model_var >= 0) {
1681 lp_vars->vars.push_back(lp_var);
1682 lp_vars->model_vars_size =
1683 std::max(lp_vars->model_vars_size, lp_var.model_var + 1);
1688 auto* search_heuristics =
model->GetOrCreate<SearchHeuristics>();
1689 if (
parameters.search_branching() == SatParameters::PARTIAL_FIXED_SEARCH) {
1690 search_heuristics->user_search =
1696 search_heuristics->fixed_search =
1698 search_heuristics->fixed_search,
model);
1702 std::vector<BooleanOrIntegerVariable> vars;
1703 std::vector<IntegerValue> values;
1704 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1705 const int ref =
model_proto.solution_hint().vars(i);
1707 BooleanOrIntegerVariable
var;
1708 if (mapping->IsBoolean(ref)) {
1709 var.bool_var = mapping->Literal(ref).Variable();
1711 var.int_var = mapping->Integer(ref);
1713 vars.push_back(
var);
1714 values.push_back(IntegerValue(
model_proto.solution_hint().values(i)));
1723 shared_response_manager,
1725 const std::vector<int64_t> solution =
1727 const IntegerValue obj_ub =
1729 if (obj_ub < best_obj_ub) {
1730 best_obj_ub = obj_ub;
1731 shared_response_manager->NewSolution(solution,
model->Name(),
model);
1735 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1737 HittingSetOptimizer* max_hs =
new HittingSetOptimizer(
1739 model->Register<HittingSetOptimizer>(max_hs);
1740 model->TakeOwnership(max_hs);
1742 CoreBasedOptimizer* core =
1743 new CoreBasedOptimizer(objective_var, objective.vars,
1744 objective.coeffs, solution_observer,
model);
1745 model->Register<CoreBasedOptimizer>(core);
1746 model->TakeOwnership(core);
1760 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1761 if (shared_response_manager->ProblemIsSolved())
return;
1765 const std::vector<int64_t> solution =
1768 const IntegerValue obj_ub =
1770 if (obj_ub < best_obj_ub) {
1771 best_obj_ub = obj_ub;
1772 shared_response_manager->NewSolution(solution,
model->Name(),
model);
1775 shared_response_manager->NewSolution(solution,
model->Name(),
model);
1782 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1784 const SatParameters&
parameters = *
model->GetOrCreate<SatParameters>();
1791 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1796 solution_observer();
1806 solution_observer();
1807 if (!
parameters.enumerate_all_solutions())
break;
1811 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1815 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1820 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1821 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1823 std::vector<int> core_in_proto_format;
1824 for (
const Literal l : core) {
1825 core_in_proto_format.push_back(
1826 mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1827 if (!l.IsPositive()) {
1828 core_in_proto_format.back() =
NegatedRef(core_in_proto_format.back());
1831 shared_response_manager->AddUnsatCore(core_in_proto_format);
1835 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1836 const IntegerVariable objective_var = objective.objective_var;
1839 if (
parameters.optimize_with_lb_tree_search()) {
1840 auto* search =
model->GetOrCreate<LbTreeSearch>();
1841 status = search->Search(solution_observer);
1842 }
else if (
parameters.optimize_with_core()) {
1846 status =
model->Mutable<HittingSetOptimizer>()->Optimize();
1848 status =
model->Mutable<CoreBasedOptimizer>()->Optimize();
1853 if (
parameters.binary_search_num_conflicts() >= 0) {
1855 solution_observer,
model);
1858 objective_var, solution_observer,
model);
1866 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1877 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1878 if (shared_response_manager->ProblemIsSolved())
return;
1889 if (
parameters->optimize_with_core())
return;
1891 const SatParameters saved_params = *
parameters;
1893 parameters->set_search_branching(SatParameters::HINT_SEARCH);
1900 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1904 const std::string& solution_info =
model->Name();
1906 const std::vector<int64_t> solution =
1908 shared_response_manager->NewSolution(
1909 solution, absl::StrCat(solution_info,
" [hint]"),
model);
1917 const IntegerVariable objective_var =
1918 model->GetOrCreate<ObjectiveDefinition>()->objective_var;
1919 model->GetOrCreate<SatSolver>()->Backtrack(0);
1920 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
1921 if (!integer_trail->Enqueue(
1924 shared_response_manager->GetInnerObjectiveUpperBound()),
1926 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1927 absl::StrCat(solution_info,
" [hint]"));
1940 shared_response_manager->SolutionsRepository().NumSolutions() == 0 &&
1941 !
model->GetOrCreate<TimeLimit>()->LimitReached() &&
1943 LOG(FATAL) <<
"QuickSolveWithHint() didn't find a feasible solution."
1944 <<
" The model name is '" <<
model_proto.name() <<
"'."
1945 <<
" Status: " <<
status <<
".";
1949 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1950 absl::StrCat(solution_info,
" [hint]"));
1958 void MinimizeL1DistanceWithHint(
const CpModelProto&
model_proto, Model*
model) {
1962 local_model.Register<ModelSharedTimeLimit>(
1963 model->GetOrCreate<ModelSharedTimeLimit>());
1968 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1969 if (shared_response_manager->ProblemIsSolved())
return;
1971 auto*
parameters = local_model.GetOrCreate<SatParameters>();
1975 if (
parameters->enumerate_all_solutions())
return;
1978 const SatParameters saved_params = *
model->GetOrCreate<SatParameters>();
1985 updated_model_proto.clear_objective();
1988 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1993 const int new_var_index = updated_model_proto.variables_size();
1994 IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1996 const int64_t max_domain =
2000 var_proto->add_domain(min_domain);
2001 var_proto->add_domain(max_domain);
2004 ConstraintProto*
const linear_constraint_proto =
2005 updated_model_proto.add_constraints();
2006 LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
2007 linear->add_vars(new_var_index);
2008 linear->add_coeffs(1);
2009 linear->add_vars(
var);
2010 linear->add_coeffs(-1);
2011 linear->add_domain(-
value);
2012 linear->add_domain(-
value);
2015 const int abs_var_index = updated_model_proto.variables_size();
2016 IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
2017 const int64_t abs_min_domain = 0;
2018 const int64_t abs_max_domain =
2019 std::max(std::abs(min_domain), std::abs(max_domain));
2020 abs_var_proto->add_domain(abs_min_domain);
2021 abs_var_proto->add_domain(abs_max_domain);
2022 auto* abs_ct = updated_model_proto.add_constraints()->mutable_lin_max();
2023 abs_ct->mutable_target()->add_vars(abs_var_index);
2024 abs_ct->mutable_target()->add_coeffs(1);
2025 LinearExpressionProto* left = abs_ct->add_exprs();
2026 left->add_vars(new_var_index);
2027 left->add_coeffs(1);
2028 LinearExpressionProto* right = abs_ct->add_exprs();
2029 right->add_vars(new_var_index);
2030 right->add_coeffs(-1);
2032 updated_model_proto.mutable_objective()->add_vars(abs_var_index);
2033 updated_model_proto.mutable_objective()->add_coeffs(1);
2036 auto* local_response_manager =
2037 local_model.GetOrCreate<SharedResponseManager>();
2038 local_response_manager->InitializeObjective(updated_model_proto);
2041 LoadCpModel(updated_model_proto, &local_model);
2044 const auto& mapping = *local_model.GetOrCreate<CpModelMapping>();
2046 mapping.Literals(updated_model_proto.assumptions()), &local_model);
2048 const std::string& solution_info =
model->Name();
2050 const std::vector<int64_t> solution =
2053 const std::vector<int64_t> updated_solution =
2054 GetSolutionValues(updated_model_proto, local_model);
2055 LOG(INFO) <<
"Found solution with repaired hint penalty = "
2059 shared_response_manager->NewSolution(
2060 solution, absl::StrCat(solution_info,
" [repaired]"), &local_model);
2068 void PostsolveResponseWithFullSolver(
int num_variables_in_original_model,
2069 CpModelProto mapping_proto,
2070 const std::vector<int>& postsolve_mapping,
2071 std::vector<int64_t>* solution) {
2076 for (
int i = 0; i < solution->size(); ++i) {
2077 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
2078 var_proto->clear_domain();
2079 var_proto->add_domain((*solution)[i]);
2080 var_proto->add_domain((*solution)[i]);
2086 Model postsolve_model;
2089 SatParameters& params = *postsolve_model.GetOrCreate<SatParameters>();
2090 params.set_linearization_level(0);
2091 params.set_cp_model_probing_level(0);
2094 auto* response_manager = postsolve_model.GetOrCreate<SharedResponseManager>();
2095 response_manager->InitializeObjective(mapping_proto);
2097 LoadCpModel(mapping_proto, &postsolve_model);
2098 SolveLoadedCpModel(mapping_proto, &postsolve_model);
2099 const CpSolverResponse postsolve_response = response_manager->GetResponse();
2105 CHECK_LE(num_variables_in_original_model,
2106 postsolve_response.solution().size());
2108 postsolve_response.solution().begin(),
2109 postsolve_response.solution().begin() + num_variables_in_original_model);
2112 void PostsolveResponseWrapper(
const SatParameters& params,
2113 int num_variable_in_original_model,
2114 const CpModelProto& mapping_proto,
2115 const std::vector<int>& postsolve_mapping,
2116 std::vector<int64_t>* solution) {
2117 if (params.debug_postsolve_with_full_solver()) {
2118 PostsolveResponseWithFullSolver(num_variable_in_original_model,
2119 mapping_proto, postsolve_mapping, solution);
2122 postsolve_mapping, solution);
2127 CpSolverResponse SolvePureSatModel(
const CpModelProto&
model_proto,
2129 SolverLogger* logger) {
2130 std::unique_ptr<SatSolver> solver(
new SatSolver());
2133 model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(
parameters);
2136 std::unique_ptr<DratProofHandler> drat_proof_handler;
2137 #if !defined(__PORTABLE_PLATFORM__)
2138 if (!absl::GetFlag(FLAGS_drat_output).empty() ||
2139 absl::GetFlag(FLAGS_drat_check)) {
2140 if (!absl::GetFlag(FLAGS_drat_output).empty()) {
2142 CHECK_OK(
file::Open(absl::GetFlag(FLAGS_drat_output),
"w", &output,
2144 drat_proof_handler = std::make_unique<DratProofHandler>(
2145 false, output, absl::GetFlag(FLAGS_drat_check));
2147 drat_proof_handler = std::make_unique<DratProofHandler>();
2149 solver->SetDratProofHandler(drat_proof_handler.get());
2153 auto get_literal = [](
int ref) {
2154 if (ref >= 0)
return Literal(BooleanVariable(ref),
true);
2155 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
2158 std::vector<Literal> temp;
2159 const int num_variables =
model_proto.variables_size();
2160 solver->SetNumVariables(num_variables);
2161 if (drat_proof_handler !=
nullptr) {
2162 drat_proof_handler->SetNumVariables(num_variables);
2166 for (
int ref = 0; ref < num_variables; ++ref) {
2168 if (domain.IsFixed()) {
2169 const Literal ref_literal =
2170 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
2171 drat_proof_handler->AddProblemClause({ref_literal});
2174 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
2175 switch (
ct.constraint_case()) {
2176 case ConstraintProto::ConstraintCase::kBoolAnd: {
2177 if (
ct.enforcement_literal_size() == 0) {
2178 for (
const int ref :
ct.bool_and().literals()) {
2179 drat_proof_handler->AddProblemClause({get_literal(ref)});
2183 const Literal not_a =
2184 get_literal(
ct.enforcement_literal(0)).Negated();
2185 for (
const int ref :
ct.bool_and().literals()) {
2186 drat_proof_handler->AddProblemClause({not_a, get_literal(ref)});
2191 case ConstraintProto::ConstraintCase::kBoolOr:
2193 for (
const int ref :
ct.bool_or().literals()) {
2194 temp.push_back(get_literal(ref));
2196 for (
const int ref :
ct.enforcement_literal()) {
2197 temp.push_back(get_literal(ref).Negated());
2199 drat_proof_handler->AddProblemClause(temp);
2202 LOG(FATAL) <<
"Not supported";
2207 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
2208 switch (
ct.constraint_case()) {
2209 case ConstraintProto::ConstraintCase::kBoolAnd: {
2210 if (
ct.enforcement_literal_size() == 0) {
2211 for (
const int ref :
ct.bool_and().literals()) {
2212 const Literal
b = get_literal(ref);
2213 solver->AddUnitClause(
b);
2217 const Literal not_a =
2218 get_literal(
ct.enforcement_literal(0)).Negated();
2219 for (
const int ref :
ct.bool_and().literals()) {
2220 const Literal
b = get_literal(ref);
2221 solver->AddProblemClause({not_a,
b},
false);
2226 case ConstraintProto::ConstraintCase::kBoolOr:
2228 for (
const int ref :
ct.bool_or().literals()) {
2229 temp.push_back(get_literal(ref));
2231 for (
const int ref :
ct.enforcement_literal()) {
2232 temp.push_back(get_literal(ref).Negated());
2234 solver->AddProblemClause(temp,
false);
2237 LOG(FATAL) <<
"Not supported";
2242 for (
int ref = 0; ref < num_variables; ++ref) {
2244 if (domain.Min() == domain.Max()) {
2245 const Literal ref_literal =
2246 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
2247 solver->AddUnitClause(ref_literal);
2254 std::vector<bool> solution;
2256 &solution, drat_proof_handler.get(), logger);
2259 for (
int ref = 0; ref < num_variables; ++ref) {
2260 response.add_solution(solution[ref]);
2264 status = solver->SolveWithTimeLimit(
model->GetOrCreate<TimeLimit>());
2267 for (
int ref = 0; ref < num_variables; ++ref) {
2269 solver->Assignment().LiteralIsTrue(get_literal(ref)) ? 1 : 0);
2276 model->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
2277 solver->model()->GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
2281 response.set_status(CpSolverStatus::UNKNOWN);
2296 LOG(FATAL) <<
"Unexpected SatSolver::Status " <<
status;
2298 response.set_num_booleans(solver->NumVariables());
2299 response.set_num_branches(solver->num_branches());
2300 response.set_num_conflicts(solver->num_failures());
2301 response.set_num_binary_propagations(solver->num_propagations());
2302 response.set_num_integer_propagations(0);
2305 model->Get<TimeLimit>()->GetElapsedDeterministicTime());
2311 absl::GetFlag(FLAGS_max_drat_time_in_seconds));
2312 switch (drat_status) {
2314 LOG(INFO) <<
"DRAT status: UNKNOWN";
2317 LOG(INFO) <<
"DRAT status: VALID";
2320 LOG(ERROR) <<
"DRAT status: INVALID";
2326 LOG(INFO) <<
"DRAT wall time: " << drat_timer.
Get();
2327 }
else if (drat_proof_handler !=
nullptr) {
2330 LOG(INFO) <<
"DRAT status: NA";
2331 LOG(INFO) <<
"DRAT wall time: NA";
2332 LOG(INFO) <<
"DRAT user time: NA";
2337 #if !defined(__PORTABLE_PLATFORM__)
2340 struct SharedClasses {
2352 bool SearchIsDone() {
2353 if (
response->ProblemIsSolved())
return true;
2360 class FullProblemSolver :
public SubSolver {
2362 FullProblemSolver(
const std::string&
name,
2363 const SatParameters& local_parameters,
bool split_in_chunks,
2364 SharedClasses* shared,
bool stop_at_first_solution =
false)
2365 : SubSolver(
name, stop_at_first_solution ? FIRST_SOLUTION : FULL_PROBLEM),
2367 split_in_chunks_(split_in_chunks),
2368 local_model_(std::make_unique<Model>(
name)),
2369 stop_at_first_solution_(stop_at_first_solution) {
2371 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2372 shared_->time_limit->UpdateLocalLimit(
2373 local_model_->GetOrCreate<TimeLimit>());
2375 if (stop_at_first_solution) {
2376 local_model_->GetOrCreate<TimeLimit>()->RegisterExternalBooleanAsLimit(
2377 shared_->response->first_solution_solvers_should_stop());
2380 if (shared->response !=
nullptr) {
2381 local_model_->Register<SharedResponseManager>(shared->response);
2384 if (shared->relaxation_solutions !=
nullptr) {
2385 local_model_->Register<SharedRelaxationSolutionRepository>(
2386 shared->relaxation_solutions);
2389 if (shared->lp_solutions !=
nullptr) {
2390 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2393 if (shared->incomplete_solutions !=
nullptr) {
2394 local_model_->Register<SharedIncompleteSolutionManager>(
2395 shared->incomplete_solutions);
2398 if (shared->bounds !=
nullptr) {
2399 local_model_->Register<SharedBoundsManager>(shared->bounds);
2402 if (shared->clauses !=
nullptr) {
2403 local_model_->Register<SharedClausesManager>(shared->clauses);
2408 local_model_->Register<SharedStatistics>(
2409 shared->global_model->GetOrCreate<SharedStatistics>());
2412 ~FullProblemSolver()
override {
2415 shared_->response->AppendResponseToBeMerged(
response);
2418 bool TaskIsAvailable()
override {
2419 if (shared_->SearchIsDone())
return false;
2421 absl::MutexLock mutex_lock(&mutex_);
2422 if (stop_at_first_solution_) {
2423 return shared_->response->SolutionsRepository().NumSolutions() == 0 &&
2424 previous_task_is_completed_;
2426 return previous_task_is_completed_;
2430 std::function<void()> GenerateTask(int64_t )
override {
2432 absl::MutexLock mutex_lock(&mutex_);
2433 previous_task_is_completed_ =
false;
2436 if (solving_first_chunk_) {
2437 LoadCpModel(*shared_->model_proto, local_model_.get());
2443 if (shared_->bounds !=
nullptr) {
2444 RegisterVariableBoundsLevelZeroExport(
2445 *shared_->model_proto, shared_->bounds, local_model_.get());
2446 RegisterVariableBoundsLevelZeroImport(
2447 *shared_->model_proto, shared_->bounds, local_model_.get());
2453 if (shared_->clauses !=
nullptr) {
2454 const int id = shared_->clauses->RegisterNewId();
2455 shared_->clauses->SetWorkerNameForId(
id, local_model_->Name());
2457 RegisterClausesLevelZeroImport(
id, shared_->clauses,
2458 local_model_.get());
2459 RegisterClausesExport(
id, shared_->clauses, local_model_.get());
2462 if (local_model_->GetOrCreate<SatParameters>()->repair_hint()) {
2463 MinimizeL1DistanceWithHint(*shared_->model_proto, local_model_.get());
2465 QuickSolveWithHint(*shared_->model_proto, local_model_.get());
2469 solving_first_chunk_ =
false;
2471 if (split_in_chunks_) {
2473 absl::MutexLock mutex_lock(&mutex_);
2474 previous_task_is_completed_ =
true;
2479 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2480 if (split_in_chunks_) {
2483 auto* params = local_model_->GetOrCreate<SatParameters>();
2484 params->set_max_deterministic_time(1);
2485 time_limit->ResetLimitFromParameters(*params);
2486 shared_->time_limit->UpdateLocalLimit(
time_limit);
2489 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2490 SolveLoadedCpModel(*shared_->model_proto, local_model_.get());
2492 absl::MutexLock mutex_lock(&mutex_);
2493 deterministic_time_since_last_synchronize_ +=
2494 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2498 if (shared_->SearchIsDone()) {
2499 shared_->time_limit->Stop();
2504 if (split_in_chunks_) {
2505 absl::MutexLock mutex_lock(&mutex_);
2506 previous_task_is_completed_ =
true;
2514 local_model_.reset();
2521 void Synchronize()
override {
2522 absl::MutexLock mutex_lock(&mutex_);
2523 deterministic_time_ += deterministic_time_since_last_synchronize_;
2524 shared_->time_limit->AdvanceDeterministicTime(
2525 deterministic_time_since_last_synchronize_);
2526 deterministic_time_since_last_synchronize_ = 0.0;
2529 std::string StatisticsString()
const override {
2533 if (local_model_ ==
nullptr)
return std::string();
2536 const std::string p4(4,
' ');
2537 const std::string p6(6,
' ');
2542 absl::StrAppend(&s, p4,
"Search statistics:\n");
2543 absl::StrAppend(&s, p6,
"booleans: ",
FormatCounter(r.num_booleans()),
2545 absl::StrAppend(&s, p6,
"conflicts: ",
FormatCounter(r.num_conflicts()),
2547 absl::StrAppend(&s, p6,
"branches: ",
FormatCounter(r.num_branches()),
2549 absl::StrAppend(&s, p6,
"binary_propagations: ",
2551 absl::StrAppend(&s, p6,
"integer_propagations: ",
2553 absl::StrAppend(&s, p6,
"restarts: ",
FormatCounter(r.num_restarts()),
2557 *local_model_->GetOrCreate<LinearProgrammingConstraintCollection>();
2558 int num_displayed = 0;
2559 for (
const auto* lp : lps) {
2560 if (num_displayed++ > 6) {
2561 absl::StrAppend(&s, p4,
"Skipping other LPs...\n");
2562 absl::StrAppend(&s, p6,
"- ", lps.size(),
" total independent LPs.\n");
2566 const std::string raw_statistics = lp->Statistics();
2567 const std::vector<absl::string_view> lines =
2568 absl::StrSplit(raw_statistics,
'\n', absl::SkipEmpty());
2569 for (
const absl::string_view&
line : lines) {
2570 absl::StrAppend(&s, p4,
line,
"\n");
2577 SharedClasses* shared_;
2578 const bool split_in_chunks_;
2579 std::unique_ptr<Model> local_model_;
2583 bool solving_first_chunk_ =
true;
2586 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2588 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2589 bool stop_at_first_solution_;
2592 class FeasibilityPumpSolver :
public SubSolver {
2594 FeasibilityPumpSolver(
const SatParameters& local_parameters,
2595 SharedClasses* shared)
2596 : SubSolver(
"feasibility_pump", INCOMPLETE),
2598 local_model_(std::make_unique<Model>(name_)) {
2600 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2601 shared_->time_limit->UpdateLocalLimit(
2602 local_model_->GetOrCreate<TimeLimit>());
2604 if (shared->response !=
nullptr) {
2605 local_model_->Register<SharedResponseManager>(shared->response);
2608 if (shared->relaxation_solutions !=
nullptr) {
2609 local_model_->Register<SharedRelaxationSolutionRepository>(
2610 shared->relaxation_solutions);
2613 if (shared->lp_solutions !=
nullptr) {
2614 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2617 if (shared->incomplete_solutions !=
nullptr) {
2618 local_model_->Register<SharedIncompleteSolutionManager>(
2619 shared->incomplete_solutions);
2623 if (shared_->bounds !=
nullptr) {
2624 RegisterVariableBoundsLevelZeroImport(
2625 *shared_->model_proto, shared_->bounds, local_model_.get());
2629 bool TaskIsAvailable()
override {
2630 if (shared_->SearchIsDone())
return false;
2631 absl::MutexLock mutex_lock(&mutex_);
2632 return previous_task_is_completed_;
2635 std::function<void()> GenerateTask(int64_t )
override {
2638 absl::MutexLock mutex_lock(&mutex_);
2639 if (!previous_task_is_completed_)
return;
2640 previous_task_is_completed_ =
false;
2643 absl::MutexLock mutex_lock(&mutex_);
2644 if (solving_first_chunk_) {
2645 LoadFeasibilityPump(*shared_->model_proto, local_model_.get());
2648 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
2649 solving_first_chunk_ =
false;
2651 previous_task_is_completed_ =
true;
2656 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2657 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2658 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
2659 if (!feasibility_pump->Solve()) {
2660 shared_->response->NotifyThatImprovingProblemIsInfeasible(name_);
2664 absl::MutexLock mutex_lock(&mutex_);
2665 deterministic_time_since_last_synchronize_ +=
2666 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2670 if (shared_->SearchIsDone()) {
2671 shared_->time_limit->Stop();
2675 absl::MutexLock mutex_lock(&mutex_);
2676 previous_task_is_completed_ =
true;
2680 void Synchronize()
override {
2681 absl::MutexLock mutex_lock(&mutex_);
2682 deterministic_time_ += deterministic_time_since_last_synchronize_;
2683 shared_->time_limit->AdvanceDeterministicTime(
2684 deterministic_time_since_last_synchronize_);
2685 deterministic_time_since_last_synchronize_ = 0.0;
2691 SharedClasses* shared_;
2692 std::unique_ptr<Model> local_model_;
2698 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
2700 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2702 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2706 class LnsSolver :
public SubSolver {
2708 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
2710 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
2711 : SubSolver(generator->
name(), INCOMPLETE),
2712 generator_(std::move(generator)),
2717 bool TaskIsAvailable()
override {
2718 if (shared_->SearchIsDone())
return false;
2719 return generator_->ReadyToGenerate();
2722 std::function<void()> GenerateTask(int64_t task_id)
override {
2723 return [task_id,
this]() {
2724 if (shared_->SearchIsDone())
return;
2729 const int32_t low =
static_cast<int32_t
>(task_id);
2730 const int32_t high =
static_cast<int32_t
>(task_id >> 32);
2731 std::seed_seq seed{low, high, parameters_.random_seed()};
2734 NeighborhoodGenerator::SolveData data;
2735 data.difficulty = generator_->difficulty();
2736 data.deterministic_limit = generator_->deterministic_limit();
2739 CpSolverResponse base_response;
2741 const SharedSolutionRepository<int64_t>& repo =
2742 shared_->response->SolutionsRepository();
2743 if (repo.NumSolutions() > 0) {
2745 const SharedSolutionRepository<int64_t>::Solution solution =
2746 repo.GetRandomBiasedSolution(random);
2747 for (
const int64_t
value : solution.variable_values) {
2748 base_response.add_solution(
value);
2753 data.initial_best_objective = repo.GetSolution(0).rank;
2754 data.base_objective = solution.rank;
2756 base_response.set_status(CpSolverStatus::UNKNOWN);
2763 data.initial_best_objective =
2764 shared_->response->GetInnerObjectiveUpperBound();
2765 data.base_objective = data.initial_best_objective;
2769 Neighborhood neighborhood =
2770 generator_->Generate(base_response, data.difficulty, random);
2772 if (!neighborhood.is_generated)
return;
2774 const int64_t num_calls =
std::max(int64_t{1}, generator_->num_calls());
2775 const double fully_solved_proportion =
2776 static_cast<double>(generator_->num_fully_solved_calls()) /
2777 static_cast<double>(num_calls);
2778 std::string source_info =
name();
2779 if (!neighborhood.source_info.empty()) {
2780 absl::StrAppend(&source_info,
"_", neighborhood.source_info);
2782 const std::string lns_info = absl::StrFormat(
2783 "%s(d=%0.2f s=%i t=%0.2f p=%0.2f)", source_info, data.difficulty,
2784 task_id, data.deterministic_limit, fully_solved_proportion);
2786 SatParameters local_params(parameters_);
2787 local_params.set_max_deterministic_time(data.deterministic_limit);
2788 local_params.set_stop_after_first_solution(
false);
2789 local_params.set_cp_model_presolve(
true);
2790 local_params.set_log_search_progress(
false);
2791 local_params.set_cp_model_probing_level(0);
2792 local_params.set_symmetry_level(0);
2793 local_params.set_find_big_linear_overlap(
false);
2794 local_params.set_solution_pool_size(1);
2796 Model local_model(lns_info);
2797 *(local_model.GetOrCreate<SatParameters>()) = local_params;
2798 TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
2799 local_time_limit->ResetLimitFromParameters(local_params);
2800 shared_->time_limit->UpdateLocalLimit(local_time_limit);
2803 CpModelProto lns_fragment;
2804 CpModelProto mapping_proto;
2805 auto context = std::make_unique<PresolveContext>(
2806 &local_model, &lns_fragment, &mapping_proto);
2808 *lns_fragment.mutable_variables() = neighborhood.delta.variables();
2810 ModelCopy copier(
context.get());
2813 if (!copier.ImportAndSimplifyConstraints(
2814 helper_->ModelProto(), neighborhood.constraints_to_ignore)) {
2819 if (!neighborhood.delta.constraints().empty() &&
2820 !copier.ImportAndSimplifyConstraints(neighborhood.delta, {})) {
2827 helper_->ModelProto(),
context.get());
2828 lns_fragment.set_name(absl::StrCat(
"lns_", task_id));
2831 if (neighborhood.delta.has_solution_hint()) {
2832 *lns_fragment.mutable_solution_hint() =
2833 neighborhood.delta.solution_hint();
2836 CpModelProto debug_copy;
2837 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
2840 debug_copy = lns_fragment;
2843 #if !defined(__PORTABLE_PLATFORM__)
2846 if (absl::GetFlag(FLAGS_cp_model_dump_lns)) {
2848 const std::string lns_name =
2849 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2850 lns_fragment.name(),
".pb.txt");
2851 LOG(INFO) <<
"Dumping LNS model to '" << lns_name <<
"'.";
2855 std::vector<int> postsolve_mapping;
2856 const CpSolverStatus presolve_status =
2861 neighborhood.delta.Clear();
2866 auto* local_response_manager =
2867 local_model.GetOrCreate<SharedResponseManager>();
2868 local_response_manager->InitializeObjective(lns_fragment);
2869 local_response_manager->SetSynchronizationMode(
true);
2871 CpSolverResponse local_response;
2872 if (presolve_status == CpSolverStatus::UNKNOWN) {
2873 LoadCpModel(lns_fragment, &local_model);
2874 QuickSolveWithHint(lns_fragment, &local_model);
2875 SolveLoadedCpModel(lns_fragment, &local_model);
2876 local_response = local_response_manager->GetResponse();
2880 if (local_response.solution_info().empty()) {
2881 local_response.set_solution_info(
2882 absl::StrCat(lns_info,
" [presolve]"));
2889 local_response_manager->NotifyThatImprovingProblemIsInfeasible(
2892 local_response = local_response_manager->GetResponse();
2893 local_response.set_status(presolve_status);
2895 const std::string solution_info = local_response.solution_info();
2896 std::vector<int64_t> solution_values(local_response.solution().begin(),
2897 local_response.solution().end());
2899 data.status = local_response.status();
2904 PostsolveResponseWrapper(
2905 local_params, helper_->ModelProto().variables_size(), mapping_proto,
2906 postsolve_mapping, &solution_values);
2907 local_response.mutable_solution()->Assign(solution_values.begin(),
2908 solution_values.end());
2911 data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
2913 bool new_solution =
false;
2915 if (!local_response.solution().empty()) {
2921 const bool feasible =
2924 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
2925 const std::string
name =
2926 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2927 debug_copy.name(),
".pb.txt");
2928 LOG(INFO) <<
"Dumping problematic LNS model to '" <<
name <<
"'.";
2931 LOG(FATAL) <<
"Infeasible LNS solution! " << solution_info
2932 <<
" solved with params "
2933 << local_params.ShortDebugString();
2956 !shared_->model_proto->has_symmetry() && !solution_values.empty() &&
2957 neighborhood.is_simple &&
2958 !neighborhood.variables_that_can_be_fixed_to_local_optimum
2960 display_lns_info =
true;
2961 shared_->bounds->FixVariablesFromPartialSolution(
2963 neighborhood.variables_that_can_be_fixed_to_local_optimum);
2967 data.new_objective = data.base_objective;
2971 shared_->model_proto->objective(), solution_values));
2978 const std::vector<int64_t> base_solution(
2979 base_response.solution().begin(), base_response.solution().end());
2980 if (solution_values != base_solution) {
2981 new_solution =
true;
2982 shared_->response->NewSolution(solution_values, solution_info,
2986 if (!neighborhood.is_reduced &&
2989 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2991 shared_->time_limit->Stop();
2995 generator_->AddSolveData(data);
2998 auto* logger = shared_->global_model->GetOrCreate<SolverLogger>();
2999 std::string s = absl::StrCat(
" LNS ",
name(),
":");
3002 shared_->model_proto->objective(),
3004 base_response.solution()));
3006 shared_->model_proto->objective(),
3009 absl::StrAppend(&s,
" [new_sol:", base_obj,
" -> ", new_obj,
"]");
3011 if (neighborhood.is_simple) {
3013 &s,
" [",
"relaxed:", neighborhood.num_relaxed_variables,
3014 " in_obj:", neighborhood.num_relaxed_variables_in_objective,
3016 neighborhood.variables_that_can_be_fixed_to_local_optimum.size(),
3019 SOLVER_LOG(logger, s,
" [d:", data.difficulty,
", id:", task_id,
3020 ", dtime:", data.deterministic_time,
"/",
3021 data.deterministic_limit,
3022 ", status:", ProtoEnumToString<CpSolverStatus>(data.status),
3023 ", #calls:", generator_->num_calls(),
3024 ", p:", fully_solved_proportion,
"]");
3029 void Synchronize()
override {
3030 generator_->Synchronize();
3031 const double old = deterministic_time_;
3032 deterministic_time_ = generator_->deterministic_time();
3033 shared_->time_limit->AdvanceDeterministicTime(deterministic_time_ - old);
3039 std::unique_ptr<NeighborhoodGenerator> generator_;
3040 NeighborhoodGeneratorHelper* helper_;
3041 const SatParameters parameters_;
3042 SharedClasses* shared_;
3045 void SolveCpModelParallel(
const CpModelProto&
model_proto,
3048 CHECK(!params.enumerate_all_solutions())
3049 <<
"Enumerating all solutions in parallel is not supported.";
3052 std::unique_ptr<SharedBoundsManager> shared_bounds_manager;
3053 if (params.share_level_zero_bounds()) {
3054 shared_bounds_manager = std::make_unique<SharedBoundsManager>(
model_proto);
3055 shared_bounds_manager->LoadDebugSolution(
3059 std::unique_ptr<SharedRelaxationSolutionRepository>
3060 shared_relaxation_solutions;
3062 auto shared_lp_solutions = std::make_unique<SharedLPSolutionRepository>(
3068 std::unique_ptr<SharedIncompleteSolutionManager> shared_incomplete_solutions;
3069 const bool use_feasibility_pump =
3070 params.use_feasibility_pump() && params.linearization_level() > 0 &&
3071 !params.use_lns_only() && !params.interleave_search();
3072 if (use_feasibility_pump) {
3073 shared_incomplete_solutions =
3074 std::make_unique<SharedIncompleteSolutionManager>();
3076 shared_incomplete_solutions.get());
3080 const bool always_synchronize =
3081 !params.interleave_search() || params.num_workers() <= 1;
3083 std::unique_ptr<SharedClausesManager> shared_clauses;
3084 if (params.share_binary_clauses()) {
3085 shared_clauses = std::make_unique<SharedClausesManager>(always_synchronize);
3088 SharedResponseManager* shared_response_manager =
3090 shared_response_manager->SetSynchronizationMode(always_synchronize);
3092 SharedClasses shared;
3096 shared.bounds = shared_bounds_manager.get();
3097 shared.response = shared_response_manager;
3098 shared.relaxation_solutions = shared_relaxation_solutions.get();
3099 shared.lp_solutions = shared_lp_solutions.get();
3100 shared.incomplete_solutions = shared_incomplete_solutions.get();
3101 shared.clauses = shared_clauses.get();
3105 std::vector<std::unique_ptr<SubSolver>> subsolvers;
3106 std::vector<std::unique_ptr<SubSolver>> incomplete_subsolvers;
3109 subsolvers.push_back(std::make_unique<SynchronizationPoint>(
3110 "synchronization_agent", [&shared]() {
3111 shared.response->Synchronize();
3112 shared.response->MutableSolutionsRepository()->Synchronize();
3113 if (shared.bounds !=
nullptr) {
3114 shared.bounds->Synchronize();
3116 if (shared.relaxation_solutions !=
nullptr) {
3117 shared.relaxation_solutions->Synchronize();
3119 if (shared.lp_solutions !=
nullptr) {
3120 shared.lp_solutions->Synchronize();
3122 if (shared.clauses !=
nullptr) {
3123 shared.clauses->Synchronize();
3125 if (shared.time_limit->LimitReached()) {
3126 *(shared.response->first_solution_solvers_should_stop()) =
true;
3130 int num_full_problem_solvers = 0;
3131 if (params.use_lns_only()) {
3138 SatParameters local_params = params;
3139 local_params.set_stop_after_first_solution(
true);
3140 local_params.set_linearization_level(0);
3141 subsolvers.push_back(std::make_unique<FullProblemSolver>(
3142 "first_solution", local_params,
3145 for (
const SatParameters& local_params :
3148 if (params.optimize_with_max_hs())
continue;
3150 subsolvers.push_back(std::make_unique<FullProblemSolver>(
3151 local_params.name(), local_params,
3152 params.interleave_search(), &shared));
3153 num_full_problem_solvers++;
3158 if (use_feasibility_pump) {
3159 incomplete_subsolvers.push_back(
3160 std::make_unique<FeasibilityPumpSolver>(params, &shared));
3165 auto unique_helper = std::make_unique<NeighborhoodGeneratorHelper>(
3166 &
model_proto, ¶ms, shared.response, shared.bounds);
3167 NeighborhoodGeneratorHelper* helper = unique_helper.get();
3168 subsolvers.push_back(std::move(unique_helper));
3171 SatParameters local_params = params;
3172 local_params.set_name(
"default");
3175 if (params.use_rins_lns() && !params.interleave_search()) {
3182 incomplete_subsolvers.push_back(std::make_unique<LnsSolver>(
3183 std::make_unique<RelaxationInducedNeighborhoodGenerator>(
3184 helper, shared.response, shared.relaxation_solutions,
3185 shared.lp_solutions,
nullptr,
3186 absl::StrCat(
"rins_lns_", local_params.name())),
3187 local_params, helper, &shared));
3190 incomplete_subsolvers.push_back(std::make_unique<LnsSolver>(
3191 std::make_unique<RelaxationInducedNeighborhoodGenerator>(
3192 helper,
nullptr, shared.relaxation_solutions,
3193 shared.lp_solutions, shared.incomplete_solutions,
3194 absl::StrCat(
"rens_lns_", local_params.name())),
3195 local_params, helper, &shared));
3215 !params.interleave_search()) {
3216 const int max_num_incomplete_solvers_running_before_the_first_solution =
3217 params.num_workers() <= 8 ? 1 : (params.num_workers() <= 16 ? 2 : 3);
3218 const int num_reserved_incomplete_solvers = std::min<int>(
3219 max_num_incomplete_solvers_running_before_the_first_solution,
3220 incomplete_subsolvers.size());
3221 const int num_first_solution_subsolvers = params.num_workers() -
3222 num_full_problem_solvers -
3223 num_reserved_incomplete_solvers;
3226 params,
model_proto, num_first_solution_subsolvers)) {
3227 subsolvers.push_back(std::make_unique<FullProblemSolver>(
3228 local_params.name(), local_params,
3229 params.interleave_search(), &shared,
3236 for (
int i = 0; i < incomplete_subsolvers.size(); ++i) {
3237 subsolvers.push_back(std::move(incomplete_subsolvers[i]));
3239 incomplete_subsolvers.clear();
3245 subsolvers.push_back(std::make_unique<LnsSolver>(
3246 std::make_unique<RelaxRandomVariablesGenerator>(
3247 helper, absl::StrCat(
"rnd_var_lns_", local_params.name())),
3248 local_params, helper, &shared));
3249 subsolvers.push_back(std::make_unique<LnsSolver>(
3250 std::make_unique<RelaxRandomConstraintsGenerator>(
3251 helper, absl::StrCat(
"rnd_cst_lns_", local_params.name())),
3252 local_params, helper, &shared));
3253 subsolvers.push_back(std::make_unique<LnsSolver>(
3254 std::make_unique<VariableGraphNeighborhoodGenerator>(
3255 helper, absl::StrCat(
"graph_var_lns_", local_params.name())),
3256 local_params, helper, &shared));
3257 subsolvers.push_back(std::make_unique<LnsSolver>(
3258 std::make_unique<ConstraintGraphNeighborhoodGenerator>(
3259 helper, absl::StrCat(
"graph_cst_lns_", local_params.name())),
3260 local_params, helper, &shared));
3266 params.objective_lns_min_size() &&
3269 subsolvers.push_back(std::make_unique<LnsSolver>(
3270 std::make_unique<RelaxObjectiveVariablesGenerator>(
3271 helper, absl::StrCat(
"rnd_obj_lns_", local_params.name())),
3272 local_params, helper, &shared));
3277 if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty() ||
3278 !helper->TypeToConstraints(ConstraintProto::kNoOverlap2D).empty() ||
3279 !helper->TypeToConstraints(ConstraintProto::kCumulative).empty()) {
3280 subsolvers.push_back(std::make_unique<LnsSolver>(
3281 std::make_unique<RandomIntervalSchedulingNeighborhoodGenerator>(
3282 helper, absl::StrCat(
"scheduling_random_intervals_lns_",
3283 local_params.name())),
3284 local_params, helper, &shared));
3285 subsolvers.push_back(std::make_unique<LnsSolver>(
3286 std::make_unique<RandomPrecedenceSchedulingNeighborhoodGenerator>(
3287 helper, absl::StrCat(
"scheduling_random_precedences_lns_",
3288 local_params.name())),
3289 local_params, helper, &shared));
3290 subsolvers.push_back(std::make_unique<LnsSolver>(
3291 std::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
3293 absl::StrCat(
"scheduling_time_window_lns_", local_params.name())),
3294 local_params, helper, &shared));
3296 const std::vector<std::vector<int>> intervals_in_constraints =
3297 helper->GetUniqueIntervalSets();
3298 if (intervals_in_constraints.size() > 2) {
3299 subsolvers.push_back(std::make_unique<LnsSolver>(
3300 std::make_unique<SchedulingResourceWindowsNeighborhoodGenerator>(
3301 helper, intervals_in_constraints,
3302 absl::StrCat(
"scheduling_resource_windows_lns_",
3303 local_params.name())),
3304 local_params, helper, &shared));
3308 const int num_circuit =
3309 helper->TypeToConstraints(ConstraintProto::kCircuit).size();
3310 const int num_routes =
3311 helper->TypeToConstraints(ConstraintProto::kRoutes).size();
3312 if (num_circuit + num_routes > 0) {
3313 subsolvers.push_back(std::make_unique<LnsSolver>(
3314 std::make_unique<RoutingRandomNeighborhoodGenerator>(
3315 helper, absl::StrCat(
"routing_random_lns_", local_params.name())),
3316 local_params, helper, &shared));
3318 subsolvers.push_back(std::make_unique<LnsSolver>(
3319 std::make_unique<RoutingPathNeighborhoodGenerator>(
3320 helper, absl::StrCat(
"routing_path_lns_", local_params.name())),
3321 local_params, helper, &shared));
3323 if (num_routes > 0 || num_circuit > 1) {
3324 subsolvers.push_back(std::make_unique<LnsSolver>(
3325 std::make_unique<RoutingFullPathNeighborhoodGenerator>(
3327 absl::StrCat(
"routing_full_path_lns_", local_params.name())),
3328 local_params, helper, &shared));
3336 subsolvers.push_back(std::make_unique<SynchronizationPoint>(
3337 "update_gap_integral",
3338 [&shared]() { shared.response->UpdateGapIntegral(); }));
3343 if (logger->LoggingIsEnabled()) {
3345 std::vector<std::string> full_problem_solver_names;
3346 std::vector<std::string> incomplete_solver_names;
3347 std::vector<std::string> first_solution_solver_names;
3348 std::vector<std::string> helper_solver_names;
3349 for (
int i = 0; i < subsolvers.size(); ++i) {
3350 const auto& subsolver = subsolvers[i];
3351 switch (subsolver->type()) {
3353 full_problem_solver_names.push_back(subsolver->name());
3356 incomplete_solver_names.push_back(subsolver->name());
3359 first_solution_solver_names.push_back(subsolver->name());
3362 helper_solver_names.push_back(subsolver->name());
3368 if (params.interleave_search()) {
3370 absl::StrFormat(
"Starting deterministic search at %.2fs with "
3371 "%i workers and batch size of %d.",
3372 shared.wall_timer->Get(), params.num_workers(),
3373 params.interleave_batch_size()));
3376 "Starting search at %.2fs with %i workers.",
3377 shared.wall_timer->Get(), params.num_workers()));
3380 auto display_subsolver_list = [logger](
3381 const std::vector<std::string>& names,
3382 const absl::string_view type_name) {
3383 if (!names.empty()) {
3385 absl::StrCat(type_name, names.size() == 1 ?
"" :
"s"),
": [",
3386 absl::StrJoin(names.begin(), names.end(),
", "),
"]");
3390 display_subsolver_list(full_problem_solver_names,
"full problem subsolver");
3391 display_subsolver_list(first_solution_solver_names,
3392 "first solution subsolver");
3393 display_subsolver_list(incomplete_solver_names,
"incomplete subsolver");
3394 display_subsolver_list(helper_solver_names,
"helper subsolver");
3398 if (params.interleave_search()) {
3399 int batch_size = params.interleave_batch_size();
3400 if (batch_size == 0) {
3401 batch_size = params.num_workers() == 1 ? 1 : params.num_workers() * 3;
3404 "Setting number of tasks in each batch of interleaved search to ",
3413 if (logger->LoggingIsEnabled()) {
3414 if (params.log_subsolver_statistics()) {
3416 for (
const auto& subsolver : subsolvers) {
3417 const std::string stats = subsolver->StatisticsString();
3418 if (stats.empty())
continue;
3421 SOLVER_LOG(logger,
"Sub-solver search statistics:");
3425 absl::StrCat(
" '", subsolver->name(),
"':\n", stats));
3429 shared.response->DisplayImprovementStatistics();
3431 if (shared.bounds) {
3432 shared.bounds->LogStatistics(logger);
3435 if (shared.clauses) {
3436 shared.clauses->LogStatistics(logger);
3441 for (
int i = 0; i < subsolvers.size(); ++i) {
3442 subsolvers[i].reset();
3451 void AddPostsolveClauses(
const std::vector<int>& postsolve_mapping,
3452 Model*
model, CpModelProto* mapping_proto) {
3453 auto* mapping =
model->GetOrCreate<CpModelMapping>();
3454 auto* postsolve =
model->GetOrCreate<PostsolveClauses>();
3455 for (
const auto& clause : postsolve->clauses) {
3456 auto*
ct = mapping_proto->add_constraints()->mutable_bool_or();
3457 for (
const Literal l : clause) {
3458 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
3460 var = postsolve_mapping[
var];
3464 postsolve->clauses.clear();
3467 void TestSolutionHintForFeasibility(
const CpModelProto&
model_proto,
3468 SolverLogger* logger,
3469 SharedResponseManager* manager =
nullptr) {
3478 std::vector<int64_t> solution(
model_proto.variables_size(), 0);
3479 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
3480 const int ref =
model_proto.solution_hint().vars(i);
3485 if (manager !=
nullptr) {
3488 manager->NewSolution(solution,
"complete_hint",
nullptr);
3490 SOLVER_LOG(logger,
"The solution hint is complete and is feasible.");
3496 "The solution hint is complete, but it is infeasible! we "
3497 "will try to repair it.");
3507 user_timer->
Start();
3509 #if !defined(__PORTABLE_PLATFORM__)
3512 #if !defined(__PORTABLE_PLATFORM__)
3514 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
3519 #if !defined(__PORTABLE_PLATFORM__)
3521 if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
3522 SatParameters params = *
model->GetOrCreate<SatParameters>();
3523 SatParameters flag_params;
3524 CHECK(google::protobuf::TextFormat::ParseFromString(
3525 absl::GetFlag(FLAGS_cp_model_params), &flag_params));
3526 params.MergeFrom(flag_params);
3527 *(
model->GetOrCreate<SatParameters>()) = params;
3532 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
3535 logger->SetLogToStdOut(params.log_to_stdout());
3536 std::string log_string;
3537 if (params.log_to_response()) {
3538 logger->AddInfoLoggingCallback([&log_string](
const std::string&
message) {
3539 absl::StrAppend(&log_string,
message,
"\n");
3545 absl::GetFlag(FLAGS_cp_model_dump_prefix));
3547 #if !defined(__PORTABLE_PLATFORM__)
3551 if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
3552 shared_response_manager->AddFinalResponsePostprocessor(
3554 const std::string
file = absl::StrCat(
3555 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"response.pb.txt");
3556 LOG(INFO) <<
"Dumping response proto to '" <<
file <<
"'.";
3564 shared_response_manager->AddFinalResponsePostprocessor(
3571 if (!log_string.empty()) {
3572 response->set_solve_log(log_string);
3580 shared_response_manager->AddResponsePostprocessor(
3582 &shared_time_limit](CpSolverResponse*
response) {
3586 shared_time_limit->GetElapsedDeterministicTime());
3595 if (!error.empty()) {
3596 SOLVER_LOG(logger,
"Invalid parameters: ", error);
3601 CpSolverResponse status_response;
3603 status_response.set_solution_info(error);
3605 shared_response_manager->AppendResponseToBeMerged(status_response);
3606 return shared_response_manager->GetResponse();
3611 model->GetOrCreate<
TimeLimit>()->ResetLimitFromParameters(params);
3613 #if !defined(__PORTABLE_PLATFORM__)
3615 if (params.catch_sigint_signal()) {
3617 [&shared_time_limit]() { shared_time_limit->Stop(); });
3623 SOLVER_LOG(logger,
"Parameters: ", params.ShortDebugString());
3626 if (params.num_workers() == 0) {
3627 model->GetOrCreate<SatParameters>()->set_num_workers(
3628 params.num_search_workers());
3632 if (params.num_workers() == 0) {
3633 #if !defined(__PORTABLE_PLATFORM__)
3635 const int num_cores =
3636 params.enumerate_all_solutions() || !
model_proto.assumptions().empty()
3638 : std::max<int>(std::thread::hardware_concurrency(), 1);
3640 const int num_cores = 1;
3642 SOLVER_LOG(logger,
"Setting number of workers to ", num_cores);
3643 model->GetOrCreate<SatParameters>()->set_num_workers(num_cores);
3646 if (logger->LoggingIsEnabled() && params.use_absl_random()) {
3654 if (!error.empty()) {
3655 SOLVER_LOG(logger,
"Invalid model: ", error);
3656 CpSolverResponse status_response;
3658 status_response.set_solution_info(error);
3660 shared_response_manager->AppendResponseToBeMerged(status_response);
3661 return shared_response_manager->GetResponse();
3672 if (!params.use_sat_inprocessing() && !
model_proto.has_objective() &&
3674 !
model_proto.has_solution_hint() && !params.enumerate_all_solutions() &&
3675 !params.use_lns_only() && params.num_workers() <= 1 &&
3677 bool is_pure_sat =
true;
3678 for (
const IntegerVariableProto&
var :
model_proto.variables()) {
3679 if (
var.domain_size() != 2 ||
var.domain(0) < 0 ||
var.domain(1) > 1) {
3680 is_pure_sat =
false;
3685 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
3686 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolOr &&
3687 ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolAnd) {
3688 is_pure_sat =
false;
3696 CpSolverResponse final_response =
3698 if (params.fill_tightened_domains_in_response()) {
3699 *final_response.mutable_tightened_variables() =
model_proto.variables();
3701 shared_response_manager->AppendResponseToBeMerged(final_response);
3702 return shared_response_manager->GetResponse();
3709 absl::StrFormat(
"Starting presolve at %.2fs",
wall_timer->
Get()));
3710 CpModelProto new_cp_model_proto;
3711 CpModelProto mapping_proto;
3712 auto context = std::make_unique<PresolveContext>(
model, &new_cp_model_proto,
3716 VLOG(1) <<
"Model found infeasible during copy";
3721 if (absl::GetFlag(FLAGS_cp_model_ignore_objective) &&
3722 (
context->working_model->has_objective() ||
3723 context->working_model->has_floating_point_objective())) {
3725 context->working_model->clear_objective();
3726 context->working_model->clear_floating_point_objective();
3730 if (params.fix_variables_to_their_hinted_value() &&
3733 " variables to their value in the solution hints.");
3734 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
3738 const IntegerVariableProto& var_proto =
3740 const std::string var_name = var_proto.name().empty()
3741 ? absl::StrCat(
"var(",
var,
")")
3745 SOLVER_LOG(logger,
"Hint found infeasible when assigning variable '",
3746 var_name,
"' with domain", var_domain.
ToString(),
3747 " the value ",
value);
3757 if (!
context->ModelIsUnsat()) {
3758 TestSolutionHintForFeasibility(
model_proto, logger);
3764 shared_response_manager->AddFinalResponsePostprocessor(
3766 &logger](CpSolverResponse*
response) {
3767 if (
response->solution().empty())
return;
3770 const auto& float_obj =
model_proto.floating_point_objective();
3771 double value = float_obj.offset();
3772 const int num_terms = float_obj.vars().size();
3773 for (
int i = 0; i < num_terms; ++i) {
3774 value += float_obj.coeffs(i) *
3775 static_cast<double>(
response->solution(float_obj.vars(i)));
3782 if (!mapping_proto.has_objective())
return;
3783 const CpObjectiveProto& integer_obj = mapping_proto.objective();
3784 *
response->mutable_integer_objective() = integer_obj;
3789 if (params.mip_compute_true_objective_bound() &&
3790 !integer_obj.scaling_was_exact()) {
3791 const int64_t integer_lb = response->inner_objective_lower_bound();
3792 const double lb = ComputeTrueObjectiveLowerBound(
3793 model_proto, integer_obj, integer_lb);
3794 SOLVER_LOG(logger,
"[Scaling] scaled_objective_bound: ",
3795 response->best_objective_bound(),
3796 " corrected_bound: ", lb,
3797 " delta: ", response->best_objective_bound() - lb);
3801 if (float_obj.maximize()) {
3802 response->set_best_objective_bound(
3803 std::max(lb, response->objective_value()));
3805 response->set_best_objective_bound(
3806 std::min(lb, response->objective_value()));
3813 const double gap = std::abs(response->objective_value() -
3814 response->best_objective_bound());
3815 if (gap > params.absolute_gap_limit()) {
3817 "[Scaling] Warning: OPTIMAL was reported, yet the "
3819 gap,
") is greater than requested absolute limit (",
3820 params.absolute_gap_limit(),
").");
3827 (params.num_workers() > 1 ||
model_proto.has_objective() ||
3829 params.enumerate_all_solutions())) {
3832 "Warning: solving with assumptions was requested in a non-fully "
3833 "supported setting.\nWe will assumes these assumptions true while "
3834 "solving, but if the model is infeasible, you will not get a useful "
3835 "'sufficient_assumptions_for_infeasibility' field in the response, it "
3836 "will include all assumptions.");
3844 shared_response_manager->AddFinalResponsePostprocessor(
3849 *
response->mutable_sufficient_assumptions_for_infeasibility() =
3854 new_cp_model_proto.clear_assumptions();
3856 context->InitializeNewDomains();
3858 if (!
context->SetLiteralToTrue(ref)) {
3859 CpSolverResponse status_response;
3861 status_response.add_sufficient_assumptions_for_infeasibility(ref);
3863 shared_response_manager->AppendResponseToBeMerged(status_response);
3864 return shared_response_manager->GetResponse();
3870 std::vector<int> postsolve_mapping;
3871 const CpSolverStatus presolve_status =
3873 if (presolve_status != CpSolverStatus::UNKNOWN) {
3874 SOLVER_LOG(logger,
"Problem closed by presolve.");
3875 CpSolverResponse status_response;
3876 status_response.set_status(presolve_status);
3878 shared_response_manager->AppendResponseToBeMerged(status_response);
3879 return shared_response_manager->GetResponse();
3885 if (params.cp_model_presolve()) {
3886 shared_response_manager->AddSolutionPostprocessor(
3888 &postsolve_mapping](std::vector<int64_t>* solution) {
3889 AddPostsolveClauses(postsolve_mapping,
model, &mapping_proto);
3890 PostsolveResponseWrapper(params,
model_proto.variables_size(),
3891 mapping_proto, postsolve_mapping, solution);
3893 shared_response_manager->AddResponsePostprocessor(
3895 &postsolve_mapping](CpSolverResponse*
response) {
3899 ->mutable_sufficient_assumptions_for_infeasibility())) {
3901 ? postsolve_mapping[ref]
3904 if (!
response->solution().empty()) {
3907 std::vector<int64_t>(
response->solution().begin(),
3909 &mapping_proto, &postsolve_mapping))
3910 <<
"postsolved solution";
3912 if (params.fill_tightened_domains_in_response()) {
3915 if (mapping_proto.variables().size() >=
3917 for (
int i = 0; i <
model_proto.variables().size(); ++i) {
3918 *
response->add_tightened_variables() =
3919 mapping_proto.variables(i);
3925 shared_response_manager->AddFinalResponsePostprocessor(
3927 if (!
response->solution().empty()) {
3933 shared_response_manager->AddResponsePostprocessor(
3936 const int initial_size =
model_proto.variables_size();
3937 if (
response->solution_size() > 0) {
3938 response->mutable_solution()->Truncate(initial_size);
3940 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
3943 std::vector<int64_t>(
response->solution().begin(),
3947 if (params.fill_tightened_domains_in_response()) {
3956 const auto& observers =
model->GetOrCreate<SolutionObservers>()->observers;
3957 if (!observers.empty()) {
3958 shared_response_manager->AddSolutionCallback(
3959 [&observers](
const CpSolverResponse&
response) {
3960 for (
const auto& observer : observers) {
3967 if (params.stop_after_first_solution()) {
3968 shared_response_manager->AddSolutionCallback(
3969 [shared_time_limit](
const CpSolverResponse&) {
3970 shared_time_limit->Stop();
3974 #if !defined(__PORTABLE_PLATFORM__)
3975 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
3976 DumpModelProto(new_cp_model_proto,
"presolved_model");
3977 DumpModelProto(mapping_proto,
"mapping_model");
3982 MPModelProto mip_model;
3984 DumpModelProto(mip_model,
"presolved_mp_model");
3989 if (params.stop_after_presolve() || shared_time_limit->LimitReached()) {
3990 int64_t num_terms = 0;
3991 for (
const ConstraintProto&
ct : new_cp_model_proto.constraints()) {
3995 logger,
"Stopped after presolve.",
3996 "\nPresolvedNumVariables: ", new_cp_model_proto.variables().size(),
3997 "\nPresolvedNumConstraints: ", new_cp_model_proto.constraints().size(),
3998 "\nPresolvedNumTerms: ", num_terms);
4000 CpSolverResponse status_response;
4002 shared_response_manager->AppendResponseToBeMerged(status_response);
4003 return shared_response_manager->GetResponse();
4013 if (new_cp_model_proto.has_objective()) {
4014 shared_response_manager->InitializeObjective(new_cp_model_proto);
4015 shared_response_manager->SetGapLimitsFromParameters(params);
4020 shared_response_manager->UpdateGapIntegral();
4033 if (!params.enumerate_all_solutions()) {
4034 TestSolutionHintForFeasibility(new_cp_model_proto, logger,
4035 shared_response_manager);
4037 TestSolutionHintForFeasibility(new_cp_model_proto, logger,
nullptr);
4040 if (params.symmetry_level() > 1) {
4044 LoadDebugSolution(new_cp_model_proto,
model);
4046 #if defined(__PORTABLE_PLATFORM__)
4050 if (params.num_workers() > 1 || params.interleave_search() ||
4051 !params.subsolvers().empty()) {
4052 SolveCpModelParallel(new_cp_model_proto,
model);
4054 }
else if (!
model->GetOrCreate<TimeLimit>()->LimitReached()) {
4056 SOLVER_LOG(logger, absl::StrFormat(
"Starting to load the model at %.2fs",
4058 shared_response_manager->SetUpdateGapIntegralOnEachChange(
true);
4064 local_model.Register<TimeLimit>(
model->GetOrCreate<TimeLimit>());
4065 local_model.Register<SatParameters>(
model->GetOrCreate<SatParameters>());
4066 local_model.Register<SharedStatistics>(
4067 model->GetOrCreate<SharedStatistics>());
4068 local_model.Register<SharedResponseManager>(shared_response_manager);
4070 LoadCpModel(new_cp_model_proto, &local_model);
4073 SOLVER_LOG(logger, absl::StrFormat(
"Starting sequential search at %.2fs",
4075 if (params.repair_hint()) {
4076 MinimizeL1DistanceWithHint(new_cp_model_proto, &local_model);
4078 QuickSolveWithHint(new_cp_model_proto, &local_model);
4080 SolveLoadedCpModel(new_cp_model_proto, &local_model);
4082 CpSolverResponse status_response;
4084 shared_response_manager->AppendResponseToBeMerged(status_response);
4087 if (logger->LoggingIsEnabled()) {
4089 *local_model.GetOrCreate<LinearProgrammingConstraintCollection>();
4092 for (
const auto* lp : lps) {
4100 if (logger->LoggingIsEnabled()) {
4101 model->GetOrCreate<SharedStatistics>()->Log(logger);
4103 return shared_response_manager->GetResponse();
4112 const SatParameters& params) {
4118 #if !defined(__PORTABLE_PLATFORM__)
4120 const std::string& params) {
bool AddEdge(int node1, int node2)
std::vector< int > GetComponentIds()
void SetNumberOfNodes(int num_nodes)
int GetNumberOfComponents() const
We call domain any subset of Int64 = [kint64min, kint64max].
std::string ToString() const
Returns a compact string of a vector of intervals like "[1,4][6][10,20]".
void EnableLogging(bool enable)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Literal(int signed_value)
Class that owns everything related to a particular optimization model.
void Register(T *non_owned_class)
Register a non-owned class that will be "singleton" in the model.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
void set_dump_prefix(const std::string &dump_prefix)
SharedBoundsManager * bounds
SharedClausesManager * clauses
SharedRelaxationSolutionRepository * relaxation_solutions
SharedLPSolutionRepository * lp_solutions
CpModelProto const * model_proto
SharedIncompleteSolutionManager * incomplete_solutions
ABSL_FLAG(std::string, cp_model_dump_prefix, "/tmp/", "Prefix filename for all dumped files")
SharedResponseManager * response
ModelSharedTimeLimit * time_limit
GurobiMPCallbackContext * context
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
absl::Status GetTextProto(const absl::string_view &filename, google::protobuf::Message *proto, int flags)
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &observer)
Creates a solution observer with the model with model.Add(NewFeasibleSolutionObserver([](response){....
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
void DetectAndAddSymmetryToProto(const SatParameters ¶ms, CpModelProto *proto, SolverLogger *logger)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
uint64_t FingerprintRepeatedField(const google::protobuf::RepeatedField< T > &sequence, uint64_t seed)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
Creates parameters for the solver, which you can add to the model with.
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
Returns a string with some statistics on the solver response.
bool LoadConstraint(const ConstraintProto &ct, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool ConvertCpModelProtoToMPModelProto(const CpModelProto &input, MPModelProto *output)
bool RefIsPositive(int ref)
std::string ValidateParameters(const SatParameters ¶ms)
void ExtractElementEncoding(const CpModelProto &model_proto, Model *m)
CpSolverResponse SolveWithParameters(const CpModelProto &model_proto, const std::string ¶ms)
Solves the given CpModelProto with the given sat parameters as string in JSon format,...
std::string CpSatSolverVersion()
Returns a string that describes the version of the solver.
std::string ValidateInputCpModel(const SatParameters ¶ms, const CpModelProto &model)
const LiteralIndex kNoLiteralIndex(-1)
std::function< BooleanOrIntegerLiteral()> ConstructUserSearchStrategy(const CpModelProto &cp_model_proto, Model *model)
bool SolutionIsFeasible(const CpModelProto &model, absl::Span< const int64_t > variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
bool WriteModelProtoToFile(const M &proto, absl::string_view filename)
void PostsolveResponse(const int64_t num_variables_in_original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, std::vector< int64_t > *solution)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
void DeterministicLoop(const std::vector< std::unique_ptr< SubSolver >> &subsolvers, int num_threads, int batch_size)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
std::function< BooleanOrIntegerLiteral()> FollowHint(const std::vector< BooleanOrIntegerVariable > &vars, const std::vector< IntegerValue > &values, Model *model)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64_t value)
std::function< BooleanOrIntegerLiteral()> ConstructFixedSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, IntegerVariable objective_var, Model *model)
void ConfigureSearchHeuristics(Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
void NonDeterministicLoop(const std::vector< std::unique_ptr< SubSolver >> &subsolvers, int num_threads)
void SplitAndLoadIntermediateConstraints(bool lb_required, bool ub_required, std::vector< IntegerVariable > *vars, std::vector< int64_t > *coeffs, Model *m)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::function< SatParameters(Model *)> NewSatParameters(const sat::SatParameters ¶meters)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
std::string FormatCounter(int64_t num)
std::string CpModelStats(const CpModelProto &model_proto)
Returns a string with some statistics on the given CpModelProto.
std::vector< SatParameters > GetDiverseSetOfParameters(const SatParameters &base_params, const CpModelProto &cp_model)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> ExcludeCurrentSolutionWithoutIgnoredVariableAndBacktrack()
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, absl::Span< const int64_t > solution)
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
void FillSolveStatsInResponse(Model *model, CpSolverResponse *response)
constexpr uint64_t kDefaultFingerprintSeed
CpSolverStatus PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
SatSolver::Status SolveWithPresolve(std::unique_ptr< SatSolver > *solver, TimeLimit *time_limit, std::vector< bool > *solution, DratProofHandler *drat_proof_handler, SolverLogger *logger)
std::vector< SatParameters > GetFirstSolutionParams(const SatParameters &base_params, const CpModelProto &cp_model, int num_params_to_generate)
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
bool VariableIsPositive(IntegerVariable i)
uint64_t FingerprintModel(const CpModelProto &model, uint64_t seed)
std::function< IntegerVariable(Model *)> ConstantIntegerVariable(int64_t value)
LinearRelaxation ComputeLinearRelaxation(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
std::function< BooleanOrIntegerLiteral()> InstrumentSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, const std::function< BooleanOrIntegerLiteral()> &instrumented_strategy, Model *model)
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
Collection of objects used to extend the Constraint Solver library.
const absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
std::string OrToolsVersionString()
std::string ProtobufDebugString(const P &message)
std::mt19937_64 random_engine_t
static int input(yyscan_t yyscanner)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< std::function< void(const CpSolverResponse &response)> > observers
#define SOLVER_LOG(logger,...)
#define VLOG(verboselevel)
#define VLOG_IS_ON(verboselevel)