33#if !defined(__PORTABLE_PLATFORM__)
37#include "absl/base/thread_annotations.h"
38#include "absl/container/btree_map.h"
39#include "absl/container/btree_set.h"
40#include "absl/container/flat_hash_map.h"
41#include "absl/flags/flag.h"
42#include "absl/log/check.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/synchronization/mutex.h"
49#include "absl/types/span.h"
50#include "google/protobuf/arena.h"
51#include "google/protobuf/text_format.h"
54#include "ortools/sat/cp_model.pb.h"
74#include "ortools/sat/sat_parameters.pb.h"
83#if !defined(__PORTABLE_PLATFORM__)
91 bool, cp_model_export_model,
false,
92 "DEBUG ONLY. When set to true, SolveCpModel() will dump its input model "
93 "protos in text format to 'FLAGS_cp_model_dump_prefix'{name}.pb.txt.");
96 "DEBUG ONLY, dump models in text proto instead of binary proto.");
99 bool, cp_model_dump_problematic_lns,
false,
100 "DEBUG ONLY. Similar to --cp_model_dump_submodels, but only dump fragment "
101 "for which we got an issue while validating the postsolved solution. This "
102 "allows to debug presolve issues without dumping all the models.");
105 "DEBUG ONLY. If true, the final response of each solve will be "
106 "dumped to 'FLAGS_cp_model_dump_prefix'response.pb.txt");
109 "This is interpreted as a text SatParameters proto. The "
110 "specified fields will override the normal ones for all solves.");
113 "If true, copy the input model as if with no basic presolve");
116 "If true, ignore the objective.");
118 "If true, ignore any supplied hints.");
119ABSL_FLAG(
bool, cp_model_fingerprint_model,
true,
"Fingerprint the model.");
131std::string Summarize(absl::string_view
input) {
132 if (
input.size() < 105)
return std::string(
input);
134 return absl::StrCat(
input.substr(0, half),
" ... ",
139void DumpModelProto(
const M&
proto, absl::string_view
name) {
140 std::string filename;
141 if (absl::GetFlag(FLAGS_cp_model_dump_text_proto)) {
142 filename = absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
name,
144 LOG(INFO) <<
"Dumping " <<
name <<
" text proto to '" << filename <<
"'.";
146 const std::string filename =
147 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
name,
".bin");
148 LOG(INFO) <<
"Dumping " <<
name <<
" binary proto to '" << filename <<
"'.";
159std::string
CpModelStats(
const CpModelProto& model_proto) {
162 absl::flat_hash_map<char const*, int> name_to_num_constraints;
163 absl::flat_hash_map<char const*, int> name_to_num_reified;
164 absl::flat_hash_map<char const*, int> name_to_num_multi_reified;
165 absl::flat_hash_map<char const*, int> name_to_num_literals;
166 absl::flat_hash_map<char const*, int> name_to_num_terms;
167 absl::flat_hash_map<char const*, int> name_to_num_complex_domain;
168 absl::flat_hash_map<char const*, int> name_to_num_expressions;
170 int no_overlap_2d_num_rectangles = 0;
171 int no_overlap_2d_num_optional_rectangles = 0;
172 int no_overlap_2d_num_linear_areas = 0;
173 int no_overlap_2d_num_quadratic_areas = 0;
174 int no_overlap_2d_num_fixed_rectangles = 0;
176 int cumulative_num_intervals = 0;
177 int cumulative_num_optional_intervals = 0;
178 int cumulative_num_variable_sizes = 0;
179 int cumulative_num_variable_demands = 0;
180 int cumulative_num_fixed_intervals = 0;
182 int no_overlap_num_intervals = 0;
183 int no_overlap_num_optional_intervals = 0;
184 int no_overlap_num_variable_sizes = 0;
185 int no_overlap_num_fixed_intervals = 0;
187 int num_fixed_intervals = 0;
189 for (
const ConstraintProto&
ct : model_proto.constraints()) {
193 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
194 if (
ct.linear().vars_size() == 0)
name =
"kLinear0";
195 if (
ct.linear().vars_size() == 1)
name =
"kLinear1";
196 if (
ct.linear().vars_size() == 2)
name =
"kLinear2";
197 if (
ct.linear().vars_size() == 3)
name =
"kLinear3";
198 if (
ct.linear().vars_size() > 3)
name =
"kLinearN";
203 name_to_num_constraints[
name]++;
204 if (!
ct.enforcement_literal().empty()) {
205 name_to_num_reified[
name]++;
206 if (
ct.enforcement_literal().size() > 1) {
207 name_to_num_multi_reified[
name]++;
211 auto variable_is_fixed = [&model_proto](
int ref) {
212 const IntegerVariableProto&
proto =
217 auto expression_is_fixed =
218 [&variable_is_fixed](
const LinearExpressionProto& expr) {
219 for (
const int ref : expr.vars()) {
220 if (!variable_is_fixed(ref)) {
227 auto interval_has_fixed_size = [&model_proto, &expression_is_fixed](
int c) {
228 return expression_is_fixed(model_proto.constraints(c).interval().size());
231 auto constraint_is_optional = [&model_proto](
int i) {
232 return !model_proto.constraints(
i).enforcement_literal().empty();
235 auto interval_is_fixed = [&variable_is_fixed,
236 expression_is_fixed](
const ConstraintProto&
ct) {
237 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
240 for (
const int lit :
ct.enforcement_literal()) {
241 if (!variable_is_fixed(
lit))
return false;
243 return (expression_is_fixed(
ct.interval().start()) &&
244 expression_is_fixed(
ct.interval().size()) &&
245 expression_is_fixed(
ct.interval().end()));
250 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
251 name_to_num_literals[
name] +=
ct.bool_or().literals().size();
252 }
else if (
ct.constraint_case() ==
253 ConstraintProto::ConstraintCase::kBoolAnd) {
254 name_to_num_literals[
name] +=
255 ct.enforcement_literal().size() +
ct.bool_and().literals().size();
256 }
else if (
ct.constraint_case() ==
257 ConstraintProto::ConstraintCase::kAtMostOne) {
258 name_to_num_literals[
name] +=
ct.at_most_one().literals().size();
259 }
else if (
ct.constraint_case() ==
260 ConstraintProto::ConstraintCase::kExactlyOne) {
261 name_to_num_literals[
name] +=
ct.exactly_one().literals().size();
262 }
else if (
ct.constraint_case() ==
263 ConstraintProto::ConstraintCase::kLinMax) {
264 name_to_num_expressions[
name] +=
ct.lin_max().exprs().size();
265 }
else if (
ct.constraint_case() ==
266 ConstraintProto::ConstraintCase::kInterval) {
267 if (interval_is_fixed(
ct)) num_fixed_intervals++;
268 }
else if (
ct.constraint_case() ==
269 ConstraintProto::ConstraintCase::kNoOverlap2D) {
270 const int num_boxes =
ct.no_overlap_2d().x_intervals_size();
271 no_overlap_2d_num_rectangles += num_boxes;
272 for (
int i = 0;
i < num_boxes; ++
i) {
273 const int x_interval =
ct.no_overlap_2d().x_intervals(
i);
274 const int y_interval =
ct.no_overlap_2d().y_intervals(
i);
275 if (constraint_is_optional(x_interval) ||
276 constraint_is_optional(y_interval)) {
277 no_overlap_2d_num_optional_rectangles++;
279 const int num_fixed = interval_has_fixed_size(x_interval) +
280 interval_has_fixed_size(y_interval);
281 if (num_fixed == 0) {
282 no_overlap_2d_num_quadratic_areas++;
283 }
else if (num_fixed == 1) {
284 no_overlap_2d_num_linear_areas++;
286 if (interval_is_fixed(model_proto.constraints(x_interval)) &&
287 interval_is_fixed(model_proto.constraints(y_interval))) {
288 no_overlap_2d_num_fixed_rectangles++;
291 }
else if (
ct.constraint_case() ==
292 ConstraintProto::ConstraintCase::kNoOverlap) {
293 const int num_intervals =
ct.no_overlap().intervals_size();
294 no_overlap_num_intervals += num_intervals;
295 for (
int i = 0;
i < num_intervals; ++
i) {
297 if (constraint_is_optional(
interval)) {
298 no_overlap_num_optional_intervals++;
300 if (!interval_has_fixed_size(
interval)) {
301 no_overlap_num_variable_sizes++;
303 if (interval_is_fixed(model_proto.constraints(
interval))) {
304 no_overlap_num_fixed_intervals++;
307 }
else if (
ct.constraint_case() ==
308 ConstraintProto::ConstraintCase::kCumulative) {
309 const int num_intervals =
ct.cumulative().intervals_size();
310 cumulative_num_intervals += num_intervals;
311 for (
int i = 0;
i < num_intervals; ++
i) {
313 if (constraint_is_optional(
interval)) {
314 cumulative_num_optional_intervals++;
316 if (!interval_has_fixed_size(
interval)) {
317 cumulative_num_variable_sizes++;
319 if (!expression_is_fixed(
ct.cumulative().demands(
i))) {
320 cumulative_num_variable_demands++;
322 if (interval_is_fixed(model_proto.constraints(
interval))) {
323 cumulative_num_fixed_intervals++;
328 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
329 ct.linear().vars_size() > 3) {
330 name_to_num_terms[
name] +=
ct.linear().vars_size();
332 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
333 ct.linear().vars_size() > 1 &&
ct.linear().domain().size() > 2) {
334 name_to_num_complex_domain[
name]++;
338 int num_constants = 0;
339 absl::btree_set<int64_t> constant_values;
340 absl::btree_map<Domain, int> num_vars_per_domains;
341 for (
const IntegerVariableProto&
var : model_proto.variables()) {
342 if (
var.domain_size() == 2 &&
var.domain(0) ==
var.domain(1)) {
344 constant_values.insert(
var.domain(0));
351 const std::string model_fingerprint_str =
352 (absl::GetFlag(FLAGS_cp_model_fingerprint_model))
353 ? absl::StrFormat(
" (model_fingerprint: %#x)",
357 if (model_proto.has_objective() ||
358 model_proto.has_floating_point_objective()) {
359 absl::StrAppend(&result,
"optimization model '", model_proto.name(),
360 "':", model_fingerprint_str,
"\n");
362 absl::StrAppend(&result,
"satisfaction model '", model_proto.name(),
363 "':", model_fingerprint_str,
"\n");
366 for (
const DecisionStrategyProto& strategy : model_proto.search_strategy()) {
368 &result,
"Search strategy: on ",
369 strategy.exprs().size() + strategy.variables().size(),
" variables, ",
371 strategy.variable_selection_strategy()),
374 strategy.domain_reduction_strategy()),
378 auto count_variables_by_type =
379 [&model_proto](
const google::protobuf::RepeatedField<int>& vars,
380 int* num_booleans,
int* num_integers) {
381 for (
const int ref : vars) {
382 const auto& var_proto = model_proto.variables(
PositiveRef(ref));
383 if (var_proto.domain_size() == 2 && var_proto.domain(0) == 0 &&
384 var_proto.domain(1) == 1) {
388 *num_integers = vars.size() - *num_booleans;
392 int num_boolean_variables_in_objective = 0;
393 int num_integer_variables_in_objective = 0;
394 if (model_proto.has_objective()) {
395 count_variables_by_type(model_proto.objective().vars(),
396 &num_boolean_variables_in_objective,
397 &num_integer_variables_in_objective);
399 if (model_proto.has_floating_point_objective()) {
400 count_variables_by_type(model_proto.floating_point_objective().vars(),
401 &num_boolean_variables_in_objective,
402 &num_integer_variables_in_objective);
405 std::vector<std::string> obj_vars_strings;
406 if (num_boolean_variables_in_objective > 0) {
407 obj_vars_strings.push_back(absl::StrCat(
408 "#bools: ",
FormatCounter(num_boolean_variables_in_objective)));
410 if (num_integer_variables_in_objective > 0) {
411 obj_vars_strings.push_back(absl::StrCat(
412 "#ints: ",
FormatCounter(num_integer_variables_in_objective)));
415 const std::string objective_string =
416 model_proto.has_objective()
417 ? absl::StrCat(
" (", absl::StrJoin(obj_vars_strings,
" "),
419 : (model_proto.has_floating_point_objective()
420 ?
absl::StrCat(
" (",
absl::StrJoin(obj_vars_strings,
" "),
421 " in floating point objective)")
423 absl::StrAppend(&result,
425 objective_string,
"\n");
427 if (num_vars_per_domains.contains(Domain(0, 1))) {
429 const int num_bools = num_vars_per_domains[Domain(0, 1)];
430 const std::string temp =
431 absl::StrCat(
" - ",
FormatCounter(num_bools),
" Booleans in ",
433 absl::StrAppend(&result, Summarize(temp));
434 num_vars_per_domains.erase(Domain(0, 1));
436 if (num_vars_per_domains.size() < 100) {
437 for (
const auto& entry : num_vars_per_domains) {
438 const std::string temp =
440 entry.first.ToString(),
"\n");
441 absl::StrAppend(&result, Summarize(temp));
444 int64_t max_complexity = 0;
445 int64_t
min = std::numeric_limits<int64_t>::max();
446 int64_t
max = std::numeric_limits<int64_t>::min();
447 for (
const auto& entry : num_vars_per_domains) {
448 min = std::min(
min, entry.first.Min());
449 max = std::max(
max, entry.first.Max());
450 max_complexity = std::max(
451 max_complexity,
static_cast<int64_t
>(entry.first.NumIntervals()));
453 absl::StrAppend(&result,
" - ",
FormatCounter(num_vars_per_domains.size()),
454 " different domains in [",
min,
",",
max,
455 "] with a largest complexity of ", max_complexity,
".\n");
458 if (num_constants > 0) {
459 const std::string temp =
460 absl::StrCat(
" - ", num_constants,
" constants in {",
461 absl::StrJoin(constant_values,
","),
"} \n");
462 absl::StrAppend(&result, Summarize(temp));
465 std::vector<std::string> constraints;
466 constraints.reserve(name_to_num_constraints.size());
467 for (
const auto& [c_name, count] : name_to_num_constraints) {
468 const std::string
name(c_name);
470 if (name_to_num_reified.contains(c_name)) {
471 if (name_to_num_multi_reified.contains(c_name)) {
475 " #multi: ",
FormatCounter(name_to_num_multi_reified[c_name]),
")");
477 absl::StrAppend(&constraints.back(),
" (#enforced: ",
481 if (name_to_num_literals.contains(c_name)) {
482 absl::StrAppend(&constraints.back(),
" (#literals: ",
485 if (name_to_num_terms.contains(c_name)) {
486 absl::StrAppend(&constraints.back(),
490 if (name_to_num_expressions.contains(c_name)) {
491 absl::StrAppend(&constraints.back(),
" (#expressions: ",
494 if (name_to_num_complex_domain.contains(c_name)) {
495 absl::StrAppend(&constraints.back(),
" (#complex_domain: ",
498 if (
name ==
"kInterval" && num_fixed_intervals > 0) {
499 absl::StrAppend(&constraints.back(),
" (#fixed: ", num_fixed_intervals,
501 }
else if (
name ==
"kNoOverlap2D") {
502 absl::StrAppend(&constraints.back(),
503 " (#rectangles: ", no_overlap_2d_num_rectangles);
504 if (no_overlap_2d_num_optional_rectangles > 0) {
505 absl::StrAppend(&constraints.back(),
506 ", #optional: ", no_overlap_2d_num_optional_rectangles);
508 if (no_overlap_2d_num_linear_areas > 0) {
509 absl::StrAppend(&constraints.back(),
510 ", #linear_areas: ", no_overlap_2d_num_linear_areas);
512 if (no_overlap_2d_num_quadratic_areas > 0) {
513 absl::StrAppend(&constraints.back(),
", #quadratic_areas: ",
514 no_overlap_2d_num_quadratic_areas);
516 if (no_overlap_2d_num_fixed_rectangles > 0) {
517 absl::StrAppend(&constraints.back(),
", #fixed_rectangles: ",
518 no_overlap_2d_num_fixed_rectangles);
520 absl::StrAppend(&constraints.back(),
")");
521 }
else if (
name ==
"kCumulative") {
522 absl::StrAppend(&constraints.back(),
523 " (#intervals: ", cumulative_num_intervals);
524 if (cumulative_num_optional_intervals > 0) {
525 absl::StrAppend(&constraints.back(),
526 ", #optional: ", cumulative_num_optional_intervals);
528 if (cumulative_num_variable_sizes > 0) {
529 absl::StrAppend(&constraints.back(),
530 ", #variable_sizes: ", cumulative_num_variable_sizes);
532 if (cumulative_num_variable_demands > 0) {
533 absl::StrAppend(&constraints.back(),
", #variable_demands: ",
534 cumulative_num_variable_demands);
536 if (cumulative_num_fixed_intervals > 0) {
537 absl::StrAppend(&constraints.back(),
538 ", #fixed_intervals: ", cumulative_num_fixed_intervals);
540 absl::StrAppend(&constraints.back(),
")");
541 }
else if (
name ==
"kNoOverlap") {
542 absl::StrAppend(&constraints.back(),
543 " (#intervals: ", no_overlap_num_intervals);
544 if (no_overlap_num_optional_intervals > 0) {
545 absl::StrAppend(&constraints.back(),
546 ", #optional: ", no_overlap_num_optional_intervals);
548 if (no_overlap_num_variable_sizes > 0) {
549 absl::StrAppend(&constraints.back(),
550 ", #variable_sizes: ", no_overlap_num_variable_sizes);
552 if (no_overlap_num_fixed_intervals > 0) {
553 absl::StrAppend(&constraints.back(),
554 ", #fixed_intervals: ", no_overlap_num_fixed_intervals);
556 absl::StrAppend(&constraints.back(),
")");
559 std::sort(constraints.begin(), constraints.end());
560 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
566 bool has_objective) {
568 absl::StrAppend(&result,
"CpSolverResponse summary:");
569 absl::StrAppend(&result,
"\nstatus: ",
572 if (has_objective && response.status() != CpSolverStatus::INFEASIBLE) {
573 absl::StrAppendFormat(&result,
"\nobjective: %.16g",
574 response.objective_value());
575 absl::StrAppendFormat(&result,
"\nbest_bound: %.16g",
576 response.best_objective_bound());
578 absl::StrAppend(&result,
"\nobjective: NA");
579 absl::StrAppend(&result,
"\nbest_bound: NA");
582 absl::StrAppend(&result,
"\nintegers: ", response.num_integers());
583 absl::StrAppend(&result,
"\nbooleans: ", response.num_booleans());
584 absl::StrAppend(&result,
"\nconflicts: ", response.num_conflicts());
585 absl::StrAppend(&result,
"\nbranches: ", response.num_branches());
589 absl::StrAppend(&result,
590 "\npropagations: ", response.num_binary_propagations());
592 &result,
"\ninteger_propagations: ", response.num_integer_propagations());
594 absl::StrAppend(&result,
"\nrestarts: ", response.num_restarts());
595 absl::StrAppend(&result,
"\nlp_iterations: ", response.num_lp_iterations());
596 absl::StrAppend(&result,
"\nwalltime: ", response.wall_time());
597 absl::StrAppend(&result,
"\nusertime: ", response.user_time());
598 absl::StrAppend(&result,
599 "\ndeterministic_time: ", response.deterministic_time());
600 absl::StrAppend(&result,
"\ngap_integral: ", response.gap_integral());
601 if (!response.solution().empty()) {
602 absl::StrAppendFormat(
603 &result,
"\nsolution_fingerprint: %#x",
606 absl::StrAppend(&result,
"\n");
612void LogSubsolverNames(absl::Span<
const std::unique_ptr<SubSolver>> subsolvers,
613 absl::Span<const std::string> ignored,
614 SolverLogger* logger) {
615 if (!logger->LoggingIsEnabled())
return;
617 std::vector<std::string> full_problem_solver_names;
618 std::vector<std::string> incomplete_solver_names;
619 std::vector<std::string> first_solution_solver_names;
620 std::vector<std::string> helper_solver_names;
621 for (
int i = 0;
i < subsolvers.size(); ++
i) {
622 const auto& subsolver = subsolvers[
i];
623 switch (subsolver->type()) {
625 full_problem_solver_names.push_back(subsolver->name());
628 incomplete_solver_names.push_back(subsolver->name());
631 first_solution_solver_names.push_back(subsolver->name());
634 helper_solver_names.push_back(subsolver->name());
641 auto display_subsolver_list = [logger](absl::Span<const std::string> names,
642 const absl::string_view type_name) {
643 if (!names.empty()) {
644 absl::btree_map<std::string, int> solvers_and_count;
645 for (
const auto&
name : names) {
646 solvers_and_count[
name]++;
648 std::vector<std::string> counted_names;
649 for (
const auto& [
name, count] : solvers_and_count) {
651 counted_names.push_back(
name);
653 counted_names.push_back(absl::StrCat(
name,
"(", count,
")"));
657 logger, names.size(),
" ",
658 absl::StrCat(type_name, names.size() == 1 ?
"" :
"s"),
": [",
659 absl::StrJoin(counted_names.begin(), counted_names.end(),
", "),
"]");
663 display_subsolver_list(full_problem_solver_names,
"full problem subsolver");
664 display_subsolver_list(first_solution_solver_names,
665 "first solution subsolver");
666 display_subsolver_list(incomplete_solver_names,
"interleaved subsolver");
667 display_subsolver_list(helper_solver_names,
"helper subsolver");
668 if (!ignored.empty()) {
669 display_subsolver_list(ignored,
"ignored subsolver");
675void LogFinalStatistics(SharedClasses* shared) {
676 if (!shared->logger->LoggingIsEnabled())
return;
678 shared->logger->FlushPendingThrottledLogs(
true);
681 shared->stat_tables.Display(shared->logger);
682 shared->response->DisplayImprovementStatistics();
684 std::vector<std::vector<std::string>> table;
685 table.push_back({
"Solution repositories",
"Added",
"Queried",
"Synchro"});
686 table.push_back(shared->response->SolutionsRepository().TableLineStats());
687 if (shared->lp_solutions !=
nullptr) {
688 table.push_back(shared->lp_solutions->TableLineStats());
690 if (shared->incomplete_solutions !=
nullptr) {
691 table.push_back(shared->incomplete_solutions->TableLineStats());
695 if (shared->bounds) {
696 shared->bounds->LogStatistics(shared->logger);
699 if (shared->clauses) {
700 shared->clauses->LogStatistics(shared->logger);
705 shared->stats->Log(shared->logger);
708void LaunchSubsolvers(
const SatParameters& params, SharedClasses* shared,
709 std::vector<std::unique_ptr<SubSolver>>& subsolvers,
710 absl::Span<const std::string> ignored) {
713 if (params.interleave_search()) {
715 absl::StrFormat(
"Starting deterministic search at %.2fs with "
716 "%i workers and batch size of %d.",
717 shared->wall_timer->Get(), params.num_workers(),
718 params.interleave_batch_size()));
722 absl::StrFormat(
"Starting search at %.2fs with %i workers.",
723 shared->wall_timer->Get(), params.num_workers()));
725 LogSubsolverNames(subsolvers, ignored, shared->logger);
728 if (params.interleave_search()) {
729 int batch_size = params.interleave_batch_size();
730 if (batch_size == 0) {
731 batch_size = params.num_workers() == 1 ? 1 : params.num_workers() * 3;
734 "Setting number of tasks in each batch of interleaved search to ",
738 params.max_num_deterministic_batches());
746 for (
int i = 0;
i < subsolvers.size(); ++
i) {
747 subsolvers[
i].reset();
749 LogFinalStatistics(shared);
753class FullProblemSolver :
public SubSolver {
755 FullProblemSolver(absl::string_view
name,
756 const SatParameters& local_parameters,
bool split_in_chunks,
757 SharedClasses* shared,
bool stop_at_first_solution =
false)
758 : SubSolver(
name, stop_at_first_solution ? FIRST_SOLUTION : FULL_PROBLEM),
760 split_in_chunks_(split_in_chunks),
761 stop_at_first_solution_(stop_at_first_solution),
762 local_model_(SubSolver::
name()) {
764 *(local_model_.GetOrCreate<SatParameters>()) = local_parameters;
765 shared_->time_limit->UpdateLocalLimit(
766 local_model_.GetOrCreate<TimeLimit>());
768 if (stop_at_first_solution) {
769 local_model_.GetOrCreate<TimeLimit>()
770 ->RegisterSecondaryExternalBooleanAsLimit(
771 shared_->response->first_solution_solvers_should_stop());
774 if (shared->response !=
nullptr) {
775 local_model_.Register<SharedResponseManager>(shared->response);
778 if (shared->lp_solutions !=
nullptr) {
779 local_model_.Register<SharedLPSolutionRepository>(
780 shared->lp_solutions.get());
783 if (shared->incomplete_solutions !=
nullptr) {
784 local_model_.Register<SharedIncompleteSolutionManager>(
785 shared->incomplete_solutions.get());
788 if (shared->bounds !=
nullptr) {
789 local_model_.Register<SharedBoundsManager>(shared->bounds.get());
792 if (shared->clauses !=
nullptr) {
793 local_model_.Register<SharedClausesManager>(shared->clauses.get());
796 if (local_parameters.use_shared_tree_search()) {
797 local_model_.Register<SharedTreeManager>(shared->shared_tree_manager);
802 local_model_.Register<SharedStatistics>(shared_->stats);
807 auto* logger = local_model_.GetOrCreate<SolverLogger>();
808 logger->EnableLogging(local_parameters.log_search_progress());
809 logger->SetLogToStdOut(local_parameters.log_to_stdout());
812 ~FullProblemSolver()
override {
813 CpSolverResponse response;
815 shared_->response->AppendResponseToBeMerged(response);
816 shared_->stat_tables.AddTimingStat(*
this);
817 shared_->stat_tables.AddLpStat(
name(), &local_model_);
818 shared_->stat_tables.AddSearchStat(
name(), &local_model_);
819 shared_->stat_tables.AddClausesStat(
name(), &local_model_);
822 bool IsDone()
override {
825 if (shared_->SearchIsDone())
return true;
827 return stop_at_first_solution_ &&
828 shared_->response->first_solution_solvers_should_stop()->load();
831 bool TaskIsAvailable()
override {
832 if (IsDone())
return false;
838 if (shared_->SearchIsDone())
return false;
840 absl::MutexLock mutex_lock(&mutex_);
841 if (previous_task_is_completed_) {
842 if (solving_first_chunk_)
return true;
843 if (split_in_chunks_)
return true;
848 std::function<void()> GenerateTask(int64_t )
override {
850 absl::MutexLock mutex_lock(&mutex_);
851 previous_task_is_completed_ =
false;
854 if (solving_first_chunk_) {
861 if (shared_->bounds !=
nullptr) {
863 shared_->model_proto, shared_->bounds.get(), &local_model_);
865 shared_->model_proto, shared_->bounds.get(), &local_model_);
870 if (shared_->clauses !=
nullptr) {
871 const int id = shared_->clauses->RegisterNewId();
872 shared_->clauses->SetWorkerNameForId(
id, local_model_.Name());
879 auto* logger = local_model_.GetOrCreate<SolverLogger>();
882 "Starting subsolver \'%s\' hint search at %.2fs",
883 name(), shared_->wall_timer->Get()));
885 if (local_model_.GetOrCreate<SatParameters>()->repair_hint()) {
892 absl::StrFormat(
"Starting subsolver \'%s\' search at %.2fs",
893 name(), shared_->wall_timer->Get()));
896 solving_first_chunk_ =
false;
898 if (split_in_chunks_) {
900 absl::MutexLock mutex_lock(&mutex_);
901 previous_task_is_completed_ =
true;
906 auto*
time_limit = local_model_.GetOrCreate<TimeLimit>();
907 if (split_in_chunks_) {
910 auto* params = local_model_.GetOrCreate<SatParameters>();
911 params->set_max_deterministic_time(1);
912 time_limit->ResetLimitFromParameters(*params);
913 shared_->time_limit->UpdateLocalLimit(
time_limit);
916 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
919 absl::MutexLock mutex_lock(&mutex_);
920 previous_task_is_completed_ =
true;
921 dtime_since_last_sync_ +=
922 time_limit->GetElapsedDeterministicTime() - saved_dtime;
929 void Synchronize()
override {
930 absl::MutexLock mutex_lock(&mutex_);
931 AddTaskDeterministicDuration(dtime_since_last_sync_);
932 shared_->time_limit->AdvanceDeterministicTime(dtime_since_last_sync_);
933 dtime_since_last_sync_ = 0.0;
937 SharedClasses* shared_;
938 const bool split_in_chunks_;
939 const bool stop_at_first_solution_;
944 bool solving_first_chunk_ =
true;
947 double dtime_since_last_sync_ ABSL_GUARDED_BY(mutex_) = 0.0;
948 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
951#if !defined(__PORTABLE_PLATFORM__)
953class FeasibilityPumpSolver :
public SubSolver {
955 FeasibilityPumpSolver(
const SatParameters& local_parameters,
956 SharedClasses* shared)
957 : SubSolver(
"feasibility_pump", INCOMPLETE),
959 local_model_(
std::make_unique<Model>(
name())) {
961 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
962 shared_->time_limit->UpdateLocalLimit(
963 local_model_->GetOrCreate<TimeLimit>());
965 if (shared->response !=
nullptr) {
966 local_model_->Register<SharedResponseManager>(shared->response);
969 if (shared->lp_solutions !=
nullptr) {
970 local_model_->Register<SharedLPSolutionRepository>(
971 shared->lp_solutions.get());
974 if (shared->incomplete_solutions !=
nullptr) {
975 local_model_->Register<SharedIncompleteSolutionManager>(
976 shared->incomplete_solutions.get());
980 if (shared_->bounds !=
nullptr) {
981 RegisterVariableBoundsLevelZeroImport(
982 shared_->model_proto, shared_->bounds.get(), local_model_.get());
986 ~FeasibilityPumpSolver()
override {
987 shared_->stat_tables.AddTimingStat(*
this);
990 bool IsDone()
override {
return shared_->SearchIsDone(); }
992 bool TaskIsAvailable()
override {
993 if (shared_->SearchIsDone())
return false;
994 absl::MutexLock mutex_lock(&mutex_);
995 return previous_task_is_completed_;
998 std::function<void()> GenerateTask(int64_t )
override {
1000 absl::MutexLock mutex_lock(&mutex_);
1001 previous_task_is_completed_ =
false;
1005 absl::MutexLock mutex_lock(&mutex_);
1006 if (solving_first_chunk_) {
1010 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
1011 solving_first_chunk_ =
false;
1013 previous_task_is_completed_ =
true;
1018 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
1019 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
1020 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
1021 if (!feasibility_pump->Solve()) {
1022 shared_->response->NotifyThatImprovingProblemIsInfeasible(
name());
1026 absl::MutexLock mutex_lock(&mutex_);
1027 dtime_since_last_sync_ +=
1028 time_limit->GetElapsedDeterministicTime() - saved_dtime;
1032 if (shared_->SearchIsDone()) {
1033 shared_->time_limit->Stop();
1037 absl::MutexLock mutex_lock(&mutex_);
1038 previous_task_is_completed_ =
true;
1042 void Synchronize()
override {
1043 absl::MutexLock mutex_lock(&mutex_);
1044 AddTaskDeterministicDuration(dtime_since_last_sync_);
1045 shared_->time_limit->AdvanceDeterministicTime(dtime_since_last_sync_);
1046 dtime_since_last_sync_ = 0.0;
1050 SharedClasses* shared_;
1051 std::unique_ptr<Model> local_model_;
1057 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
1059 double dtime_since_last_sync_ ABSL_GUARDED_BY(mutex_) = 0.0;
1060 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
1064class LnsSolver :
public SubSolver {
1066 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
1067 const SatParameters& lns_parameters,
1068 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
1069 : SubSolver(generator->
name(), INCOMPLETE),
1070 generator_(
std::move(generator)),
1072 lns_parameters_(lns_parameters),
1075 ~LnsSolver()
override {
1076 shared_->stat_tables.AddTimingStat(*
this);
1077 shared_->stat_tables.AddLnsStat(
name(), *generator_);
1080 bool TaskIsAvailable()
override {
1081 if (shared_->SearchIsDone())
return false;
1082 return generator_->ReadyToGenerate();
1085 std::function<void()> GenerateTask(int64_t task_id)
override {
1086 return [task_id,
this]() {
1087 if (shared_->SearchIsDone())
return;
1092 const int32_t low =
static_cast<int32_t
>(task_id);
1093 const int32_t high =
static_cast<int32_t
>(task_id >> 32);
1094 std::seed_seq seed{low, high, lns_parameters_.random_seed()};
1097 NeighborhoodGenerator::SolveData data;
1098 data.difficulty = generator_->difficulty();
1099 data.deterministic_limit = generator_->deterministic_limit();
1102 CpSolverResponse base_response;
1104 const SharedSolutionRepository<int64_t>& repo =
1105 shared_->response->SolutionsRepository();
1106 if (repo.NumSolutions() > 0) {
1107 base_response.set_status(CpSolverStatus::FEASIBLE);
1108 const SharedSolutionRepository<int64_t>::Solution
solution =
1109 repo.GetRandomBiasedSolution(random);
1111 base_response.add_solution(
value);
1116 data.initial_best_objective = repo.GetSolution(0).rank;
1117 data.base_objective =
solution.rank;
1119 base_response.set_status(CpSolverStatus::UNKNOWN);
1126 data.initial_best_objective =
1127 shared_->response->GetInnerObjectiveUpperBound();
1128 data.base_objective = data.initial_best_objective;
1132 Neighborhood neighborhood =
1133 generator_->Generate(base_response, data.difficulty, random);
1135 if (!neighborhood.is_generated)
return;
1137 SatParameters local_params(lns_parameters_);
1138 local_params.set_max_deterministic_time(data.deterministic_limit);
1142 const int64_t stall = generator_->num_consecutive_non_improving_calls();
1143 const int search_index = stall < 10 ? 0 : task_id % 2;
1144 absl::string_view search_info;
1145 switch (search_index) {
1147 local_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
1148 local_params.set_linearization_level(0);
1149 search_info =
"auto_l0";
1152 local_params.set_search_branching(SatParameters::PORTFOLIO_SEARCH);
1153 local_params.set_search_random_variable_pool_size(5);
1154 search_info =
"folio_rnd";
1158 std::string source_info =
1159 neighborhood.source_info.empty() ?
name() : neighborhood.source_info;
1160 const int64_t num_calls = std::max(int64_t{1}, generator_->num_calls());
1161 const double fully_solved_proportion =
1162 static_cast<double>(generator_->num_fully_solved_calls()) /
1163 static_cast<double>(num_calls);
1164 const std::string lns_info = absl::StrFormat(
1165 "%s (d=%0.2f s=%i t=%0.2f p=%0.2f stall=%d h=%s)", source_info,
1166 data.difficulty, task_id, data.deterministic_limit,
1167 fully_solved_proportion, stall, search_info);
1169 Model local_model(lns_info);
1170 *(local_model.GetOrCreate<SatParameters>()) = local_params;
1171 TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
1172 local_time_limit->ResetLimitFromParameters(local_params);
1173 shared_->time_limit->UpdateLocalLimit(local_time_limit);
1176 CpModelProto lns_fragment;
1177 CpModelProto mapping_proto;
1178 auto context = std::make_unique<PresolveContext>(
1179 &local_model, &lns_fragment, &mapping_proto);
1181 *lns_fragment.mutable_variables() = neighborhood.delta.variables();
1183 ModelCopy copier(
context.get());
1186 if (!copier.ImportAndSimplifyConstraints(helper_->ModelProto())) {
1191 if (!neighborhood.delta.constraints().empty() &&
1192 !copier.ImportAndSimplifyConstraints(neighborhood.delta)) {
1198 context->WriteVariableDomainsToProto();
1203 helper_->ModelProto(),
context.get());
1204 lns_fragment.set_name(absl::StrCat(
"lns_", task_id,
"_", source_info));
1207 if (neighborhood.delta.has_solution_hint()) {
1208 *lns_fragment.mutable_solution_hint() =
1209 neighborhood.delta.solution_hint();
1211 if (generator_->num_consecutive_non_improving_calls() > 10 &&
1212 absl::Bernoulli(random, 0.5)) {
1216 lns_fragment.clear_solution_hint();
1218 if (neighborhood.is_simple &&
1219 neighborhood.num_relaxed_variables_in_objective == 0) {
1225 if (generator_->num_consecutive_non_improving_calls() > 10) {
1227 lns_fragment.clear_solution_hint();
1235 CpModelProto debug_copy;
1236 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
1239 debug_copy = lns_fragment;
1242 if (absl::GetFlag(FLAGS_cp_model_dump_submodels)) {
1244 const std::string lns_name =
1245 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
1246 lns_fragment.name(),
".pb.txt");
1247 LOG(INFO) <<
"Dumping LNS model to '" << lns_name <<
"'.";
1251 std::vector<int> postsolve_mapping;
1252 const CpSolverStatus presolve_status =
1257 neighborhood.delta.Clear();
1262 auto* local_response_manager =
1263 local_model.GetOrCreate<SharedResponseManager>();
1264 local_response_manager->InitializeObjective(lns_fragment);
1265 local_response_manager->SetSynchronizationMode(
true);
1267 CpSolverResponse local_response;
1268 if (presolve_status == CpSolverStatus::UNKNOWN) {
1271 if (shared_->SearchIsDone())
return;
1276 local_response = local_response_manager->GetResponse();
1281 if (local_response.solution_info().empty()) {
1282 local_response.set_solution_info(
1283 absl::StrCat(lns_info,
" [presolve]"));
1289 if (presolve_status == CpSolverStatus::INFEASIBLE) {
1290 local_response_manager->NotifyThatImprovingProblemIsInfeasible(
1293 local_response = local_response_manager->GetResponse();
1294 local_response.set_status(presolve_status);
1296 const std::string solution_info = local_response.solution_info();
1297 std::vector<int64_t> solution_values(local_response.solution().begin(),
1298 local_response.solution().end());
1300 data.status = local_response.status();
1303 if (data.status == CpSolverStatus::OPTIMAL ||
1304 data.status == CpSolverStatus::FEASIBLE) {
1306 local_params, helper_->ModelProto().variables_size(), mapping_proto,
1307 postsolve_mapping, &solution_values);
1308 local_response.mutable_solution()->Assign(solution_values.begin(),
1309 solution_values.end());
1312 data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
1314 bool new_solution =
false;
1315 bool display_lns_info = VLOG_IS_ON(2);
1316 if (!local_response.solution().empty()) {
1322 const bool feasible =
1325 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
1326 const std::string
name =
1327 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
1328 debug_copy.name(),
".pb.txt");
1329 LOG(INFO) <<
"Dumping problematic LNS model to '" <<
name <<
"'.";
1332 LOG(ERROR) <<
"Infeasible LNS solution! " << solution_info
1333 <<
" solved with params " << local_params;
1356 if (data.status == CpSolverStatus::OPTIMAL &&
1357 !shared_->model_proto.has_symmetry() && !solution_values.empty() &&
1358 neighborhood.is_simple &&
1359 !neighborhood.variables_that_can_be_fixed_to_local_optimum
1361 display_lns_info =
true;
1362 shared_->bounds->FixVariablesFromPartialSolution(
1364 neighborhood.variables_that_can_be_fixed_to_local_optimum);
1368 data.new_objective = data.base_objective;
1369 if (data.status == CpSolverStatus::OPTIMAL ||
1370 data.status == CpSolverStatus::FEASIBLE) {
1372 shared_->model_proto.objective(), solution_values));
1377 if (data.status == CpSolverStatus::OPTIMAL ||
1378 data.status == CpSolverStatus::FEASIBLE) {
1379 const std::vector<int64_t> base_solution(
1380 base_response.solution().begin(), base_response.solution().end());
1381 if (solution_values != base_solution) {
1382 new_solution =
true;
1383 shared_->response->NewSolution(solution_values, solution_info,
1387 if (!neighborhood.is_reduced &&
1388 (data.status == CpSolverStatus::OPTIMAL ||
1389 data.status == CpSolverStatus::INFEASIBLE)) {
1390 shared_->response->NotifyThatImprovingProblemIsInfeasible(
1392 shared_->time_limit->Stop();
1396 generator_->AddSolveData(data);
1398 if (VLOG_IS_ON(2) && display_lns_info) {
1399 std::string s = absl::StrCat(
" LNS ",
name(),
":");
1402 shared_->model_proto.objective(),
1404 base_response.solution()));
1406 shared_->model_proto.objective(),
1409 absl::StrAppend(&s,
" [new_sol:", base_obj,
" -> ", new_obj,
"]");
1411 if (neighborhood.is_simple) {
1413 &s,
" [",
"relaxed:", neighborhood.num_relaxed_variables,
1414 " in_obj:", neighborhood.num_relaxed_variables_in_objective,
1416 neighborhood.variables_that_can_be_fixed_to_local_optimum.size(),
1419 SOLVER_LOG(shared_->logger, s,
" [d:", data.difficulty,
1420 ", id:", task_id,
", dtime:", data.deterministic_time,
"/",
1421 data.deterministic_limit,
1423 ", #calls:", generator_->num_calls(),
1424 ", p:", fully_solved_proportion,
"]");
1429 void Synchronize()
override {
1430 const double dtime = generator_->Synchronize();
1431 AddTaskDeterministicDuration(dtime);
1432 shared_->time_limit->AdvanceDeterministicTime(dtime);
1436 std::unique_ptr<NeighborhoodGenerator> generator_;
1437 NeighborhoodGeneratorHelper* helper_;
1438 const SatParameters lns_parameters_;
1439 SharedClasses* shared_;
1442void SolveCpModelParallel(SharedClasses* shared, Model* global_model) {
1443 const SatParameters& params = *global_model->GetOrCreate<SatParameters>();
1444 CHECK(!params.enumerate_all_solutions())
1445 <<
"Enumerating all solutions in parallel is not supported.";
1446 if (global_model->GetOrCreate<TimeLimit>()->LimitReached())
return;
1450 SubsolverNameFilter name_filter(params);
1455 std::vector<std::unique_ptr<SubSolver>> subsolvers;
1466 std::vector<std::unique_ptr<SubSolver>> full_worker_subsolvers;
1467 std::vector<std::unique_ptr<SubSolver>> first_solution_full_subsolvers;
1468 std::vector<std::unique_ptr<SubSolver>> reentrant_interleaved_subsolvers;
1469 std::vector<std::unique_ptr<SubSolver>> interleaved_subsolvers;
1472 subsolvers.push_back(std::make_unique<SynchronizationPoint>(
1473 "synchronization_agent", [shared]() {
1474 shared->response->Synchronize();
1475 shared->response->MutableSolutionsRepository()->Synchronize();
1476 if (shared->bounds !=
nullptr) {
1477 shared->bounds->Synchronize();
1479 if (shared->lp_solutions !=
nullptr) {
1480 shared->lp_solutions->Synchronize();
1482 if (shared->clauses !=
nullptr) {
1483 shared->clauses->Synchronize();
1488 const SatParameters& lns_params = name_to_params.at(
"lns");
1492 auto unique_helper = std::make_unique<NeighborhoodGeneratorHelper>(
1493 &shared->model_proto, ¶ms, shared->response, shared->bounds.get());
1494 NeighborhoodGeneratorHelper* helper = unique_helper.get();
1495 subsolvers.push_back(std::move(unique_helper));
1498 if (params.shared_tree_num_workers() > 0 &&
1499 shared->model_proto.assumptions().empty()) {
1501 name_filter.Filter({name_to_params.at(
"shared_tree")}),
1502 params.shared_tree_num_workers())) {
1503 full_worker_subsolvers.push_back(std::make_unique<FullProblemSolver>(
1504 local_params.name(), local_params,
1505 params.interleave_search(), shared));
1511 params, shared->model_proto,
1512 full_worker_subsolvers.size(),
1514 if (!name_filter.Keep(local_params.name()))
continue;
1517 if (params.optimize_with_max_hs())
continue;
1520 if (local_params.use_objective_shaving_search()) {
1521 full_worker_subsolvers.push_back(std::make_unique<ObjectiveShavingSolver>(
1522 local_params, helper, shared));
1527 if (local_params.use_variables_shaving_search()) {
1528 full_worker_subsolvers.push_back(
1529 std::make_unique<VariablesShavingSolver>(local_params, shared));
1533 full_worker_subsolvers.push_back(std::make_unique<FullProblemSolver>(
1534 local_params.name(), local_params,
1535 params.interleave_search(), shared));
1539 int num_interleaved_subsolver_that_do_not_need_solution = 0;
1540 if (params.use_feasibility_pump() && name_filter.Keep(
"feasibility_pump")) {
1541 ++num_interleaved_subsolver_that_do_not_need_solution;
1542 interleaved_subsolvers.push_back(
1543 std::make_unique<FeasibilityPumpSolver>(params, shared));
1548 if (params.use_rins_lns() && name_filter.Keep(
"rins/rens")) {
1554 ++num_interleaved_subsolver_that_do_not_need_solution;
1555 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1556 std::make_unique<RelaxationInducedNeighborhoodGenerator>(
1557 helper, shared->response, shared->lp_solutions.get(),
1558 shared->incomplete_solutions.get(), name_filter.LastName()),
1559 lns_params, helper, shared));
1567 if (params.use_lns() && shared->model_proto.has_objective() &&
1568 !shared->model_proto.objective().vars().empty()) {
1571 if (name_filter.Keep(
"rnd_var_lns")) {
1572 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1573 std::make_unique<RelaxRandomVariablesGenerator>(
1574 helper, name_filter.LastName()),
1575 lns_params, helper, shared));
1577 if (name_filter.Keep(
"rnd_cst_lns")) {
1578 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1579 std::make_unique<RelaxRandomConstraintsGenerator>(
1580 helper, name_filter.LastName()),
1581 lns_params, helper, shared));
1583 if (name_filter.Keep(
"graph_var_lns")) {
1584 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1585 std::make_unique<VariableGraphNeighborhoodGenerator>(
1586 helper, name_filter.LastName()),
1587 lns_params, helper, shared));
1589 if (name_filter.Keep(
"graph_arc_lns")) {
1590 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1591 std::make_unique<ArcGraphNeighborhoodGenerator>(
1592 helper, name_filter.LastName()),
1593 lns_params, helper, shared));
1595 if (name_filter.Keep(
"graph_cst_lns")) {
1596 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1597 std::make_unique<ConstraintGraphNeighborhoodGenerator>(
1598 helper, name_filter.LastName()),
1599 lns_params, helper, shared));
1601 if (name_filter.Keep(
"graph_dec_lns")) {
1602 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1603 std::make_unique<DecompositionGraphNeighborhoodGenerator>(
1604 helper, name_filter.LastName()),
1605 lns_params, helper, shared));
1607 if (params.use_lb_relax_lns() && name_filter.Keep(
"lb_relax_lns")) {
1608 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1609 std::make_unique<LocalBranchingLpBasedNeighborhoodGenerator>(
1610 helper, name_filter.LastName(),
1611 [](
const CpModelProto cp_model, Model*
model) {
1612 model->GetOrCreate<SharedResponseManager>()
1613 ->InitializeObjective(cp_model);
1614 LoadCpModel(cp_model, model);
1615 SolveLoadedCpModel(cp_model, model);
1617 shared->time_limit),
1618 lns_params, helper, shared));
1621 const bool has_no_overlap_or_cumulative =
1622 !helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty() ||
1623 !helper->TypeToConstraints(ConstraintProto::kCumulative).empty();
1626 if (has_no_overlap_or_cumulative) {
1627 if (name_filter.Keep(
"scheduling_intervals_lns")) {
1628 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1629 std::make_unique<RandomIntervalSchedulingNeighborhoodGenerator>(
1630 helper, name_filter.LastName()),
1631 lns_params, helper, shared));
1633 if (name_filter.Keep(
"scheduling_time_window_lns")) {
1634 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1635 std::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
1636 helper, name_filter.LastName()),
1637 lns_params, helper, shared));
1639 const std::vector<std::vector<int>> intervals_in_constraints =
1640 helper->GetUniqueIntervalSets();
1641 if (intervals_in_constraints.size() > 2 &&
1642 name_filter.Keep(
"scheduling_resource_windows_lns")) {
1643 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1644 std::make_unique<SchedulingResourceWindowsNeighborhoodGenerator>(
1645 helper, intervals_in_constraints, name_filter.LastName()),
1646 lns_params, helper, shared));
1651 const bool has_no_overlap2d =
1652 !helper->TypeToConstraints(ConstraintProto::kNoOverlap2D).empty();
1653 if (has_no_overlap2d) {
1654 if (name_filter.Keep(
"packing_rectangles_lns")) {
1655 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1656 std::make_unique<RandomRectanglesPackingNeighborhoodGenerator>(
1657 helper, name_filter.LastName()),
1658 lns_params, helper, shared));
1660 if (name_filter.Keep(
"packing_precedences_lns")) {
1661 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1662 std::make_unique<RandomPrecedencesPackingNeighborhoodGenerator>(
1663 helper, name_filter.LastName()),
1664 lns_params, helper, shared));
1666 if (name_filter.Keep(
"packing_slice_lns")) {
1667 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1668 std::make_unique<SlicePackingNeighborhoodGenerator>(
1669 helper, name_filter.LastName()),
1670 lns_params, helper, shared));
1675 if (has_no_overlap_or_cumulative || has_no_overlap2d) {
1676 if (name_filter.Keep(
"scheduling_precedences_lns")) {
1677 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1678 std::make_unique<RandomPrecedenceSchedulingNeighborhoodGenerator>(
1679 helper, name_filter.LastName()),
1680 lns_params, helper, shared));
1684 const int num_circuit =
static_cast<int>(
1685 helper->TypeToConstraints(ConstraintProto::kCircuit).
size());
1686 const int num_routes =
static_cast<int>(
1687 helper->TypeToConstraints(ConstraintProto::kRoutes).
size());
1688 if (num_circuit + num_routes > 0) {
1689 if (name_filter.Keep(
"routing_random_lns")) {
1690 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1691 std::make_unique<RoutingRandomNeighborhoodGenerator>(
1692 helper, name_filter.LastName()),
1693 lns_params, helper, shared));
1695 if (name_filter.Keep(
"routing_path_lns")) {
1696 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1697 std::make_unique<RoutingPathNeighborhoodGenerator>(
1698 helper, name_filter.LastName()),
1699 lns_params, helper, shared));
1702 if (num_routes > 0 || num_circuit > 1) {
1703 if (name_filter.Keep(
"routing_full_path_lns")) {
1704 reentrant_interleaved_subsolvers.push_back(std::make_unique<LnsSolver>(
1705 std::make_unique<RoutingFullPathNeighborhoodGenerator>(
1706 helper, name_filter.LastName()),
1707 lns_params, helper, shared));
1714 const auto get_linear_model = [&]() {
1715 auto* candidate = global_model->Get<LinearModel>();
1716 if (candidate !=
nullptr)
return candidate;
1719 LinearModel* linear_model =
new LinearModel(shared->model_proto);
1720 global_model->TakeOwnership(linear_model);
1721 global_model->Register(linear_model);
1722 return global_model->Get<LinearModel>();
1729 if (shared->model_proto.has_objective()) {
1733 const int num_thread_for_interleaved_workers =
1734 params.num_workers() - full_worker_subsolvers.size();
1735 int num_violation_ls = params.has_num_violation_ls()
1736 ? params.num_violation_ls()
1737 : (num_thread_for_interleaved_workers + 1) / 2;
1741 if (reentrant_interleaved_subsolvers.empty()) {
1743 std::max(num_violation_ls,
1744 num_thread_for_interleaved_workers -
1745 static_cast<int>(interleaved_subsolvers.size()));
1748 const absl::string_view ls_name =
"ls";
1749 const absl::string_view lin_ls_name =
"ls_lin";
1751 const int num_ls_lin =
1752 name_filter.Keep(lin_ls_name) ? num_violation_ls / 3 : 0;
1753 const int num_ls_default =
1754 name_filter.Keep(ls_name) ? num_violation_ls - num_ls_lin : 0;
1756 if (num_ls_default > 0) {
1757 std::shared_ptr<SharedLsStates> states = std::make_shared<SharedLsStates>(
1758 ls_name, params, &shared->stat_tables);
1759 for (
int i = 0;
i < num_ls_default; ++
i) {
1760 SatParameters local_params = params;
1761 local_params.set_random_seed(
1762 CombineSeed(params.random_seed(), interleaved_subsolvers.size()));
1763 local_params.set_feasibility_jump_linearization_level(0);
1764 interleaved_subsolvers.push_back(
1765 std::make_unique<FeasibilityJumpSolver>(
1767 local_params, states, shared->time_limit, shared->response,
1768 shared->bounds.get(), shared->stats, &shared->stat_tables));
1772 if (num_ls_lin > 0) {
1773 std::shared_ptr<SharedLsStates> lin_states =
1774 std::make_shared<SharedLsStates>(lin_ls_name, params,
1775 &shared->stat_tables);
1776 for (
int i = 0;
i < num_ls_lin; ++
i) {
1777 SatParameters local_params = params;
1778 local_params.set_random_seed(
1779 CombineSeed(params.random_seed(), interleaved_subsolvers.size()));
1780 local_params.set_feasibility_jump_linearization_level(2);
1781 interleaved_subsolvers.push_back(
1782 std::make_unique<FeasibilityJumpSolver>(
1784 local_params, lin_states, shared->time_limit, shared->response,
1785 shared->bounds.get(), shared->stats, &shared->stat_tables));
1796 int num_thread_available =
1797 params.num_workers() -
static_cast<int>(full_worker_subsolvers.size());
1802 if (!params.use_feasibility_jump() &&
1803 num_interleaved_subsolver_that_do_not_need_solution > 0) {
1804 --num_thread_available;
1806 num_thread_available = std::max(num_thread_available, 0);
1808 const std::vector<SatParameters> all_params =
1810 num_thread_available);
1812 std::shared_ptr<SharedLsStates> fj_states;
1813 std::shared_ptr<SharedLsStates> fj_lin_states;
1816 for (
const SatParameters& local_params : all_params) {
1817 if (local_params.use_feasibility_jump()) {
1819 std::shared_ptr<SharedLsStates> states;
1820 if (local_params.feasibility_jump_linearization_level() == 0) {
1821 if (fj_states ==
nullptr) {
1822 fj_states = std::make_shared<SharedLsStates>(
1823 local_params.name(), params, &shared->stat_tables);
1827 if (fj_lin_states ==
nullptr) {
1828 fj_lin_states = std::make_shared<SharedLsStates>(
1829 local_params.name(), params, &shared->stat_tables);
1831 states = fj_lin_states;
1834 interleaved_subsolvers.push_back(
1835 std::make_unique<FeasibilityJumpSolver>(
1837 get_linear_model(), local_params, states, shared->time_limit,
1838 shared->response, shared->bounds.get(), shared->stats,
1839 &shared->stat_tables));
1841 first_solution_full_subsolvers.push_back(
1842 std::make_unique<FullProblemSolver>(
1843 local_params.name(), local_params,
1844 local_params.interleave_search(), shared,
1853 const auto move_all =
1854 [&subsolvers](std::vector<std::unique_ptr<SubSolver>>& from) {
1855 for (
int i = 0;
i < from.size(); ++
i) {
1856 subsolvers.push_back(std::move(from[
i]));
1860 move_all(full_worker_subsolvers);
1861 move_all(first_solution_full_subsolvers);
1862 move_all(reentrant_interleaved_subsolvers);
1863 move_all(interleaved_subsolvers);
1868 if (shared->model_proto.has_objective() &&
1869 !shared->model_proto.objective().vars().empty()) {
1870 subsolvers.push_back(std::make_unique<SynchronizationPoint>(
1871 "update_gap_integral",
1872 [shared]() { shared->response->UpdateGapIntegral(); }));
1875 LaunchSubsolvers(params, shared, subsolvers, name_filter.AllIgnored());
1883void AddPostsolveClauses(
const std::vector<int>& postsolve_mapping,
1884 Model*
model, CpModelProto* mapping_proto) {
1885 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1886 auto* postsolve =
model->GetOrCreate<PostsolveClauses>();
1887 for (
const auto& clause : postsolve->clauses) {
1888 auto*
ct = mapping_proto->add_constraints()->mutable_bool_or();
1889 for (
const Literal l : clause) {
1890 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
1892 var = postsolve_mapping[
var];
1896 postsolve->clauses.clear();
1899bool VarIsFixed(
const CpModelProto& model_proto,
int i) {
1900 return model_proto.variables(
i).domain_size() == 2 &&
1901 model_proto.variables(
i).domain(0) ==
1902 model_proto.variables(
i).domain(1);
1905void TestSolutionHintForFeasibility(
const CpModelProto& model_proto,
1906 SolverLogger* logger,
1907 SharedResponseManager* manager =
nullptr) {
1908 if (!model_proto.has_solution_hint())
return;
1910 int num_active_variables = 0;
1911 int num_hinted_variables = 0;
1912 for (
int var = 0;
var < model_proto.variables_size(); ++
var) {
1913 if (VarIsFixed(model_proto,
var))
continue;
1914 ++num_active_variables;
1917 for (
int i = 0;
i < model_proto.solution_hint().vars_size(); ++
i) {
1918 const int ref = model_proto.solution_hint().vars(
i);
1919 if (VarIsFixed(model_proto,
PositiveRef(ref)))
continue;
1920 ++num_hinted_variables;
1922 CHECK_LE(num_hinted_variables, num_active_variables);
1924 if (num_active_variables != num_hinted_variables) {
1926 logger,
"The solution hint is incomplete: ", num_hinted_variables,
1927 " out of ", num_active_variables,
" non fixed variables hinted.");
1931 std::vector<int64_t>
solution(model_proto.variables_size(), 0);
1933 for (
int var = 0;
var < model_proto.variables_size(); ++
var) {
1934 if (VarIsFixed(model_proto,
var)) {
1939 for (
int i = 0;
i < model_proto.solution_hint().vars_size(); ++
i) {
1940 const int ref = model_proto.solution_hint().vars(
i);
1942 const int64_t
value = model_proto.solution_hint().values(
i);
1945 if (!domain.Contains(hinted_value)) {
1947 "The solution hint is complete but it contains values outside "
1948 "of the domain of the variables.");
1955 if (manager !=
nullptr) {
1958 manager->NewSolution(
solution,
"complete_hint",
nullptr);
1960 SOLVER_LOG(logger,
"The solution hint is complete and is feasible.");
1966 "The solution hint is complete, but it is infeasible! we "
1967 "will try to repair it.");
1974 const std::function<
void(
const CpSolverResponse& response)>&
callback) {
1981 const std::function<std::string(
const CpSolverResponse& response)>&
1989 const std::function<
void(
double)>&
callback) {
1995#if !defined(__PORTABLE_PLATFORM__)
1998 const std::string& params) {
2000 if (!params.empty()) {
2001 CHECK(google::protobuf::TextFormat::ParseFromString(params, &
parameters))
2027 wall_timer->
Start();
2028 user_timer->Start();
2030#if !defined(__PORTABLE_PLATFORM__)
2032 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
2033 DumpModelProto(model_proto,
"model");
2035 if (absl::GetFlag(FLAGS_cp_model_export_model)) {
2036 if (model_proto.name().empty()) {
2037 DumpModelProto(model_proto,
"unnamed_model");
2039 DumpModelProto(model_proto, model_proto.name());
2044 if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
2045 SatParameters params = *
model->GetOrCreate<SatParameters>();
2046 SatParameters flag_params;
2047 CHECK(google::protobuf::TextFormat::ParseFromString(
2048 absl::GetFlag(FLAGS_cp_model_params), &flag_params));
2049 params.MergeFrom(flag_params);
2050 *(
model->GetOrCreate<SatParameters>()) = params;
2055 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
2056 SolverLogger* logger =
model->GetOrCreate<SolverLogger>();
2057 logger->EnableLogging(params.log_search_progress());
2058 logger->SetLogToStdOut(params.log_to_stdout());
2059 std::string log_string;
2060 if (params.log_to_response()) {
2061 logger->AddInfoLoggingCallback([&log_string](absl::string_view
message) {
2062 absl::StrAppend(&log_string,
message,
"\n");
2066 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
2067 shared_response_manager->set_dump_prefix(
2068 absl::GetFlag(FLAGS_cp_model_dump_prefix));
2070#if !defined(__PORTABLE_PLATFORM__)
2074 if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
2075 shared_response_manager->AddFinalResponsePostprocessor(
2076 [](CpSolverResponse* response) {
2077 const std::string
file = absl::StrCat(
2078 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"response.pb.txt");
2079 LOG(INFO) <<
"Dumping response proto to '" <<
file <<
"'.";
2087 shared_response_manager->AddFinalResponsePostprocessor(
2088 [logger, &model_proto, &log_string](CpSolverResponse* response) {
2091 model_proto.has_objective() ||
2092 model_proto.has_floating_point_objective()));
2093 if (!log_string.empty()) {
2094 response->set_solve_log(log_string);
2101 auto* shared_time_limit =
model->GetOrCreate<ModelSharedTimeLimit>();
2102 shared_response_manager->AddResponsePostprocessor(
2103 [&wall_timer, &user_timer,
2104 &shared_time_limit](CpSolverResponse* response) {
2105 response->set_wall_time(wall_timer->Get());
2106 response->set_user_time(user_timer->Get());
2107 response->set_deterministic_time(
2108 shared_time_limit->GetElapsedDeterministicTime());
2117 if (!error.empty()) {
2118 SOLVER_LOG(logger,
"Invalid parameters: ", error);
2123 CpSolverResponse status_response;
2124 status_response.set_status(CpSolverStatus::MODEL_INVALID);
2125 status_response.set_solution_info(error);
2127 shared_response_manager->AppendResponseToBeMerged(status_response);
2128 return shared_response_manager->GetResponse();
2133 model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(params);
2135#if !defined(__PORTABLE_PLATFORM__)
2137 if (params.catch_sigint_signal()) {
2138 model->GetOrCreate<SigintHandler>()->Register(
2139 [&shared_time_limit]() { shared_time_limit->Stop(); });
2151 if (logger->LoggingIsEnabled() && params.use_absl_random()) {
2152 model->GetOrCreate<ModelRandomGenerator>()->LogSalt();
2159 if (!error.empty()) {
2160 SOLVER_LOG(logger,
"Invalid model: ", error);
2161 CpSolverResponse status_response;
2162 status_response.set_status(CpSolverStatus::MODEL_INVALID);
2163 status_response.set_solution_info(error);
2165 shared_response_manager->AppendResponseToBeMerged(status_response);
2166 return shared_response_manager->GetResponse();
2176 absl::StrFormat(
"Starting presolve at %.2fs", wall_timer->Get()));
2180 google::protobuf::Arena arena;
2181 CpModelProto* new_cp_model_proto =
2182 google::protobuf::Arena::Create<CpModelProto>(&arena);
2183 CpModelProto* mapping_proto =
2184 google::protobuf::Arena::Create<CpModelProto>(&arena);
2185 auto context = std::make_unique<PresolveContext>(
model, new_cp_model_proto,
2188 if (absl::GetFlag(FLAGS_debug_model_copy)) {
2189 *new_cp_model_proto = model_proto;
2192 const std::string info =
"Problem proven infeasible during initial copy.";
2194 CpSolverResponse status_response;
2195 status_response.set_status(CpSolverStatus::INFEASIBLE);
2196 status_response.set_solution_info(info);
2197 shared_response_manager->AppendResponseToBeMerged(status_response);
2198 return shared_response_manager->GetResponse();
2201 if (absl::GetFlag(FLAGS_cp_model_ignore_objective) &&
2202 (
context->working_model->has_objective() ||
2203 context->working_model->has_floating_point_objective())) {
2205 context->working_model->clear_objective();
2206 context->working_model->clear_floating_point_objective();
2209 if (absl::GetFlag(FLAGS_cp_model_ignore_hints) &&
2210 context->working_model->has_solution_hint()) {
2211 SOLVER_LOG(logger,
"Ignoring solution hint");
2212 context->working_model->clear_solution_hint();
2216 if (params.fix_variables_to_their_hinted_value() &&
2217 model_proto.has_solution_hint()) {
2218 SOLVER_LOG(logger,
"Fixing ", model_proto.solution_hint().vars().size(),
2219 " variables to their value in the solution hints.");
2220 for (
int i = 0;
i < model_proto.solution_hint().vars_size(); ++
i) {
2221 const int var = model_proto.solution_hint().vars(
i);
2222 const int64_t
value = model_proto.solution_hint().values(
i);
2224 const IntegerVariableProto& var_proto =
2226 const std::string var_name = var_proto.name().empty()
2227 ? absl::StrCat(
"var(",
var,
")")
2231 SOLVER_LOG(logger,
"Hint found infeasible when assigning variable '",
2232 var_name,
"' with domain", var_domain.ToString(),
2233 " the value ",
value);
2243 if (!
context->ModelIsUnsat()) {
2244 TestSolutionHintForFeasibility(model_proto, logger);
2249 if (model_proto.has_floating_point_objective()) {
2250 shared_response_manager->AddFinalResponsePostprocessor(
2251 [¶ms, &model_proto, mapping_proto,
2252 &logger](CpSolverResponse* response) {
2253 if (response->solution().empty())
return;
2256 const auto& float_obj = model_proto.floating_point_objective();
2257 double value = float_obj.offset();
2258 const int num_terms = float_obj.vars().size();
2259 for (
int i = 0;
i < num_terms; ++
i) {
2260 value += float_obj.coeffs(
i) *
2261 static_cast<double>(response->solution(float_obj.vars(
i)));
2263 response->set_objective_value(
value);
2268 if (!mapping_proto->has_objective())
return;
2269 const CpObjectiveProto& integer_obj = mapping_proto->objective();
2270 *response->mutable_integer_objective() = integer_obj;
2275 if (params.mip_compute_true_objective_bound() &&
2276 !integer_obj.scaling_was_exact()) {
2277 const int64_t integer_lb = response->inner_objective_lower_bound();
2279 model_proto, integer_obj, integer_lb);
2280 SOLVER_LOG(logger,
"[Scaling] scaled_objective_bound: ",
2281 response->best_objective_bound(),
2282 " corrected_bound: ", lb,
2283 " delta: ", response->best_objective_bound() - lb);
2287 if (float_obj.maximize()) {
2288 response->set_best_objective_bound(
2289 std::max(lb, response->objective_value()));
2291 response->set_best_objective_bound(
2292 std::min(lb, response->objective_value()));
2298 if (response->status() == CpSolverStatus::OPTIMAL) {
2299 const double gap = std::abs(response->objective_value() -
2300 response->best_objective_bound());
2301 if (gap > params.absolute_gap_limit()) {
2303 "[Scaling] Warning: OPTIMAL was reported, yet the "
2305 gap,
") is greater than requested absolute limit (",
2306 params.absolute_gap_limit(),
").");
2312 if (!model_proto.assumptions().empty() &&
2313 (params.num_workers() > 1 || model_proto.has_objective() ||
2314 model_proto.has_floating_point_objective() ||
2315 params.enumerate_all_solutions())) {
2318 "Warning: solving with assumptions was requested in a non-fully "
2319 "supported setting.\nWe will assumes these assumptions true while "
2320 "solving, but if the model is infeasible, you will not get a useful "
2321 "'sufficient_assumptions_for_infeasibility' field in the response, it "
2322 "will include all assumptions.");
2330 shared_response_manager->AddFinalResponsePostprocessor(
2331 [&model_proto](CpSolverResponse* response) {
2332 if (response->status() != CpSolverStatus::INFEASIBLE)
return;
2335 *response->mutable_sufficient_assumptions_for_infeasibility() =
2336 model_proto.assumptions();
2340 new_cp_model_proto->clear_assumptions();
2342 context->InitializeNewDomains();
2343 for (
const int ref : model_proto.assumptions()) {
2344 if (!
context->SetLiteralToTrue(ref)) {
2345 CpSolverResponse status_response;
2346 status_response.set_status(CpSolverStatus::INFEASIBLE);
2347 status_response.add_sufficient_assumptions_for_infeasibility(ref);
2349 shared_response_manager->AppendResponseToBeMerged(status_response);
2350 return shared_response_manager->GetResponse();
2356 std::vector<int> postsolve_mapping;
2357 const CpSolverStatus presolve_status =
2364 if (presolve_status != CpSolverStatus::UNKNOWN) {
2365 SOLVER_LOG(logger,
"Problem closed by presolve.");
2366 CpSolverResponse status_response;
2367 status_response.set_status(presolve_status);
2369 shared_response_manager->AppendResponseToBeMerged(status_response);
2370 return shared_response_manager->GetResponse();
2377 SharedClasses shared(new_cp_model_proto,
model);
2379 if (params.fill_tightened_domains_in_response()) {
2380 shared_response_manager->AddResponsePostprocessor(
2381 [&model_proto, new_cp_model_proto, mapping_proto, &postsolve_mapping,
2382 logger, &shared](CpSolverResponse* response) {
2386 std::vector<Domain> bounds;
2387 for (
const IntegerVariableProto& vars :
2388 new_cp_model_proto->variables()) {
2393 if (shared.bounds !=
nullptr) {
2394 shared.bounds->UpdateDomains(&bounds);
2399 postsolve_mapping, bounds, response,
2407 auto check_solution = [&model_proto, ¶ms, mapping_proto,
2408 &postsolve_mapping](CpSolverResponse* response) {
2409 if (response->solution().empty())
return;
2410 if (params.cp_model_presolve()) {
2414 &postsolve_mapping));
2420 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
2421 shared_response_manager->AddResponsePostprocessor(
2422 std::move(check_solution));
2424 shared_response_manager->AddFinalResponsePostprocessor(
2425 std::move(check_solution));
2429 if (params.cp_model_presolve()) {
2430 shared_response_manager->AddSolutionPostprocessor(
2431 [&model_proto, ¶ms, mapping_proto, &
model,
2432 &postsolve_mapping](std::vector<int64_t>*
solution) {
2433 AddPostsolveClauses(postsolve_mapping,
model, mapping_proto);
2435 *mapping_proto, postsolve_mapping,
solution);
2437 shared_response_manager->AddResponsePostprocessor(
2438 [&postsolve_mapping](CpSolverResponse* response) {
2442 ->mutable_sufficient_assumptions_for_infeasibility())) {
2444 ? postsolve_mapping[ref]
2449 shared_response_manager->AddResponsePostprocessor(
2450 [&model_proto](CpSolverResponse* response) {
2452 const int initial_size = model_proto.variables_size();
2453 if (!response->solution().empty()) {
2454 response->mutable_solution()->Truncate(initial_size);
2460 if (params.stop_after_first_solution()) {
2461 shared_response_manager->AddSolutionCallback(
2462 [shared_time_limit](
const CpSolverResponse&) {
2463 shared_time_limit->Stop();
2467#if !defined(__PORTABLE_PLATFORM__)
2468 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
2469 DumpModelProto(*new_cp_model_proto,
"presolved_model");
2470 DumpModelProto(*mapping_proto,
"mapping_model");
2475 MPModelProto mip_model;
2477 DumpModelProto(mip_model,
"presolved_mp_model");
2481 std::string cnf_string;
2483 const std::string filename = absl::StrCat(
2484 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"presolved_cnf_model.cnf");
2485 LOG(INFO) <<
"Dumping cnf model to '" << filename <<
"'.";
2486 const absl::Status
status =
2493 if (params.stop_after_presolve() || shared_time_limit->LimitReached()) {
2494 int64_t num_terms = 0;
2495 for (
const ConstraintProto&
ct : new_cp_model_proto->constraints()) {
2499 logger,
"Stopped after presolve.",
2500 "\nPresolvedNumVariables: ", new_cp_model_proto->variables().size(),
2501 "\nPresolvedNumConstraints: ", new_cp_model_proto->constraints().size(),
2502 "\nPresolvedNumTerms: ", num_terms);
2504 CpSolverResponse status_response;
2506 shared_response_manager->AppendResponseToBeMerged(status_response);
2507 return shared_response_manager->GetResponse();
2517 if (new_cp_model_proto->has_objective()) {
2518 shared_response_manager->InitializeObjective(*new_cp_model_proto);
2519 shared_response_manager->SetGapLimitsFromParameters(params);
2524 shared_response_manager->UpdateGapIntegral();
2537 if (!params.enumerate_all_solutions()) {
2538 TestSolutionHintForFeasibility(*new_cp_model_proto, logger,
2539 shared_response_manager);
2541 TestSolutionHintForFeasibility(*new_cp_model_proto, logger,
nullptr);
2544 if (params.symmetry_level() > 1) {
2550 if (!
model->GetOrCreate<TimeLimit>()->LimitReached()) {
2551#if defined(__PORTABLE_PLATFORM__)
2555 if (params.num_workers() > 1 || params.interleave_search() ||
2556 !params.subsolvers().empty() || params.use_ls_only()) {
2557 SolveCpModelParallel(&shared,
model);
2560 shared_response_manager->SetUpdateGapIntegralOnEachChange(
true);
2564 std::vector<std::unique_ptr<SubSolver>> subsolvers;
2565 subsolvers.push_back(std::make_unique<FullProblemSolver>(
2566 "main", params,
false, &shared));
2567 LaunchSubsolvers(params, &shared, subsolvers, {});
2571 return shared_response_manager->GetResponse();
2574CpSolverResponse
Solve(
const CpModelProto& model_proto) {
2580 const SatParameters& params) {
2586#if !defined(__PORTABLE_PLATFORM__)
2588 const std::string& params) {
2596 model->GetOrCreate<SharedResponseManager>()->InitializeObjective(model_proto);
void Start()
When Start() is called multiple times, only the most recent is used.
T Add(std::function< T(Model *)> f)
CpModelProto proto
The output proto.
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.")
const std::string name
A name for logging purposes.
GurobiMPCallbackContext * context
absl::Status SetContents(absl::string_view filename, absl::string_view contents, Options options)
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
void SolveLoadedCpModel(const CpModelProto &model_proto, Model *model)
constexpr uint64_t kDefaultFingerprintSeed
Default seed for fingerprints.
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)
void FillTightenedDomainInResponse(const CpModelProto &original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, const std::vector< Domain > &search_domains, CpSolverResponse *response, SolverLogger *logger)
void LoadAndSolveCpModelForTest(const CpModelProto &model_proto, Model *model)
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)
void FillSolveStatsInResponse(Model *model, CpSolverResponse *response)
bool SolutionIsFeasible(const CpModelProto &model, absl::Span< const int64_t > variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
int CombineSeed(int base_seed, int64_t delta)
We assume delta >= 0 and we only use the low bit of delta.
bool WriteModelProtoToFile(const M &proto, absl::string_view filename)
void PostsolveResponseWrapper(const SatParameters ¶ms, int num_variable_in_original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, std::vector< int64_t > *solution)
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
CpSolverStatus PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
Convenient wrapper to call the full presolve.
void RegisterClausesExport(int id, SharedClausesManager *shared_clauses_manager, Model *model)
Registers a callback that will export good clauses discovered during search.
void MinimizeL1DistanceWithHint(const CpModelProto &model_proto, Model *model)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64_t value)
Scales back a objective value to a double value from the original model.
bool ConvertCpModelProtoToCnf(const CpModelProto &cp_model, std::string *out)
uint64_t FingerprintModel(const CpModelProto &model, uint64_t seed)
Returns a stable fingerprint of a model.
void NonDeterministicLoop(std::vector< std::unique_ptr< SubSolver > > &subsolvers, const int num_threads)
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::string FormatCounter(int64_t num)
Prints a positive number with separators for easier reading (ex: 1'348'065).
std::vector< SatParameters > GetFirstSolutionBaseParams(const SatParameters &base_params)
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.
void DetectAndAddSymmetryToProto(const SatParameters ¶ms, CpModelProto *proto, SolverLogger *logger)
Detects symmetries and fill the symmetry field.
std::function< void(Model *)> NewFeasibleSolutionLogCallback(const std::function< std::string(const CpSolverResponse &response)> &callback)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a 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)
Copies the non constraint, non variables part of the model.
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 RegisterVariableBoundsLevelZeroImport(const CpModelProto &model_proto, SharedBoundsManager *shared_bounds_manager, Model *model)
void LoadCpModel(const CpModelProto &model_proto, Model *model)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
absl::flat_hash_map< std::string, SatParameters > GetNamedParameters(SatParameters base_params)
int RegisterClausesLevelZeroImport(int id, SharedClausesManager *shared_clauses_manager, Model *model)
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
std::string FormatTable(std::vector< std::vector< std::string > > &table, int spacing)
In SWIG mode, we don't want anything besides these top-level includes.
std::string OrToolsVersionString()
std::mt19937_64 random_engine_t
std::string ProtoEnumToString(ProtoEnumType enum_value)
std::string ProtobufShortDebugString(const P &message)
absl::string_view ToString(MPSolver::OptimizationProblemType optimization_problem_type)
static int input(yyscan_t yyscanner)
#define SOLVER_LOG(logger,...)