34#include "absl/base/log_severity.h"
35#include "absl/base/thread_annotations.h"
36#include "absl/container/btree_map.h"
37#include "absl/container/btree_set.h"
38#include "absl/container/flat_hash_map.h"
39#include "absl/flags/flag.h"
40#include "absl/log/check.h"
41#include "absl/log/log.h"
42#include "absl/log/vlog_is_on.h"
43#include "absl/random/distributions.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/string_view.h"
48#include "absl/strings/strip.h"
49#include "absl/synchronization/mutex.h"
50#include "absl/types/span.h"
51#include "google/protobuf/arena.h"
52#include "google/protobuf/text_format.h"
105 bool, cp_model_export_model,
false,
106 "DEBUG ONLY. When set to true, SolveCpModel() will dump its input model "
107 "protos in text format to 'FLAGS_cp_model_dump_prefix'{name}.pb.txt.");
110 "DEBUG ONLY, dump models in text proto instead of binary proto.");
113 bool, cp_model_dump_problematic_lns,
false,
114 "DEBUG ONLY. Similar to --cp_model_dump_submodels, but only dump fragment "
115 "for which we got an issue while validating the postsolved solution. This "
116 "allows to debug presolve issues without dumping all the models.");
119 "DEBUG ONLY. If true, the final response of each solve will be "
120 "dumped to 'FLAGS_cp_model_dump_prefix'response.pb.txt");
123 "This is interpreted as a text SatParameters proto. The "
124 "specified fields will override the normal ones for all solves.");
127 "If true, ignore the objective.");
129 "If true, ignore any supplied hints.");
131 "If true, ignore any supplied hints, but if the hint is valid and "
132 "complete, validate that no buggy propagator make it infeasible.");
133ABSL_FLAG(
bool, cp_model_fingerprint_model,
true,
"Fingerprint the model.");
135ABSL_FLAG(
bool, cp_model_check_intermediate_solutions,
false,
136 "When true, all intermediate solutions found by the solver will be "
137 "checked. This can be expensive, therefore it is off by default.");
149std::string Summarize(absl::string_view
input) {
150 if (
input.size() < 105)
return std::string(
input);
152 return absl::StrCat(
input.substr(0, half),
" ... ",
157void DumpModelProto(
const M& proto, absl::string_view name) {
158 std::string filename;
159 if (absl::GetFlag(FLAGS_cp_model_dump_text_proto)) {
160 filename = absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix), name,
162 LOG(INFO) <<
"Dumping " << name <<
" text proto to '" << filename <<
"'.";
165 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix), name,
".bin");
166 LOG(INFO) <<
"Dumping " << name <<
" binary proto to '" << filename <<
"'.";
173 IntegerValue result = expr.offset();
174 for (
int i = 0;
i < expr.vars_size(); ++
i) {
176 if (expr.coeffs(
i) > 0) {
177 result += expr.coeffs(
i) * var_proto.domain(0);
179 result += expr.coeffs(
i) * var_proto.domain(var_proto.domain_size() - 1);
187 IntegerValue result = expr.offset();
188 for (
int i = 0;
i < expr.vars_size(); ++
i) {
190 if (expr.coeffs(
i) > 0) {
191 result += expr.coeffs(
i) * var_proto.domain(var_proto.domain_size() - 1);
193 result += expr.coeffs(
i) * var_proto.domain(0);
201 std::vector<RectangleInRange> non_fixed_boxes;
202 std::vector<Rectangle> fixed_boxes;
203 for (
int i = 0;
i < ct.no_overlap_2d().x_intervals_size(); ++
i) {
204 const int x_interval = ct.no_overlap_2d().x_intervals(
i);
205 const int y_interval = ct.no_overlap_2d().y_intervals(
i);
214 .x_min = ExprMin(x_ct.interval().start(), model_proto),
215 .x_max = ExprMax(x_ct.interval().end(), model_proto),
216 .y_min = ExprMin(y_ct.interval().start(), model_proto),
217 .y_max = ExprMax(y_ct.interval().end(), model_proto),
220 .x_size = ExprMin(x_ct.interval().size(), model_proto),
221 .y_size = ExprMin(y_ct.interval().size(), model_proto)};
222 if (box.x_size == box.bounding_area.SizeX() &&
223 box.y_size == box.bounding_area.SizeY()) {
224 fixed_boxes.push_back(box.bounding_area);
226 non_fixed_boxes.push_back(box);
229 VLOG(2) <<
"NoOverlap2D with " << fixed_boxes.size() <<
" fixed boxes and "
230 << non_fixed_boxes.size() <<
" non-fixed boxes.";
232 Rectangle bounding_box = non_fixed_boxes.front().bounding_area;
234 bounding_box.GrowToInclude(r.bounding_area);
236 VLOG(3) <<
"Fixed boxes: " <<
RenderDot(bounding_box, fixed_boxes);
237 std::vector<Rectangle> non_fixed_boxes_to_render;
238 for (
const auto& r : non_fixed_boxes) {
239 non_fixed_boxes_to_render.push_back(r.bounding_area);
241 VLOG(3) <<
"Non-fixed boxes: "
242 <<
RenderDot(bounding_box, non_fixed_boxes_to_render);
243 VLOG(3) <<
"BB: " << bounding_box
244 <<
" non-fixed boxes: " << absl::StrJoin(non_fixed_boxes,
", ");
245 VLOG(3) <<
"BB size: " << bounding_box.SizeX() <<
"x" << bounding_box.SizeY()
246 <<
" non-fixed boxes sizes: "
247 << absl::StrJoin(non_fixed_boxes,
", ",
249 absl::StrAppend(out, r.bounding_area.SizeX(),
"x",
250 r.bounding_area.SizeY());
252 std::vector<Rectangle> sizes_to_render;
253 IntegerValue
x = bounding_box.x_min;
255 for (
const auto& r : non_fixed_boxes) {
257 .x_min =
x, .x_max =
x + r.x_size, .y_min = y, .y_max = y + r.y_size});
259 if (x > bounding_box.x_max) {
264 VLOG(3) <<
"Sizes: " <<
RenderDot(bounding_box, sizes_to_render);
276 absl::flat_hash_map<char const*, int> name_to_num_constraints;
277 absl::flat_hash_map<char const*, int> name_to_num_reified;
278 absl::flat_hash_map<char const*, int> name_to_num_multi_reified;
279 absl::flat_hash_map<char const*, int> name_to_num_literals;
280 absl::flat_hash_map<char const*, int> name_to_num_terms;
281 absl::flat_hash_map<char const*, int> name_to_num_complex_domain;
282 absl::flat_hash_map<char const*, int> name_to_num_expressions;
284 int no_overlap_2d_num_rectangles = 0;
285 int no_overlap_2d_num_optional_rectangles = 0;
286 int no_overlap_2d_num_linear_areas = 0;
287 int no_overlap_2d_num_quadratic_areas = 0;
288 int no_overlap_2d_num_fixed_rectangles = 0;
290 int cumulative_num_intervals = 0;
291 int cumulative_num_optional_intervals = 0;
292 int cumulative_num_variable_sizes = 0;
293 int cumulative_num_variable_demands = 0;
294 int cumulative_num_fixed_intervals = 0;
296 int no_overlap_num_intervals = 0;
297 int no_overlap_num_optional_intervals = 0;
298 int no_overlap_num_variable_sizes = 0;
299 int no_overlap_num_fixed_intervals = 0;
301 int num_fixed_intervals = 0;
310 if (ct.linear().vars_size() == 0) name =
"kLinear0";
311 if (ct.linear().vars_size() == 1) name =
"kLinear1";
312 if (ct.linear().vars_size() == 2) name =
"kLinear2";
313 if (ct.linear().vars_size() == 3) name =
"kLinear3";
314 if (ct.linear().vars_size() > 3) name =
"kLinearN";
316 ct.enforcement_literal().size() > 1) {
320 name =
"kBoolAndClauses";
325 name_to_num_constraints[name]++;
326 if (!ct.enforcement_literal().empty()) {
327 name_to_num_reified[name]++;
328 if (ct.enforcement_literal().size() > 1) {
329 name_to_num_multi_reified[name]++;
333 auto variable_is_fixed = [&model_proto](
int ref) {
336 return proto.
domain_size() == 2 && proto.domain(0) == proto.domain(1);
339 auto expression_is_fixed =
341 for (
const int ref : expr.vars()) {
342 if (!variable_is_fixed(ref)) {
349 auto interval_has_fixed_size = [&model_proto, &expression_is_fixed](
int c) {
350 return expression_is_fixed(model_proto.constraints(c).interval().size());
353 auto constraint_is_optional = [&model_proto](
int i) {
354 return !model_proto.constraints(
i).enforcement_literal().empty();
357 auto interval_is_fixed = [&variable_is_fixed,
362 for (
const int lit : ct.enforcement_literal()) {
363 if (!variable_is_fixed(lit))
return false;
365 return (expression_is_fixed(ct.interval().start()) &&
366 expression_is_fixed(ct.interval().size()) &&
367 expression_is_fixed(ct.interval().end()));
373 name_to_num_literals[name] += ct.bool_or().literals().size();
374 }
else if (ct.constraint_case() ==
376 name_to_num_literals[name] +=
377 ct.enforcement_literal().size() + ct.bool_and().literals().size();
378 }
else if (ct.constraint_case() ==
380 name_to_num_literals[name] += ct.at_most_one().literals().size();
381 }
else if (ct.constraint_case() ==
383 name_to_num_literals[name] += ct.exactly_one().literals().size();
384 }
else if (ct.constraint_case() ==
386 name_to_num_expressions[name] += ct.lin_max().exprs().size();
387 }
else if (ct.constraint_case() ==
389 if (interval_is_fixed(ct)) num_fixed_intervals++;
390 }
else if (ct.constraint_case() ==
392 const int num_boxes = ct.no_overlap_2d().x_intervals_size();
393 no_overlap_2d_num_rectangles += num_boxes;
394 for (
int i = 0;
i < num_boxes; ++
i) {
395 const int x_interval = ct.no_overlap_2d().x_intervals(
i);
396 const int y_interval = ct.no_overlap_2d().y_intervals(
i);
397 if (constraint_is_optional(x_interval) ||
398 constraint_is_optional(y_interval)) {
399 no_overlap_2d_num_optional_rectangles++;
401 const int num_fixed = interval_has_fixed_size(x_interval) +
402 interval_has_fixed_size(y_interval);
403 if (num_fixed == 0) {
404 no_overlap_2d_num_quadratic_areas++;
405 }
else if (num_fixed == 1) {
406 no_overlap_2d_num_linear_areas++;
408 if (interval_is_fixed(model_proto.constraints(x_interval)) &&
409 interval_is_fixed(model_proto.constraints(y_interval))) {
410 no_overlap_2d_num_fixed_rectangles++;
414 DumpNoOverlap2dProblem(ct, model_proto);
416 }
else if (ct.constraint_case() ==
418 const int num_intervals = ct.no_overlap().intervals_size();
419 no_overlap_num_intervals += num_intervals;
420 for (
int i = 0;
i < num_intervals; ++
i) {
421 const int interval = ct.no_overlap().intervals(
i);
422 if (constraint_is_optional(interval)) {
423 no_overlap_num_optional_intervals++;
425 if (!interval_has_fixed_size(interval)) {
426 no_overlap_num_variable_sizes++;
428 if (interval_is_fixed(model_proto.constraints(interval))) {
429 no_overlap_num_fixed_intervals++;
432 }
else if (ct.constraint_case() ==
434 const int num_intervals = ct.cumulative().intervals_size();
435 cumulative_num_intervals += num_intervals;
436 for (
int i = 0;
i < num_intervals; ++
i) {
437 const int interval = ct.cumulative().intervals(
i);
438 if (constraint_is_optional(interval)) {
439 cumulative_num_optional_intervals++;
441 if (!interval_has_fixed_size(interval)) {
442 cumulative_num_variable_sizes++;
444 if (!expression_is_fixed(ct.cumulative().demands(
i))) {
445 cumulative_num_variable_demands++;
447 if (interval_is_fixed(model_proto.constraints(interval))) {
448 cumulative_num_fixed_intervals++;
454 ct.linear().vars_size() > 3) {
455 name_to_num_terms[name] += ct.linear().vars_size();
458 ct.linear().vars_size() > 1 && ct.linear().domain().size() > 2) {
459 name_to_num_complex_domain[name]++;
463 int num_constants = 0;
464 absl::btree_set<int64_t> constant_values;
465 absl::btree_map<Domain, int> num_vars_per_domains;
467 if (var.domain_size() == 2 && var.domain(0) == var.domain(1)) {
469 constant_values.insert(var.domain(0));
476 const std::string model_fingerprint_str =
477 (absl::GetFlag(FLAGS_cp_model_fingerprint_model))
478 ? absl::StrFormat(
" (model_fingerprint: %#x)",
482 if (model_proto.has_objective() ||
483 model_proto.has_floating_point_objective()) {
484 absl::StrAppend(&result,
"optimization model '", model_proto.name(),
485 "':", model_fingerprint_str,
"\n");
487 absl::StrAppend(&result,
"satisfaction model '", model_proto.name(),
488 "':", model_fingerprint_str,
"\n");
492 absl::StrAppend(&result,
"Search strategy: on ",
493 strategy.exprs().size() + strategy.variables().size(),
496 strategy.variable_selection_strategy()),
499 strategy.domain_reduction_strategy()),
503 auto count_variables_by_type =
504 [&model_proto](
const google::protobuf::RepeatedField<int>& vars,
505 int* num_booleans,
int* num_integers) {
506 for (
const int ref : vars) {
507 const auto& var_proto = model_proto.variables(
PositiveRef(ref));
508 if (var_proto.domain_size() == 2 && var_proto.domain(0) == 0 &&
509 var_proto.domain(1) == 1) {
513 *num_integers = vars.size() - *num_booleans;
517 int num_boolean_variables_in_objective = 0;
518 int num_integer_variables_in_objective = 0;
519 if (model_proto.has_objective()) {
520 count_variables_by_type(model_proto.objective().vars(),
521 &num_boolean_variables_in_objective,
522 &num_integer_variables_in_objective);
524 if (model_proto.has_floating_point_objective()) {
525 count_variables_by_type(model_proto.floating_point_objective().vars(),
526 &num_boolean_variables_in_objective,
527 &num_integer_variables_in_objective);
530 std::vector<std::string> obj_vars_strings;
531 if (num_boolean_variables_in_objective > 0) {
532 obj_vars_strings.push_back(absl::StrCat(
533 "#bools: ",
FormatCounter(num_boolean_variables_in_objective)));
535 if (num_integer_variables_in_objective > 0) {
536 obj_vars_strings.push_back(absl::StrCat(
537 "#ints: ",
FormatCounter(num_integer_variables_in_objective)));
540 const std::string objective_string =
541 model_proto.has_objective()
542 ? absl::StrCat(
" (", absl::StrJoin(obj_vars_strings,
" "),
544 : (model_proto.has_floating_point_objective()
545 ? absl::StrCat(
" (", absl::StrJoin(obj_vars_strings,
" "),
546 " in floating point objective)")
548 absl::StrAppend(&result,
550 objective_string,
" (",
553 " primary variables)\n");
555 if (num_vars_per_domains.contains(Domain(0, 1))) {
557 const int num_bools = num_vars_per_domains[Domain(0, 1)];
558 const std::string temp =
559 absl::StrCat(
" - ",
FormatCounter(num_bools),
" Booleans in ",
561 absl::StrAppend(&result, Summarize(temp));
562 num_vars_per_domains.erase(Domain(0, 1));
564 if (num_vars_per_domains.size() < 100) {
565 for (
const auto& entry : num_vars_per_domains) {
566 const std::string temp =
568 entry.first.ToString(),
"\n");
569 absl::StrAppend(&result, Summarize(temp));
572 int64_t max_complexity = 0;
573 int64_t min = std::numeric_limits<int64_t>::max();
574 int64_t max = std::numeric_limits<int64_t>::min();
575 for (
const auto& entry : num_vars_per_domains) {
576 min = std::min(min, entry.first.Min());
577 max = std::max(max, entry.first.Max());
578 max_complexity = std::max(
579 max_complexity,
static_cast<int64_t
>(entry.first.NumIntervals()));
581 absl::StrAppend(&result,
" - ",
FormatCounter(num_vars_per_domains.size()),
582 " different domains in [", min,
",", max,
583 "] with a largest complexity of ", max_complexity,
".\n");
586 if (num_constants > 0) {
587 const std::string temp =
588 absl::StrCat(
" - ",
FormatCounter(num_constants),
" constants in {",
589 absl::StrJoin(constant_values,
","),
"} \n");
590 absl::StrAppend(&result, Summarize(temp));
593 std::vector<std::string> constraints;
594 constraints.reserve(name_to_num_constraints.size());
595 for (
const auto& [c_name, count] : name_to_num_constraints) {
596 const std::string name(c_name);
597 constraints.push_back(absl::StrCat(
"#", name,
": ",
FormatCounter(count)));
598 if (name_to_num_reified.contains(c_name)) {
599 if (name_to_num_multi_reified.contains(c_name)) {
603 " #multi: ",
FormatCounter(name_to_num_multi_reified[c_name]),
")");
605 absl::StrAppend(&constraints.back(),
" (#enforced: ",
609 if (name_to_num_literals.contains(c_name)) {
610 absl::StrAppend(&constraints.back(),
" (#literals: ",
613 if (name_to_num_terms.contains(c_name)) {
614 absl::StrAppend(&constraints.back(),
618 if (name_to_num_expressions.contains(c_name)) {
619 absl::StrAppend(&constraints.back(),
" (#expressions: ",
622 if (name_to_num_complex_domain.contains(c_name)) {
623 absl::StrAppend(&constraints.back(),
" (#complex_domain: ",
626 if (name ==
"kInterval" && num_fixed_intervals > 0) {
627 absl::StrAppend(&constraints.back(),
629 }
else if (name ==
"kNoOverlap2D") {
630 absl::StrAppend(&constraints.back(),
" (#rectangles: ",
632 if (no_overlap_2d_num_optional_rectangles > 0) {
633 absl::StrAppend(&constraints.back(),
", #optional: ",
636 if (no_overlap_2d_num_linear_areas > 0) {
637 absl::StrAppend(&constraints.back(),
", #linear_areas: ",
640 if (no_overlap_2d_num_quadratic_areas > 0) {
641 absl::StrAppend(&constraints.back(),
", #quadratic_areas: ",
644 if (no_overlap_2d_num_fixed_rectangles > 0) {
645 absl::StrAppend(&constraints.back(),
", #fixed_rectangles: ",
648 absl::StrAppend(&constraints.back(),
")");
649 }
else if (name ==
"kCumulative") {
650 absl::StrAppend(&constraints.back(),
" (#intervals: ",
652 if (cumulative_num_optional_intervals > 0) {
653 absl::StrAppend(&constraints.back(),
", #optional: ",
656 if (cumulative_num_variable_sizes > 0) {
657 absl::StrAppend(&constraints.back(),
", #variable_sizes: ",
660 if (cumulative_num_variable_demands > 0) {
661 absl::StrAppend(&constraints.back(),
", #variable_demands: ",
662 cumulative_num_variable_demands);
664 if (cumulative_num_fixed_intervals > 0) {
665 absl::StrAppend(&constraints.back(),
", #fixed_intervals: ",
668 absl::StrAppend(&constraints.back(),
")");
669 }
else if (name ==
"kNoOverlap") {
670 absl::StrAppend(&constraints.back(),
" (#intervals: ",
672 if (no_overlap_num_optional_intervals > 0) {
673 absl::StrAppend(&constraints.back(),
", #optional: ",
676 if (no_overlap_num_variable_sizes > 0) {
677 absl::StrAppend(&constraints.back(),
", #variable_sizes: ",
680 if (no_overlap_num_fixed_intervals > 0) {
681 absl::StrAppend(&constraints.back(),
", #fixed_intervals: ",
684 absl::StrAppend(&constraints.back(),
")");
687 std::sort(constraints.begin(), constraints.end());
688 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
694 bool has_objective) {
696 absl::StrAppend(&result,
"CpSolverResponse summary:");
697 absl::StrAppend(&result,
701 absl::StrAppendFormat(&result,
"\nobjective: %.16g",
702 response.objective_value());
703 absl::StrAppendFormat(&result,
"\nbest_bound: %.16g",
704 response.best_objective_bound());
706 absl::StrAppend(&result,
"\nobjective: NA");
707 absl::StrAppend(&result,
"\nbest_bound: NA");
710 absl::StrAppend(&result,
"\nintegers: ", response.num_integers());
711 absl::StrAppend(&result,
"\nbooleans: ", response.num_booleans());
712 absl::StrAppend(&result,
"\nconflicts: ", response.num_conflicts());
713 absl::StrAppend(&result,
"\nbranches: ", response.num_branches());
717 absl::StrAppend(&result,
718 "\npropagations: ", response.num_binary_propagations());
720 &result,
"\ninteger_propagations: ", response.num_integer_propagations());
722 absl::StrAppend(&result,
"\nrestarts: ", response.num_restarts());
723 absl::StrAppend(&result,
"\nlp_iterations: ", response.num_lp_iterations());
724 absl::StrAppend(&result,
"\nwalltime: ", response.wall_time());
725 absl::StrAppend(&result,
"\nusertime: ", response.user_time());
726 absl::StrAppend(&result,
727 "\ndeterministic_time: ", response.deterministic_time());
728 absl::StrAppend(&result,
"\ngap_integral: ", response.gap_integral());
729 if (!response.solution().empty()) {
730 absl::StrAppendFormat(
731 &result,
"\nsolution_fingerprint: %#x",
734 absl::StrAppend(&result,
"\n");
740void LogSubsolverNames(absl::Span<
const std::unique_ptr<SubSolver>> subsolvers,
741 absl::Span<const std::string> ignored,
742 SolverLogger* logger) {
743 if (!logger->LoggingIsEnabled())
return;
745 std::vector<std::string> full_problem_solver_names;
746 std::vector<std::string> incomplete_solver_names;
747 std::vector<std::string> first_solution_solver_names;
748 std::vector<std::string> helper_solver_names;
749 for (
int i = 0;
i < subsolvers.size(); ++
i) {
750 const auto& subsolver = subsolvers[
i];
751 switch (subsolver->type()) {
753 full_problem_solver_names.push_back(subsolver->name());
756 incomplete_solver_names.push_back(subsolver->name());
759 first_solution_solver_names.push_back(subsolver->name());
762 helper_solver_names.push_back(subsolver->name());
769 auto display_subsolver_list = [logger](absl::Span<const std::string> names,
770 const absl::string_view type_name) {
771 if (!names.empty()) {
772 absl::btree_map<std::string, int> solvers_and_count;
773 for (
const auto& name : names) {
774 solvers_and_count[name]++;
776 std::vector<std::string> counted_names;
777 for (
const auto& [name, count] : solvers_and_count) {
779 counted_names.push_back(name);
781 counted_names.push_back(absl::StrCat(name,
"(", count,
")"));
785 logger, names.size(),
" ",
786 absl::StrCat(type_name, names.size() == 1 ?
"" :
"s"),
": [",
787 absl::StrJoin(counted_names.begin(), counted_names.end(),
", "),
"]");
791 display_subsolver_list(full_problem_solver_names,
"full problem subsolver");
792 display_subsolver_list(first_solution_solver_names,
793 "first solution subsolver");
794 display_subsolver_list(incomplete_solver_names,
"interleaved subsolver");
795 display_subsolver_list(helper_solver_names,
"helper subsolver");
796 if (!ignored.empty()) {
797 display_subsolver_list(ignored,
"ignored subsolver");
804 std::vector<std::unique_ptr<SubSolver>>& subsolvers,
805 absl::Span<const std::string> ignored) {
809 if (params.interleave_search()) {
811 absl::StrFormat(
"Starting deterministic search at %.2fs with "
812 "%i workers and batch size of %d.",
813 shared->wall_timer->Get(), params.num_workers(),
814 params.interleave_batch_size()));
818 absl::StrFormat(
"Starting search at %.2fs with %i workers.",
819 shared->wall_timer->Get(), params.num_workers()));
821 LogSubsolverNames(subsolvers, ignored, shared->logger);
824 if (params.interleave_search()) {
825 int batch_size = params.interleave_batch_size();
826 if (batch_size == 0) {
827 batch_size = params.num_workers() == 1 ? 1 : params.num_workers() * 3;
830 "Setting number of tasks in each batch of interleaved search to ",
834 params.max_num_deterministic_batches());
842 for (
int i = 0;
i < subsolvers.size(); ++
i) {
843 subsolvers[
i].reset();
846 shared->shared_tree_manager->CloseLratProof();
847 if (params.check_merged_lrat_proof() && shared->response->ProblemIsSolved() &&
848 !shared->response->HasFeasibleSolution()) {
850 .
Merge(shared->lrat_proof_status->GetProofFilenames());
853 shared->LogFinalStatistics();
857 return model_proto.variables(
i).domain_size() == 2 &&
858 model_proto.variables(
i).domain(0) ==
859 model_proto.variables(
i).domain(1);
865void RestrictObjectiveUsingHint(
CpModelProto* model_proto) {
866 if (!model_proto->has_objective())
return;
867 if (!model_proto->has_solution_hint())
return;
870 const int num_vars = model_proto->variables().size();
872 std::vector<bool> filled(num_vars,
false);
873 std::vector<int64_t>
solution(num_vars, 0);
874 for (
int var = 0; var < num_vars; ++var) {
875 if (VarIsFixed(*model_proto, var)) {
876 solution[var] = model_proto->variables(var).domain(0);
881 const auto& hint_proto = model_proto->solution_hint();
882 const int num_hinted = hint_proto.vars().size();
883 for (
int i = 0;
i < num_hinted; ++
i) {
884 const int var = hint_proto.vars(
i);
886 if (filled[var])
continue;
888 const int64_t value = hint_proto.values(
i);
893 if (num_filled != num_vars)
return;
895 const int64_t obj_upper_bound =
897 const Domain restriction =
898 Domain(std::numeric_limits<int64_t>::min(), obj_upper_bound);
900 if (model_proto->objective().domain().empty()) {
905 model_proto->mutable_objective());
911bool SolutionHintIsCompleteAndFeasible(
912 const CpModelProto& model_proto, SolverLogger* logger =
nullptr,
914 if (!model_proto.has_solution_hint() && model_proto.variables_size() > 0) {
918 int num_active_variables = 0;
919 int num_hinted_variables = 0;
920 for (
int var = 0; var < model_proto.variables_size(); ++var) {
921 if (VarIsFixed(model_proto, var))
continue;
922 ++num_active_variables;
925 for (
int i = 0;
i < model_proto.solution_hint().vars_size(); ++
i) {
926 const int ref = model_proto.solution_hint().vars(
i);
927 if (VarIsFixed(model_proto,
PositiveRef(ref)))
continue;
928 ++num_hinted_variables;
930 CHECK_LE(num_hinted_variables, num_active_variables);
932 if (num_active_variables != num_hinted_variables) {
933 if (logger !=
nullptr) {
935 logger,
"The solution hint is incomplete: ", num_hinted_variables,
936 " out of ", num_active_variables,
" non fixed variables hinted.");
941 std::vector<int64_t>
solution(model_proto.variables_size(), 0);
943 for (
int var = 0; var < model_proto.variables_size(); ++var) {
944 if (VarIsFixed(model_proto, var)) {
945 solution[var] = model_proto.variables(var).domain(0);
949 for (
int i = 0;
i < model_proto.solution_hint().vars_size(); ++
i) {
950 const int ref = model_proto.solution_hint().vars(
i);
952 const int64_t value = model_proto.solution_hint().values(
i);
953 const int64_t hinted_value =
RefIsPositive(ref) ? value : -value;
955 if (!domain.
Contains(hinted_value)) {
956 if (logger !=
nullptr) {
959 "The solution hint is complete but it contains values outside "
960 "of the domain of the variables.");
968 bool breaks_assumptions =
false;
970 for (
const int literal_ref : model_proto.assumptions()) {
973 breaks_assumptions =
true;
978 if (is_feasible && breaks_assumptions) {
979 if (logger !=
nullptr) {
982 "The solution hint is complete and feasible, but it breaks the "
983 "assumptions of the model.");
988 if (manager !=
nullptr && !
solution.empty()) {
991 manager->NewSolution(
solution,
"complete_hint",
nullptr);
992 }
else if (logger !=
nullptr) {
993 std::string message =
"The solution hint is complete and is feasible.";
994 if (model_proto.has_objective()) {
996 &message,
" Its objective value is ",
1000 model_proto.objective(),
1010 if (logger !=
nullptr) {
1012 "The solution hint is complete, but it is infeasible! we "
1013 "will try to repair it.");
1020class FullProblemSolver :
public SubSolver {
1022 FullProblemSolver(absl::string_view name,
1023 const SatParameters& local_parameters,
bool split_in_chunks,
1024 SharedClasses* shared,
bool stop_at_first_solution =
false)
1025 : SubSolver(name, stop_at_first_solution ? FIRST_SOLUTION : FULL_PROBLEM),
1027 split_in_chunks_(split_in_chunks),
1028 stop_at_first_solution_(stop_at_first_solution),
1029 local_model_(SubSolver::name()) {
1031 *(local_model_.GetOrCreate<SatParameters>()) = local_parameters;
1032 shared_->time_limit->UpdateLocalLimit(
1033 local_model_.GetOrCreate<TimeLimit>());
1035 if (stop_at_first_solution) {
1036 local_model_.GetOrCreate<TimeLimit>()
1037 ->RegisterSecondaryExternalBooleanAsLimit(
1038 shared_->response->first_solution_solvers_should_stop());
1043 shared_->RegisterSharedClassesInLocalModel(&local_model_);
1045 std::unique_ptr<LratProofHandler> lrat_proof_handler =
1047 if (lrat_proof_handler !=
nullptr) {
1048 local_model_.Register<LratProofHandler>(lrat_proof_handler.get());
1049 local_model_.TakeOwnership(lrat_proof_handler.release());
1055 auto* logger = local_model_.GetOrCreate<SolverLogger>();
1056 logger->EnableLogging(local_parameters.log_search_progress());
1057 logger->SetLogToStdOut(local_parameters.log_to_stdout());
1060 ~FullProblemSolver()
override {
1061 CpSolverResponse response;
1062 shared_->response->FillSolveStatsInResponse(&local_model_, &response);
1063 shared_->response->AppendResponseToBeMerged(response);
1064 shared_->stat_tables->AddTimingStat(*
this);
1065 shared_->stat_tables->AddLpStat(name(), &local_model_);
1066 shared_->stat_tables->AddSearchStat(name(), &local_model_);
1067 shared_->stat_tables->AddClausesStat(name(), &local_model_);
1068 LratProofHandler* lrat_proof_handler =
1069 local_model_.Mutable<LratProofHandler>();
1070 if (lrat_proof_handler !=
nullptr) {
1071 lrat_proof_handler->Close(
1072 local_model_.GetOrCreate<SatSolver>()->ModelIsUnsat());
1076 bool IsDone()
override {
1079 if (shared_->SearchIsDone())
return true;
1081 return stop_at_first_solution_ &&
1082 shared_->response->first_solution_solvers_should_stop()->load();
1085 bool TaskIsAvailable()
override {
1086 if (IsDone())
return false;
1092 if (shared_->SearchIsDone())
return false;
1094 absl::MutexLock mutex_lock(mutex_);
1095 if (previous_task_is_completed_) {
1096 if (solving_first_chunk_)
return true;
1097 if (split_in_chunks_)
return true;
1102 std::function<void()> GenerateTask(int64_t )
override {
1104 absl::MutexLock mutex_lock(mutex_);
1105 previous_task_is_completed_ =
false;
1108 auto* time_limit = local_model_.GetOrCreate<TimeLimit>();
1109 if (solving_first_chunk_) {
1110 const double init_dtime = time_limit->GetElapsedDeterministicTime();
1117 if (shared_->bounds !=
nullptr) {
1119 shared_->model_proto, shared_->bounds.get(), &local_model_);
1121 shared_->model_proto, shared_->bounds.get(), &local_model_);
1124 if (shared_->linear2_bounds !=
nullptr) {
1131 if (shared_->clauses !=
nullptr) {
1132 const int id = shared_->clauses->RegisterNewId(
1133 local_model_.Name(),
1134 stop_at_first_solution_ &&
1135 local_model_.GetOrCreate<CpModelProto>()->has_objective());
1142 auto* logger = local_model_.GetOrCreate<SolverLogger>();
1145 "Starting subsolver \'%s\' hint search at %.2fs",
1146 name(), shared_->wall_timer->Get()));
1148 if (local_model_.GetOrCreate<SatParameters>()->repair_hint()) {
1155 absl::StrFormat(
"Starting subsolver \'%s\' search at %.2fs",
1156 name(), shared_->wall_timer->Get()));
1159 solving_first_chunk_ =
false;
1162 absl::MutexLock mutex_lock(mutex_);
1163 dtime_since_last_sync_ +=
1164 time_limit->GetElapsedDeterministicTime() - init_dtime;
1167 if (split_in_chunks_) {
1168 previous_task_is_completed_ =
true;
1173 if (split_in_chunks_) {
1176 auto* params = local_model_.GetOrCreate<SatParameters>();
1177 params->set_max_deterministic_time(1);
1178 time_limit->ResetLimitFromParameters(*params);
1179 shared_->time_limit->UpdateLocalLimit(time_limit);
1182 const double saved_dtime = time_limit->GetElapsedDeterministicTime();
1185 absl::MutexLock mutex_lock(mutex_);
1186 previous_task_is_completed_ =
true;
1187 dtime_since_last_sync_ +=
1188 time_limit->GetElapsedDeterministicTime() - saved_dtime;
1195 void Synchronize()
override {
1196 absl::MutexLock mutex_lock(mutex_);
1197 AddTaskDeterministicDuration(dtime_since_last_sync_);
1198 shared_->time_limit->AdvanceDeterministicTime(dtime_since_last_sync_);
1199 dtime_since_last_sync_ = 0.0;
1203 SharedClasses* shared_;
1204 const bool split_in_chunks_;
1205 const bool stop_at_first_solution_;
1210 bool solving_first_chunk_ =
true;
1213 double dtime_since_last_sync_ ABSL_GUARDED_BY(mutex_) = 0.0;
1214 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
1217#if ORTOOLS_TARGET_OS_SUPPORTS_THREADS
1219class FeasibilityPumpSolver :
public SubSolver {
1221 FeasibilityPumpSolver(
const SatParameters& local_parameters,
1222 SharedClasses* shared)
1223 : SubSolver(
"feasibility_pump", INCOMPLETE),
1225 local_model_(std::make_unique<
Model>(name())) {
1227 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
1228 shared_->time_limit->UpdateLocalLimit(
1229 local_model_->GetOrCreate<TimeLimit>());
1230 shared_->RegisterSharedClassesInLocalModel(local_model_.get());
1233 ~FeasibilityPumpSolver()
override {
1234 shared_->stat_tables->AddTimingStat(*
this);
1237 bool IsDone()
override {
return shared_->SearchIsDone(); }
1239 bool TaskIsAvailable()
override {
1240 if (shared_->SearchIsDone())
return false;
1241 absl::MutexLock mutex_lock(mutex_);
1242 return previous_task_is_completed_;
1245 std::function<void()> GenerateTask(int64_t )
override {
1247 absl::MutexLock mutex_lock(mutex_);
1248 previous_task_is_completed_ =
false;
1252 absl::MutexLock mutex_lock(mutex_);
1253 if (solving_first_chunk_) {
1257 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
1258 solving_first_chunk_ =
false;
1260 previous_task_is_completed_ =
true;
1265 auto* time_limit = local_model_->GetOrCreate<TimeLimit>();
1266 const double saved_dtime = time_limit->GetElapsedDeterministicTime();
1267 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
1268 if (!feasibility_pump->Solve()) {
1269 shared_->response->NotifyThatImprovingProblemIsInfeasible(name());
1273 absl::MutexLock mutex_lock(mutex_);
1274 dtime_since_last_sync_ +=
1275 time_limit->GetElapsedDeterministicTime() - saved_dtime;
1279 if (shared_->SearchIsDone()) {
1280 shared_->time_limit->Stop();
1284 absl::MutexLock mutex_lock(mutex_);
1285 previous_task_is_completed_ =
true;
1289 void Synchronize()
override {
1290 absl::MutexLock mutex_lock(mutex_);
1291 AddTaskDeterministicDuration(dtime_since_last_sync_);
1292 shared_->time_limit->AdvanceDeterministicTime(dtime_since_last_sync_);
1293 dtime_since_last_sync_ = 0.0;
1297 SharedClasses* shared_;
1298 std::unique_ptr<Model> local_model_;
1304 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
1306 double dtime_since_last_sync_ ABSL_GUARDED_BY(mutex_) = 0.0;
1307 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
1313 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
1314 const SatParameters& lns_parameters_base,
1315 const SatParameters& lns_parameters_stalling,
1316 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
1317 : SubSolver(generator->name(), INCOMPLETE),
1318 generator_(std::move(generator)),
1320 lns_parameters_base_(lns_parameters_base),
1321 lns_parameters_stalling_(lns_parameters_stalling),
1324 ~LnsSolver()
override {
1325 shared_->stat_tables->AddTimingStat(*
this);
1326 shared_->stat_tables->AddLnsStat(
1328 generator_->num_fully_solved_calls(),
1329 generator_->num_calls(),
1330 generator_->num_improving_calls(),
1331 generator_->difficulty(),
1332 generator_->deterministic_limit());
1335 bool TaskIsAvailable()
override {
1336 if (shared_->SearchIsDone())
return false;
1337 return generator_->ReadyToGenerate();
1340 std::function<void()> GenerateTask(int64_t task_id)
override {
1341 return [task_id,
this]() {
1342 if (shared_->SearchIsDone())
return;
1347 const int32_t low =
static_cast<int32_t
>(task_id);
1348 const int32_t high =
static_cast<int32_t
>(task_id >> 32);
1349 std::seed_seq seed{low, high, lns_parameters_base_.random_seed()};
1352 NeighborhoodGenerator::SolveData data;
1353 data.task_id = task_id;
1354 data.difficulty = generator_->difficulty();
1355 data.deterministic_limit = generator_->deterministic_limit();
1356 data.initial_best_objective =
1357 shared_->response->GetBestSolutionObjective();
1360 const auto base_solution =
1361 shared_->response->SolutionPool().GetSolutionToImprove(random);
1362 CpSolverResponse base_response;
1363 if (base_solution !=
nullptr) {
1364 base_response.set_status(CpSolverStatus::FEASIBLE);
1365 base_response.mutable_solution()->Assign(
1366 base_solution->variable_values.begin(),
1367 base_solution->variable_values.end());
1371 data.base_objective = base_solution->rank;
1373 base_response.set_status(CpSolverStatus::UNKNOWN);
1378 data.base_objective = data.initial_best_objective;
1381 Neighborhood neighborhood =
1382 generator_->Generate(base_response, data, random);
1384 if (!neighborhood.is_generated)
return;
1386 SatParameters local_params;
1389 const int64_t stall = generator_->num_consecutive_non_improving_calls();
1390 const int search_index = stall < 10 ? 0 : task_id % 2;
1391 switch (search_index) {
1393 local_params = lns_parameters_base_;
1396 local_params = lns_parameters_stalling_;
1399 const std::string_view search_info =
1400 absl::StripPrefix(absl::string_view(local_params.name()),
"lns_");
1401 local_params.set_max_deterministic_time(data.deterministic_limit);
1403 std::string source_info =
1404 neighborhood.source_info.empty() ? name() : neighborhood.source_info;
1405 const int64_t num_calls = std::max(int64_t{1}, generator_->num_calls());
1406 const double fully_solved_proportion =
1407 static_cast<double>(generator_->num_fully_solved_calls()) /
1408 static_cast<double>(num_calls);
1409 const std::string lns_info = absl::StrFormat(
1410 "%s (d=%0.2e s=%i t=%0.2f p=%0.2f stall=%d h=%s)", source_info,
1411 data.difficulty, task_id, data.deterministic_limit,
1412 fully_solved_proportion, stall, search_info);
1414 Model local_model(lns_info);
1415 *(local_model.GetOrCreate<SatParameters>()) = local_params;
1416 TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
1417 local_time_limit->ResetLimitFromParameters(local_params);
1418 shared_->time_limit->UpdateLocalLimit(local_time_limit);
1423 absl::MutexLock l(next_arena_size_mutex_);
1424 buffer_size = next_arena_size_;
1426 google::protobuf::Arena arena(
1427 google::protobuf::ArenaOptions({.start_block_size = buffer_size}));
1428 CpModelProto& lns_fragment =
1429 *google::protobuf::Arena::Create<CpModelProto>(&arena);
1430 CpModelProto& mapping_proto =
1431 *google::protobuf::Arena::Create<CpModelProto>(&arena);
1432 auto context = std::make_unique<PresolveContext>(
1433 &local_model, &lns_fragment, &mapping_proto);
1435 *lns_fragment.mutable_variables() = neighborhood.delta.variables();
1437 ModelCopy copier(context.get());
1440 if (!copier.ImportAndSimplifyConstraints(helper_->ModelProto())) {
1445 if (!neighborhood.delta.constraints().empty() &&
1446 !copier.ImportAndSimplifyConstraints(neighborhood.delta)) {
1452 context->WriteVariableDomainsToProto();
1457 helper_->ModelProto(), context.get());
1458 lns_fragment.set_name(absl::StrCat(
"lns_", task_id,
"_", source_info));
1465 lns_fragment.clear_symmetry();
1468 if (neighborhood.delta.has_solution_hint()) {
1469 *lns_fragment.mutable_solution_hint() =
1470 neighborhood.delta.solution_hint();
1472 if (generator_->num_consecutive_non_improving_calls() > 10 &&
1473 absl::Bernoulli(random, 0.5)) {
1477 lns_fragment.clear_solution_hint();
1479 if (neighborhood.is_simple &&
1480 neighborhood.num_relaxed_variables_in_objective == 0) {
1486 if (generator_->num_consecutive_non_improving_calls() > 10) {
1488 lns_fragment.clear_solution_hint();
1495 bool hint_feasible_before_presolve =
false;
1496 if (lns_parameters_base_.debug_crash_if_presolve_breaks_hint()) {
1497 hint_feasible_before_presolve =
1498 SolutionHintIsCompleteAndFeasible(lns_fragment,
nullptr);
1505 RestrictObjectiveUsingHint(&lns_fragment);
1507 CpModelProto debug_copy;
1508 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
1511 debug_copy = lns_fragment;
1514 if (absl::GetFlag(FLAGS_cp_model_dump_submodels)) {
1516 const std::string lns_name =
1517 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
1518 lns_fragment.name(),
".pb.txt");
1519 LOG(INFO) <<
"Dumping LNS model to '" << lns_name <<
"'.";
1523 std::vector<int> postsolve_mapping;
1529 if (local_time_limit->LimitReached())
return;
1532 context.reset(
nullptr);
1533 neighborhood.delta.Clear();
1535 if (lns_parameters_base_.debug_crash_if_presolve_breaks_hint() &&
1536 hint_feasible_before_presolve &&
1537 !SolutionHintIsCompleteAndFeasible(lns_fragment,
1539 LOG(FATAL) <<
"Presolve broke a feasible LNS hint. The model name is '"
1540 << lns_fragment.name()
1541 <<
"' (use the --cp_model_dump_submodels flag to dump it).";
1547 auto* local_response_manager =
1548 local_model.GetOrCreate<SharedResponseManager>();
1549 local_response_manager->InitializeObjective(lns_fragment);
1550 local_response_manager->SetSynchronizationMode(
true);
1552 CpSolverResponse local_response;
1553 if (presolve_status == CpSolverStatus::UNKNOWN) {
1556 if (shared_->SearchIsDone())
return;
1561 local_response = local_response_manager->GetResponse();
1566 if (local_response.solution_info().empty()) {
1567 local_response.set_solution_info(
1568 absl::StrCat(lns_info,
" [presolve]"));
1574 if (presolve_status == CpSolverStatus::INFEASIBLE) {
1575 local_response_manager->NotifyThatImprovingProblemIsInfeasible(
1578 local_response = local_response_manager->GetResponse();
1579 local_response.set_status(presolve_status);
1581 const std::string solution_info = local_response.solution_info();
1582 std::vector<int64_t> solution_values(local_response.solution().begin(),
1583 local_response.solution().end());
1585 data.status = local_response.status();
1588 if (data.status == CpSolverStatus::OPTIMAL ||
1589 data.status == CpSolverStatus::FEASIBLE) {
1591 local_params, helper_->ModelProto().variables_size(), mapping_proto,
1592 postsolve_mapping, &solution_values);
1593 local_response.mutable_solution()->Assign(solution_values.begin(),
1594 solution_values.end());
1597 data.deterministic_time +=
1598 local_time_limit->GetElapsedDeterministicTime();
1600 bool new_solution =
false;
1601 bool display_lns_info = VLOG_IS_ON(2);
1602 if (!local_response.solution().empty()) {
1608 const bool feasible =
1611 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
1612 const std::string name =
1613 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
1614 debug_copy.name(),
".pb.txt");
1615 LOG(INFO) <<
"Dumping problematic LNS model to '" << name <<
"'.";
1618 LOG(ERROR) <<
"Infeasible LNS solution! " << solution_info
1619 <<
" solved with params " << local_params;
1642 if (data.status == CpSolverStatus::OPTIMAL &&
1643 !shared_->model_proto.has_symmetry() && !solution_values.empty() &&
1644 neighborhood.is_simple && shared_->bounds !=
nullptr &&
1645 !neighborhood.variables_that_can_be_fixed_to_local_optimum
1647 display_lns_info =
true;
1648 shared_->bounds->FixVariablesFromPartialSolution(
1650 neighborhood.variables_that_can_be_fixed_to_local_optimum);
1654 data.new_objective = data.base_objective;
1655 if (data.status == CpSolverStatus::OPTIMAL ||
1656 data.status == CpSolverStatus::FEASIBLE) {
1658 shared_->model_proto.objective(), solution_values));
1663 if (data.status == CpSolverStatus::OPTIMAL ||
1664 data.status == CpSolverStatus::FEASIBLE) {
1665 if (absl::MakeSpan(solution_values) !=
1666 absl::MakeSpan(base_response.solution())) {
1667 new_solution =
true;
1669 solution_values, solution_info,
1673 if (!neighborhood.is_reduced &&
1674 (data.status == CpSolverStatus::OPTIMAL ||
1675 data.status == CpSolverStatus::INFEASIBLE)) {
1676 shared_->response->NotifyThatImprovingProblemIsInfeasible(
1678 shared_->time_limit->Stop();
1682 generator_->AddSolveData(data);
1684 if (VLOG_IS_ON(2) && display_lns_info) {
1685 std::string s = absl::StrCat(
" LNS ", name(),
":");
1688 shared_->model_proto.objective(),
1690 base_response.solution()));
1692 shared_->model_proto.objective(),
1695 absl::StrAppend(&s,
" [new_sol:", base_obj,
" -> ", new_obj,
"]");
1697 if (neighborhood.is_simple) {
1700 "relaxed:",
FormatCounter(neighborhood.num_relaxed_variables),
1702 FormatCounter(neighborhood.num_relaxed_variables_in_objective),
1704 neighborhood.variables_that_can_be_fixed_to_local_optimum.size(),
1709 " [d:", absl::StrFormat(
"%0.2e", data.difficulty),
", id:", task_id,
1710 ", dtime:", absl::StrFormat(
"%0.2f", data.deterministic_time),
"/",
1711 data.deterministic_limit,
1713 ", #calls:", generator_->num_calls(),
1714 ", p:", fully_solved_proportion,
"]");
1717 absl::MutexLock l(next_arena_size_mutex_);
1718 next_arena_size_ = arena.SpaceUsed();
1723 void Synchronize()
override {
1725 const absl::Span<const double> dtimes = generator_->Synchronize();
1726 for (
const double dtime : dtimes) {
1728 AddTaskDeterministicDuration(dtime);
1730 shared_->time_limit->AdvanceDeterministicTime(sum);
1734 std::unique_ptr<NeighborhoodGenerator> generator_;
1735 NeighborhoodGeneratorHelper* helper_;
1736 const SatParameters lns_parameters_base_;
1737 const SatParameters lns_parameters_stalling_;
1738 SharedClasses* shared_;
1742 absl::Mutex next_arena_size_mutex_;
1743 int64_t next_arena_size_ ABSL_GUARDED_BY(next_arena_size_mutex_) =
1744 helper_->ModelProto().GetArena() ==
nullptr
1745 ? Neighborhood::kDefaultArenaSizePerVariable
1746 * helper_->ModelProto().variables_size()
1747 : helper_->ModelProto().GetArena()->SpaceUsed();
1752 if (global_model->GetOrCreate<TimeLimit>()->LimitReached())
return;
1754 if (params.check_drat_proof() || params.output_drat_proof()) {
1755 LOG(FATAL) <<
"DRAT proofs are not supported with several workers";
1765 std::vector<std::unique_ptr<SubSolver>> subsolvers;
1776 std::vector<std::unique_ptr<SubSolver>> full_worker_subsolvers;
1777 std::vector<std::unique_ptr<SubSolver>> first_solution_full_subsolvers;
1778 std::vector<std::unique_ptr<SubSolver>> reentrant_interleaved_subsolvers;
1779 std::vector<std::unique_ptr<SubSolver>> interleaved_subsolvers;
1782 subsolvers.push_back(std::make_unique<SynchronizationPoint>(
1783 "synchronization_agent", [shared]() {
1784 shared->response->Synchronize();
1785 shared->ls_hints->Synchronize();
1786 if (shared->bounds !=
nullptr) {
1787 shared->bounds->Synchronize();
1789 if (shared->lp_solutions !=
nullptr) {
1790 shared->lp_solutions->Synchronize();
1792 if (shared->clauses !=
nullptr) {
1793 shared->clauses->Synchronize();
1798 const SatParameters& lns_params_base = name_to_params.at(
"lns_base");
1799 const SatParameters& lns_params_stalling = name_to_params.at(
"lns_stalling");
1800 const SatParameters& lns_params_routing = name_to_params.at(
"lns_routing");
1804 auto unique_helper = std::make_unique<NeighborhoodGeneratorHelper>(
1805 &shared->model_proto, ¶ms, shared->response, shared->time_limit,
1806 shared->bounds.get());
1808 subsolvers.push_back(std::move(unique_helper));
1811 const int num_shared_tree_workers = shared->shared_tree_manager->NumWorkers();
1814 if (num_shared_tree_workers >= 2 &&
1815 shared->model_proto.assumptions().empty()) {
1817 name_filter.Filter({name_to_params.at(
"shared_tree")}),
1818 num_shared_tree_workers)) {
1819 full_worker_subsolvers.push_back(std::make_unique<FullProblemSolver>(
1820 local_params.name(), local_params,
1821 params.interleave_search(), shared));
1827 params, shared->model_proto,
1828 full_worker_subsolvers.size(),
1830 if (!name_filter.Keep(local_params.name()))
continue;
1833 if (params.optimize_with_max_hs())
continue;
1836 if (local_params.use_objective_shaving_search()) {
1837 full_worker_subsolvers.push_back(std::make_unique<ObjectiveShavingSolver>(
1838 local_params, helper, shared));
1842 full_worker_subsolvers.push_back(std::make_unique<FullProblemSolver>(
1843 local_params.name(), local_params,
1844 params.interleave_search(), shared));
1848 int num_interleaved_subsolver_that_do_not_need_solution = 0;
1849 if (params.use_feasibility_pump() && name_filter.Keep(
"feasibility_pump")) {
1850 ++num_interleaved_subsolver_that_do_not_need_solution;
1851 interleaved_subsolvers.push_back(
1852 std::make_unique<FeasibilityPumpSolver>(params, shared));
1858 int shaving_level = params.variables_shaving_level() >= 0
1859 ? params.variables_shaving_level()
1860 : params.num_workers() / 20;
1861 if (shaving_level > 0) {
1862 if (shaving_level > 3) shaving_level = 3;
1863 const std::string names[] = {
"variables_shaving",
"variables_shaving_no_lp",
1864 "variables_shaving_max_lp"};
1865 for (
int i = 0;
i < shaving_level; ++
i) {
1866 if (name_filter.Keep(names[
i])) {
1868 ++num_interleaved_subsolver_that_do_not_need_solution;
1869 reentrant_interleaved_subsolvers.push_back(
1870 std::make_unique<VariablesShavingSolver>(local_params, helper,
1879 if (params.use_rins_lns() && name_filter.Keep(
"rins/rens")) {
1885 ++num_interleaved_subsolver_that_do_not_need_solution;
1886 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1887 std::make_unique<RelaxationInducedNeighborhoodGenerator>(
1888 helper, shared->response, shared->lp_solutions.get(),
1889 shared->incomplete_solutions.get(), name_filter.LastName()),
1890 lns_params_base, lns_params_stalling, helper, shared));
1898 if (params.use_lns() && shared->model_proto.has_objective() &&
1899 !shared->model_proto.objective().vars().empty()) {
1902 if (name_filter.Keep(
"rnd_var_lns")) {
1903 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1904 std::make_unique<RelaxRandomVariablesGenerator>(
1905 helper, name_filter.LastName()),
1906 lns_params_base, lns_params_stalling, helper, shared));
1908 if (name_filter.Keep(
"rnd_cst_lns")) {
1909 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1910 std::make_unique<RelaxRandomConstraintsGenerator>(
1911 helper, name_filter.LastName()),
1912 lns_params_base, lns_params_stalling, helper, shared));
1914 if (name_filter.Keep(
"graph_var_lns")) {
1915 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1916 std::make_unique<VariableGraphNeighborhoodGenerator>(
1917 helper, name_filter.LastName()),
1918 lns_params_base, lns_params_stalling, helper, shared));
1920 if (name_filter.Keep(
"graph_arc_lns")) {
1921 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1922 std::make_unique<ArcGraphNeighborhoodGenerator>(
1923 helper, name_filter.LastName()),
1924 lns_params_base, lns_params_stalling, helper, shared));
1926 if (name_filter.Keep(
"graph_cst_lns")) {
1927 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1928 std::make_unique<ConstraintGraphNeighborhoodGenerator>(
1929 helper, name_filter.LastName()),
1930 lns_params_base, lns_params_stalling, helper, shared));
1932 if (name_filter.Keep(
"graph_dec_lns")) {
1933 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1934 std::make_unique<DecompositionGraphNeighborhoodGenerator>(
1935 helper, name_filter.LastName()),
1936 lns_params_base, lns_params_stalling, helper, shared));
1938 if (params.use_lb_relax_lns() &&
1939 params.num_workers() >= params.lb_relax_num_workers_threshold() &&
1940 name_filter.Keep(
"lb_relax_lns")) {
1941 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1942 std::make_unique<LocalBranchingLpBasedNeighborhoodGenerator>(
1943 helper, name_filter.LastName(), shared->time_limit, shared),
1944 lns_params_base, lns_params_stalling, helper, shared));
1947 const bool has_no_overlap_or_cumulative =
1952 if (has_no_overlap_or_cumulative) {
1953 if (name_filter.Keep(
"scheduling_intervals_lns")) {
1954 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1955 std::make_unique<RandomIntervalSchedulingNeighborhoodGenerator>(
1956 helper, name_filter.LastName()),
1957 lns_params_base, lns_params_stalling, helper, shared));
1959 if (name_filter.Keep(
"scheduling_time_window_lns")) {
1960 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1961 std::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
1962 helper, name_filter.LastName()),
1963 lns_params_base, lns_params_stalling, helper, shared));
1965 const std::vector<std::vector<int>> intervals_in_constraints =
1966 helper->GetUniqueIntervalSets();
1967 if (intervals_in_constraints.size() > 2 &&
1968 name_filter.Keep(
"scheduling_resource_windows_lns")) {
1969 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1970 std::make_unique<SchedulingResourceWindowsNeighborhoodGenerator>(
1971 helper, intervals_in_constraints, name_filter.LastName()),
1972 lns_params_base, lns_params_stalling, helper, shared));
1977 const bool has_no_overlap2d =
1979 if (has_no_overlap2d) {
1980 if (name_filter.Keep(
"packing_random_lns")) {
1981 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1982 std::make_unique<RandomRectanglesPackingNeighborhoodGenerator>(
1983 helper, name_filter.LastName()),
1984 lns_params_base, lns_params_stalling, helper, shared));
1986 if (name_filter.Keep(
"packing_square_lns")) {
1987 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1988 std::make_unique<RectanglesPackingRelaxOneNeighborhoodGenerator>(
1989 helper, name_filter.LastName()),
1990 lns_params_base, lns_params_stalling, helper, shared));
1992 if (name_filter.Keep(
"packing_swap_lns")) {
1993 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1994 std::make_unique<RectanglesPackingRelaxTwoNeighborhoodsGenerator>(
1995 helper, name_filter.LastName()),
1996 lns_params_base, lns_params_stalling, helper, shared));
1998 if (name_filter.Keep(
"packing_precedences_lns")) {
1999 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
2000 std::make_unique<RandomPrecedencesPackingNeighborhoodGenerator>(
2001 helper, name_filter.LastName()),
2002 lns_params_base, lns_params_stalling, helper, shared));
2004 if (name_filter.Keep(
"packing_slice_lns")) {
2005 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
2006 std::make_unique<SlicePackingNeighborhoodGenerator>(
2007 helper, name_filter.LastName()),
2008 lns_params_base, lns_params_stalling, helper, shared));
2013 if (has_no_overlap_or_cumulative || has_no_overlap2d) {
2014 if (name_filter.Keep(
"scheduling_precedences_lns")) {
2015 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
2016 std::make_unique<RandomPrecedenceSchedulingNeighborhoodGenerator>(
2017 helper, name_filter.LastName()),
2018 lns_params_base, lns_params_stalling, helper, shared));
2022 const int num_circuit =
static_cast<int>(
2024 const int num_routes =
static_cast<int>(
2026 if (num_circuit + num_routes > 0) {
2027 if (name_filter.Keep(
"routing_random_lns")) {
2028 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
2029 std::make_unique<RoutingRandomNeighborhoodGenerator>(
2030 helper, name_filter.LastName()),
2031 lns_params_routing, lns_params_stalling, helper, shared));
2033 if (name_filter.Keep(
"routing_path_lns")) {
2034 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
2035 std::make_unique<RoutingPathNeighborhoodGenerator>(
2036 helper, name_filter.LastName()),
2037 lns_params_routing, lns_params_stalling, helper, shared));
2040 if (num_routes > 0 || num_circuit > 1) {
2041 if (name_filter.Keep(
"routing_full_path_lns")) {
2042 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
2043 std::make_unique<RoutingFullPathNeighborhoodGenerator>(
2044 helper, name_filter.LastName()),
2045 lns_params_routing, lns_params_stalling, helper, shared));
2052 const auto get_linear_model = [&]() {
2053 auto* candidate = global_model->Get<
LinearModel>();
2054 if (candidate !=
nullptr)
return candidate;
2058 global_model->TakeOwnership(linear_model);
2059 global_model->Register(linear_model);
2067 if (shared->model_proto.has_objective()) {
2071 const int num_thread_for_interleaved_workers =
2072 params.num_workers() - full_worker_subsolvers.size();
2073 int num_violation_ls = params.has_num_violation_ls()
2074 ? params.num_violation_ls()
2075 : (num_thread_for_interleaved_workers + 2) / 3;
2079 if (reentrant_interleaved_subsolvers.empty()) {
2081 std::max(num_violation_ls,
2082 num_thread_for_interleaved_workers -
2083 static_cast<int>(interleaved_subsolvers.size()));
2086 const absl::string_view ls_name =
"ls";
2087 const absl::string_view lin_ls_name =
"ls_lin";
2089 const int num_ls_lin =
2090 name_filter.Keep(lin_ls_name) ? (num_violation_ls + 1) / 3 : 0;
2091 const int num_ls_default =
2092 name_filter.Keep(ls_name) ? num_violation_ls - num_ls_lin : 0;
2094 if (num_ls_default > 0) {
2095 std::shared_ptr<SharedLsStates> states = std::make_shared<SharedLsStates>(
2096 ls_name, params, shared->stat_tables);
2097 for (
int i = 0;
i < num_ls_default; ++
i) {
2100 CombineSeed(params.random_seed(), interleaved_subsolvers.size()));
2101 local_params.set_feasibility_jump_linearization_level(0);
2102 interleaved_subsolvers.push_back(
2103 std::make_unique<FeasibilityJumpSolver>(
2105 local_params, states, shared->time_limit, shared->response,
2106 shared->bounds.get(), shared->ls_hints, shared->stats,
2107 shared->stat_tables));
2111 if (num_ls_lin > 0) {
2112 std::shared_ptr<SharedLsStates> lin_states =
2113 std::make_shared<SharedLsStates>(lin_ls_name, params,
2114 shared->stat_tables);
2115 for (
int i = 0;
i < num_ls_lin; ++
i) {
2118 CombineSeed(params.random_seed(), interleaved_subsolvers.size()));
2119 local_params.set_feasibility_jump_linearization_level(2);
2120 interleaved_subsolvers.push_back(
2121 std::make_unique<FeasibilityJumpSolver>(
2123 local_params, lin_states, shared->time_limit, shared->response,
2124 shared->bounds.get(), shared->ls_hints, shared->stats,
2125 shared->stat_tables));
2136 int num_thread_available =
2137 params.num_workers() -
static_cast<int>(full_worker_subsolvers.size());
2142 if (!params.use_feasibility_jump() &&
2143 num_interleaved_subsolver_that_do_not_need_solution > 0) {
2144 --num_thread_available;
2146 num_thread_available = std::max(num_thread_available, 0);
2150 if (params.interleave_search() && params.num_workers() == 1) {
2152 num_thread_available = 1;
2155 const std::vector<SatParameters> all_params =
2157 num_thread_available);
2159 std::shared_ptr<SharedLsStates> fj_states;
2160 std::shared_ptr<SharedLsStates> fj_lin_states;
2164 if (local_params.use_feasibility_jump()) {
2166 std::shared_ptr<SharedLsStates> states;
2167 if (local_params.feasibility_jump_linearization_level() == 0) {
2168 if (fj_states ==
nullptr) {
2169 fj_states = std::make_shared<SharedLsStates>(
2170 local_params.name(), params, shared->stat_tables);
2174 if (fj_lin_states ==
nullptr) {
2175 fj_lin_states = std::make_shared<SharedLsStates>(
2176 local_params.name(), params, shared->stat_tables);
2178 states = fj_lin_states;
2181 interleaved_subsolvers.push_back(
2182 std::make_unique<FeasibilityJumpSolver>(
2184 get_linear_model(), local_params, states, shared->time_limit,
2185 shared->response, shared->bounds.get(), shared->ls_hints,
2186 shared->stats, shared->stat_tables));
2188 first_solution_full_subsolvers.push_back(
2189 std::make_unique<FullProblemSolver>(
2190 local_params.name(), local_params,
2191 local_params.interleave_search(), shared,
2200 const auto move_all =
2201 [&subsolvers](std::vector<std::unique_ptr<SubSolver>>& from) {
2202 for (
int i = 0;
i < from.size(); ++
i) {
2203 subsolvers.push_back(std::move(from[
i]));
2207 move_all(full_worker_subsolvers);
2208 move_all(first_solution_full_subsolvers);
2209 move_all(reentrant_interleaved_subsolvers);
2210 move_all(interleaved_subsolvers);
2215 if (shared->model_proto.has_objective() &&
2216 !shared->model_proto.objective().vars().empty()) {
2217 subsolvers.push_back(std::make_unique<SynchronizationPoint>(
2218 "update_gap_integral",
2219 [shared]() { shared->response->UpdateGapIntegral(); }));
2222 LaunchSubsolvers(global_model, shared, subsolvers, name_filter.AllIgnored());
2230void AddPostsolveClauses(absl::Span<const int> postsolve_mapping,
Model* model,
2234 for (
const auto& clause : postsolve->clauses) {
2235 auto* ct = mapping_proto->add_constraints()->mutable_bool_or();
2236 for (
const Literal l : clause) {
2237 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
2239 var = postsolve_mapping[var];
2240 ct->add_literals(l.IsPositive() ? var :
NegatedRef(var));
2250 return [callback = callback](
Model* model) {
2256 const std::function<std::string(
const CpSolverResponse& response)>&
2258 return [callback = callback](
Model* model) {
2264 const std::function<
void(
double)>& callback) {
2265 return [callback = callback](
Model* model) {
2271template <
typename T>
2272void ParseFromStringOrDie(absl::string_view str, T* proto) {
2273 if constexpr (std::is_base_of_v<google::protobuf::Message, T>) {
2274 CHECK(google::protobuf::TextFormat::ParseFromString(str, proto)) << str;
2276 LOG(FATAL) <<
"Calling NewSatParameters() with a textual proto is not "
2277 "supported when using Lite Protobuf.";
2284 absl::string_view params) {
2286 if (!params.empty()) {
2287 ParseFromStringOrDie<SatParameters>(params, ¶meters);
2293 const sat::SatParameters& parameters) {
2294 return [parameters = parameters](
Model* model) {
2309 model->GetOrCreate<ModelSharedTimeLimit>()->Stop();
2313void RegisterSearchStatisticCallback(
Model* global_model) {
2314 global_model->GetOrCreate<SharedResponseManager>()
2315 ->AddStatisticsPostprocessor([](
Model* local_model,
2316 CpSolverResponse* response) {
2317 auto* sat_solver = local_model->Get<SatSolver>();
2318 if (sat_solver !=
nullptr) {
2319 response->set_num_booleans(sat_solver->NumVariables());
2320 response->set_num_fixed_booleans(sat_solver->NumFixedVariables());
2321 response->set_num_branches(sat_solver->num_branches());
2322 response->set_num_conflicts(sat_solver->num_failures());
2323 response->set_num_binary_propagations(sat_solver->num_propagations());
2324 response->set_num_restarts(sat_solver->num_restarts());
2326 response->set_num_booleans(0);
2327 response->set_num_fixed_booleans(0);
2328 response->set_num_branches(0);
2329 response->set_num_conflicts(0);
2330 response->set_num_binary_propagations(0);
2331 response->set_num_restarts(0);
2334 auto* integer_trail = local_model->Get<IntegerTrail>();
2335 response->set_num_integers(
2336 integer_trail ==
nullptr
2338 : integer_trail->NumIntegerVariables().value() / 2);
2339 response->set_num_integer_propagations(
2340 integer_trail ==
nullptr ? 0 : integer_trail->num_enqueues());
2342 int64_t num_lp_iters = 0;
2343 auto* lp_constraints =
2344 local_model->GetOrCreate<LinearProgrammingConstraintCollection>();
2345 for (
const LinearProgrammingConstraint* lp : *lp_constraints) {
2346 num_lp_iters += lp->total_num_simplex_iterations();
2348 response->set_num_lp_iterations(num_lp_iters);
2352void MergeParamsWithFlagsAndDefaults(SatParameters* params) {
2353 if constexpr (std::is_base_of_v<google::protobuf::Message, SatParameters>) {
2355 if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
2356 SatParameters flag_params;
2357 ParseFromStringOrDie<SatParameters>(absl::GetFlag(FLAGS_cp_model_params),
2359 params->MergeFrom(flag_params);
2366 SOLVER_LOG(logger,
"Fixing ", solution_hint.vars().size(),
2367 " variables to their value in the solution hints.");
2368 for (
int i = 0;
i < solution_hint.vars_size(); ++
i) {
2369 const int var = solution_hint.vars(
i);
2370 const int64_t value = solution_hint.values(
i);
2371 if (!context->IntersectDomainWith(var, Domain(value))) {
2373 context->working_model->variables(var);
2374 const std::string var_name = var_proto.
name().empty()
2375 ? absl::StrCat(
"var(", var,
")")
2379 SOLVER_LOG(logger,
"Hint found infeasible when assigning variable '",
2380 var_name,
"' with domain", var_domain.ToString(),
2381 " the value ", value);
2387void ClearInternalFields(
CpModelProto* model_proto, SolverLogger* logger) {
2388 if (model_proto->has_symmetry()) {
2389 SOLVER_LOG(logger,
"Ignoring internal symmetry field");
2390 model_proto->clear_symmetry();
2392 if (model_proto->has_objective()) {
2394 if (objective->integer_scaling_factor() != 0 ||
2395 objective->integer_before_offset() != 0 ||
2396 objective->integer_after_offset() != 0) {
2397 SOLVER_LOG(logger,
"Ignoring internal objective.integer_* fields.");
2398 objective->clear_integer_scaling_factor();
2399 objective->clear_integer_before_offset();
2400 objective->clear_integer_after_offset();
2408 auto* wall_timer = model->GetOrCreate<WallTimer>();
2410 wall_timer->Start();
2411 user_timer->Start();
2413 if constexpr (std::is_base_of_v<google::protobuf::Message, CpModelProto>) {
2415 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
2416 DumpModelProto(model_proto,
"model");
2418 if (absl::GetFlag(FLAGS_cp_model_export_model)) {
2419 if (model_proto.name().empty()) {
2420 DumpModelProto(model_proto,
"unnamed_model");
2422 DumpModelProto(model_proto, model_proto.name());
2427 MergeParamsWithFlagsAndDefaults(model->GetOrCreate<SatParameters>());
2428 const SatParameters& params = *model->GetOrCreate<SatParameters>();
2433 logger->SetLogToStdOut(params.log_to_stdout());
2434 std::string log_string;
2435 if (params.log_to_response()) {
2436 logger->AddInfoLoggingCallback([&log_string](absl::string_view message) {
2437 absl::StrAppend(&log_string, message,
"\n");
2443 absl::GetFlag(FLAGS_cp_model_dump_prefix));
2445 if (logger->LoggingIsEnabled()) {
2449 model_proto.has_objective() ||
2450 model_proto.has_floating_point_objective());
2451 shared_response_manager->AddStatusChangeCallback(
2453 progress_logger->UpdateProgress(info);
2456 RegisterSearchStatisticCallback(model);
2458 if constexpr (std::is_base_of_v<google::protobuf::Message,
2463 if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
2464 shared_response_manager->AddFinalResponsePostprocessor(
2466 const std::string file = absl::StrCat(
2467 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"response.pb.txt");
2468 LOG(INFO) <<
"Dumping response proto to '" << file <<
"'.";
2476 shared_response_manager->AddFinalResponsePostprocessor(
2480 model_proto.has_objective() ||
2481 model_proto.has_floating_point_objective()));
2482 if (!log_string.empty()) {
2483 response->set_solve_log(log_string);
2492 shared_response_manager->AddResponsePostprocessor(
2493 [&wall_timer, &user_timer,
2495 response->set_wall_time(wall_timer->Get());
2496 response->set_user_time(user_timer->Get());
2497 response->set_deterministic_time(
2498 shared_time_limit->GetElapsedDeterministicTime());
2507 if (!error.empty()) {
2508 SOLVER_LOG(logger,
"Invalid parameters: ", error);
2515 status_response.set_solution_info(error);
2516 shared_response_manager->FillSolveStatsInResponse(model,
2518 shared_response_manager->AppendResponseToBeMerged(status_response);
2519 return shared_response_manager->GetResponse();
2524 model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(params);
2526#if ORTOOLS_TARGET_OS_SUPPORTS_THREADS
2528 if (params.catch_sigint_signal()) {
2529 model->GetOrCreate<SigintHandler>()->Register(
2530 [shared_time_limit]() { shared_time_limit->Stop(); });
2535 LOG_EVERY_N_SEC(WARNING, 0.1)
2536 <<
"WARNING: CP-SAT is running in debug mode. The solver will "
2537 "be slow because we will do a lot of extra checks. Compile in "
2538 "optimization mode to gain an order of magnitude speedup.";
2548 if (logger->LoggingIsEnabled() && params.use_absl_random()) {
2556 if (!error.empty()) {
2557 SOLVER_LOG(logger,
"Invalid model: ", error);
2560 status_response.set_solution_info(error);
2561 shared_response_manager->FillSolveStatsInResponse(model,
2563 shared_response_manager->AppendResponseToBeMerged(status_response);
2564 return shared_response_manager->GetResponse();
2574 absl::StrFormat(
"Starting presolve at %.2fs", wall_timer->Get()));
2578 google::protobuf::Arena arena;
2580 google::protobuf::Arena::Create<CpModelProto>(&arena);
2582 google::protobuf::Arena::Create<CpModelProto>(&arena);
2583 auto context = std::make_unique<PresolveContext>(model, new_cp_model_proto,
2586 std::unique_ptr<LratProofHandler> lrat_proof_handler =
2589 lrat_proof_handler.get())) {
2590 const std::string info =
"Problem proven infeasible during initial copy.";
2592 if (lrat_proof_handler !=
nullptr) {
2593 lrat_proof_handler->Close(
true);
2597 status_response.set_solution_info(info);
2598 shared_response_manager->AppendResponseToBeMerged(status_response);
2599 return shared_response_manager->GetResponse();
2606 const auto [num_routes, num_dimensions] =
2608 *new_cp_model_proto);
2609 if (num_dimensions > 0) {
2610 SOLVER_LOG(logger,
"Routes: ", num_dimensions,
2611 " dimension(s) automatically inferred for ", num_routes,
2612 " routes constraint(s).");
2615 ClearInternalFields(context->working_model, logger);
2617 if (absl::GetFlag(FLAGS_cp_model_ignore_objective) &&
2618 (context->working_model->has_objective() ||
2619 context->working_model->has_floating_point_objective())) {
2621 context->working_model->clear_objective();
2622 context->working_model->clear_floating_point_objective();
2625 if (absl::GetFlag(FLAGS_cp_model_ignore_hints) &&
2626 context->working_model->has_solution_hint()) {
2627 SOLVER_LOG(logger,
"Ignoring solution hint");
2628 context->working_model->clear_solution_hint();
2632 if (params.fix_variables_to_their_hinted_value() &&
2633 model_proto.has_solution_hint()) {
2634 FixVariablesToHintValue(model_proto.solution_hint(), context.get(), logger);
2641 bool hint_feasible_before_presolve =
false;
2642 if (!context->ModelIsUnsat()) {
2643 hint_feasible_before_presolve =
2644 SolutionHintIsCompleteAndFeasible(model_proto, logger);
2649 if (model_proto.has_floating_point_objective()) {
2650 shared_response_manager->AddFinalResponsePostprocessor(
2651 [¶ms, &model_proto, mapping_proto,
2653 if (response->solution().empty())
return;
2656 const auto& float_obj = model_proto.floating_point_objective();
2657 double value = float_obj.offset();
2658 const int num_terms = float_obj.vars().size();
2659 for (
int i = 0;
i < num_terms; ++
i) {
2660 value += float_obj.coeffs(
i) *
2661 static_cast<double>(response->solution(float_obj.vars(
i)));
2663 response->set_objective_value(value);
2668 if (!mapping_proto->has_objective())
return;
2670 *response->mutable_integer_objective() = integer_obj;
2675 if (params.mip_compute_true_objective_bound() &&
2676 !integer_obj.scaling_was_exact()) {
2677 const int64_t integer_lb = response->inner_objective_lower_bound();
2679 model_proto, integer_obj, integer_lb);
2680 SOLVER_LOG(logger,
"[Scaling] scaled_objective_bound: ",
2681 response->best_objective_bound(),
2682 " corrected_bound: ", lb,
2683 " delta: ", response->best_objective_bound() - lb);
2687 if (float_obj.maximize()) {
2688 response->set_best_objective_bound(
2689 std::max(lb, response->objective_value()));
2691 response->set_best_objective_bound(
2692 std::min(lb, response->objective_value()));
2699 const double gap = std::abs(response->objective_value() -
2700 response->best_objective_bound());
2701 if (gap > params.absolute_gap_limit()) {
2703 "[Scaling] Warning: OPTIMAL was reported, yet the "
2705 gap,
") is greater than requested absolute limit (",
2706 params.absolute_gap_limit(),
").");
2712 if (!model_proto.assumptions().empty() &&
2713 (params.num_workers() > 1 || model_proto.has_objective() ||
2714 model_proto.has_floating_point_objective() ||
2715 params.enumerate_all_solutions() || params.interleave_search())) {
2718 "Warning: solving with assumptions was requested in a non-fully "
2719 "supported setting.\nWe will assumes these assumptions true while "
2720 "solving, but if the model is infeasible, you will not get a useful "
2721 "'sufficient_assumptions_for_infeasibility' field in the response, it "
2722 "will include all assumptions.");
2730 shared_response_manager->AddFinalResponsePostprocessor(
2735 *response->mutable_sufficient_assumptions_for_infeasibility() =
2736 model_proto.assumptions();
2740 new_cp_model_proto->clear_assumptions();
2742 context->InitializeNewDomains();
2743 for (
const int ref : model_proto.assumptions()) {
2744 if (!context->SetLiteralToTrue(ref)) {
2747 status_response.add_sufficient_assumptions_for_infeasibility(ref);
2748 shared_response_manager->FillSolveStatsInResponse(model,
2750 shared_response_manager->AppendResponseToBeMerged(status_response);
2751 return shared_response_manager->GetResponse();
2757 std::vector<int> postsolve_mapping;
2763 context.reset(
nullptr);
2764 if (lrat_proof_handler !=
nullptr) {
2766 lrat_proof_handler.reset();
2771 hint_feasible_before_presolve &&
2772 params.debug_crash_if_presolve_breaks_hint()) {
2773 LOG(FATAL) <<
"Presolve bug: model with feasible hint found UNSAT "
2776 SOLVER_LOG(logger,
"Problem closed by presolve.");
2779 shared_response_manager->FillSolveStatsInResponse(model, &status_response);
2780 shared_response_manager->AppendResponseToBeMerged(status_response);
2781 return shared_response_manager->GetResponse();
2787 std::vector<int64_t> debug_solution_from_hint;
2788 if (absl::GetFlag(FLAGS_cp_model_use_hint_for_debug_only) &&
2789 hint_feasible_before_presolve) {
2790 SOLVER_LOG(logger,
"Using solution hint only as debug solution");
2791 debug_solution_from_hint.resize(
2792 new_cp_model_proto->solution_hint().values_size());
2793 for (
int i = 0;
i < new_cp_model_proto->solution_hint().values_size();
2795 debug_solution_from_hint[new_cp_model_proto->solution_hint().vars(
i)] =
2796 new_cp_model_proto->solution_hint().values(
i);
2798 new_cp_model_proto->clear_solution_hint();
2806 if (params.symmetry_level() > 1 && !params.stop_after_presolve() &&
2807 !shared_time_limit->LimitReached()) {
2808 if (params.keep_symmetry_in_presolve() &&
2809 new_cp_model_proto->has_symmetry()) {
2814 TimeLimit time_limit;
2815 shared_time_limit->UpdateLocalLimit(&time_limit);
2824 if (params.fill_tightened_domains_in_response()) {
2825 shared_response_manager->AddResponsePostprocessor(
2826 [&model_proto, new_cp_model_proto, mapping_proto, &postsolve_mapping,
2831 std::vector<Domain> bounds;
2833 new_cp_model_proto->variables()) {
2838 if (shared.bounds !=
nullptr) {
2839 shared.bounds->UpdateDomains(&bounds);
2844 postsolve_mapping, bounds, response,
2852 auto check_solution = [&model_proto, ¶ms, mapping_proto,
2854 if (response.solution().empty())
return;
2856 bool solution_is_feasible =
true;
2857 if (params.cp_model_presolve()) {
2861 model_proto, response.solution(), mapping_proto, &postsolve_mapping);
2863 solution_is_feasible =
2867 solution_is_feasible =
2872 if (!solution_is_feasible) {
2873 const std::string file = absl::StrCat(
2874 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"wrong_response.pb.txt");
2875 LOG(INFO) <<
"Dumping infeasible response proto to '" << file <<
"'.";
2879 LOG(FATAL) <<
"Infeasible solution!"
2880 <<
" source: '" << response.solution_info() <<
"'"
2881 <<
" dumped CpSolverResponse to '" << file <<
"'.";
2885 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
2886 shared_response_manager->AddSolutionCallback(std::move(check_solution));
2888 shared_response_manager->AddFinalResponsePostprocessor(
2895 if (params.cp_model_presolve()) {
2896 shared_response_manager->AddSolutionPostprocessor(
2897 [&model_proto, ¶ms, mapping_proto, &model,
2898 &postsolve_mapping](std::vector<int64_t>*
solution) {
2899 AddPostsolveClauses(postsolve_mapping, model, mapping_proto);
2901 *mapping_proto, postsolve_mapping,
solution);
2903 shared_response_manager->AddResponsePostprocessor(
2908 ->mutable_sufficient_assumptions_for_infeasibility())) {
2910 ? postsolve_mapping[ref]
2915 shared_response_manager->AddResponsePostprocessor(
2918 const int initial_size = model_proto.variables_size();
2919 if (!response->solution().empty()) {
2920 response->mutable_solution()->Truncate(initial_size);
2926 if (params.stop_after_first_solution()) {
2927 shared_response_manager->AddSolutionCallback(
2929 shared_time_limit->Stop();
2933 if constexpr (std::is_base_of_v<google::protobuf::Message, CpModelProto> &&
2934 std::is_base_of_v<google::protobuf::Message, MPModelProto>) {
2935 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
2936 DumpModelProto(*new_cp_model_proto,
"presolved_model");
2937 DumpModelProto(*mapping_proto,
"mapping_model");
2944 DumpModelProto(mip_model,
"presolved_mp_model");
2948 std::string cnf_string;
2950 const std::string suffix =
2951 new_cp_model_proto->has_objective() ?
"_no_objective" :
"";
2952 const std::string filename =
2953 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2954 "presolved_cnf_model", suffix,
".cnf");
2955 LOG(INFO) <<
"Dumping presolved cnf model to '" << filename <<
"'.";
2956 const absl::Status status =
2958 if (!status.ok()) LOG(ERROR) << status;
2960 const std::string filename =
2961 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2962 "presolved_wcnf_model.wcnf");
2963 LOG(INFO) <<
"Dumping presolved wcnf model to '" << filename <<
"'.";
2964 const absl::Status status =
2966 if (!status.ok()) LOG(ERROR) << status;
2971 if (params.stop_after_presolve() || shared_time_limit->LimitReached()) {
2972 int64_t num_terms = 0;
2977 logger,
"Stopped after presolve.",
2978 "\nPresolvedNumVariables: ", new_cp_model_proto->variables().size(),
2979 "\nPresolvedNumConstraints: ", new_cp_model_proto->constraints().size(),
2980 "\nPresolvedNumTerms: ", num_terms);
2983 shared_response_manager->FillSolveStatsInResponse(model, &status_response);
2984 shared_response_manager->AppendResponseToBeMerged(status_response);
2985 return shared_response_manager->GetResponse();
2995 if (new_cp_model_proto->has_objective()) {
2996 shared_response_manager->InitializeObjective(*new_cp_model_proto);
2997 shared_response_manager->SetGapLimitsFromParameters(params);
3002 shared_response_manager->UpdateGapIntegral();
3015 bool hint_feasible_after_presolve;
3016 if (!params.enumerate_all_solutions()) {
3017 hint_feasible_after_presolve = SolutionHintIsCompleteAndFeasible(
3018 *new_cp_model_proto, logger, shared_response_manager);
3020 hint_feasible_after_presolve =
3021 SolutionHintIsCompleteAndFeasible(*new_cp_model_proto, logger,
nullptr);
3024 if (hint_feasible_before_presolve && !hint_feasible_after_presolve &&
3025 params.debug_crash_if_presolve_breaks_hint()) {
3026 LOG(FATAL) <<
"Presolve broke a feasible hint";
3029 if (!debug_solution_from_hint.empty()) {
3031 debug_solution_from_hint);
3036 if (!model->GetOrCreate<TimeLimit>()->LimitReached()) {
3037#if ORTOOLS_TARGET_OS_SUPPORTS_THREADS
3038 if (params.num_workers() > 1 || params.interleave_search() ||
3039 !params.subsolvers().empty() || !params.filter_subsolvers().empty() ||
3040 params.use_ls_only()) {
3041 SolveCpModelParallel(&shared, model);
3047 shared_response_manager->SetUpdateGapIntegralOnEachChange(
true);
3051 std::vector<std::unique_ptr<SubSolver>> subsolvers;
3052 subsolvers.push_back(std::make_unique<FullProblemSolver>(
3053 "main", params,
false, &shared));
3054 LaunchSubsolvers(model, &shared, subsolvers, {});
3058 return shared_response_manager->GetResponse();
3067 const SatParameters& params) {
3074 absl::string_view params) {
Domain IntersectionWith(const Domain &domain) const
void EnableLogging(bool enable)
void set_status(::operations_research::sat::CpSolverStatus value)
static const ::std::string & VariableSelectionStrategy_Name(T value)
static const ::std::string & DomainReductionStrategy_Name(T value)
const ::std::string & name() const
bool Merge(absl::Span< const std::string > proof_filenames)
static std::unique_ptr< LratProofHandler > MaybeCreate(Model *model)
T Add(std::function< T(Model *)> f)
void set_random_seed(::int32_t value)
void set_dump_prefix(absl::string_view dump_prefix)
void SetIsOptimization(bool is_optimization)
ABSL_FLAG(bool, cp_model_export_model, false, "DEBUG ONLY. When set to true, SolveCpModel() will dump its input model " "protos in text format to 'FLAGS_cp_model_dump_prefix'{name}.pb.txt.")
absl::Status SetContents(absl::string_view file_name, absl::string_view contents, Options options)
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
void SolveLoadedCpModel(const CpModelProto &model_proto, Model *model)
constexpr uint64_t kDefaultFingerprintSeed
uint64_t FingerprintRepeatedField(const google::protobuf::RepeatedField< T > &sequence, uint64_t seed)
std::string ValidateInputCpModel(const SatParameters ¶ms, const CpModelProto &model)
bool RefIsPositive(int ref)
std::string CpModelStats(const CpModelProto &model_proto)
Returns a string with some statistics on the given CpModelProto.
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, absl::Span< const int64_t > solution)
std::vector< SatParameters > GetFullWorkerParameters(const SatParameters &base_params, const CpModelProto &cp_model, int num_already_present, SubsolverNameFilter *filter)
bool SolutionIsFeasible(const CpModelProto &model, absl::Span< const int64_t > variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
bool SolutionCanBeOptimal(const CpModelProto &model, absl::Span< const int64_t > variable_values)
std::pair< int, int > MaybeFillMissingRoutesConstraintNodeExpressions(const CpModelProto &input_model, CpModelProto &output_model)
bool ConvertCpModelProtoToWCnf(const CpModelProto &cp_model, std::string *out)
PushedSolutionPointers PushAndMaybeCombineSolution(SharedResponseManager *response_manager, const CpModelProto &model_proto, absl::Span< const int64_t > new_solution, absl::string_view solution_info, std::shared_ptr< const SharedSolutionRepository< int64_t >::Solution > base_solution)
int CombineSeed(int base_seed, int64_t delta)
bool WriteModelProtoToFile(const M &proto, absl::string_view filename)
CpSolverStatus PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
void DetectAndAddSymmetryToProto(const SatParameters ¶ms, CpModelProto *proto, SolverLogger *logger, TimeLimit *time_limit)
void RegisterClausesExport(int id, SharedClausesManager *shared_clauses_manager, Model *model)
uint64_t FingerprintModel(const CpModelProto &model, uint64_t seed)
void MinimizeL1DistanceWithHint(const CpModelProto &model_proto, Model *model)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64_t value)
bool ConvertCpModelProtoToCnf(const CpModelProto &cp_model, std::string *out)
std::function< SatParameters(Model *)> NewSatParameters(absl::string_view params)
void DeterministicLoop(std::vector< std::unique_ptr< SubSolver > > &subsolvers, int num_threads, int batch_size, int max_num_batches)
std::string ValidateParameters(const SatParameters ¶ms)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void LoadFeasibilityPump(const CpModelProto &model_proto, Model *model)
std::string CpSatSolverVersion()
Returns a string that describes the version of the solver.
std::vector< SatParameters > GetFirstSolutionBaseParams(const SatParameters &base_params)
void NonDeterministicLoop(std::vector< std::unique_ptr< SubSolver > > &subsolvers, const int num_threads, ModelSharedTimeLimit *time_limit)
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context, LratProofHandler *lrat_proof_handler)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
const ::std::string & CpSolverStatus_Name(T value)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
void AdaptGlobalParameters(const CpModelProto &model_proto, Model *model)
std::function< void(Model *)> NewBestBoundCallback(const std::function< void(double)> &callback)
CpSolverResponse SolveWithParameters(const CpModelProto &model_proto, const SatParameters ¶ms)
Solves the given CpModelProto with the given parameters.
std::function< void(Model *)> NewFeasibleSolutionLogCallback(const std::function< std::string(const CpSolverResponse &response)> &callback)
void StopSearch(Model *model)
Stops the current search.
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void QuickSolveWithHint(const CpModelProto &model_proto, Model *model)
std::vector< SatParameters > RepeatParameters(absl::Span< const SatParameters > base_params, int num_params_to_generate)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
absl::string_view ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
void LoadDebugSolution(const CpModelProto &model_proto, Model *model)
double ComputeTrueObjectiveLowerBound(const CpModelProto &model_proto_with_floating_point_objective, const CpObjectiveProto &integer_objective, const int64_t inner_integer_objective_lower_bound)
bool ConvertCpModelProtoToMPModelProto(const CpModelProto &input, MPModelProto *output)
void RegisterVariableBoundsLevelZeroExport(const CpModelProto &, SharedBoundsManager *shared_bounds_manager, Model *model)
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &callback)
void PostsolveResponseWrapper(const SatParameters ¶ms, int num_variable_in_original_model, const CpModelProto &mapping_proto, absl::Span< const int > postsolve_mapping, std::vector< int64_t > *solution)
void RegisterVariableBoundsLevelZeroImport(const CpModelProto &model_proto, SharedBoundsManager *shared_bounds_manager, Model *model)
void LoadCpModel(const CpModelProto &model_proto, Model *model)
absl::flat_hash_map< std::string, SatParameters > GetNamedParameters(SatParameters base_params)
void RegisterLinear2BoundsImport(SharedLinear2Bounds *shared_linear2_bounds, Model *model)
int RegisterClausesLevelZeroImport(int id, SharedClausesManager *shared_clauses_manager, Model *model)
void FillTightenedDomainInResponse(const CpModelProto &original_model, const CpModelProto &mapping_proto, absl::Span< const int > postsolve_mapping, absl::Span< const Domain > search_domains, CpSolverResponse *response, SolverLogger *logger)
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
VariableRelationships ComputeVariableRelationships(const CpModelProto &model)
std::string RenderDot(std::optional< Rectangle > bb, absl::Span< const Rectangle > solution, std::string_view extra_dot_payload)
std::string OrToolsVersionString()
bool ProbablyRunningInsideUnitTest()
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
std::mt19937_64 random_engine_t
std::string ProtobufShortDebugString(const P &message)
std::string FormatCounter(int64_t num)
absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
static int input(yyscan_t yyscanner)
bool Contains(int64_t value) const
std::deque< std::vector< Literal > > clauses
std::vector< int > secondary_variables
#define SOLVER_LOG(logger,...)