32#if !defined(__PORTABLE_PLATFORM__)
33#include "google/protobuf/text_format.h"
36#include "absl/base/thread_annotations.h"
37#include "absl/container/btree_map.h"
38#include "absl/container/btree_set.h"
39#include "absl/container/flat_hash_set.h"
40#include "absl/flags/flag.h"
41#include "absl/memory/memory.h"
42#include "absl/status/status.h"
43#include "absl/status/statusor.h"
44#include "absl/strings/str_cat.h"
45#include "absl/strings/str_format.h"
46#include "absl/strings/str_join.h"
47#include "absl/strings/str_split.h"
48#include "absl/strings/string_view.h"
49#include "absl/synchronization/mutex.h"
50#include "absl/types/span.h"
56#include "ortools/sat/cp_model.pb.h"
88#include "ortools/sat/sat_parameters.pb.h"
96#if !defined(__PORTABLE_PLATFORM__)
105ABSL_FLAG(std::string, cp_model_dump_prefix,
".\\",
106 "Prefix filename for all dumped files");
109 "Prefix filename for all dumped files");
112 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
113 "protos (original model, presolved model, mapping model) in text "
114 "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|"
115 "mapping_model}.pb.txt.");
118 "DEBUG ONLY. When set to true, solve will dump all "
119 "lns models proto in text format to "
120 "'FLAGS_cp_model_dump_prefix'lns_xxx.pb.txt.");
123 bool, cp_model_dump_problematic_lns,
false,
124 "DEBUG ONLY. Similar to --cp_model_dump_lns, but only dump fragment for "
125 "which we got an issue while validating the postsolved solution. This "
126 "allows to debug presolve issues without dumping all the models.");
129 "DEBUG ONLY. If true, the final response of each solve will be "
130 "dumped to 'FLAGS_cp_model_dump_prefix'response.pb.txt");
133 "This is interpreted as a text SatParameters proto. The "
134 "specified fields will override the normal ones for all solves.");
137 "If non-empty, a proof in DRAT format will be written to this file. "
138 "This will only be used for pure-SAT problems.");
141 "If true, a proof in DRAT format will be stored in memory and "
142 "checked if the problem is UNSAT. This will only be used for "
143 "pure-SAT problems.");
146 std::numeric_limits<double>::infinity(),
147 "Maximum time in seconds to check the DRAT proof. This will only "
148 "be used is the drat_check flag is enabled.");
150ABSL_FLAG(
bool, cp_model_check_intermediate_solutions,
false,
151 "When true, all intermediate solutions found by the solver will be "
152 "checked. This can be expensive, therefore it is off by default.");
155 "If non-empty, dump a contention pprof proto to the specified "
156 "destination at the end of the solve.");
164std::string Summarize(
const std::string&
input) {
167 return absl::StrCat(
input.substr(0, half),
" ... ",
178 absl::btree_map<std::string, int> num_constraints_by_name;
179 absl::btree_map<std::string, int> num_reif_constraints_by_name;
180 absl::btree_map<std::string, int> name_to_num_literals;
181 absl::btree_map<std::string, int> name_to_num_terms;
182 absl::btree_map<std::string, int> name_to_num_complex_domain;
184 int no_overlap_2d_num_rectangles = 0;
185 int no_overlap_2d_num_optional_rectangles = 0;
186 int no_overlap_2d_num_linear_areas = 0;
187 int no_overlap_2d_num_quadratic_areas = 0;
189 int cumulative_num_intervals = 0;
190 int cumulative_num_optional_intervals = 0;
191 int cumulative_num_variable_sizes = 0;
192 int cumulative_num_variable_demands = 0;
194 int no_overlap_num_intervals = 0;
195 int no_overlap_num_optional_intervals = 0;
196 int no_overlap_num_variable_sizes = 0;
198 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
203 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
204 if (
ct.linear().vars_size() == 1)
name +=
"1";
205 if (
ct.linear().vars_size() == 2)
name +=
"2";
206 if (
ct.linear().vars_size() == 3)
name +=
"3";
207 if (
ct.linear().vars_size() > 3)
name +=
"N";
210 num_constraints_by_name[
name]++;
211 if (!
ct.enforcement_literal().empty()) {
212 num_reif_constraints_by_name[
name]++;
216 const IntegerVariableProto&
proto =
221 auto expression_is_fixed =
222 [&variable_is_fixed](
const LinearExpressionProto& expr) {
223 for (
const int ref : expr.vars()) {
224 if (!variable_is_fixed(ref)) {
231 auto interval_has_fixed_size = [&
model_proto, &expression_is_fixed](
int c) {
232 return expression_is_fixed(
model_proto.constraints(c).interval().size());
235 auto constraint_is_optional = [&
model_proto](
int i) {
236 return !
model_proto.constraints(i).enforcement_literal().empty();
241 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
242 name_to_num_literals[
name] +=
ct.bool_or().literals().size();
243 }
else if (
ct.constraint_case() ==
244 ConstraintProto::ConstraintCase::kBoolAnd) {
245 name_to_num_literals[
name] +=
ct.bool_and().literals().size();
246 }
else if (
ct.constraint_case() ==
247 ConstraintProto::ConstraintCase::kAtMostOne) {
248 name_to_num_literals[
name] +=
ct.at_most_one().literals().size();
249 }
else if (
ct.constraint_case() ==
250 ConstraintProto::ConstraintCase::kExactlyOne) {
251 name_to_num_literals[
name] +=
ct.exactly_one().literals().size();
252 }
else if (
ct.constraint_case() ==
253 ConstraintProto::ConstraintCase::kNoOverlap2D) {
254 const int num_boxes =
ct.no_overlap_2d().x_intervals_size();
255 no_overlap_2d_num_rectangles += num_boxes;
256 for (
int i = 0; i < num_boxes; ++i) {
257 const int x_interval =
ct.no_overlap_2d().x_intervals(i);
258 const int y_interval =
ct.no_overlap_2d().y_intervals(i);
259 if (constraint_is_optional(x_interval) ||
260 constraint_is_optional(y_interval)) {
261 no_overlap_2d_num_optional_rectangles++;
263 const int num_fixed = interval_has_fixed_size(x_interval) +
264 interval_has_fixed_size(y_interval);
265 if (num_fixed == 0) {
266 no_overlap_2d_num_quadratic_areas++;
267 }
else if (num_fixed == 1) {
268 no_overlap_2d_num_linear_areas++;
271 }
else if (
ct.constraint_case() ==
272 ConstraintProto::ConstraintCase::kNoOverlap) {
273 const int num_intervals =
ct.no_overlap().intervals_size();
274 no_overlap_num_intervals += num_intervals;
275 for (
int i = 0; i < num_intervals; ++i) {
276 const int interval =
ct.no_overlap().intervals(i);
277 if (constraint_is_optional(
interval)) {
278 no_overlap_num_optional_intervals++;
280 if (!interval_has_fixed_size(
interval)) {
281 no_overlap_num_variable_sizes++;
284 }
else if (
ct.constraint_case() ==
285 ConstraintProto::ConstraintCase::kCumulative) {
286 const int num_intervals =
ct.cumulative().intervals_size();
287 cumulative_num_intervals += num_intervals;
288 for (
int i = 0; i < num_intervals; ++i) {
289 const int interval =
ct.cumulative().intervals(i);
290 if (constraint_is_optional(
interval)) {
291 cumulative_num_optional_intervals++;
293 if (!interval_has_fixed_size(
interval)) {
294 cumulative_num_variable_sizes++;
296 if (!expression_is_fixed(
ct.cumulative().demands(i))) {
297 cumulative_num_variable_demands++;
302 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
303 ct.linear().vars_size() > 3) {
304 name_to_num_terms[
name] +=
ct.linear().vars_size();
306 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
307 ct.linear().vars_size() > 1 &&
ct.linear().domain().size() > 2) {
308 name_to_num_complex_domain[
name]++;
312 int num_constants = 0;
313 absl::btree_set<int64_t> constant_values;
314 absl::btree_map<Domain, int> num_vars_per_domains;
316 if (
var.domain_size() == 2 &&
var.domain(0) ==
var.domain(1)) {
318 constant_values.insert(
var.domain(0));
327 absl::StrAppend(&result,
"optimization model '",
model_proto.name(),
330 absl::StrAppend(&result,
"satisfaction model '",
model_proto.name(),
334 for (
const DecisionStrategyProto& strategy :
model_proto.search_strategy()) {
336 &result,
"Search strategy: on ", strategy.variables_size(),
338 ProtoEnumToString<DecisionStrategyProto::VariableSelectionStrategy>(
339 strategy.variable_selection_strategy()),
341 ProtoEnumToString<DecisionStrategyProto::DomainReductionStrategy>(
342 strategy.domain_reduction_strategy()),
346 auto count_variables_by_type =
347 [&
model_proto](
const google::protobuf::RepeatedField<int>& vars,
348 int* num_booleans,
int* num_integers) {
349 for (
const int ref : vars) {
351 if (var_proto.domain_size() == 2 && var_proto.domain(0) == 0 &&
352 var_proto.domain(1) == 1) {
356 *num_integers =
model_proto.objective().vars_size() - *num_booleans;
360 int num_boolean_variables_in_objective = 0;
361 int num_integer_variables_in_objective = 0;
363 count_variables_by_type(
model_proto.objective().vars(),
364 &num_boolean_variables_in_objective,
365 &num_integer_variables_in_objective);
368 count_variables_by_type(
model_proto.floating_point_objective().vars(),
369 &num_boolean_variables_in_objective,
370 &num_integer_variables_in_objective);
373 std::vector<std::string> obj_vars_strings;
374 if (num_boolean_variables_in_objective > 0) {
375 obj_vars_strings.push_back(
376 absl::StrCat(
"#bools:", num_boolean_variables_in_objective));
378 if (num_integer_variables_in_objective > 0) {
379 obj_vars_strings.push_back(
380 absl::StrCat(
"#ints:", num_integer_variables_in_objective));
383 const std::string objective_string =
385 ? absl::StrCat(
" (", absl::StrJoin(obj_vars_strings,
" "),
388 ? absl::StrCat(
" (", absl::StrJoin(obj_vars_strings,
" "),
389 " in floating point objective)")
391 absl::StrAppend(&result,
"#Variables: ",
model_proto.variables_size(),
392 objective_string,
"\n");
394 if (num_vars_per_domains.size() < 100) {
395 for (
const auto& entry : num_vars_per_domains) {
396 const std::string temp = absl::StrCat(
" - ", entry.second,
" in ",
397 entry.first.ToString(),
"\n");
398 absl::StrAppend(&result, Summarize(temp));
401 int64_t max_complexity = 0;
404 for (
const auto& entry : num_vars_per_domains) {
408 max_complexity,
static_cast<int64_t
>(entry.first.NumIntervals()));
410 absl::StrAppend(&result,
" - ", num_vars_per_domains.size(),
411 " different domains in [",
min,
",",
max,
412 "] with a largest complexity of ", max_complexity,
".\n");
415 if (num_constants > 0) {
416 const std::string temp =
417 absl::StrCat(
" - ", num_constants,
" constants in {",
418 absl::StrJoin(constant_values,
","),
"} \n");
419 absl::StrAppend(&result, Summarize(temp));
422 std::vector<std::string> constraints;
423 constraints.reserve(num_constraints_by_name.size());
424 for (
const auto& entry : num_constraints_by_name) {
425 const std::string&
name = entry.first;
426 constraints.push_back(absl::StrCat(
"#",
name,
": ", entry.second));
428 absl::StrAppend(&constraints.back(),
429 " (#enforced: ", num_reif_constraints_by_name[
name],
")");
432 absl::StrAppend(&constraints.back(),
433 " (#literals: ", name_to_num_literals[
name],
")");
436 absl::StrAppend(&constraints.back(),
437 " (#terms: ", name_to_num_terms[
name],
")");
440 absl::StrAppend(&constraints.back(),
441 " (#complex_domain: ", name_to_num_complex_domain[
name],
444 if (
name ==
"kNoOverlap2D") {
445 absl::StrAppend(&constraints.back(),
446 " (#rectangles: ", no_overlap_2d_num_rectangles);
447 if (no_overlap_2d_num_optional_rectangles > 0) {
448 absl::StrAppend(&constraints.back(),
449 ", #optional: ", no_overlap_2d_num_optional_rectangles);
451 if (no_overlap_2d_num_linear_areas > 0) {
452 absl::StrAppend(&constraints.back(),
453 ", #linear_areas: ", no_overlap_2d_num_linear_areas);
455 if (no_overlap_2d_num_quadratic_areas > 0) {
456 absl::StrAppend(&constraints.back(),
", #quadratic_areas: ",
457 no_overlap_2d_num_quadratic_areas);
459 absl::StrAppend(&constraints.back(),
")");
460 }
else if (
name ==
"kCumulative") {
461 absl::StrAppend(&constraints.back(),
462 " (#intervals: ", cumulative_num_intervals);
463 if (cumulative_num_optional_intervals > 0) {
464 absl::StrAppend(&constraints.back(),
465 ", #optional: ", cumulative_num_optional_intervals);
467 if (cumulative_num_variable_sizes > 0) {
468 absl::StrAppend(&constraints.back(),
469 ", #variable_sizes: ", cumulative_num_variable_sizes);
471 if (cumulative_num_variable_demands > 0) {
472 absl::StrAppend(&constraints.back(),
", #variable_demands: ",
473 cumulative_num_variable_demands);
475 absl::StrAppend(&constraints.back(),
")");
476 }
else if (
name ==
"kNoOverlap") {
477 absl::StrAppend(&constraints.back(),
478 " (#intervals: ", no_overlap_num_intervals);
479 if (no_overlap_num_optional_intervals > 0) {
480 absl::StrAppend(&constraints.back(),
481 ", #optional: ", no_overlap_num_optional_intervals);
483 if (no_overlap_num_variable_sizes > 0) {
484 absl::StrAppend(&constraints.back(),
485 ", #variable_sizes: ", no_overlap_num_variable_sizes);
487 absl::StrAppend(&constraints.back(),
")");
490 std::sort(constraints.begin(), constraints.end());
491 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
497 bool has_objective) {
499 absl::StrAppend(&result,
"CpSolverResponse summary:");
500 absl::StrAppend(&result,
"\nstatus: ",
501 ProtoEnumToString<CpSolverStatus>(
response.status()));
503 if (has_objective &&
response.status() != CpSolverStatus::INFEASIBLE) {
504 absl::StrAppendFormat(&result,
"\nobjective: %.16g",
506 absl::StrAppendFormat(&result,
"\nbest_bound: %.16g",
509 absl::StrAppend(&result,
"\nobjective: NA");
510 absl::StrAppend(&result,
"\nbest_bound: NA");
513 absl::StrAppend(&result,
"\nbooleans: ",
response.num_booleans());
514 absl::StrAppend(&result,
"\nconflicts: ",
response.num_conflicts());
515 absl::StrAppend(&result,
"\nbranches: ",
response.num_branches());
519 absl::StrAppend(&result,
520 "\npropagations: ",
response.num_binary_propagations());
522 &result,
"\ninteger_propagations: ",
response.num_integer_propagations());
524 absl::StrAppend(&result,
"\nrestarts: ",
response.num_restarts());
525 absl::StrAppend(&result,
"\nlp_iterations: ",
response.num_lp_iterations());
526 absl::StrAppend(&result,
"\nwalltime: ",
response.wall_time());
527 absl::StrAppend(&result,
"\nusertime: ",
response.user_time());
528 absl::StrAppend(&result,
529 "\ndeterministic_time: ",
response.deterministic_time());
530 absl::StrAppend(&result,
"\ngap_integral: ",
response.gap_integral());
531 absl::StrAppend(&result,
"\n");
537#if !defined(__PORTABLE_PLATFORM__)
540void FillSolutionInResponse(
const CpModelProto&
model_proto,
const Model&
model,
544 auto* mapping =
model.Get<CpModelMapping>();
547 std::vector<int64_t> solution;
548 for (
int i = 0; i <
model_proto.variables_size(); ++i) {
549 if (mapping->IsInteger(i)) {
550 const IntegerVariable
var = mapping->Integer(i);
556 DCHECK(mapping->IsBoolean(i));
558 if (trail->Assignment().LiteralIsAssigned(
literal)) {
562 solution.push_back(0);
568 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
572 for (
const int64_t
value : solution) {
579IntegerVariable GetOrCreateVariableWithTightBound(
580 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
583 if (terms.size() == 1 && terms.front().second == 1) {
584 return terms.front().first;
586 if (terms.size() == 1 && terms.front().second == -1) {
592 for (
const std::pair<IntegerVariable, int64_t>& var_coeff : terms) {
595 const int64_t
coeff = var_coeff.second;
596 const int64_t prod1 = min_domain *
coeff;
597 const int64_t prod2 = max_domain *
coeff;
604IntegerVariable GetOrCreateVariableGreaterOrEqualToSumOf(
605 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
608 if (terms.size() == 1 && terms.front().second == 1) {
609 return terms.front().first;
611 if (terms.size() == 1 && terms.front().second == -1) {
616 const IntegerVariable new_var =
617 GetOrCreateVariableWithTightBound(terms,
model);
618 std::vector<IntegerVariable> vars;
619 std::vector<int64_t> coeffs;
620 for (
const auto& term : terms) {
621 vars.push_back(term.first);
622 coeffs.push_back(term.second);
624 vars.push_back(new_var);
625 coeffs.push_back(-1);
633IntegerVariable AddLPConstraints(
const CpModelProto&
model_proto, Model* m) {
642 const int num_lp_constraints = relaxation.linear_constraints.size();
643 const int num_lp_cut_generators = relaxation.cut_generators.size();
644 const int num_integer_variables =
645 m->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
648 num_integer_variables);
649 auto get_constraint_index = [](
int ct_index) {
return ct_index; };
650 auto get_cut_generator_index = [num_lp_constraints](
int cut_index) {
651 return num_lp_constraints + cut_index;
653 auto get_var_index = [num_lp_constraints,
654 num_lp_cut_generators](IntegerVariable
var) {
655 return num_lp_constraints + num_lp_cut_generators +
var.value();
657 for (
int i = 0; i < num_lp_constraints; i++) {
658 for (
const IntegerVariable
var : relaxation.linear_constraints[i].vars) {
659 components.
AddEdge(get_constraint_index(i), get_var_index(
var));
662 for (
int i = 0; i < num_lp_cut_generators; ++i) {
663 for (
const IntegerVariable
var : relaxation.cut_generators[i].vars) {
664 components.
AddEdge(get_cut_generator_index(i), get_var_index(
var));
669 std::vector<int> component_sizes(num_components, 0);
670 const std::vector<int> index_to_component = components.
GetComponentIds();
671 for (
int i = 0; i < num_lp_constraints; i++) {
672 ++component_sizes[index_to_component[get_constraint_index(i)]];
674 for (
int i = 0; i < num_lp_cut_generators; i++) {
675 ++component_sizes[index_to_component[get_cut_generator_index(i)]];
683 auto* mapping = m->GetOrCreate<CpModelMapping>();
684 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
685 const IntegerVariable
var =
687 ++component_sizes[index_to_component[get_var_index(
var)]];
691 std::vector<LinearProgrammingConstraint*> lp_constraints(num_components,
693 std::vector<std::vector<LinearConstraint>> component_to_constraints(
695 for (
int i = 0; i < num_lp_constraints; i++) {
696 const int c = index_to_component[get_constraint_index(i)];
697 if (component_sizes[c] <= 1)
continue;
698 component_to_constraints[c].push_back(relaxation.linear_constraints[i]);
699 if (lp_constraints[c] ==
nullptr) {
700 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
703 lp_constraints[c]->AddLinearConstraint(relaxation.linear_constraints[i]);
707 for (
int i = 0; i < num_lp_cut_generators; i++) {
708 const int c = index_to_component[get_cut_generator_index(i)];
709 if (lp_constraints[c] ==
nullptr) {
710 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
712 lp_constraints[c]->AddCutGenerator(std::move(relaxation.cut_generators[i]));
716 std::vector<std::vector<std::pair<IntegerVariable, int64_t>>>
717 component_to_cp_terms(num_components);
718 std::vector<std::pair<IntegerVariable, int64_t>> top_level_cp_terms;
719 int num_components_containing_objective = 0;
723 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
724 const IntegerVariable
var =
727 const int c = index_to_component[get_var_index(
var)];
728 if (lp_constraints[c] !=
nullptr) {
729 lp_constraints[c]->SetObjectiveCoefficient(
var, IntegerValue(
coeff));
730 component_to_cp_terms[c].push_back(std::make_pair(
var,
coeff));
733 top_level_cp_terms.push_back(std::make_pair(
var,
coeff));
737 for (
int c = 0; c < num_components; ++c) {
738 if (component_to_cp_terms[c].empty())
continue;
739 const IntegerVariable sub_obj_var =
740 GetOrCreateVariableGreaterOrEqualToSumOf(component_to_cp_terms[c], m);
741 top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
742 lp_constraints[c]->SetMainObjectiveVariable(sub_obj_var);
743 num_components_containing_objective++;
747 const IntegerVariable main_objective_var =
749 ? GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m)
754 for (LinearProgrammingConstraint* lp_constraint : lp_constraints) {
755 if (lp_constraint ==
nullptr)
continue;
756 lp_constraint->RegisterWith(m);
757 VLOG(3) <<
"LP constraint: " << lp_constraint->DimensionString() <<
".";
760 VLOG(3) << top_level_cp_terms.size()
761 <<
" terms in the main objective linear equation ("
762 << num_components_containing_objective <<
" from LP constraints).";
763 return main_objective_var;
775 const std::function<
void(
const CpSolverResponse&
response)>& observer) {
781#if !defined(__PORTABLE_PLATFORM__)
784 const std::string& params) {
786 if (!params.empty()) {
787 CHECK(google::protobuf::TextFormat::ParseFromString(params, &
parameters))
814void RegisterVariableBoundsLevelZeroExport(
815 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
817 CHECK(shared_bounds_manager !=
nullptr);
819 auto* mapping =
model->GetOrCreate<CpModelMapping>();
821 auto* integer_trail =
model->Get<IntegerTrail>();
823 int saved_trail_index = 0;
824 std::vector<int> model_variables;
825 std::vector<int64_t> new_lower_bounds;
826 std::vector<int64_t> new_upper_bounds;
827 absl::flat_hash_set<int> visited_variables;
829 auto broadcast_level_zero_bounds =
830 [=](
const std::vector<IntegerVariable>& modified_vars)
mutable {
832 for (
const IntegerVariable&
var : modified_vars) {
834 const int model_var =
835 mapping->GetProtoVariableFromIntegerVariable(positive_var);
837 if (model_var == -1)
continue;
838 const auto [_, inserted] = visited_variables.insert(model_var);
839 if (!inserted)
continue;
841 const int64_t new_lb =
842 integer_trail->LevelZeroLowerBound(positive_var).value();
843 const int64_t new_ub =
844 integer_trail->LevelZeroUpperBound(positive_var).value();
848 model_variables.push_back(model_var);
849 new_lower_bounds.push_back(new_lb);
850 new_upper_bounds.push_back(new_ub);
854 for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
855 const Literal fixed_literal = (*trail)[saved_trail_index];
856 const int model_var = mapping->GetProtoVariableFromBooleanVariable(
857 fixed_literal.Variable());
859 if (model_var == -1)
continue;
860 const auto [_, inserted] = visited_variables.insert(model_var);
861 if (!inserted)
continue;
863 model_variables.push_back(model_var);
864 if (fixed_literal.IsPositive()) {
865 new_lower_bounds.push_back(1);
866 new_upper_bounds.push_back(1);
868 new_lower_bounds.push_back(0);
869 new_upper_bounds.push_back(0);
873 if (!model_variables.empty()) {
874 shared_bounds_manager->ReportPotentialNewBounds(
879 model_variables.clear();
880 new_lower_bounds.clear();
881 new_upper_bounds.clear();
882 visited_variables.clear();
885 if (!
model->Get<SatParameters>()->interleave_search()) {
886 shared_bounds_manager->Synchronize();
898 const IntegerVariable num_vars =
899 model->GetOrCreate<IntegerTrail>()->NumIntegerVariables();
900 std::vector<IntegerVariable> all_variables;
901 all_variables.reserve(num_vars.value());
902 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
903 all_variables.push_back(
var);
905 broadcast_level_zero_bounds(all_variables);
907 model->GetOrCreate<GenericLiteralWatcher>()
908 ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
914void RegisterVariableBoundsLevelZeroImport(
915 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
917 CHECK(shared_bounds_manager !=
nullptr);
918 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
919 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
920 const int id = shared_bounds_manager->RegisterNewId();
922 const auto& import_level_zero_bounds = [&
model_proto, shared_bounds_manager,
923 model, integer_trail, id, mapping]() {
924 std::vector<int> model_variables;
925 std::vector<int64_t> new_lower_bounds;
926 std::vector<int64_t> new_upper_bounds;
927 shared_bounds_manager->GetChangedBounds(
928 id, &model_variables, &new_lower_bounds, &new_upper_bounds);
929 bool new_bounds_have_been_imported =
false;
930 for (
int i = 0; i < model_variables.size(); ++i) {
931 const int model_var = model_variables[i];
934 if (!mapping->IsInteger(model_var))
continue;
935 const IntegerVariable
var = mapping->Integer(model_var);
936 const IntegerValue new_lb(new_lower_bounds[i]);
937 const IntegerValue new_ub(new_upper_bounds[i]);
938 const IntegerValue old_lb = integer_trail->LowerBound(
var);
939 const IntegerValue old_ub = integer_trail->UpperBound(
var);
940 const bool changed_lb = new_lb > old_lb;
941 const bool changed_ub = new_ub < old_ub;
942 if (!changed_lb && !changed_ub)
continue;
944 new_bounds_have_been_imported =
true;
946 const IntegerVariableProto& var_proto =
948 const std::string& var_name =
949 var_proto.name().empty()
950 ? absl::StrCat(
"anonymous_var(", model_var,
")")
952 LOG(
INFO) <<
" '" <<
model->Name() <<
"' imports new bounds for "
953 << var_name <<
": from [" << old_lb <<
", " << old_ub
954 <<
"] to [" << new_lb <<
", " << new_ub <<
"]";
968 if (new_bounds_have_been_imported &&
969 !
model->GetOrCreate<SatSolver>()->FinishPropagation()) {
974 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
975 import_level_zero_bounds);
980void RegisterObjectiveBestBoundExport(
981 IntegerVariable objective_var,
982 SharedResponseManager* shared_response_manager, Model*
model) {
983 auto* integer_trail =
model->Get<IntegerTrail>();
984 const auto broadcast_objective_lower_bound =
985 [objective_var, integer_trail, shared_response_manager,
986 model](
const std::vector<IntegerVariable>& unused) {
987 shared_response_manager->UpdateInnerObjectiveBounds(
988 model->Name(), integer_trail->LevelZeroLowerBound(objective_var),
989 integer_trail->LevelZeroUpperBound(objective_var));
991 if (!
model->Get<SatParameters>()->interleave_search()) {
992 shared_response_manager->Synchronize();
995 model->GetOrCreate<GenericLiteralWatcher>()
996 ->RegisterLevelZeroModifiedVariablesCallback(
997 broadcast_objective_lower_bound);
1003void RegisterObjectiveBoundsImport(
1004 SharedResponseManager* shared_response_manager, Model*
model) {
1005 auto* solver =
model->GetOrCreate<SatSolver>();
1006 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1007 auto* objective =
model->GetOrCreate<ObjectiveDefinition>();
1009 const auto import_objective_bounds = [
name, solver, integer_trail, objective,
1010 shared_response_manager]() {
1011 if (solver->AssumptionLevel() != 0)
return true;
1012 bool propagate =
false;
1014 const IntegerValue external_lb =
1015 shared_response_manager->SynchronizedInnerObjectiveLowerBound();
1016 const IntegerValue current_lb =
1017 integer_trail->LowerBound(objective->objective_var);
1018 if (external_lb > current_lb) {
1020 objective->objective_var, external_lb),
1027 const IntegerValue external_ub =
1028 shared_response_manager->SynchronizedInnerObjectiveUpperBound();
1029 const IntegerValue current_ub =
1030 integer_trail->UpperBound(objective->objective_var);
1031 if (external_ub < current_ub) {
1033 objective->objective_var, external_ub),
1040 if (!propagate)
return true;
1042 VLOG(3) <<
"'" <<
name <<
"' imports objective bounds: external ["
1043 << objective->ScaleIntegerObjective(external_lb) <<
", "
1044 << objective->ScaleIntegerObjective(external_ub) <<
"], current ["
1045 << objective->ScaleIntegerObjective(current_lb) <<
", "
1046 << objective->ScaleIntegerObjective(current_ub) <<
"]";
1048 return solver->FinishPropagation();
1051 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1052 import_objective_bounds);
1057void RegisterClausesExport(
int id, SharedClausesManager* shared_clauses_manager,
1059 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1060 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1061 const auto& share_binary_clause = [mapping, id, shared_clauses_manager](
1062 Literal l1, Literal l2) {
1064 mapping->GetProtoVariableFromBooleanVariable(l1.Variable());
1065 if (var1 == -1)
return;
1067 mapping->GetProtoVariableFromBooleanVariable(l2.Variable());
1068 if (var2 == -1)
return;
1069 const int lit1 = l1.IsPositive() ? var1 :
NegatedRef(var1);
1070 const int lit2 = l2.IsPositive() ? var2 :
NegatedRef(var2);
1071 shared_clauses_manager->AddBinaryClause(
id, lit1, lit2);
1073 sat_solver->SetShareBinaryClauseCallback(share_binary_clause);
1082int RegisterClausesLevelZeroImport(
int id,
1083 SharedClausesManager* shared_clauses_manager,
1085 CHECK(shared_clauses_manager !=
nullptr);
1086 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
1087 SatSolver* sat_solver =
model->GetOrCreate<SatSolver>();
1088 const auto& import_level_zero_clauses = [shared_clauses_manager, id, mapping,
1090 std::vector<std::pair<int, int>> new_binary_clauses;
1091 shared_clauses_manager->GetUnseenBinaryClauses(
id, &new_binary_clauses);
1092 for (
const auto& [ref1, ref2] : new_binary_clauses) {
1093 const Literal l1 = mapping->Literal(ref1);
1094 const Literal l2 = mapping->Literal(ref2);
1095 if (!sat_solver->AddBinaryClause(l1, l2)) {
1101 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1102 import_level_zero_clauses);
1107 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1108 CHECK(shared_response_manager !=
nullptr);
1109 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1112 const auto unsat = [shared_response_manager, sat_solver,
model] {
1113 sat_solver->NotifyThatModelIsUnsat();
1114 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1115 absl::StrCat(
model->Name(),
" [loading]"));
1119 model->GetOrCreate<IntegerEncoder>()->DisableImplicationBetweenLiteral();
1121 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1122 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1123 const bool view_all_booleans_as_integers =
1125 (
parameters.search_branching() == SatParameters::FIXED_SEARCH &&
1144 if (sat_solver->IsModelUnsat())
return unsat();
1150 absl::btree_set<std::string> unsupported_types;
1151 int num_ignored_constraints = 0;
1152 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1153 if (mapping->ConstraintIsAlreadyLoaded(&
ct)) {
1154 ++num_ignored_constraints;
1169 if (sat_solver->FinishPropagation()) {
1170 Trail* trail =
model->GetOrCreate<Trail>();
1171 const int old_num_fixed = trail->Index();
1172 if (trail->Index() > old_num_fixed) {
1173 VLOG(3) <<
"Constraint fixed " << trail->Index() - old_num_fixed
1178 if (sat_solver->IsModelUnsat()) {
1179 VLOG(2) <<
"UNSAT during extraction (after adding '"
1185 if (num_ignored_constraints > 0) {
1186 VLOG(3) << num_ignored_constraints <<
" constraints were skipped.";
1188 if (!unsupported_types.empty()) {
1189 VLOG(1) <<
"There is unsupported constraints types in this model: ";
1190 for (
const std::string& type : unsupported_types) {
1191 VLOG(1) <<
" - " << type;
1196 model->GetOrCreate<IntegerEncoder>()
1197 ->AddAllImplicationsBetweenAssociatedLiterals();
1198 if (!sat_solver->FinishPropagation())
return unsat();
1204 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1205 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1206 if (
parameters.linearization_level() == 0)
return;
1209 const LinearRelaxation relaxation =
1211 const int num_lp_constraints = relaxation.linear_constraints.size();
1212 if (num_lp_constraints == 0)
return;
1213 auto* feasibility_pump =
model->GetOrCreate<FeasibilityPump>();
1214 for (
int i = 0; i < num_lp_constraints; i++) {
1215 feasibility_pump->AddLinearConstraint(relaxation.linear_constraints[i]);
1219 for (
int i = 0; i <
model_proto.objective().coeffs_size(); ++i) {
1220 const IntegerVariable
var =
1221 mapping->Integer(
model_proto.objective().vars(i));
1223 feasibility_pump->SetObjectiveCoefficient(
var, IntegerValue(
coeff));
1233 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1238 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1239 const auto unsat = [shared_response_manager, sat_solver,
model] {
1240 sat_solver->NotifyThatModelIsUnsat();
1241 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1242 absl::StrCat(
model->Name(),
" [loading]"));
1245 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1246 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1252 if (
model->Mutable<PrecedencesPropagator>() !=
nullptr &&
1253 parameters.auto_detect_greater_than_at_least_one_of()) {
1254 model->Mutable<PrecedencesPropagator>()
1255 ->AddGreaterThanAtLeastOneOfConstraints(
model);
1256 if (!sat_solver->FinishPropagation())
return unsat();
1262 if (
parameters.cp_model_probing_level() > 1) {
1263 Prober* prober =
model->GetOrCreate<Prober>();
1264 prober->ProbeBooleanVariables(1.0);
1265 if (
model->GetOrCreate<SatSolver>()->IsModelUnsat()) {
1268 if (!
model->GetOrCreate<BinaryImplicationGraph>()
1269 ->ComputeTransitiveReduction()) {
1281 const CpObjectiveProto& obj =
model_proto.objective();
1282 std::vector<std::pair<IntegerVariable, int64_t>> terms;
1283 terms.reserve(obj.vars_size());
1284 for (
int i = 0; i < obj.vars_size(); ++i) {
1286 std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i)));
1289 objective_var = GetOrCreateVariableWithTightBound(terms,
model);
1291 objective_var = GetOrCreateVariableGreaterOrEqualToSumOf(terms,
model);
1298 const CpObjectiveProto& objective_proto =
model_proto.objective();
1299 auto* objective_definition =
model->GetOrCreate<ObjectiveDefinition>();
1301 objective_definition->scaling_factor = objective_proto.scaling_factor();
1302 if (objective_definition->scaling_factor == 0.0) {
1303 objective_definition->scaling_factor = 1.0;
1305 objective_definition->offset = objective_proto.offset();
1306 objective_definition->objective_var = objective_var;
1308 const int size = objective_proto.vars_size();
1309 objective_definition->vars.resize(size);
1310 objective_definition->coeffs.resize(size);
1311 for (
int i = 0; i < objective_proto.vars_size(); ++i) {
1314 objective_definition->vars[i] = mapping->Integer(objective_proto.vars(i));
1315 objective_definition->coeffs[i] = IntegerValue(objective_proto.coeffs(i));
1318 const int ref = objective_proto.vars(i);
1319 if (mapping->IsInteger(ref)) {
1320 const IntegerVariable
var = mapping->Integer(objective_proto.vars(i));
1321 objective_definition->objective_impacting_variables.insert(
1327 model->TakeOwnership(
1328 new LevelZeroEquality(objective_var, objective_definition->vars,
1329 objective_definition->coeffs,
model));
1335 const Domain automatic_domain =
1336 model->GetOrCreate<IntegerTrail>()->InitialVariableDomain(
1339 <<
" scaling_factor:" <<
model_proto.objective().scaling_factor();
1340 VLOG(3) <<
"Automatic internal objective domain: " << automatic_domain;
1341 VLOG(3) <<
"User specified internal objective domain: " << user_domain;
1343 const bool ok =
model->GetOrCreate<IntegerTrail>()->UpdateInitialDomain(
1344 objective_var, user_domain);
1346 VLOG(2) <<
"UNSAT due to the objective domain.";
1357 if (!automatic_domain.IsIncludedIn(user_domain)) {
1358 std::vector<IntegerVariable> vars;
1359 std::vector<int64_t> coeffs;
1360 const CpObjectiveProto& obj =
model_proto.objective();
1361 for (
int i = 0; i < obj.vars_size(); ++i) {
1362 vars.push_back(mapping->Integer(obj.vars(i)));
1363 coeffs.push_back(obj.coeffs(i));
1365 vars.push_back(objective_var);
1366 coeffs.push_back(-1);
1374 "Initial num_bool: ", sat_solver->NumVariables());
1375 if (!sat_solver->FinishPropagation())
return unsat();
1379 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1380 shared_response_manager->UpdateInnerObjectiveBounds(
1381 absl::StrCat(
model->Name(),
" initial_propagation"),
1382 integer_trail->LowerBound(objective_var),
1383 integer_trail->UpperBound(objective_var));
1386 RegisterObjectiveBestBoundExport(objective_var, shared_response_manager,
1392 if (
model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1393 RegisterObjectiveBoundsImport(shared_response_manager,
model);
1399 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1400 auto* lp_dispatcher =
model->GetOrCreate<LinearProgrammingDispatcher>();
1401 auto* lp_vars =
model->GetOrCreate<LPVariables>();
1402 IntegerVariable size = integer_trail->NumIntegerVariables();
1403 for (IntegerVariable positive_var(0); positive_var < size;
1404 positive_var += 2) {
1406 lp_var.positive_var = positive_var;
1408 mapping->GetProtoVariableFromIntegerVariable(positive_var);
1411 if (lp_var.model_var >= 0) {
1412 lp_vars->vars.push_back(lp_var);
1413 lp_vars->model_vars_size =
1414 std::max(lp_vars->model_vars_size, lp_var.model_var + 1);
1419 auto* search_heuristics =
model->GetOrCreate<SearchHeuristics>();
1423 search_heuristics->fixed_search =
1425 search_heuristics->fixed_search,
model);
1429 std::vector<BooleanOrIntegerVariable> vars;
1430 std::vector<IntegerValue> values;
1431 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1432 const int ref =
model_proto.solution_hint().vars(i);
1434 BooleanOrIntegerVariable
var;
1435 if (mapping->IsBoolean(ref)) {
1436 var.bool_var = mapping->Literal(ref).Variable();
1438 var.int_var = mapping->Integer(ref);
1440 vars.push_back(
var);
1441 values.push_back(IntegerValue(
model_proto.solution_hint().values(i)));
1449 const std::string solution_info =
model->Name();
1451 shared_response_manager]() {
1454 response.set_solution_info(solution_info);
1458 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1460 HittingSetOptimizer* max_hs =
new HittingSetOptimizer(
1462 model->Register<HittingSetOptimizer>(max_hs);
1463 model->TakeOwnership(max_hs);
1465 CoreBasedOptimizer* core =
1466 new CoreBasedOptimizer(objective_var, objective.vars,
1467 objective.coeffs, solution_observer,
model);
1468 model->Register<CoreBasedOptimizer>(core);
1469 model->TakeOwnership(core);
1481 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1482 if (shared_response_manager->ProblemIsSolved())
return;
1484 const std::string& solution_info =
model->Name();
1486 &shared_response_manager]() {
1489 response.set_solution_info(solution_info);
1496 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1498 const SatParameters&
parameters = *
model->GetOrCreate<SatParameters>();
1505 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1510 solution_observer();
1519 if (
status != SatSolver::Status::FEASIBLE)
break;
1520 solution_observer();
1521 if (!
parameters.enumerate_all_solutions())
break;
1525 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1529 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1534 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1535 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1537 std::vector<int> core_in_proto_format;
1538 for (
const Literal l : core) {
1539 core_in_proto_format.push_back(
1540 mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1541 if (!l.IsPositive()) {
1542 core_in_proto_format.back() =
NegatedRef(core_in_proto_format.back());
1545 shared_response_manager->AddUnsatCore(core_in_proto_format);
1549 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1550 const IntegerVariable objective_var = objective.objective_var;
1553 if (
parameters.optimize_with_lb_tree_search()) {
1554 auto* search =
model->GetOrCreate<LbTreeSearch>();
1555 status = search->Search(solution_observer);
1556 }
else if (
parameters.optimize_with_core()) {
1560 status =
model->Mutable<HittingSetOptimizer>()->Optimize();
1562 status =
model->Mutable<CoreBasedOptimizer>()->Optimize();
1567 if (
parameters.binary_search_num_conflicts() >= 0) {
1569 solution_observer,
model);
1572 objective_var, solution_observer,
model);
1580 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1587 shared_response_manager->SetStatsFromModel(
model);
1595 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1596 if (shared_response_manager->ProblemIsSolved())
return;
1607 if (
parameters->optimize_with_core())
return;
1609 const SatParameters saved_params = *
parameters;
1611 parameters->set_search_branching(SatParameters::HINT_SEARCH);
1618 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1622 const std::string& solution_info =
model->Name();
1623 if (
status == SatSolver::Status::FEASIBLE) {
1626 response.set_solution_info(absl::StrCat(solution_info,
" [hint]"));
1635 const IntegerVariable objective_var =
1636 model->GetOrCreate<ObjectiveDefinition>()->objective_var;
1637 model->GetOrCreate<SatSolver>()->Backtrack(0);
1638 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
1639 if (!integer_trail->Enqueue(
1642 shared_response_manager->GetInnerObjectiveUpperBound()),
1644 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1645 absl::StrCat(solution_info,
" [hint]"));
1646 shared_response_manager->SetStatsFromModel(
model);
1656 !
model->GetOrCreate<TimeLimit>()->LimitReached() &&
1657 status != SatSolver::Status::FEASIBLE) {
1658 LOG(
FATAL) <<
"QuickSolveWithHint() didn't find a feasible solution. "
1659 <<
"The model name is '" <<
model_proto.name() <<
"'.";
1666void MinimizeL1DistanceWithHint(
const CpModelProto&
model_proto, Model*
model) {
1670 local_model.Register<ModelSharedTimeLimit>(
1671 model->GetOrCreate<ModelSharedTimeLimit>());
1676 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1677 if (shared_response_manager->ProblemIsSolved())
return;
1679 auto*
parameters = local_model.GetOrCreate<SatParameters>();
1683 if (
parameters->enumerate_all_solutions())
return;
1686 const SatParameters saved_params = *
model->GetOrCreate<SatParameters>();
1693 updated_model_proto.clear_objective();
1696 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
1701 const int new_var_index = updated_model_proto.variables_size();
1702 IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1704 const int64_t max_domain =
1708 var_proto->add_domain(min_domain);
1709 var_proto->add_domain(max_domain);
1712 ConstraintProto*
const linear_constraint_proto =
1713 updated_model_proto.add_constraints();
1714 LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
1715 linear->add_vars(new_var_index);
1716 linear->add_coeffs(1);
1717 linear->add_vars(
var);
1718 linear->add_coeffs(-1);
1719 linear->add_domain(-
value);
1720 linear->add_domain(-
value);
1723 const int abs_var_index = updated_model_proto.variables_size();
1724 IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
1725 const int64_t abs_min_domain = 0;
1726 const int64_t abs_max_domain =
1727 std::max(std::abs(min_domain), std::abs(max_domain));
1728 abs_var_proto->add_domain(abs_min_domain);
1729 abs_var_proto->add_domain(abs_max_domain);
1730 auto* abs_ct = updated_model_proto.add_constraints()->mutable_lin_max();
1731 abs_ct->mutable_target()->add_vars(abs_var_index);
1732 abs_ct->mutable_target()->add_coeffs(1);
1733 LinearExpressionProto* left = abs_ct->add_exprs();
1734 left->add_vars(new_var_index);
1735 left->add_coeffs(1);
1736 LinearExpressionProto* right = abs_ct->add_exprs();
1737 right->add_vars(new_var_index);
1738 right->add_coeffs(-1);
1740 updated_model_proto.mutable_objective()->add_vars(abs_var_index);
1741 updated_model_proto.mutable_objective()->add_coeffs(1);
1744 auto* local_response_manager =
1745 local_model.GetOrCreate<SharedResponseManager>();
1746 local_response_manager->InitializeObjective(updated_model_proto);
1749 LoadCpModel(updated_model_proto, &local_model);
1752 const auto& mapping = *local_model.GetOrCreate<CpModelMapping>();
1754 mapping.Literals(updated_model_proto.assumptions()), &local_model);
1756 const std::string& solution_info =
model->Name();
1757 if (
status == SatSolver::Status::FEASIBLE) {
1761 CpSolverResponse updated_response;
1762 FillSolutionInResponse(updated_model_proto, local_model,
1764 LOG(
INFO) <<
"Found solution with repaired hint penalty = "
1768 response.set_solution_info(absl::StrCat(solution_info,
" [repaired]"));
1777void PostsolveResponseWithFullSolver(
int num_variables_in_original_model,
1778 CpModelProto mapping_proto,
1779 const std::vector<int>& postsolve_mapping,
1780 std::vector<int64_t>* solution) {
1785 for (
int i = 0; i < solution->size(); ++i) {
1786 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1787 var_proto->clear_domain();
1788 var_proto->add_domain((*solution)[i]);
1789 var_proto->add_domain((*solution)[i]);
1795 Model postsolve_model;
1798 SatParameters& params = *postsolve_model.GetOrCreate<SatParameters>();
1799 params.set_linearization_level(0);
1800 params.set_cp_model_probing_level(0);
1803 LoadCpModel(mapping_proto, &postsolve_model);
1804 SolveLoadedCpModel(mapping_proto, &postsolve_model);
1805 const CpSolverResponse postsolve_response =
1806 postsolve_model.GetOrCreate<SharedResponseManager>()->GetResponse();
1807 CHECK(postsolve_response.status() == CpSolverStatus::FEASIBLE ||
1808 postsolve_response.status() == CpSolverStatus::OPTIMAL)
1812 CHECK_LE(num_variables_in_original_model,
1813 postsolve_response.solution().size());
1815 postsolve_response.solution().begin(),
1816 postsolve_response.solution().begin() + num_variables_in_original_model);
1819void PostsolveResponseWrapper(
const SatParameters& params,
1820 int num_variable_in_original_model,
1821 const CpModelProto& mapping_proto,
1822 const std::vector<int>& postsolve_mapping,
1823 std::vector<int64_t>* solution) {
1824 if (params.debug_postsolve_with_full_solver()) {
1825 PostsolveResponseWithFullSolver(num_variable_in_original_model,
1826 mapping_proto, postsolve_mapping, solution);
1829 postsolve_mapping, solution);
1834CpSolverResponse SolvePureSatModel(
const CpModelProto&
model_proto,
1836 SolverLogger* logger) {
1837 std::unique_ptr<SatSolver> solver(
new SatSolver());
1840 model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(
parameters);
1843 std::unique_ptr<DratProofHandler> drat_proof_handler;
1844#if !defined(__PORTABLE_PLATFORM__)
1845 if (!absl::GetFlag(FLAGS_drat_output).empty() ||
1846 absl::GetFlag(FLAGS_drat_check)) {
1847 if (!absl::GetFlag(FLAGS_drat_output).empty()) {
1851 drat_proof_handler = absl::make_unique<DratProofHandler>(
1852 false, output, absl::GetFlag(FLAGS_drat_check));
1854 drat_proof_handler = absl::make_unique<DratProofHandler>();
1856 solver->SetDratProofHandler(drat_proof_handler.get());
1860 auto get_literal = [](
int ref) {
1861 if (ref >= 0)
return Literal(BooleanVariable(ref),
true);
1862 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
1865 std::vector<Literal> temp;
1866 const int num_variables =
model_proto.variables_size();
1867 solver->SetNumVariables(num_variables);
1868 if (drat_proof_handler !=
nullptr) {
1869 drat_proof_handler->SetNumVariables(num_variables);
1873 for (
int ref = 0; ref < num_variables; ++ref) {
1875 if (domain.IsFixed()) {
1876 const Literal ref_literal =
1877 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1878 drat_proof_handler->AddProblemClause({ref_literal});
1881 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1882 switch (
ct.constraint_case()) {
1883 case ConstraintProto::ConstraintCase::kBoolAnd: {
1884 if (
ct.enforcement_literal_size() == 0) {
1885 for (
const int ref :
ct.bool_and().literals()) {
1886 drat_proof_handler->AddProblemClause({get_literal(ref)});
1890 const Literal not_a =
1891 get_literal(
ct.enforcement_literal(0)).Negated();
1892 for (
const int ref :
ct.bool_and().literals()) {
1893 drat_proof_handler->AddProblemClause({not_a, get_literal(ref)});
1898 case ConstraintProto::ConstraintCase::kBoolOr:
1900 for (
const int ref :
ct.bool_or().literals()) {
1901 temp.push_back(get_literal(ref));
1903 for (
const int ref :
ct.enforcement_literal()) {
1904 temp.push_back(get_literal(ref).Negated());
1906 drat_proof_handler->AddProblemClause(temp);
1914 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
1915 switch (
ct.constraint_case()) {
1916 case ConstraintProto::ConstraintCase::kBoolAnd: {
1917 if (
ct.enforcement_literal_size() == 0) {
1918 for (
const int ref :
ct.bool_and().literals()) {
1919 const Literal
b = get_literal(ref);
1920 solver->AddUnitClause(
b);
1924 const Literal not_a =
1925 get_literal(
ct.enforcement_literal(0)).Negated();
1926 for (
const int ref :
ct.bool_and().literals()) {
1927 const Literal
b = get_literal(ref);
1928 solver->AddProblemClause({not_a,
b});
1933 case ConstraintProto::ConstraintCase::kBoolOr:
1935 for (
const int ref :
ct.bool_or().literals()) {
1936 temp.push_back(get_literal(ref));
1938 for (
const int ref :
ct.enforcement_literal()) {
1939 temp.push_back(get_literal(ref).Negated());
1941 solver->AddProblemClause(temp);
1949 for (
int ref = 0; ref < num_variables; ++ref) {
1951 if (domain.Min() == domain.Max()) {
1952 const Literal ref_literal =
1953 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1954 solver->AddUnitClause(ref_literal);
1961 std::vector<bool> solution;
1963 &solution, drat_proof_handler.get(), logger);
1966 for (
int ref = 0; ref < num_variables; ++ref) {
1967 response.add_solution(solution[ref]);
1971 status = solver->SolveWithTimeLimit(
model->GetOrCreate<TimeLimit>());
1974 for (
int ref = 0; ref < num_variables; ++ref) {
1976 solver->Assignment().LiteralIsTrue(get_literal(ref)) ? 1 : 0);
1983 model->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
1984 solver->model()->GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
1988 response.set_status(CpSolverStatus::UNKNOWN);
1995 response.set_status(CpSolverStatus::OPTIMAL);
1999 response.set_status(CpSolverStatus::INFEASIBLE);
2005 response.set_num_booleans(solver->NumVariables());
2006 response.set_num_branches(solver->num_branches());
2007 response.set_num_conflicts(solver->num_failures());
2008 response.set_num_binary_propagations(solver->num_propagations());
2009 response.set_num_integer_propagations(0);
2012 model->Get<TimeLimit>()->GetElapsedDeterministicTime());
2018 absl::GetFlag(FLAGS_max_drat_time_in_seconds));
2019 switch (drat_status) {
2021 LOG(
INFO) <<
"DRAT status: UNKNOWN";
2024 LOG(
INFO) <<
"DRAT status: VALID";
2027 LOG(ERROR) <<
"DRAT status: INVALID";
2033 LOG(
INFO) <<
"DRAT wall time: " << drat_timer.
Get();
2034 }
else if (drat_proof_handler !=
nullptr) {
2037 LOG(
INFO) <<
"DRAT status: NA";
2038 LOG(
INFO) <<
"DRAT wall time: NA";
2039 LOG(
INFO) <<
"DRAT user time: NA";
2044#if !defined(__PORTABLE_PLATFORM__)
2047struct SharedClasses {
2059 bool SearchIsDone() {
2060 if (
response->ProblemIsSolved())
return true;
2067class FullProblemSolver :
public SubSolver {
2069 FullProblemSolver(
const std::string&
name,
2070 const SatParameters& local_parameters,
bool split_in_chunks,
2071 SharedClasses* shared)
2074 split_in_chunks_(split_in_chunks),
2075 local_model_(
absl::make_unique<Model>(
name)) {
2077 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2078 shared_->time_limit->UpdateLocalLimit(
2079 local_model_->GetOrCreate<TimeLimit>());
2081 if (shared->response !=
nullptr) {
2082 local_model_->Register<SharedResponseManager>(shared->response);
2085 if (shared->relaxation_solutions !=
nullptr) {
2086 local_model_->Register<SharedRelaxationSolutionRepository>(
2087 shared->relaxation_solutions);
2090 if (shared->lp_solutions !=
nullptr) {
2091 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2094 if (shared->incomplete_solutions !=
nullptr) {
2095 local_model_->Register<SharedIncompleteSolutionManager>(
2096 shared->incomplete_solutions);
2099 if (shared->bounds !=
nullptr) {
2100 local_model_->Register<SharedBoundsManager>(shared->bounds);
2103 if (shared->clauses !=
nullptr) {
2104 local_model_->Register<SharedClausesManager>(shared->clauses);
2108 bool TaskIsAvailable()
override {
2109 if (shared_->SearchIsDone())
return false;
2111 absl::MutexLock mutex_lock(&mutex_);
2112 return previous_task_is_completed_;
2115 std::function<void()> GenerateTask(int64_t task_id)
override {
2117 absl::MutexLock mutex_lock(&mutex_);
2118 previous_task_is_completed_ =
false;
2121 if (solving_first_chunk_) {
2122 LoadCpModel(*shared_->model_proto, local_model_.get());
2128 if (shared_->bounds !=
nullptr) {
2129 RegisterVariableBoundsLevelZeroExport(
2130 *shared_->model_proto, shared_->bounds, local_model_.get());
2131 RegisterVariableBoundsLevelZeroImport(
2132 *shared_->model_proto, shared_->bounds, local_model_.get());
2138 if (shared_->clauses !=
nullptr) {
2139 const int id = shared_->clauses->RegisterNewId();
2140 shared_->clauses->SetWorkerNameForId(
id, local_model_->Name());
2142 RegisterClausesLevelZeroImport(
id, shared_->clauses,
2143 local_model_.get());
2144 RegisterClausesExport(
id, shared_->clauses, local_model_.get());
2147 if (local_model_->GetOrCreate<SatParameters>()->repair_hint()) {
2148 MinimizeL1DistanceWithHint(*shared_->model_proto, local_model_.get());
2150 QuickSolveWithHint(*shared_->model_proto, local_model_.get());
2154 solving_first_chunk_ =
false;
2156 if (split_in_chunks_) {
2158 absl::MutexLock mutex_lock(&mutex_);
2159 previous_task_is_completed_ =
true;
2164 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2165 if (split_in_chunks_) {
2168 auto* params = local_model_->GetOrCreate<SatParameters>();
2169 params->set_max_deterministic_time(1);
2170 time_limit->ResetLimitFromParameters(*params);
2171 shared_->time_limit->UpdateLocalLimit(
time_limit);
2174 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2175 SolveLoadedCpModel(*shared_->model_proto, local_model_.get());
2177 absl::MutexLock mutex_lock(&mutex_);
2178 deterministic_time_since_last_synchronize_ +=
2179 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2183 if (shared_->SearchIsDone()) {
2184 shared_->time_limit->Stop();
2189 if (split_in_chunks_) {
2190 absl::MutexLock mutex_lock(&mutex_);
2191 previous_task_is_completed_ =
true;
2199 local_model_.reset();
2206 void Synchronize()
override {
2207 absl::MutexLock mutex_lock(&mutex_);
2208 deterministic_time_ += deterministic_time_since_last_synchronize_;
2209 shared_->time_limit->AdvanceDeterministicTime(
2210 deterministic_time_since_last_synchronize_);
2211 deterministic_time_since_last_synchronize_ = 0.0;
2214 std::string StatisticsString()
const override {
2218 if (local_model_ ==
nullptr)
return std::string();
2221 *local_model_->GetOrCreate<LinearProgrammingConstraintCollection>();
2222 std::string lp_stats;
2224 local_model_->GetOrCreate<SatParameters>()->linearization_level() >=
2226 for (
const auto* lp : lps) {
2227 const std::string raw_statistics = lp->Statistics();
2228 const std::vector<absl::string_view> lines =
2229 absl::StrSplit(raw_statistics,
'\n', absl::SkipEmpty());
2230 for (
const absl::string_view& line : lines) {
2231 absl::StrAppend(&lp_stats,
" ", line,
"\n");
2239 SharedClasses* shared_;
2240 const bool split_in_chunks_;
2241 std::unique_ptr<Model> local_model_;
2245 bool solving_first_chunk_ =
true;
2248 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2250 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2253class FeasibilityPumpSolver :
public SubSolver {
2255 FeasibilityPumpSolver(
const SatParameters& local_parameters,
2256 SharedClasses* shared)
2257 : SubSolver(
"feasibility_pump"),
2259 local_model_(
absl::make_unique<Model>(name_)) {
2261 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2262 shared_->time_limit->UpdateLocalLimit(
2263 local_model_->GetOrCreate<TimeLimit>());
2265 if (shared->response !=
nullptr) {
2266 local_model_->Register<SharedResponseManager>(shared->response);
2269 if (shared->relaxation_solutions !=
nullptr) {
2270 local_model_->Register<SharedRelaxationSolutionRepository>(
2271 shared->relaxation_solutions);
2274 if (shared->lp_solutions !=
nullptr) {
2275 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2278 if (shared->incomplete_solutions !=
nullptr) {
2279 local_model_->Register<SharedIncompleteSolutionManager>(
2280 shared->incomplete_solutions);
2284 if (shared_->bounds !=
nullptr) {
2285 RegisterVariableBoundsLevelZeroImport(
2286 *shared_->model_proto, shared_->bounds, local_model_.get());
2290 bool TaskIsAvailable()
override {
2291 if (shared_->SearchIsDone())
return false;
2292 absl::MutexLock mutex_lock(&mutex_);
2293 return previous_task_is_completed_;
2296 std::function<void()> GenerateTask(int64_t task_id)
override {
2299 absl::MutexLock mutex_lock(&mutex_);
2300 if (!previous_task_is_completed_)
return;
2301 previous_task_is_completed_ =
false;
2304 absl::MutexLock mutex_lock(&mutex_);
2305 if (solving_first_chunk_) {
2306 LoadFeasibilityPump(*shared_->model_proto, local_model_.get());
2309 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
2310 solving_first_chunk_ =
false;
2312 previous_task_is_completed_ =
true;
2317 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2318 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2319 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
2320 if (!feasibility_pump->Solve()) {
2321 shared_->response->NotifyThatImprovingProblemIsInfeasible(name_);
2325 absl::MutexLock mutex_lock(&mutex_);
2326 deterministic_time_since_last_synchronize_ +=
2327 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2331 if (shared_->SearchIsDone()) {
2332 shared_->time_limit->Stop();
2336 absl::MutexLock mutex_lock(&mutex_);
2337 previous_task_is_completed_ =
true;
2341 void Synchronize()
override {
2342 absl::MutexLock mutex_lock(&mutex_);
2343 deterministic_time_ += deterministic_time_since_last_synchronize_;
2344 shared_->time_limit->AdvanceDeterministicTime(
2345 deterministic_time_since_last_synchronize_);
2346 deterministic_time_since_last_synchronize_ = 0.0;
2352 SharedClasses* shared_;
2353 std::unique_ptr<Model> local_model_;
2359 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
2361 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2363 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2367class LnsSolver :
public SubSolver {
2369 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
2371 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
2372 : SubSolver(generator->
name()),
2373 generator_(
std::move(generator)),
2378 bool TaskIsAvailable()
override {
2379 if (shared_->SearchIsDone())
return false;
2380 return generator_->ReadyToGenerate();
2383 std::function<void()> GenerateTask(int64_t task_id)
override {
2384 return [task_id,
this]() {
2385 if (shared_->SearchIsDone())
return;
2390 const int32_t low =
static_cast<int32_t
>(task_id);
2391 const int32_t high =
static_cast<int32_t
>(task_id >> 32);
2392 std::seed_seq seed{low, high, parameters_.random_seed()};
2395 NeighborhoodGenerator::SolveData data;
2396 data.difficulty = generator_->difficulty();
2397 data.deterministic_limit = generator_->deterministic_limit();
2400 CpSolverResponse base_response;
2402 const SharedSolutionRepository<int64_t>& repo =
2403 shared_->response->SolutionsRepository();
2404 if (repo.NumSolutions() > 0) {
2405 base_response.set_status(CpSolverStatus::FEASIBLE);
2406 const SharedSolutionRepository<int64_t>::Solution solution =
2407 repo.GetRandomBiasedSolution(random);
2408 for (
const int64_t
value : solution.variable_values) {
2409 base_response.add_solution(
value);
2414 data.initial_best_objective = repo.GetSolution(0).rank;
2415 data.base_objective = solution.rank;
2417 base_response.set_status(CpSolverStatus::UNKNOWN);
2424 data.initial_best_objective =
2425 shared_->response->GetInnerObjectiveUpperBound();
2426 data.base_objective = data.initial_best_objective;
2430 Neighborhood neighborhood =
2431 generator_->Generate(base_response, data.difficulty, random);
2433 if (!neighborhood.is_generated)
return;
2435 data.neighborhood_id = neighborhood.id;
2437 const int64_t num_calls =
std::max(int64_t{1}, generator_->num_calls());
2438 const double fully_solved_proportion =
2439 static_cast<double>(generator_->num_fully_solved_calls()) /
2440 static_cast<double>(num_calls);
2441 std::string source_info =
name();
2442 if (!neighborhood.source_info.empty()) {
2443 absl::StrAppend(&source_info,
"_", neighborhood.source_info);
2445 const std::string solution_info = absl::StrFormat(
2446 "%s(d=%0.2f s=%i t=%0.2f p=%0.2f)", source_info, data.difficulty,
2447 task_id, data.deterministic_limit, fully_solved_proportion);
2449 SatParameters local_params(parameters_);
2450 local_params.set_max_deterministic_time(data.deterministic_limit);
2451 local_params.set_stop_after_first_solution(
false);
2452 local_params.set_log_search_progress(
false);
2453 local_params.set_cp_model_probing_level(0);
2454 local_params.set_symmetry_level(0);
2455 local_params.set_solution_pool_size(0);
2457 Model local_model(solution_info);
2458 *(local_model.GetOrCreate<SatParameters>()) = local_params;
2459 TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
2460 local_time_limit->ResetLimitFromParameters(local_params);
2461 shared_->time_limit->UpdateLocalLimit(local_time_limit);
2464 CpModelProto lns_fragment;
2465 CpModelProto mapping_proto;
2466 auto context = absl::make_unique<PresolveContext>(
2467 &local_model, &lns_fragment, &mapping_proto);
2469 *lns_fragment.mutable_variables() = neighborhood.delta.variables();
2471 ModelCopy copier(
context.get());
2474 if (!copier.ImportAndSimplifyConstraints(
2475 helper_->ModelProto(), neighborhood.constraints_to_ignore)) {
2480 if (!neighborhood.delta.constraints().empty() &&
2481 !copier.ImportAndSimplifyConstraints(neighborhood.delta, {})) {
2488 helper_->ModelProto(),
context.get());
2489 lns_fragment.set_name(absl::StrCat(
"lns_", task_id));
2492 if (neighborhood.delta.has_solution_hint()) {
2493 *lns_fragment.mutable_solution_hint() =
2494 neighborhood.delta.solution_hint();
2497 CpModelProto debug_copy;
2498 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
2501 debug_copy = lns_fragment;
2504#if !defined(__PORTABLE_PLATFORM__)
2507 if (absl::GetFlag(FLAGS_cp_model_dump_lns)) {
2509 const std::string lns_name =
2510 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2511 lns_fragment.name(),
".pb.txt");
2512 LOG(
INFO) <<
"Dumping LNS model to '" << lns_name <<
"'.";
2516 std::vector<int> postsolve_mapping;
2517 const CpSolverStatus presolve_status =
2522 neighborhood.delta.Clear();
2527 auto* local_response_manager =
2528 local_model.GetOrCreate<SharedResponseManager>();
2529 local_response_manager->InitializeObjective(lns_fragment);
2531 if (presolve_status == CpSolverStatus::UNKNOWN) {
2532 LoadCpModel(lns_fragment, &local_model);
2533 QuickSolveWithHint(lns_fragment, &local_model);
2534 SolveLoadedCpModel(lns_fragment, &local_model);
2536 local_response_manager->MutableResponse()->set_status(presolve_status);
2538 CpSolverResponse local_response = local_response_manager->GetResponse();
2542 if (local_params.cp_model_presolve() &&
2543 (local_response.status() == CpSolverStatus::OPTIMAL ||
2544 local_response.status() == CpSolverStatus::FEASIBLE)) {
2545 std::vector<int64_t> solution(local_response.solution().begin(),
2546 local_response.solution().end());
2547 PostsolveResponseWrapper(local_params,
2548 helper_->ModelProto().variables_size(),
2549 mapping_proto, postsolve_mapping, &solution);
2550 local_response.mutable_solution()->Assign(solution.begin(),
2554 data.status = local_response.status();
2555 data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
2557 bool new_solution =
false;
2558 bool display_lns_info =
false;
2559 if (generator_->IsRelaxationGenerator()) {
2560 bool has_feasible_solution =
false;
2561 if (local_response.status() == CpSolverStatus::OPTIMAL ||
2562 local_response.status() == CpSolverStatus::FEASIBLE) {
2563 has_feasible_solution =
true;
2566 if (local_response.status() == CpSolverStatus::INFEASIBLE) {
2567 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2568 local_response.solution_info());
2571 if (shared_->model_proto->has_objective()) {
2574 const IntegerValue current_obj_lb =
2575 shared_->response->GetInnerObjectiveLowerBound();
2577 const IntegerValue local_obj_lb =
2578 local_response_manager->GetInnerObjectiveLowerBound();
2581 lns_fragment.objective(), local_obj_lb.value());
2584 const IntegerValue new_inner_obj_lb = IntegerValue(
2586 scaled_local_obj_bound) -
2588 data.new_objective_bound = new_inner_obj_lb;
2589 data.initial_best_objective_bound = current_obj_lb;
2590 if (new_inner_obj_lb > current_obj_lb) {
2591 shared_->response->UpdateInnerObjectiveBounds(
2598 if (has_feasible_solution) {
2600 *shared_->model_proto,
2601 std::vector<int64_t>(local_response.solution().begin(),
2602 local_response.solution().end()))) {
2603 shared_->response->NewSolution(local_response,
2607 if (local_response.status() == CpSolverStatus::OPTIMAL) {
2608 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2609 local_response.solution_info());
2610 shared_->time_limit->Stop();
2613 shared_->relaxation_solutions->NewRelaxationSolution(local_response);
2616 const std::vector<int64_t> solution(local_response.solution().begin(),
2617 local_response.solution().end());
2619 if (!solution.empty()) {
2625 const bool feasible =
2628 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
2629 const std::string
name =
2630 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2631 debug_copy.name(),
".pb.txt");
2632 LOG(
INFO) <<
"Dumping problematic LNS model to '" <<
name <<
"'.";
2635 LOG(
FATAL) <<
"Infeasible LNS solution! " << solution_info
2636 <<
" solved with params "
2637 << local_params.ShortDebugString();
2652 if (local_response.status() == CpSolverStatus::OPTIMAL &&
2653 !shared_->model_proto->has_symmetry() && !solution.empty() &&
2654 neighborhood.is_simple &&
2655 !neighborhood.variables_that_can_be_fixed_to_local_optimum
2657 display_lns_info =
true;
2658 shared_->bounds->FixVariablesFromPartialSolution(
2660 neighborhood.variables_that_can_be_fixed_to_local_optimum);
2664 data.new_objective = data.base_objective;
2665 if (local_response.status() == CpSolverStatus::OPTIMAL ||
2666 local_response.status() == CpSolverStatus::FEASIBLE) {
2668 shared_->model_proto->objective(), local_response));
2673 if (local_response.status() == CpSolverStatus::OPTIMAL ||
2674 local_response.status() == CpSolverStatus::FEASIBLE) {
2675 const std::vector<int64_t> base_solution(
2676 base_response.solution().begin(), base_response.solution().end());
2677 if (solution != base_solution) {
2678 new_solution =
true;
2679 shared_->response->NewSolution(local_response,
nullptr);
2682 if (!neighborhood.is_reduced &&
2683 (local_response.status() == CpSolverStatus::OPTIMAL ||
2684 local_response.status() == CpSolverStatus::INFEASIBLE)) {
2685 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2686 local_response.solution_info());
2687 shared_->time_limit->Stop();
2691 generator_->AddSolveData(data);
2694 auto* logger = shared_->global_model->GetOrCreate<SolverLogger>();
2695 std::string s = absl::StrCat(
" LNS ",
name(),
":");
2698 shared_->model_proto->objective(),
2702 shared_->model_proto->objective(),
2705 absl::StrAppend(&s,
" [new_sol:", base_obj,
" -> ", new_obj,
"]");
2707 if (neighborhood.is_simple) {
2709 &s,
" [",
"relaxed:", neighborhood.num_relaxed_variables,
2710 " in_obj:", neighborhood.num_relaxed_variables_in_objective,
2712 neighborhood.variables_that_can_be_fixed_to_local_optimum.size(),
2715 SOLVER_LOG(logger, s,
" [d:", data.difficulty,
", id:", task_id,
2716 ", dtime:", data.deterministic_time,
"/",
2717 data.deterministic_limit,
2718 ", status:", ProtoEnumToString<CpSolverStatus>(data.status),
2719 ", #calls:", generator_->num_calls(),
2720 ", p:", fully_solved_proportion,
"]");
2725 void Synchronize()
override {
2726 generator_->Synchronize();
2727 const double old = deterministic_time_;
2728 deterministic_time_ = generator_->deterministic_time();
2729 shared_->time_limit->AdvanceDeterministicTime(deterministic_time_ - old);
2735 std::unique_ptr<NeighborhoodGenerator> generator_;
2736 NeighborhoodGeneratorHelper* helper_;
2737 const SatParameters parameters_;
2738 SharedClasses* shared_;
2741void SolveCpModelParallel(
const CpModelProto&
model_proto,
2744 CHECK(!params.enumerate_all_solutions())
2745 <<
"Enumerating all solutions in parallel is not supported.";
2747 std::unique_ptr<SharedBoundsManager> shared_bounds_manager;
2748 if (params.share_level_zero_bounds()) {
2749 shared_bounds_manager = absl::make_unique<SharedBoundsManager>(
model_proto);
2752 std::unique_ptr<SharedRelaxationSolutionRepository>
2753 shared_relaxation_solutions;
2754 if (params.use_relaxation_lns()) {
2755 shared_relaxation_solutions =
2756 absl::make_unique<SharedRelaxationSolutionRepository>(
2759 shared_relaxation_solutions.get());
2762 auto shared_lp_solutions = absl::make_unique<SharedLPSolutionRepository>(
2768 std::unique_ptr<SharedIncompleteSolutionManager> shared_incomplete_solutions;
2769 const bool use_feasibility_pump =
2770 params.use_feasibility_pump() && params.linearization_level() > 0 &&
2771 !params.use_lns_only() && !params.interleave_search();
2772 if (use_feasibility_pump) {
2773 shared_incomplete_solutions =
2774 absl::make_unique<SharedIncompleteSolutionManager>();
2776 shared_incomplete_solutions.get());
2779 std::unique_ptr<SharedClausesManager> shared_clauses;
2780 if (params.share_binary_clauses()) {
2781 shared_clauses = absl::make_unique<SharedClausesManager>();
2784 SharedClasses shared;
2788 shared.bounds = shared_bounds_manager.get();
2790 shared.relaxation_solutions = shared_relaxation_solutions.get();
2791 shared.lp_solutions = shared_lp_solutions.get();
2792 shared.incomplete_solutions = shared_incomplete_solutions.get();
2793 shared.clauses = shared_clauses.get();
2797 std::vector<std::unique_ptr<SubSolver>> subsolvers;
2800 subsolvers.push_back(absl::make_unique<SynchronizationPoint>([&shared]() {
2801 shared.response->Synchronize();
2802 shared.response->MutableSolutionsRepository()->Synchronize();
2803 if (shared.bounds !=
nullptr) {
2804 shared.bounds->Synchronize();
2806 if (shared.relaxation_solutions !=
nullptr) {
2807 shared.relaxation_solutions->Synchronize();
2809 if (shared.lp_solutions !=
nullptr) {
2810 shared.lp_solutions->Synchronize();
2814 if (params.use_lns_only()) {
2818 SatParameters local_params = params;
2819 local_params.set_stop_after_first_solution(
true);
2820 local_params.set_linearization_level(0);
2821 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2822 "first_solution", local_params,
2825 for (
const SatParameters& local_params :
2828 if (params.optimize_with_max_hs())
continue;
2830 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2831 local_params.name(), local_params,
2832 params.interleave_search(), &shared));
2835 const int num_full_subsolvers = subsolvers.size();
2838 if (use_feasibility_pump) {
2839 subsolvers.push_back(
2840 absl::make_unique<FeasibilityPumpSolver>(params, &shared));
2847 auto unique_helper = absl::make_unique<NeighborhoodGeneratorHelper>(
2848 &
model_proto, ¶ms, shared.response, shared.time_limit, shared.bounds);
2849 NeighborhoodGeneratorHelper* helper = unique_helper.get();
2850 subsolvers.push_back(std::move(unique_helper));
2853 std::vector<SatParameters> lns_params = {params};
2854 lns_params.back().set_name(
"default");
2855 if (params.diversify_lns_params()) {
2856 SatParameters copy = params;
2857 copy.set_num_workers(6);
2858 copy.set_min_num_lns_workers(0);
2861 for (
const SatParameters& local_params : lns_params) {
2866 subsolvers.push_back(absl::make_unique<LnsSolver>(
2867 absl::make_unique<RelaxRandomVariablesGenerator>(
2868 helper, absl::StrCat(
"rnd_var_lns_", local_params.name())),
2869 local_params, helper, &shared));
2870 subsolvers.push_back(absl::make_unique<LnsSolver>(
2871 absl::make_unique<RelaxRandomConstraintsGenerator>(
2872 helper, absl::StrCat(
"rnd_cst_lns_", local_params.name())),
2873 local_params, helper, &shared));
2874 subsolvers.push_back(absl::make_unique<LnsSolver>(
2875 absl::make_unique<VariableGraphNeighborhoodGenerator>(
2876 helper, absl::StrCat(
"graph_var_lns_", local_params.name())),
2877 local_params, helper, &shared));
2878 subsolvers.push_back(absl::make_unique<LnsSolver>(
2879 absl::make_unique<ConstraintGraphNeighborhoodGenerator>(
2880 helper, absl::StrCat(
"graph_cst_lns_", local_params.name())),
2881 local_params, helper, &shared));
2885 if (!helper->TypeToConstraints(ConstraintProto::kNoOverlap).empty() ||
2886 !helper->TypeToConstraints(ConstraintProto::kNoOverlap2D).empty() ||
2887 !helper->TypeToConstraints(ConstraintProto::kCumulative).empty()) {
2888 subsolvers.push_back(absl::make_unique<LnsSolver>(
2889 absl::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
2890 helper, absl::StrCat(
"scheduling_time_window_lns_",
2891 local_params.name())),
2892 local_params, helper, &shared));
2893 subsolvers.push_back(absl::make_unique<LnsSolver>(
2894 absl::make_unique<SchedulingNeighborhoodGenerator>(
2896 absl::StrCat(
"scheduling_random_lns_", local_params.name())),
2897 local_params, helper, &shared));
2901 const int num_circuit =
2902 helper->TypeToConstraints(ConstraintProto::kCircuit).size();
2903 const int num_routes =
2904 helper->TypeToConstraints(ConstraintProto::kRoutes).size();
2905 if (num_circuit + num_routes > 0) {
2906 subsolvers.push_back(absl::make_unique<LnsSolver>(
2907 absl::make_unique<RoutingRandomNeighborhoodGenerator>(
2908 helper, absl::StrCat(
"routing_random_lns_", local_params.name())),
2909 local_params, helper, &shared));
2911 subsolvers.push_back(absl::make_unique<LnsSolver>(
2912 absl::make_unique<RoutingPathNeighborhoodGenerator>(
2913 helper, absl::StrCat(
"routing_path_lns_", local_params.name())),
2914 local_params, helper, &shared));
2916 if (num_routes > 0 || num_circuit > 1) {
2917 subsolvers.push_back(absl::make_unique<LnsSolver>(
2918 absl::make_unique<RoutingFullPathNeighborhoodGenerator>(
2920 absl::StrCat(
"routing_full_path_lns_", local_params.name())),
2921 local_params, helper, &shared));
2926 if (params.use_rins_lns() && !params.interleave_search()) {
2933 subsolvers.push_back(absl::make_unique<LnsSolver>(
2934 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2935 helper, shared.response, shared.relaxation_solutions,
2936 shared.lp_solutions,
nullptr,
2937 absl::StrCat(
"rins_lns_", local_params.name())),
2938 local_params, helper, &shared));
2941 subsolvers.push_back(absl::make_unique<LnsSolver>(
2942 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2943 helper,
nullptr, shared.relaxation_solutions,
2944 shared.lp_solutions, shared.incomplete_solutions,
2945 absl::StrCat(
"rens_lns_", local_params.name())),
2946 local_params, helper, &shared));
2949 if (params.use_relaxation_lns()) {
2950 subsolvers.push_back(absl::make_unique<LnsSolver>(
2952 ConsecutiveConstraintsRelaxationNeighborhoodGenerator>(
2953 helper, absl::StrCat(
"rnd_rel_lns_", local_params.name())),
2954 local_params, helper, &shared));
2956 subsolvers.push_back(absl::make_unique<LnsSolver>(
2957 absl::make_unique<WeightedRandomRelaxationNeighborhoodGenerator>(
2958 helper, absl::StrCat(
"wgt_rel_lns_", local_params.name())),
2959 local_params, helper, &shared));
2966 subsolvers.push_back(absl::make_unique<SynchronizationPoint>(
2967 [&shared]() { shared.response->UpdateGapIntegral(); }));
2971 if (logger->LoggingIsEnabled()) {
2972 std::vector<std::string> names;
2973 int full_subsolver_index = 0;
2974 for (
int i = 0; i < subsolvers.size(); ++i) {
2975 const auto& subsolver = subsolvers[i];
2976 if (!subsolver->name().empty()) {
2977 names.push_back(subsolver->name());
2978 if (i < num_full_subsolvers) ++full_subsolver_index;
2983 absl::StrFormat(
"Starting Search at %.2fs with %i workers.",
2984 shared.wall_timer->Get(), params.num_workers()));
2985 SOLVER_LOG(logger, full_subsolver_index,
" full subsolvers: [",
2986 absl::StrJoin(names.begin(),
2987 names.begin() + full_subsolver_index,
", "),
2990 logger,
"Interleaved subsolvers: [",
2991 absl::StrJoin(names.begin() + full_subsolver_index, names.end(),
", "),
2996 if (params.interleave_search()) {
2998 params.interleave_batch_size());
3004 if (logger->LoggingIsEnabled()) {
3005 if (params.log_subsolver_statistics()) {
3007 for (
const auto& subsolver : subsolvers) {
3008 const std::string stats = subsolver->StatisticsString();
3009 if (stats.empty())
continue;
3012 SOLVER_LOG(logger,
"Sub-solver search statistics:");
3016 absl::StrCat(
" '", subsolver->name(),
"':\n", stats));
3020 shared.response->DisplayImprovementStatistics();
3022 if (shared.bounds) {
3023 shared.bounds->LogStatistics(logger);
3026 if (shared.clauses) {
3027 shared.clauses->LogStatistics(logger);
3037void AddPostsolveClauses(
const std::vector<int>& postsolve_mapping,
3038 Model*
model, CpModelProto* mapping_proto) {
3039 auto* mapping =
model->GetOrCreate<CpModelMapping>();
3040 auto* postsolve =
model->GetOrCreate<PostsolveClauses>();
3041 for (
const auto& clause : postsolve->clauses) {
3042 auto*
ct = mapping_proto->add_constraints()->mutable_bool_or();
3043 for (
const Literal l : clause) {
3044 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
3046 var = postsolve_mapping[
var];
3050 postsolve->clauses.clear();
3059 user_timer->
Start();
3061#if !defined(__PORTABLE_PLATFORM__)
3064#if !defined(__PORTABLE_PLATFORM__)
3066 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
3067 const std::string
file =
3068 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
"model.pb.txt");
3069 LOG(
INFO) <<
"Dumping cp model proto to '" <<
file <<
"'.";
3074#if !defined(__PORTABLE_PLATFORM__)
3076 if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
3077 SatParameters params = *
model->GetOrCreate<SatParameters>();
3078 SatParameters flag_params;
3079 CHECK(google::protobuf::TextFormat::ParseFromString(
3080 absl::GetFlag(FLAGS_cp_model_params), &flag_params));
3081 params.MergeFrom(flag_params);
3082 *(
model->GetOrCreate<SatParameters>()) = params;
3087 const SatParameters& params = *
model->GetOrCreate<SatParameters>();
3090 logger->SetLogToStdOut(params.log_to_stdout());
3091 std::string log_string;
3092 if (params.log_to_response()) {
3093 logger->AddInfoLoggingCallback([&log_string](
const std::string&
message) {
3094 absl::StrAppend(&log_string,
message,
"\n");
3100 absl::GetFlag(FLAGS_cp_model_dump_prefix));
3102#if !defined(__PORTABLE_PLATFORM__)
3106 if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
3107 shared_response_manager->AddFinalResponsePostprocessor(
3109 const std::string
file = absl::StrCat(
3110 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"response.pb.txt");
3111 LOG(
INFO) <<
"Dumping response proto to '" <<
file <<
"'.";
3119 shared_response_manager->AddFinalResponsePostprocessor(
3126 if (!log_string.empty()) {
3127 response->set_solve_log(log_string);
3135 shared_response_manager->AddResponsePostprocessor(
3137 &shared_time_limit](CpSolverResponse*
response) {
3141 shared_time_limit->GetElapsedDeterministicTime());
3150 if (!error.empty()) {
3151 SOLVER_LOG(logger,
"Invalid parameters: ", error);
3156 shared_response_manager->MutableResponse()->set_status(
3157 CpSolverStatus::MODEL_INVALID);
3158 shared_response_manager->MutableResponse()->set_solution_info(error);
3159 return shared_response_manager->GetResponse();
3164 model->GetOrCreate<
TimeLimit>()->ResetLimitFromParameters(params);
3166#if !defined(__PORTABLE_PLATFORM__)
3168 if (params.catch_sigint_signal()) {
3170 [&shared_time_limit]() { shared_time_limit->Stop(); });
3177 SOLVER_LOG(logger,
"Parameters: ", params.ShortDebugString());
3180 if (params.num_workers() == 0) {
3181 model->GetOrCreate<SatParameters>()->set_num_workers(
3182 params.num_search_workers());
3186 if (params.num_workers() == 0) {
3187#if !defined(__PORTABLE_PLATFORM__)
3189 const int num_cores =
3190 params.enumerate_all_solutions() || !
model_proto.assumptions().empty()
3192 : std::max<int>(std::thread::hardware_concurrency(), 1);
3194 const int num_cores = 1;
3196 SOLVER_LOG(logger,
"Setting number of workers to ", num_cores);
3197 model->GetOrCreate<SatParameters>()->set_num_workers(num_cores);
3200 if (logger->LoggingIsEnabled() && params.use_absl_random()) {
3208 if (!error.empty()) {
3209 SOLVER_LOG(logger,
"Invalid model: ", error);
3210 shared_response_manager->MutableResponse()->set_status(
3211 CpSolverStatus::MODEL_INVALID);
3212 shared_response_manager->MutableResponse()->set_solution_info(error);
3213 return shared_response_manager->GetResponse();
3224 if (!params.use_sat_inprocessing() && !
model_proto.has_objective() &&
3226 !
model_proto.has_solution_hint() && !params.enumerate_all_solutions() &&
3227 !params.use_lns_only() && params.num_workers() <= 1 &&
3229 bool is_pure_sat =
true;
3230 for (
const IntegerVariableProto&
var :
model_proto.variables()) {
3231 if (
var.domain_size() != 2 ||
var.domain(0) < 0 ||
var.domain(1) > 1) {
3232 is_pure_sat =
false;
3237 for (
const ConstraintProto&
ct :
model_proto.constraints()) {
3238 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolOr &&
3239 ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolAnd) {
3240 is_pure_sat =
false;
3248 *shared_response_manager->MutableResponse() =
3250 if (params.fill_tightened_domains_in_response()) {
3251 *shared_response_manager->MutableResponse()
3252 ->mutable_tightened_variables() =
model_proto.variables();
3254 return shared_response_manager->GetResponse();
3261 absl::StrFormat(
"Starting presolve at %.2fs",
wall_timer->
Get()));
3262 CpModelProto new_cp_model_proto;
3263 CpModelProto mapping_proto;
3264 auto context = absl::make_unique<PresolveContext>(
model, &new_cp_model_proto,
3268 VLOG(1) <<
"Model found infeasible during copy";
3274 if (params.fix_variables_to_their_hinted_value() &&
3278 " variables to their value in the solution hints.");
3279 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
3283 const IntegerVariableProto& var_proto =
3285 const std::string var_name = var_proto.name().empty()
3286 ? absl::StrCat(
"var(",
var,
")")
3291 "Hint found infeasible when assigning variable '", var_name,
3292 "' with domain", var_domain.
ToString(),
" the value ",
3308 std::vector<int64_t> solution(
model_proto.variables_size(), 0);
3309 for (
int i = 0; i <
model_proto.solution_hint().vars_size(); ++i) {
3310 const int ref =
model_proto.solution_hint().vars(i);
3316 "The solution hint is complete and is feasible.");
3321 "The solution hint is complete, but it is infeasible! we "
3322 "will try to repair it.");
3329 shared_response_manager->AddFinalResponsePostprocessor(
3331 &logger](CpSolverResponse*
response) {
3332 if (
response->solution().empty())
return;
3335 const auto& float_obj =
model_proto.floating_point_objective();
3336 double value = float_obj.offset();
3337 const int num_terms = float_obj.vars().size();
3338 for (
int i = 0; i < num_terms; ++i) {
3339 value += float_obj.coeffs(i) *
3340 static_cast<double>(
response->solution(float_obj.vars(i)));
3347 if (!mapping_proto.has_objective())
return;
3348 const CpObjectiveProto& integer_obj = mapping_proto.objective();
3349 *
response->mutable_integer_objective() = integer_obj;
3354 if (params.mip_compute_true_objective_bound() &&
3355 !integer_obj.scaling_was_exact()) {
3356 const int64_t integer_lb = response->inner_objective_lower_bound();
3357 const double lb = ComputeTrueObjectiveLowerBound(
3358 model_proto, integer_obj, integer_lb);
3359 SOLVER_LOG(logger,
"[Scaling] scaled_objective_bound: ",
3360 response->best_objective_bound(),
3361 " corrected_bound: ", lb,
3362 " delta: ", response->best_objective_bound() - lb);
3366 if (float_obj.maximize()) {
3367 response->set_best_objective_bound(
3368 std::max(lb, response->objective_value()));
3370 response->set_best_objective_bound(
3371 std::min(lb, response->objective_value()));
3377 if (
response->status() == CpSolverStatus::OPTIMAL) {
3378 const double gap = std::abs(response->objective_value() -
3379 response->best_objective_bound());
3380 if (gap > params.absolute_gap_limit()) {
3382 "[Scaling] Warning: OPTIMAL was reported, yet the "
3384 gap,
") is greater than requested absolute limit (",
3385 params.absolute_gap_limit(),
").");
3392 (params.num_workers() > 1 ||
model_proto.has_objective() ||
3396 "Warning: solving with assumptions was requested in a non-fully "
3397 "supported setting.\nWe will assumes these assumptions true while "
3398 "solving, but if the model is infeasible, you will not get a useful "
3399 "'sufficient_assumptions_for_infeasibility' field in the response, it "
3400 "will include all assumptions.");
3408 shared_response_manager->AddFinalResponsePostprocessor(
3410 if (
response->status() != CpSolverStatus::INFEASIBLE)
return;
3413 *
response->mutable_sufficient_assumptions_for_infeasibility() =
3418 new_cp_model_proto.clear_assumptions();
3420 context->InitializeNewDomains();
3422 if (!
context->SetLiteralToTrue(ref)) {
3423 shared_response_manager->MutableResponse()->set_status(
3424 CpSolverStatus::INFEASIBLE);
3425 shared_response_manager->MutableResponse()
3426 ->add_sufficient_assumptions_for_infeasibility(ref);
3427 return shared_response_manager->GetResponse();
3433 std::vector<int> postsolve_mapping;
3434 const CpSolverStatus presolve_status =
3436 if (presolve_status != CpSolverStatus::UNKNOWN) {
3437 SOLVER_LOG(logger,
"Problem closed by presolve.");
3438 shared_response_manager->MutableResponse()->set_status(presolve_status);
3439 return shared_response_manager->GetResponse();
3447 if (params.cp_model_presolve()) {
3448 shared_response_manager->AddSolutionPostprocessor(
3450 &postsolve_mapping](std::vector<int64_t>* solution) {
3451 AddPostsolveClauses(postsolve_mapping,
model, &mapping_proto);
3452 PostsolveResponseWrapper(params,
model_proto.variables_size(),
3453 mapping_proto, postsolve_mapping, solution);
3455 shared_response_manager->AddResponsePostprocessor(
3461 ->mutable_sufficient_assumptions_for_infeasibility())) {
3463 ? postsolve_mapping[ref]
3466 if (!
response->solution().empty()) {
3469 std::vector<int64_t>(
response->solution().begin(),
3471 &mapping_proto, &postsolve_mapping))
3472 <<
"postsolved solution";
3474 if (params.fill_tightened_domains_in_response()) {
3477 if (mapping_proto.variables().size() >=
3479 for (
int i = 0; i <
model_proto.variables().size(); ++i) {
3480 *
response->add_tightened_variables() =
3481 mapping_proto.variables(i);
3487 shared_response_manager->AddFinalResponsePostprocessor(
3489 if (!
response->solution().empty()) {
3495 shared_response_manager->AddResponsePostprocessor(
3498 const int initial_size =
model_proto.variables_size();
3499 if (
response->solution_size() > 0) {
3500 response->mutable_solution()->Truncate(initial_size);
3502 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
3505 std::vector<int64_t>(
response->solution().begin(),
3509 if (params.fill_tightened_domains_in_response()) {
3518 if (params.symmetry_level() > 1) {
3522 const auto& observers =
model->GetOrCreate<SolutionObservers>()->observers;
3523 if (!observers.empty()) {
3524 shared_response_manager->AddSolutionCallback(
3525 [&observers](
const CpSolverResponse&
response) {
3526 for (
const auto& observer : observers) {
3536 if (new_cp_model_proto.has_objective()) {
3537 shared_response_manager->InitializeObjective(new_cp_model_proto);
3538 shared_response_manager->SetGapLimitsFromParameters(params);
3543 shared_response_manager->UpdateGapIntegral();
3545#if !defined(__PORTABLE_PLATFORM__)
3546 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
3547 const std::string presolved_file = absl::StrCat(
3548 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"presolved_model.pb.txt");
3549 LOG(
INFO) <<
"Dumping presolved CpModelProto to '" << presolved_file
3554 const std::string mapping_file = absl::StrCat(
3555 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"mapping_model.pb.txt");
3556 LOG(
INFO) <<
"Dumping mapping CpModelProto to '" << mapping_file <<
"'.";
3562 MPModelProto mip_model;
3564 const std::string
file =
3565 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
3566 "presolved.mp_model.pb.txt");
3567 LOG(
INFO) <<
"Presolved problem is pure linear IP. Dumping presolved "
3575 if (params.stop_after_presolve() || shared_time_limit->LimitReached()) {
3576 int64_t num_terms = 0;
3577 for (
const ConstraintProto&
ct : new_cp_model_proto.constraints()) {
3581 logger,
"Stopped after presolve.",
3582 "\nPresolvedNumVariables: ", new_cp_model_proto.variables().size(),
3583 "\nPresolvedNumConstraints: ", new_cp_model_proto.constraints().size(),
3584 "\nPresolvedNumTerms: ", num_terms);
3586 shared_response_manager->SetStatsFromModel(
model);
3587 return shared_response_manager->GetResponse();
3591 if (params.stop_after_first_solution()) {
3592 shared_response_manager->AddSolutionCallback(
3593 [shared_time_limit](
const CpSolverResponse&
response) {
3594 shared_time_limit->Stop();
3598#if defined(__PORTABLE_PLATFORM__)
3602 if (params.num_workers() > 1 || params.interleave_search()) {
3603 SolveCpModelParallel(new_cp_model_proto,
model);
3607 SOLVER_LOG(logger, absl::StrFormat(
"Starting to load the model at %.2fs",
3609 shared_response_manager->SetUpdateGapIntegralOnEachChange(
true);
3610 LoadCpModel(new_cp_model_proto,
model);
3611 shared_response_manager->LoadDebugSolution(
model);
3614 SOLVER_LOG(logger, absl::StrFormat(
"Starting sequential search at %.2fs",
3616 if (params.repair_hint()) {
3617 MinimizeL1DistanceWithHint(new_cp_model_proto,
model);
3619 QuickSolveWithHint(new_cp_model_proto,
model);
3621 SolveLoadedCpModel(new_cp_model_proto,
model);
3624 if (logger->LoggingIsEnabled()) {
3626 *
model->GetOrCreate<LinearProgrammingConstraintCollection>();
3629 for (
const auto* lp : lps) {
3637 return shared_response_manager->GetResponse();
3646 const SatParameters& params) {
3652#if !defined(__PORTABLE_PLATFORM__)
3654 const std::string& params) {
#define CHECK_NE(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define VLOG(verboselevel)
bool AddEdge(int node1, int node2)
std::vector< int > GetComponentIds()
void SetNumberOfNodes(int num_nodes)
int GetNumberOfComponents() const
We call domain any subset of Int64 = [kint64min, kint64max].
std::string ToString() const
Returns a compact string of a vector of intervals like "[1,4][6][10,20]".
void EnableLogging(bool enable)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Literal(int signed_value)
Class that owns everything related to a particular optimization model.
void Register(T *non_owned_class)
Register a non-owned class that will be "singleton" in the model.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
void set_dump_prefix(const std::string &dump_prefix)
void NewSolution(const CpSolverResponse &response, Model *model)
SharedBoundsManager * bounds
SharedClausesManager * clauses
SharedRelaxationSolutionRepository * relaxation_solutions
SharedLPSolutionRepository * lp_solutions
CpModelProto const * model_proto
SharedIncompleteSolutionManager * incomplete_solutions
ABSL_FLAG(std::string, cp_model_dump_prefix, "/tmp/", "Prefix filename for all dumped files")
SharedResponseManager * response
ModelSharedTimeLimit * time_limit
GurobiMPCallbackContext * context
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
absl::Status SetTextProto(const absl::string_view &filename, const google::protobuf::Message &proto, int flags)
absl::Status Open(const absl::string_view &filename, const absl::string_view &mode, File **f, int flags)
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
bool ContainsKey(const Collection &collection, const Key &key)
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &observer)
Creates a solution observer with the model with model.Add(NewFeasibleSolutionObserver([](response){....
void DeterministicLoop(const std::vector< std::unique_ptr< SubSolver > > &subsolvers, int num_threads, int batch_size)
void DetectAndAddSymmetryToProto(const SatParameters ¶ms, CpModelProto *proto, SolverLogger *logger)
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, const CpSolverResponse &response)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
Creates parameters for the solver, which you can add to the model with.
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
Returns a string with some statistics on the solver response.
bool LoadConstraint(const ConstraintProto &ct, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
bool ConvertCpModelProtoToMPModelProto(const CpModelProto &input, MPModelProto *output)
double UnscaleObjectiveValue(const CpObjectiveProto &proto, double value)
bool RefIsPositive(int ref)
std::string ValidateParameters(const SatParameters ¶ms)
void ExtractElementEncoding(const CpModelProto &model_proto, Model *m)
CpSolverResponse SolveWithParameters(const CpModelProto &model_proto, const std::string ¶ms)
Solves the given CpModelProto with the given sat parameters as string in JSon format,...
std::string ValidateInputCpModel(const SatParameters ¶ms, const CpModelProto &model)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
void PostsolveResponse(const int64_t num_variables_in_original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, std::vector< int64_t > *solution)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
std::function< SatParameters(Model *)> NewSatParameters(const sat::SatParameters ¶meters)
const IntegerVariable kNoIntegerVariable(-1)
std::function< BooleanOrIntegerLiteral()> FollowHint(const std::vector< BooleanOrIntegerVariable > &vars, const std::vector< IntegerValue > &values, Model *model)
void NonDeterministicLoop(const std::vector< std::unique_ptr< SubSolver > > &subsolvers, int num_threads)
std::function< IntegerVariable(Model *)> ConstantIntegerVariable(int64_t value)
double ScaleObjectiveValue(const CpObjectiveProto &proto, int64_t value)
void ConfigureSearchHeuristics(Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
std::string CpModelStats(const CpModelProto &model_proto)
Returns a string with some statistics on the given CpModelProto.
std::vector< SatParameters > GetDiverseSetOfParameters(const SatParameters &base_params, const CpModelProto &cp_model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> ExcludeCurrentSolutionWithoutIgnoredVariableAndBacktrack()
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
CpSolverStatus PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
SatSolver::Status SolveWithPresolve(std::unique_ptr< SatSolver > *solver, TimeLimit *time_limit, std::vector< bool > *solution, DratProofHandler *drat_proof_handler, SolverLogger *logger)
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
bool ImportModelWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
bool SolutionIsFeasible(const CpModelProto &model, const std::vector< int64_t > &variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
std::function< BooleanOrIntegerLiteral()> ConstructSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, IntegerVariable objective_var, Model *model)
LinearRelaxation ComputeLinearRelaxation(const CpModelProto &model_proto, Model *m)
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
std::function< BooleanOrIntegerLiteral()> InstrumentSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, const std::function< BooleanOrIntegerLiteral()> &instrumented_strategy, Model *model)
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
Collection of objects used to extend the Constraint Solver library.
int OrToolsMinorVersion()
std::mt19937 random_engine_t
std::string ProtobufDebugString(const P &message)
int OrToolsMajorVersion()
int OrToolsPatchVersion()
static int input(yyscan_t yyscanner)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< std::function< void(const CpSolverResponse &response)> > observers
SolutionObservers(Model *model)
#define SOLVER_LOG(logger,...)
#define VLOG_IS_ON(verboselevel)