31#if !defined(__PORTABLE_PLATFORM__)
32#include "absl/synchronization/notification.h"
33#include "google/protobuf/text_format.h"
38#include "absl/container/flat_hash_set.h"
39#include "absl/memory/memory.h"
40#include "absl/status/status.h"
41#include "absl/strings/str_cat.h"
42#include "absl/strings/str_format.h"
43#include "absl/strings/str_join.h"
44#include "absl/strings/str_split.h"
45#include "absl/synchronization/mutex.h"
96ABSL_FLAG(std::string, cp_model_dump_prefix,
".\\",
97 "Prefix filename for all dumped files");
99ABSL_FLAG(std::string, cp_model_dump_prefix,
"/tmp/",
100 "Prefix filename for all dumped files");
103 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
104 "protos (original model, presolved model, mapping model) in text "
105 "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|"
106 "mapping_model}.pb.txt.");
109 "DEBUG ONLY. When set to true, solve will dump all "
110 "lns models proto in text format to "
111 "'FLAGS_cp_model_dump_prefix'lns_xxx.pb.txt.");
114 bool, cp_model_dump_problematic_lns,
false,
115 "DEBUG ONLY. Similar to --cp_model_dump_lns, but only dump fragment for "
116 "which we got an issue while validating the postsolved solution. This "
117 "allows to debug presolve issues without dumping all the models.");
120 "DEBUG ONLY. If true, the final response of each solve will be "
121 "dumped to 'FLAGS_cp_model_dump_prefix'response.pb.txt");
124 "This is interpreted as a text SatParameters proto. The "
125 "specified fields will override the normal ones for all solves.");
128 "If non-empty, a proof in DRAT format will be written to this file. "
129 "This will only be used for pure-SAT problems.");
132 "If true, a proof in DRAT format will be stored in memory and "
133 "checked if the problem is UNSAT. This will only be used for "
134 "pure-SAT problems.");
137 std::numeric_limits<double>::infinity(),
138 "Maximum time in seconds to check the DRAT proof. This will only "
139 "be used is the drat_check flag is enabled.");
141ABSL_FLAG(
bool, cp_model_check_intermediate_solutions,
false,
142 "When true, all intermediate solutions found by the solver will be "
143 "checked. This can be expensive, therefore it is off by default.");
146 "If non-empty, dump a contention pprof proto to the specified "
147 "destination at the end of the solve.");
155std::string Summarize(
const std::string&
input) {
158 return absl::StrCat(
input.substr(0, half),
" ... ",
169 std::map<std::string, int> num_constraints_by_name;
170 std::map<std::string, int> num_reif_constraints_by_name;
171 std::map<std::string, int> name_to_num_literals;
172 std::map<std::string, int> name_to_num_terms;
174 int no_overlap_2d_num_rectangles = 0;
175 int no_overlap_2d_num_optional_rectangles = 0;
176 int no_overlap_2d_num_linear_areas = 0;
177 int no_overlap_2d_num_quadratic_areas = 0;
179 int cumulative_num_intervals = 0;
180 int cumulative_num_optional_intervals = 0;
181 int cumulative_num_variable_sizes = 0;
182 int cumulative_num_variable_demands = 0;
184 int no_overlap_num_intervals = 0;
185 int no_overlap_num_optional_intervals = 0;
186 int no_overlap_num_variable_sizes = 0;
193 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
194 if (
ct.linear().vars_size() == 1)
name +=
"1";
195 if (
ct.linear().vars_size() == 2)
name +=
"2";
196 if (
ct.linear().vars_size() == 3)
name +=
"3";
197 if (
ct.linear().vars_size() > 3)
name +=
"N";
200 num_constraints_by_name[
name]++;
201 if (!
ct.enforcement_literal().empty()) {
202 num_reif_constraints_by_name[
name]++;
211 auto interval_has_fixed_size = [&
model_proto, &variable_is_fixed](
int c) {
214 if (
proto.has_size_view()) {
215 for (
const int ref :
proto.size_view().vars()) {
216 if (!variable_is_fixed(ref)) {
222 return variable_is_fixed(
proto.size());
226 auto constraint_is_optional = [&
model_proto](
int i) {
232 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
233 name_to_num_literals[
name] +=
ct.bool_or().literals().size();
234 }
else if (
ct.constraint_case() ==
235 ConstraintProto::ConstraintCase::kBoolAnd) {
236 name_to_num_literals[
name] +=
ct.bool_and().literals().size();
237 }
else if (
ct.constraint_case() ==
238 ConstraintProto::ConstraintCase::kAtMostOne) {
239 name_to_num_literals[
name] +=
ct.at_most_one().literals().size();
240 }
else if (
ct.constraint_case() ==
241 ConstraintProto::ConstraintCase::kExactlyOne) {
242 name_to_num_literals[
name] +=
ct.exactly_one().literals().size();
243 }
else if (
ct.constraint_case() ==
244 ConstraintProto::ConstraintCase::kNoOverlap2D) {
245 const int num_boxes =
ct.no_overlap_2d().x_intervals_size();
246 no_overlap_2d_num_rectangles += num_boxes;
247 for (
int i = 0; i < num_boxes; ++i) {
248 const int x_interval =
ct.no_overlap_2d().x_intervals(i);
249 const int y_interval =
ct.no_overlap_2d().y_intervals(i);
250 if (constraint_is_optional(x_interval) ||
251 constraint_is_optional(y_interval)) {
252 no_overlap_2d_num_optional_rectangles++;
254 const int num_fixed = interval_has_fixed_size(x_interval) +
255 interval_has_fixed_size(y_interval);
256 if (num_fixed == 0) {
257 no_overlap_2d_num_quadratic_areas++;
258 }
else if (num_fixed == 1) {
259 no_overlap_2d_num_linear_areas++;
262 }
else if (
ct.constraint_case() ==
263 ConstraintProto::ConstraintCase::kNoOverlap) {
264 const int num_intervals =
ct.no_overlap().intervals_size();
265 no_overlap_num_intervals += num_intervals;
266 for (
int i = 0; i < num_intervals; ++i) {
267 const int interval =
ct.no_overlap().intervals(i);
268 if (constraint_is_optional(
interval)) {
269 no_overlap_num_optional_intervals++;
271 if (!interval_has_fixed_size(
interval)) {
272 no_overlap_num_variable_sizes++;
275 }
else if (
ct.constraint_case() ==
276 ConstraintProto::ConstraintCase::kCumulative) {
277 const int num_intervals =
ct.cumulative().intervals_size();
278 cumulative_num_intervals += num_intervals;
279 for (
int i = 0; i < num_intervals; ++i) {
280 const int interval =
ct.cumulative().intervals(i);
281 if (constraint_is_optional(
interval)) {
282 cumulative_num_optional_intervals++;
284 if (!interval_has_fixed_size(
interval)) {
285 cumulative_num_variable_sizes++;
287 if (!variable_is_fixed(
ct.cumulative().demands(i))) {
288 cumulative_num_variable_demands++;
293 if (
ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear &&
294 ct.linear().vars_size() > 3) {
295 name_to_num_terms[
name] +=
ct.linear().vars_size();
299 int num_constants = 0;
300 std::set<int64_t> constant_values;
301 std::map<Domain, int> num_vars_per_domains;
303 if (
var.domain_size() == 2 &&
var.domain(0) ==
var.domain(1)) {
305 constant_values.insert(
var.domain(0));
322 &result,
"Search strategy: on ", strategy.variables_size(),
324 ProtoEnumToString<DecisionStrategyProto::VariableSelectionStrategy>(
325 strategy.variable_selection_strategy()),
327 ProtoEnumToString<DecisionStrategyProto::DomainReductionStrategy>(
328 strategy.domain_reduction_strategy()),
332 const std::string objective_string =
338 objective_string,
"\n");
339 if (num_vars_per_domains.size() < 100) {
340 for (
const auto& entry : num_vars_per_domains) {
341 const std::string temp = absl::StrCat(
" - ", entry.second,
" in ",
342 entry.first.ToString(),
"\n");
343 absl::StrAppend(&result, Summarize(temp));
346 int64_t max_complexity = 0;
349 for (
const auto& entry : num_vars_per_domains) {
353 max_complexity,
static_cast<int64_t
>(entry.first.NumIntervals()));
355 absl::StrAppend(&result,
" - ", num_vars_per_domains.size(),
356 " different domains in [",
min,
",",
max,
357 "] with a largest complexity of ", max_complexity,
".\n");
360 if (num_constants > 0) {
361 const std::string temp =
362 absl::StrCat(
" - ", num_constants,
" constants in {",
363 absl::StrJoin(constant_values,
","),
"} \n");
364 absl::StrAppend(&result, Summarize(temp));
367 std::vector<std::string> constraints;
368 constraints.reserve(num_constraints_by_name.size());
369 for (
const auto& entry : num_constraints_by_name) {
370 const std::string&
name = entry.first;
371 constraints.push_back(absl::StrCat(
"#",
name,
": ", entry.second));
373 absl::StrAppend(&constraints.back(),
374 " (#enforced: ", num_reif_constraints_by_name[
name],
")");
377 absl::StrAppend(&constraints.back(),
378 " (#literals: ", name_to_num_literals[
name],
")");
381 absl::StrAppend(&constraints.back(),
382 " (#terms: ", name_to_num_terms[
name],
")");
384 if (
name ==
"kNoOverlap2D") {
385 absl::StrAppend(&constraints.back(),
386 " (#rectangles: ", no_overlap_2d_num_rectangles);
387 if (no_overlap_2d_num_optional_rectangles > 0) {
388 absl::StrAppend(&constraints.back(),
389 ", #optional: ", no_overlap_2d_num_optional_rectangles);
391 if (no_overlap_2d_num_linear_areas > 0) {
392 absl::StrAppend(&constraints.back(),
393 ", #linear_areas: ", no_overlap_2d_num_linear_areas);
395 if (no_overlap_2d_num_quadratic_areas > 0) {
396 absl::StrAppend(&constraints.back(),
", #quadratic_areas: ",
397 no_overlap_2d_num_quadratic_areas);
399 absl::StrAppend(&constraints.back(),
")");
400 }
else if (
name ==
"kCumulative") {
401 absl::StrAppend(&constraints.back(),
402 " (#intervals: ", cumulative_num_intervals);
403 if (cumulative_num_optional_intervals > 0) {
404 absl::StrAppend(&constraints.back(),
405 ", #optional: ", cumulative_num_optional_intervals);
407 if (cumulative_num_variable_sizes > 0) {
408 absl::StrAppend(&constraints.back(),
409 ", #variable_sizes: ", cumulative_num_variable_sizes);
411 if (cumulative_num_variable_demands > 0) {
412 absl::StrAppend(&constraints.back(),
", #variable_demands: ",
413 cumulative_num_variable_demands);
415 absl::StrAppend(&constraints.back(),
")");
416 }
else if (
name ==
"kNoOverlap") {
417 absl::StrAppend(&constraints.back(),
418 " (#intervals: ", no_overlap_num_intervals);
419 if (no_overlap_num_optional_intervals > 0) {
420 absl::StrAppend(&constraints.back(),
421 ", #optional: ", no_overlap_num_optional_intervals);
423 if (no_overlap_num_variable_sizes > 0) {
424 absl::StrAppend(&constraints.back(),
425 ", #variable_sizes: ", no_overlap_num_variable_sizes);
427 absl::StrAppend(&constraints.back(),
")");
430 std::sort(constraints.begin(), constraints.end());
431 absl::StrAppend(&result, absl::StrJoin(constraints,
"\n"));
437 bool has_objective) {
439 absl::StrAppend(&result,
"CpSolverResponse summary:");
440 absl::StrAppend(&result,
"\nstatus: ",
441 ProtoEnumToString<CpSolverStatus>(
response.status()));
444 absl::StrAppendFormat(&result,
"\nobjective: %.16g",
446 absl::StrAppendFormat(&result,
"\nbest_bound: %.16g",
449 absl::StrAppend(&result,
"\nobjective: NA");
450 absl::StrAppend(&result,
"\nbest_bound: NA");
453 absl::StrAppend(&result,
"\nbooleans: ",
response.num_booleans());
454 absl::StrAppend(&result,
"\nconflicts: ",
response.num_conflicts());
455 absl::StrAppend(&result,
"\nbranches: ",
response.num_branches());
459 absl::StrAppend(&result,
460 "\npropagations: ",
response.num_binary_propagations());
462 &result,
"\ninteger_propagations: ",
response.num_integer_propagations());
464 absl::StrAppend(&result,
"\nrestarts: ",
response.num_restarts());
465 absl::StrAppend(&result,
"\nlp_iterations: ",
response.num_lp_iterations());
466 absl::StrAppend(&result,
"\nwalltime: ",
response.wall_time());
467 absl::StrAppend(&result,
"\nusertime: ",
response.user_time());
468 absl::StrAppend(&result,
469 "\ndeterministic_time: ",
response.deterministic_time());
470 absl::StrAppend(&result,
"\nprimal_integral: ",
response.primal_integral());
471 absl::StrAppend(&result,
"\n");
477#if !defined(__PORTABLE_PLATFORM__)
480void FillSolutionInResponse(
const CpModelProto&
model_proto,
const Model&
model,
483 response->clear_solution_lower_bounds();
484 response->clear_solution_upper_bounds();
486 auto* mapping =
model.Get<CpModelMapping>();
488 auto* integer_trail =
model.Get<IntegerTrail>();
490 std::vector<int64_t> solution;
492 if (mapping->IsInteger(i)) {
493 const IntegerVariable
var = mapping->Integer(i);
494 if (integer_trail->IsCurrentlyIgnored(
var)) {
507 DCHECK(mapping->IsBoolean(i));
509 if (trail->Assignment().LiteralIsAssigned(
literal)) {
518 if (!solution.empty()) {
520 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
528 const auto& assignment = trail->Assignment();
530 if (mapping->IsBoolean(i)) {
531 if (assignment.VariableIsAssigned(mapping->Literal(i).Variable())) {
536 response->add_solution_lower_bounds(0);
537 response->add_solution_upper_bounds(1);
540 response->add_solution_lower_bounds(
542 response->add_solution_upper_bounds(
551IntegerVariable GetOrCreateVariableWithTightBound(
552 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
555 if (terms.size() == 1 && terms.front().second == 1) {
556 return terms.front().first;
558 if (terms.size() == 1 && terms.front().second == -1) {
564 for (
const std::pair<IntegerVariable, int64_t> var_coeff : terms) {
567 const int64_t coeff = var_coeff.second;
568 const int64_t prod1 = min_domain * coeff;
569 const int64_t prod2 = max_domain * coeff;
576IntegerVariable GetOrCreateVariableGreaterOrEqualToSumOf(
577 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
580 if (terms.size() == 1 && terms.front().second == 1) {
581 return terms.front().first;
583 if (terms.size() == 1 && terms.front().second == -1) {
588 const IntegerVariable new_var =
589 GetOrCreateVariableWithTightBound(terms,
model);
590 std::vector<IntegerVariable> vars;
591 std::vector<int64_t> coeffs;
592 for (
const auto& term : terms) {
593 vars.push_back(term.first);
594 coeffs.push_back(term.second);
596 vars.push_back(new_var);
597 coeffs.push_back(-1);
605IntegerVariable AddLPConstraints(
const CpModelProto&
model_proto,
606 int linearization_level, Model* m) {
607 LinearRelaxation relaxation;
616 const int num_lp_constraints = relaxation.linear_constraints.size();
617 const int num_lp_cut_generators = relaxation.cut_generators.size();
618 const int num_integer_variables =
619 m->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value();
622 num_integer_variables);
623 auto get_constraint_index = [](
int ct_index) {
return ct_index; };
624 auto get_cut_generator_index = [num_lp_constraints](
int cut_index) {
625 return num_lp_constraints + cut_index;
627 auto get_var_index = [num_lp_constraints,
628 num_lp_cut_generators](IntegerVariable
var) {
629 return num_lp_constraints + num_lp_cut_generators +
var.value();
631 for (
int i = 0; i < num_lp_constraints; i++) {
632 for (
const IntegerVariable
var : relaxation.linear_constraints[i].vars) {
633 components.
AddEdge(get_constraint_index(i), get_var_index(
var));
636 for (
int i = 0; i < num_lp_cut_generators; ++i) {
637 for (
const IntegerVariable
var : relaxation.cut_generators[i].vars) {
638 components.
AddEdge(get_cut_generator_index(i), get_var_index(
var));
646 for (
const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
648 for (
const Literal
literal : at_most_one) {
651 const bool unused ABSL_ATTRIBUTE_UNUSED =
652 builder.AddLiteralTerm(
literal, IntegerValue(1));
654 LinearConstraint lc = builder.Build();
655 for (
int i = 1; i < lc.vars.size(); ++i) {
656 components.
AddEdge(get_var_index(lc.vars[0]), get_var_index(lc.vars[i]));
661 std::vector<int> component_sizes(num_components, 0);
662 const std::vector<int> index_to_component = components.
GetComponentIds();
663 for (
int i = 0; i < num_lp_constraints; i++) {
664 ++component_sizes[index_to_component[get_constraint_index(i)]];
666 for (
int i = 0; i < num_lp_cut_generators; i++) {
667 ++component_sizes[index_to_component[get_cut_generator_index(i)]];
675 auto* mapping = m->GetOrCreate<CpModelMapping>();
677 const IntegerVariable
var =
679 ++component_sizes[index_to_component[get_var_index(
var)]];
683 std::vector<LinearProgrammingConstraint*> lp_constraints(num_components,
685 std::vector<std::vector<LinearConstraint>> component_to_constraints(
687 for (
int i = 0; i < num_lp_constraints; i++) {
688 const int c = index_to_component[get_constraint_index(i)];
689 if (component_sizes[c] <= 1)
continue;
690 component_to_constraints[c].push_back(relaxation.linear_constraints[i]);
691 if (lp_constraints[c] ==
nullptr) {
692 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
695 lp_constraints[c]->AddLinearConstraint(relaxation.linear_constraints[i]);
699 for (
int i = 0; i < num_lp_cut_generators; i++) {
700 const int c = index_to_component[get_cut_generator_index(i)];
701 if (lp_constraints[c] ==
nullptr) {
702 lp_constraints[c] = m->Create<LinearProgrammingConstraint>();
704 lp_constraints[c]->AddCutGenerator(std::move(relaxation.cut_generators[i]));
708 const SatParameters& params = *(m->GetOrCreate<SatParameters>());
709 if (params.add_clique_cuts() && params.linearization_level() > 1) {
710 for (LinearProgrammingConstraint* lp : lp_constraints) {
711 if (lp ==
nullptr)
continue;
716 if (params.add_knapsack_cuts() && params.linearization_level() > 1) {
717 for (
int c = 0; c < num_components; ++c) {
718 if (component_to_constraints[c].empty())
continue;
720 component_to_constraints[c], lp_constraints[c]->integer_variables(),
726 std::vector<std::vector<std::pair<IntegerVariable, int64_t>>>
727 component_to_cp_terms(num_components);
728 std::vector<std::pair<IntegerVariable, int64_t>> top_level_cp_terms;
729 int num_components_containing_objective = 0;
734 const IntegerVariable
var =
737 const int c = index_to_component[get_var_index(
var)];
738 if (lp_constraints[c] !=
nullptr) {
739 lp_constraints[c]->SetObjectiveCoefficient(
var, IntegerValue(coeff));
740 component_to_cp_terms[c].push_back(std::make_pair(
var, coeff));
743 top_level_cp_terms.push_back(std::make_pair(
var, coeff));
747 for (
int c = 0; c < num_components; ++c) {
748 if (component_to_cp_terms[c].empty())
continue;
749 const IntegerVariable sub_obj_var =
750 GetOrCreateVariableGreaterOrEqualToSumOf(component_to_cp_terms[c], m);
751 top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
752 lp_constraints[c]->SetMainObjectiveVariable(sub_obj_var);
753 num_components_containing_objective++;
757 const IntegerVariable main_objective_var =
759 ? GetOrCreateVariableGreaterOrEqualToSumOf(top_level_cp_terms, m)
764 for (LinearProgrammingConstraint* lp_constraint : lp_constraints) {
765 if (lp_constraint ==
nullptr)
continue;
766 lp_constraint->RegisterWith(m);
767 VLOG(3) <<
"LP constraint: " << lp_constraint->DimensionString() <<
".";
770 VLOG(3) << top_level_cp_terms.size()
771 <<
" terms in the main objective linear equation ("
772 << num_components_containing_objective <<
" from LP constraints).";
773 return main_objective_var;
791#if !defined(__PORTABLE_PLATFORM__)
794 const std::string& params) {
796 if (!params.empty()) {
797 CHECK(google::protobuf::TextFormat::ParseFromString(params, &
parameters))
824void RegisterVariableBoundsLevelZeroExport(
825 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
827 CHECK(shared_bounds_manager !=
nullptr);
828 int saved_trail_index = 0;
829 auto broadcast_level_zero_bounds =
831 const std::vector<IntegerVariable>& modified_vars)
mutable {
832 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
834 std::vector<int> model_variables;
835 std::vector<int64_t> new_lower_bounds;
836 std::vector<int64_t> new_upper_bounds;
837 absl::flat_hash_set<int> visited_variables;
840 auto* integer_trail =
model->Get<IntegerTrail>();
841 for (
const IntegerVariable&
var : modified_vars) {
843 const int model_var =
844 mapping->GetProtoVariableFromIntegerVariable(positive_var);
845 if (model_var == -1 || visited_variables.contains(model_var)) {
852 visited_variables.insert(model_var);
853 const int64_t new_lb =
854 integer_trail->LevelZeroLowerBound(positive_var).value();
855 const int64_t new_ub =
856 integer_trail->LevelZeroUpperBound(positive_var).value();
859 model_variables.push_back(model_var);
860 new_lower_bounds.push_back(new_lb);
861 new_upper_bounds.push_back(new_ub);
865 auto* trail =
model->Get<Trail>();
866 for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
867 const Literal fixed_literal = (*trail)[saved_trail_index];
868 const int model_var = mapping->GetProtoVariableFromBooleanVariable(
869 fixed_literal.Variable());
870 if (model_var == -1 || visited_variables.contains(model_var)) {
877 visited_variables.insert(model_var);
878 model_variables.push_back(model_var);
879 if (fixed_literal.IsPositive()) {
880 new_lower_bounds.push_back(1);
881 new_upper_bounds.push_back(1);
883 new_lower_bounds.push_back(0);
884 new_upper_bounds.push_back(0);
888 if (!model_variables.empty()) {
889 shared_bounds_manager->ReportPotentialNewBounds(
894 if (!
model->Get<SatParameters>()->interleave_search()) {
895 shared_bounds_manager->Synchronize();
907 const IntegerVariable num_vars =
908 model->GetOrCreate<IntegerTrail>()->NumIntegerVariables();
909 std::vector<IntegerVariable> all_variables;
910 all_variables.reserve(num_vars.value());
911 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
912 all_variables.push_back(
var);
914 broadcast_level_zero_bounds(all_variables);
916 model->GetOrCreate<GenericLiteralWatcher>()
917 ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
923void RegisterVariableBoundsLevelZeroImport(
924 const CpModelProto&
model_proto, SharedBoundsManager* shared_bounds_manager,
926 CHECK(shared_bounds_manager !=
nullptr);
927 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
928 CpModelMapping*
const mapping =
model->GetOrCreate<CpModelMapping>();
929 const int id = shared_bounds_manager->RegisterNewId();
931 const auto& import_level_zero_bounds = [&
model_proto, shared_bounds_manager,
932 model, integer_trail, id, mapping]() {
933 std::vector<int> model_variables;
934 std::vector<int64_t> new_lower_bounds;
935 std::vector<int64_t> new_upper_bounds;
936 shared_bounds_manager->GetChangedBounds(
937 id, &model_variables, &new_lower_bounds, &new_upper_bounds);
938 bool new_bounds_have_been_imported =
false;
939 for (
int i = 0; i < model_variables.size(); ++i) {
940 const int model_var = model_variables[i];
943 if (!mapping->IsInteger(model_var))
continue;
944 const IntegerVariable
var = mapping->Integer(model_var);
945 const IntegerValue new_lb(new_lower_bounds[i]);
946 const IntegerValue new_ub(new_upper_bounds[i]);
947 const IntegerValue old_lb = integer_trail->LowerBound(
var);
948 const IntegerValue old_ub = integer_trail->UpperBound(
var);
949 const bool changed_lb = new_lb > old_lb;
950 const bool changed_ub = new_ub < old_ub;
951 if (!changed_lb && !changed_ub)
continue;
953 new_bounds_have_been_imported =
true;
955 const IntegerVariableProto& var_proto =
957 const std::string& var_name =
958 var_proto.
name().empty()
959 ? absl::StrCat(
"anonymous_var(", model_var,
")")
961 LOG(
INFO) <<
" '" <<
model->Name() <<
"' imports new bounds for "
962 << var_name <<
": from [" << old_lb <<
", " << old_ub
963 <<
"] to [" << new_lb <<
", " << new_ub <<
"]";
977 if (new_bounds_have_been_imported &&
978 !
model->GetOrCreate<SatSolver>()->FinishPropagation()) {
983 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
984 import_level_zero_bounds);
989void RegisterObjectiveBestBoundExport(
990 IntegerVariable objective_var,
991 SharedResponseManager* shared_response_manager, Model*
model) {
992 auto* integer_trail =
model->Get<IntegerTrail>();
993 const auto broadcast_objective_lower_bound =
994 [objective_var, integer_trail, shared_response_manager,
995 model](
const std::vector<IntegerVariable>& unused) {
996 shared_response_manager->UpdateInnerObjectiveBounds(
997 model->Name(), integer_trail->LevelZeroLowerBound(objective_var),
998 integer_trail->LevelZeroUpperBound(objective_var));
1000 if (!
model->Get<SatParameters>()->interleave_search()) {
1001 shared_response_manager->Synchronize();
1004 model->GetOrCreate<GenericLiteralWatcher>()
1005 ->RegisterLevelZeroModifiedVariablesCallback(
1006 broadcast_objective_lower_bound);
1012void RegisterObjectiveBoundsImport(
1013 SharedResponseManager* shared_response_manager, Model*
model) {
1014 auto* solver =
model->GetOrCreate<SatSolver>();
1015 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1016 auto* objective =
model->GetOrCreate<ObjectiveDefinition>();
1018 const auto import_objective_bounds = [
name, solver, integer_trail, objective,
1019 shared_response_manager]() {
1020 if (solver->AssumptionLevel() != 0)
return true;
1021 bool propagate =
false;
1023 const IntegerValue external_lb =
1024 shared_response_manager->SynchronizedInnerObjectiveLowerBound();
1025 const IntegerValue current_lb =
1026 integer_trail->LowerBound(objective->objective_var);
1027 if (external_lb > current_lb) {
1029 objective->objective_var, external_lb),
1036 const IntegerValue external_ub =
1037 shared_response_manager->SynchronizedInnerObjectiveUpperBound();
1038 const IntegerValue current_ub =
1039 integer_trail->UpperBound(objective->objective_var);
1040 if (external_ub < current_ub) {
1042 objective->objective_var, external_ub),
1049 if (!propagate)
return true;
1051 VLOG(2) <<
"'" <<
name <<
"' imports objective bounds: external ["
1052 << objective->ScaleIntegerObjective(external_lb) <<
", "
1053 << objective->ScaleIntegerObjective(external_ub) <<
"], current ["
1054 << objective->ScaleIntegerObjective(current_lb) <<
", "
1055 << objective->ScaleIntegerObjective(current_ub) <<
"]";
1057 return solver->FinishPropagation();
1060 model->GetOrCreate<LevelZeroCallbackHelper>()->callbacks.push_back(
1061 import_objective_bounds);
1065 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1066 CHECK(shared_response_manager !=
nullptr);
1067 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1070 const auto unsat = [shared_response_manager, sat_solver,
model] {
1071 sat_solver->NotifyThatModelIsUnsat();
1072 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1073 absl::StrCat(
model->Name(),
" [loading]"));
1077 model->GetOrCreate<IntegerEncoder>()->DisableImplicationBetweenLiteral();
1079 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1080 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1081 const bool view_all_booleans_as_integers =
1100 if (sat_solver->IsModelUnsat())
return unsat();
1109 std::set<std::string> unsupported_types;
1110 int num_ignored_constraints = 0;
1112 if (mapping->ConstraintIsAlreadyLoaded(&
ct)) {
1113 ++num_ignored_constraints;
1128 if (sat_solver->FinishPropagation()) {
1129 Trail* trail =
model->GetOrCreate<Trail>();
1130 const int old_num_fixed = trail->Index();
1131 if (trail->Index() > old_num_fixed) {
1132 VLOG(3) <<
"Constraint fixed " << trail->Index() - old_num_fixed
1137 if (sat_solver->IsModelUnsat()) {
1138 VLOG(2) <<
"UNSAT during extraction (after adding '"
1144 if (num_ignored_constraints > 0) {
1145 VLOG(3) << num_ignored_constraints <<
" constraints were skipped.";
1147 if (!unsupported_types.empty()) {
1148 VLOG(1) <<
"There is unsupported constraints types in this model: ";
1149 for (
const std::string& type : unsupported_types) {
1150 VLOG(1) <<
" - " << type;
1155 model->GetOrCreate<IntegerEncoder>()
1156 ->AddAllImplicationsBetweenAssociatedLiterals();
1157 if (!sat_solver->FinishPropagation())
return unsat();
1163 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1164 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1168 LinearRelaxation relaxation;
1171 const int num_lp_constraints = relaxation.linear_constraints.size();
1172 if (num_lp_constraints == 0)
return;
1173 auto* feasibility_pump =
model->GetOrCreate<FeasibilityPump>();
1174 for (
int i = 0; i < num_lp_constraints; i++) {
1175 feasibility_pump->AddLinearConstraint(relaxation.linear_constraints[i]);
1180 const IntegerVariable
var =
1183 feasibility_pump->SetObjectiveCoefficient(
var, IntegerValue(coeff));
1193 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1198 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1199 const auto unsat = [shared_response_manager, sat_solver,
model] {
1200 sat_solver->NotifyThatModelIsUnsat();
1201 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1202 absl::StrCat(
model->Name(),
" [loading]"));
1205 auto* mapping =
model->GetOrCreate<CpModelMapping>();
1206 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1212 if (
model->Mutable<PrecedencesPropagator>() !=
nullptr &&
1214 model->Mutable<PrecedencesPropagator>()
1215 ->AddGreaterThanAtLeastOneOfConstraints(
model);
1216 if (!sat_solver->FinishPropagation())
return unsat();
1223 Prober* prober =
model->GetOrCreate<Prober>();
1224 prober->ProbeBooleanVariables(1.0);
1225 if (
model->GetOrCreate<SatSolver>()->IsModelUnsat()) {
1228 if (!
model->GetOrCreate<BinaryImplicationGraph>()
1229 ->ComputeTransitiveReduction()) {
1243 std::vector<std::pair<IntegerVariable, int64_t>> terms;
1244 terms.reserve(obj.vars_size());
1245 for (
int i = 0; i < obj.vars_size(); ++i) {
1247 std::make_pair(mapping->Integer(obj.vars(i)), obj.coeffs(i)));
1250 objective_var = GetOrCreateVariableWithTightBound(terms,
model);
1252 objective_var = GetOrCreateVariableGreaterOrEqualToSumOf(terms,
model);
1260 auto* objective_definition =
model->GetOrCreate<ObjectiveDefinition>();
1262 objective_definition->scaling_factor = objective_proto.scaling_factor();
1263 if (objective_definition->scaling_factor == 0.0) {
1264 objective_definition->scaling_factor = 1.0;
1266 objective_definition->offset = objective_proto.offset();
1267 objective_definition->objective_var = objective_var;
1269 const int size = objective_proto.vars_size();
1270 objective_definition->vars.resize(size);
1271 objective_definition->coeffs.resize(size);
1272 for (
int i = 0; i < objective_proto.vars_size(); ++i) {
1275 objective_definition->vars[i] = mapping->Integer(objective_proto.vars(i));
1276 objective_definition->coeffs[i] = IntegerValue(objective_proto.coeffs(i));
1279 const int ref = objective_proto.vars(i);
1280 if (mapping->IsInteger(ref)) {
1281 const IntegerVariable
var = mapping->Integer(objective_proto.vars(i));
1282 objective_definition->objective_impacting_variables.insert(
1288 model->TakeOwnership(
1289 new LevelZeroEquality(objective_var, objective_definition->vars,
1290 objective_definition->coeffs,
model));
1296 const Domain automatic_domain =
1297 model->GetOrCreate<IntegerTrail>()->InitialVariableDomain(
1301 VLOG(3) <<
"Automatic internal objective domain: " << automatic_domain;
1302 VLOG(3) <<
"User specified internal objective domain: " << user_domain;
1304 const bool ok =
model->GetOrCreate<IntegerTrail>()->UpdateInitialDomain(
1305 objective_var, user_domain);
1307 VLOG(2) <<
"UNSAT due to the objective domain.";
1318 if (!automatic_domain.IsIncludedIn(user_domain)) {
1319 std::vector<IntegerVariable> vars;
1320 std::vector<int64_t> coeffs;
1322 for (
int i = 0; i < obj.vars_size(); ++i) {
1323 vars.push_back(mapping->Integer(obj.vars(i)));
1324 coeffs.push_back(obj.coeffs(i));
1326 vars.push_back(objective_var);
1327 coeffs.push_back(-1);
1335 "Initial num_bool: ", sat_solver->NumVariables());
1336 if (!sat_solver->FinishPropagation())
return unsat();
1340 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1341 shared_response_manager->UpdateInnerObjectiveBounds(
1342 absl::StrCat(
model->Name(),
" initial_propagation"),
1343 integer_trail->LowerBound(objective_var),
1344 integer_trail->UpperBound(objective_var));
1347 RegisterObjectiveBestBoundExport(objective_var, shared_response_manager,
1353 if (
model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1354 RegisterObjectiveBoundsImport(shared_response_manager,
model);
1360 auto* integer_trail =
model->GetOrCreate<IntegerTrail>();
1361 auto* lp_dispatcher =
model->GetOrCreate<LinearProgrammingDispatcher>();
1362 auto* lp_vars =
model->GetOrCreate<LPVariables>();
1363 IntegerVariable size = integer_trail->NumIntegerVariables();
1364 for (IntegerVariable positive_var(0); positive_var < size;
1365 positive_var += 2) {
1367 lp_var.positive_var = positive_var;
1369 mapping->GetProtoVariableFromIntegerVariable(positive_var);
1372 if (lp_var.model_var >= 0) {
1373 lp_vars->vars.push_back(lp_var);
1374 lp_vars->model_vars_size =
1375 std::max(lp_vars->model_vars_size, lp_var.model_var + 1);
1380 auto* search_heuristics =
model->GetOrCreate<SearchHeuristics>();
1384 search_heuristics->fixed_search =
1386 search_heuristics->fixed_search,
model);
1390 std::vector<BooleanOrIntegerVariable> vars;
1391 std::vector<IntegerValue> values;
1395 BooleanOrIntegerVariable
var;
1396 if (mapping->IsBoolean(ref)) {
1397 var.bool_var = mapping->Literal(ref).Variable();
1399 var.int_var = mapping->Integer(ref);
1401 vars.push_back(
var);
1410 const std::string solution_info =
model->Name();
1412 shared_response_manager]() {
1415 response.set_solution_info(solution_info);
1419 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1420 CoreBasedOptimizer* core =
1421 new CoreBasedOptimizer(objective_var, objective.vars, objective.coeffs,
1422 solution_observer,
model);
1423 model->Register<CoreBasedOptimizer>(core);
1424 model->TakeOwnership(core);
1435 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1436 if (shared_response_manager->ProblemIsSolved())
return;
1438 const std::string& solution_info =
model->Name();
1440 &shared_response_manager]() {
1443 response.set_solution_info(solution_info);
1450 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1452 const SatParameters&
parameters = *
model->GetOrCreate<SatParameters>();
1454 std::vector<BooleanVariable> bool_vars;
1455 std::vector<IntegerVariable> int_vars;
1456 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
1457 absl::flat_hash_set<BooleanVariable> visited;
1459 if (mapping.IsBoolean(v)) {
1460 const BooleanVariable bool_var = mapping.Literal(v).Variable();
1461 if (!visited.contains(bool_var)) {
1462 visited.insert(bool_var);
1463 bool_vars.push_back(bool_var);
1466 IntegerVariable
var = mapping.Integer(v);
1467 if (integer_trail->IsFixed(
var))
continue;
1468 int_vars.push_back(
var);
1477 solution_observer();
1482 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1486 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1491 auto* sat_solver =
model->GetOrCreate<SatSolver>();
1492 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1494 std::vector<int> core_in_proto_format;
1495 for (
const Literal l : core) {
1496 core_in_proto_format.push_back(
1497 mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1498 if (!l.IsPositive()) {
1499 core_in_proto_format.back() =
NegatedRef(core_in_proto_format.back());
1502 shared_response_manager->AddUnsatCore(core_in_proto_format);
1506 const auto& objective = *
model->GetOrCreate<ObjectiveDefinition>();
1507 const IntegerVariable objective_var = objective.objective_var;
1511 auto* search =
model->GetOrCreate<LbTreeSearch>();
1512 status = search->Search(solution_observer);
1518 objective, solution_observer,
model);
1520 status =
model->Mutable<CoreBasedOptimizer>()->Optimize();
1527 solution_observer,
model);
1530 objective_var, solution_observer,
model);
1538 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1545 shared_response_manager->SetStatsFromModel(
model);
1553 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1554 if (shared_response_manager->ProblemIsSolved())
return;
1558 const SatParameters saved_params = *
parameters;
1567 const auto& mapping = *
model->GetOrCreate<CpModelMapping>();
1571 const std::string& solution_info =
model->Name();
1575 response.set_solution_info(absl::StrCat(solution_info,
" [hint]"));
1584 const IntegerVariable objective_var =
1585 model->GetOrCreate<ObjectiveDefinition>()->objective_var;
1586 model->GetOrCreate<SatSolver>()->Backtrack(0);
1587 IntegerTrail* integer_trail =
model->GetOrCreate<IntegerTrail>();
1588 if (!integer_trail->Enqueue(
1591 shared_response_manager->GetInnerObjectiveUpperBound()),
1593 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1594 absl::StrCat(solution_info,
" [hint]"));
1595 shared_response_manager->SetStatsFromModel(
model);
1605void MinimizeL1DistanceWithHint(
const CpModelProto&
model_proto, Model*
model) {
1609 local_model.Register<ModelSharedTimeLimit>(
1610 model->GetOrCreate<ModelSharedTimeLimit>());
1615 auto* shared_response_manager =
model->GetOrCreate<SharedResponseManager>();
1616 if (shared_response_manager->ProblemIsSolved())
return;
1618 auto*
parameters = local_model.GetOrCreate<SatParameters>();
1625 const SatParameters saved_params = *
model->GetOrCreate<SatParameters>();
1640 const int new_var_index = updated_model_proto.variables_size();
1641 IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1643 const int64_t max_domain =
1647 var_proto->add_domain(min_domain);
1648 var_proto->add_domain(max_domain);
1651 ConstraintProto*
const linear_constraint_proto =
1652 updated_model_proto.add_constraints();
1653 LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
1654 linear->add_vars(new_var_index);
1655 linear->add_coeffs(1);
1656 linear->add_vars(
var);
1657 linear->add_coeffs(-1);
1658 linear->add_domain(-
value);
1659 linear->add_domain(-
value);
1662 const int abs_var_index = updated_model_proto.variables_size();
1663 IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
1664 const int64_t abs_min_domain = 0;
1665 const int64_t abs_max_domain =
1666 std::max(std::abs(min_domain), std::abs(max_domain));
1667 abs_var_proto->add_domain(abs_min_domain);
1668 abs_var_proto->add_domain(abs_max_domain);
1669 ConstraintProto*
const abs_constraint_proto =
1670 updated_model_proto.add_constraints();
1671 abs_constraint_proto->mutable_int_max()->set_target(abs_var_index);
1672 abs_constraint_proto->mutable_int_max()->add_vars(new_var_index);
1673 abs_constraint_proto->mutable_int_max()->add_vars(
1676 updated_model_proto.mutable_objective()->add_vars(abs_var_index);
1677 updated_model_proto.mutable_objective()->add_coeffs(1);
1680 auto* local_response_manager =
1681 local_model.GetOrCreate<SharedResponseManager>();
1682 local_response_manager->InitializeObjective(updated_model_proto);
1685 LoadCpModel(updated_model_proto, &local_model);
1688 const auto& mapping = *local_model.GetOrCreate<CpModelMapping>();
1690 mapping.Literals(updated_model_proto.assumptions()), &local_model);
1692 const std::string& solution_info =
model->Name();
1697 CpSolverResponse updated_response;
1698 FillSolutionInResponse(updated_model_proto, local_model,
1700 LOG(
INFO) <<
"Found solution with repaired hint penalty = "
1704 response.set_solution_info(absl::StrCat(solution_info,
" [repaired]"));
1713void PostsolveResponseWithFullSolver(
1714 const int64_t num_variables_in_original_model, CpModelProto mapping_proto,
1723 if (mapping_proto.variables_size() == 0) {
1728 for (
int i = 0; i <
response->solution_size(); ++i) {
1729 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1730 var_proto->clear_domain();
1731 var_proto->add_domain(
response->solution(i));
1732 var_proto->add_domain(
response->solution(i));
1734 for (
int i = 0; i <
response->solution_lower_bounds_size(); ++i) {
1735 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[i]);
1738 .IntersectionWith({
response->solution_lower_bounds(i),
1739 response->solution_upper_bounds(i)}),
1746 Model postsolve_model;
1749 SatParameters& params = *postsolve_model.GetOrCreate<SatParameters>();
1750 params.set_linearization_level(0);
1751 params.set_cp_model_probing_level(0);
1754 LoadCpModel(mapping_proto, &postsolve_model);
1755 SolveLoadedCpModel(mapping_proto, &postsolve_model);
1756 const CpSolverResponse postsolve_response =
1757 postsolve_model.GetOrCreate<SharedResponseManager>()->GetResponse();
1764 response->clear_solution_lower_bounds();
1765 response->clear_solution_upper_bounds();
1766 if (!postsolve_response.solution().empty()) {
1767 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1768 response->add_solution(postsolve_response.solution(i));
1771 for (
int i = 0; i < num_variables_in_original_model; ++i) {
1772 response->add_solution_lower_bounds(
1773 postsolve_response.solution_lower_bounds(i));
1774 response->add_solution_upper_bounds(
1775 postsolve_response.solution_upper_bounds(i));
1780void PostsolveResponseWrapper(
const SatParameters& params,
1781 const int64_t num_variables_in_original_model,
1782 const CpModelProto& mapping_proto,
1783 const std::vector<int>& postsolve_mapping,
1786 if (params.cp_model_postsolve_with_full_solver()) {
1787 PostsolveResponseWithFullSolver(num_variables_in_original_model,
1788 mapping_proto, postsolve_mapping,
1797CpSolverResponse SolvePureSatModel(
const CpModelProto&
model_proto,
1799 SolverLogger* logger) {
1800 std::unique_ptr<SatSolver> solver(
new SatSolver());
1803 model->GetOrCreate<TimeLimit>()->ResetLimitFromParameters(
parameters);
1806 std::unique_ptr<DratProofHandler> drat_proof_handler;
1807#if !defined(__PORTABLE_PLATFORM__)
1808 if (!absl::GetFlag(FLAGS_drat_output).empty() ||
1809 absl::GetFlag(FLAGS_drat_check)) {
1810 if (!absl::GetFlag(FLAGS_drat_output).empty()) {
1814 drat_proof_handler = absl::make_unique<DratProofHandler>(
1815 false, output, absl::GetFlag(FLAGS_drat_check));
1817 drat_proof_handler = absl::make_unique<DratProofHandler>();
1819 solver->SetDratProofHandler(drat_proof_handler.get());
1823 auto get_literal = [](
int ref) {
1824 if (ref >= 0)
return Literal(BooleanVariable(ref),
true);
1825 return Literal(BooleanVariable(
NegatedRef(ref)),
false);
1828 std::vector<Literal> temp;
1830 solver->SetNumVariables(num_variables);
1831 if (drat_proof_handler !=
nullptr) {
1832 drat_proof_handler->SetNumVariables(num_variables);
1836 for (
int ref = 0; ref < num_variables; ++ref) {
1838 if (domain.IsFixed()) {
1839 const Literal ref_literal =
1840 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1841 drat_proof_handler->AddProblemClause({ref_literal});
1845 switch (
ct.constraint_case()) {
1846 case ConstraintProto::ConstraintCase::kBoolAnd: {
1847 if (
ct.enforcement_literal_size() == 0) {
1848 for (
const int ref :
ct.bool_and().literals()) {
1849 drat_proof_handler->AddProblemClause({get_literal(ref)});
1853 const Literal not_a =
1854 get_literal(
ct.enforcement_literal(0)).Negated();
1855 for (
const int ref :
ct.bool_and().literals()) {
1856 drat_proof_handler->AddProblemClause({not_a, get_literal(ref)});
1861 case ConstraintProto::ConstraintCase::kBoolOr:
1863 for (
const int ref :
ct.bool_or().literals()) {
1864 temp.push_back(get_literal(ref));
1866 for (
const int ref :
ct.enforcement_literal()) {
1867 temp.push_back(get_literal(ref).Negated());
1869 drat_proof_handler->AddProblemClause(temp);
1878 switch (
ct.constraint_case()) {
1879 case ConstraintProto::ConstraintCase::kBoolAnd: {
1880 if (
ct.enforcement_literal_size() == 0) {
1881 for (
const int ref :
ct.bool_and().literals()) {
1882 const Literal
b = get_literal(ref);
1883 solver->AddUnitClause(
b);
1887 const Literal not_a =
1888 get_literal(
ct.enforcement_literal(0)).Negated();
1889 for (
const int ref :
ct.bool_and().literals()) {
1890 const Literal
b = get_literal(ref);
1891 solver->AddProblemClause({not_a,
b});
1896 case ConstraintProto::ConstraintCase::kBoolOr:
1898 for (
const int ref :
ct.bool_or().literals()) {
1899 temp.push_back(get_literal(ref));
1901 for (
const int ref :
ct.enforcement_literal()) {
1902 temp.push_back(get_literal(ref).Negated());
1904 solver->AddProblemClause(temp);
1912 for (
int ref = 0; ref < num_variables; ++ref) {
1914 if (domain.Min() == domain.Max()) {
1915 const Literal ref_literal =
1916 domain.Min() == 0 ? get_literal(ref).Negated() : get_literal(ref);
1917 solver->AddUnitClause(ref_literal);
1924 std::vector<bool> solution;
1926 &solution, drat_proof_handler.get(), logger);
1929 for (
int ref = 0; ref < num_variables; ++ref) {
1930 response.add_solution(solution[ref]);
1934 status = solver->SolveWithTimeLimit(
model->GetOrCreate<TimeLimit>());
1937 for (
int ref = 0; ref < num_variables; ++ref) {
1939 solver->Assignment().LiteralIsTrue(get_literal(ref)) ? 1 : 0);
1946 model->GetOrCreate<TimeLimit>()->AdvanceDeterministicTime(
1947 solver->model()->GetOrCreate<TimeLimit>()->GetElapsedDeterministicTime());
1966 LOG(
FATAL) <<
"Unexpected SatSolver::Status " << status;
1968 response.set_num_booleans(solver->NumVariables());
1969 response.set_num_branches(solver->num_branches());
1970 response.set_num_conflicts(solver->num_failures());
1971 response.set_num_binary_propagations(solver->num_propagations());
1972 response.set_num_integer_propagations(0);
1975 model->Get<TimeLimit>()->GetElapsedDeterministicTime());
1981 absl::GetFlag(FLAGS_max_drat_time_in_seconds));
1982 switch (drat_status) {
1984 LOG(
INFO) <<
"DRAT status: UNKNOWN";
1987 LOG(
INFO) <<
"DRAT status: VALID";
1990 LOG(
ERROR) <<
"DRAT status: INVALID";
1996 LOG(
INFO) <<
"DRAT wall time: " << drat_timer.
Get();
1997 }
else if (drat_proof_handler !=
nullptr) {
2000 LOG(
INFO) <<
"DRAT status: NA";
2001 LOG(
INFO) <<
"DRAT wall time: NA";
2002 LOG(
INFO) <<
"DRAT user time: NA";
2007#if !defined(__PORTABLE_PLATFORM__)
2010struct SharedClasses {
2021 bool SearchIsDone() {
2022 if (
response->ProblemIsSolved())
return true;
2029class FullProblemSolver :
public SubSolver {
2031 FullProblemSolver(
const std::string&
name,
2032 const SatParameters& local_parameters,
bool split_in_chunks,
2033 SharedClasses* shared)
2036 split_in_chunks_(split_in_chunks),
2037 local_model_(
absl::make_unique<Model>(
name)) {
2039 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2040 shared_->time_limit->UpdateLocalLimit(
2041 local_model_->GetOrCreate<TimeLimit>());
2043 if (shared->response !=
nullptr) {
2044 local_model_->Register<SharedResponseManager>(shared->response);
2047 if (shared->relaxation_solutions !=
nullptr) {
2048 local_model_->Register<SharedRelaxationSolutionRepository>(
2049 shared->relaxation_solutions);
2052 if (shared->lp_solutions !=
nullptr) {
2053 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2056 if (shared->incomplete_solutions !=
nullptr) {
2057 local_model_->Register<SharedIncompleteSolutionManager>(
2058 shared->incomplete_solutions);
2062 bool TaskIsAvailable()
override {
2063 if (shared_->SearchIsDone())
return false;
2065 absl::MutexLock mutex_lock(&mutex_);
2066 return previous_task_is_completed_;
2069 std::function<void()> GenerateTask(int64_t task_id)
override {
2071 absl::MutexLock mutex_lock(&mutex_);
2072 previous_task_is_completed_ =
false;
2075 if (solving_first_chunk_) {
2076 LoadCpModel(*shared_->model_proto, local_model_.get());
2082 if (shared_->bounds !=
nullptr) {
2083 RegisterVariableBoundsLevelZeroExport(
2084 *shared_->model_proto, shared_->bounds, local_model_.get());
2085 RegisterVariableBoundsLevelZeroImport(
2086 *shared_->model_proto, shared_->bounds, local_model_.get());
2089 if (local_model_->GetOrCreate<SatParameters>()->repair_hint()) {
2090 MinimizeL1DistanceWithHint(*shared_->model_proto, local_model_.get());
2092 QuickSolveWithHint(*shared_->model_proto, local_model_.get());
2096 solving_first_chunk_ =
false;
2098 if (split_in_chunks_) {
2100 absl::MutexLock mutex_lock(&mutex_);
2101 previous_task_is_completed_ =
true;
2106 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2107 if (split_in_chunks_) {
2110 auto* params = local_model_->GetOrCreate<SatParameters>();
2111 params->set_max_deterministic_time(1);
2112 time_limit->ResetLimitFromParameters(*params);
2113 shared_->time_limit->UpdateLocalLimit(
time_limit);
2116 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2117 SolveLoadedCpModel(*shared_->model_proto, local_model_.get());
2119 absl::MutexLock mutex_lock(&mutex_);
2120 deterministic_time_since_last_synchronize_ +=
2121 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2125 if (shared_->SearchIsDone()) {
2126 shared_->time_limit->Stop();
2131 if (split_in_chunks_) {
2132 absl::MutexLock mutex_lock(&mutex_);
2133 previous_task_is_completed_ =
true;
2141 local_model_.reset();
2148 void Synchronize()
override {
2149 absl::MutexLock mutex_lock(&mutex_);
2150 deterministic_time_ += deterministic_time_since_last_synchronize_;
2151 shared_->time_limit->AdvanceDeterministicTime(
2152 deterministic_time_since_last_synchronize_);
2153 deterministic_time_since_last_synchronize_ = 0.0;
2156 std::string StatisticsString()
const override {
2160 if (local_model_ ==
nullptr)
return std::string();
2163 *local_model_->GetOrCreate<LinearProgrammingConstraintCollection>();
2164 std::string lp_stats;
2166 local_model_->GetOrCreate<SatParameters>()->linearization_level() >=
2168 for (
const auto* lp : lps) {
2169 const std::string raw_statistics = lp->Statistics();
2170 const std::vector<absl::string_view> lines =
2171 absl::StrSplit(raw_statistics,
'\n', absl::SkipEmpty());
2172 for (
const absl::string_view& line : lines) {
2173 absl::StrAppend(&lp_stats,
" ", line,
"\n");
2181 SharedClasses* shared_;
2182 const bool split_in_chunks_;
2183 std::unique_ptr<Model> local_model_;
2187 bool solving_first_chunk_ =
true;
2190 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2192 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2195class FeasibilityPumpSolver :
public SubSolver {
2197 FeasibilityPumpSolver(
const SatParameters& local_parameters,
2198 SharedClasses* shared)
2199 : SubSolver(
"feasibility_pump"),
2201 local_model_(
absl::make_unique<Model>(name_)) {
2203 *(local_model_->GetOrCreate<SatParameters>()) = local_parameters;
2204 shared_->time_limit->UpdateLocalLimit(
2205 local_model_->GetOrCreate<TimeLimit>());
2207 if (shared->response !=
nullptr) {
2208 local_model_->Register<SharedResponseManager>(shared->response);
2211 if (shared->relaxation_solutions !=
nullptr) {
2212 local_model_->Register<SharedRelaxationSolutionRepository>(
2213 shared->relaxation_solutions);
2216 if (shared->lp_solutions !=
nullptr) {
2217 local_model_->Register<SharedLPSolutionRepository>(shared->lp_solutions);
2220 if (shared->incomplete_solutions !=
nullptr) {
2221 local_model_->Register<SharedIncompleteSolutionManager>(
2222 shared->incomplete_solutions);
2226 if (shared_->bounds !=
nullptr) {
2227 RegisterVariableBoundsLevelZeroImport(
2228 *shared_->model_proto, shared_->bounds, local_model_.get());
2232 bool TaskIsAvailable()
override {
2233 if (shared_->SearchIsDone())
return false;
2234 absl::MutexLock mutex_lock(&mutex_);
2235 return previous_task_is_completed_;
2238 std::function<void()> GenerateTask(int64_t task_id)
override {
2241 absl::MutexLock mutex_lock(&mutex_);
2242 if (!previous_task_is_completed_)
return;
2243 previous_task_is_completed_ =
false;
2246 absl::MutexLock mutex_lock(&mutex_);
2247 if (solving_first_chunk_) {
2248 LoadFeasibilityPump(*shared_->model_proto, local_model_.get());
2251 if (local_model_->Get<FeasibilityPump>() ==
nullptr)
return;
2252 solving_first_chunk_ =
false;
2254 previous_task_is_completed_ =
true;
2259 auto*
time_limit = local_model_->GetOrCreate<TimeLimit>();
2260 const double saved_dtime =
time_limit->GetElapsedDeterministicTime();
2261 auto* feasibility_pump = local_model_->Mutable<FeasibilityPump>();
2262 if (!feasibility_pump->Solve()) {
2263 shared_->response->NotifyThatImprovingProblemIsInfeasible(name_);
2267 absl::MutexLock mutex_lock(&mutex_);
2268 deterministic_time_since_last_synchronize_ +=
2269 time_limit->GetElapsedDeterministicTime() - saved_dtime;
2273 if (shared_->SearchIsDone()) {
2274 shared_->time_limit->Stop();
2278 absl::MutexLock mutex_lock(&mutex_);
2279 previous_task_is_completed_ =
true;
2283 void Synchronize()
override {
2284 absl::MutexLock mutex_lock(&mutex_);
2285 deterministic_time_ += deterministic_time_since_last_synchronize_;
2286 shared_->time_limit->AdvanceDeterministicTime(
2287 deterministic_time_since_last_synchronize_);
2288 deterministic_time_since_last_synchronize_ = 0.0;
2294 SharedClasses* shared_;
2295 std::unique_ptr<Model> local_model_;
2301 bool solving_first_chunk_ ABSL_GUARDED_BY(mutex_) =
true;
2303 double deterministic_time_since_last_synchronize_ ABSL_GUARDED_BY(mutex_) =
2305 bool previous_task_is_completed_ ABSL_GUARDED_BY(mutex_) =
true;
2309class LnsSolver :
public SubSolver {
2311 LnsSolver(std::unique_ptr<NeighborhoodGenerator> generator,
2313 NeighborhoodGeneratorHelper* helper, SharedClasses* shared)
2314 : SubSolver(generator->
name()),
2315 generator_(
std::move(generator)),
2320 bool TaskIsAvailable()
override {
2321 if (shared_->SearchIsDone())
return false;
2322 return generator_->ReadyToGenerate();
2325 std::function<void()> GenerateTask(int64_t task_id)
override {
2326 return [task_id,
this]() {
2327 if (shared_->SearchIsDone())
return;
2332 const int32_t low =
static_cast<int32_t
>(task_id);
2333 const int32_t high =
static_cast<int32_t
>(task_id >> 32);
2334 std::seed_seq seed{low, high, parameters_.random_seed()};
2337 NeighborhoodGenerator::SolveData data;
2338 data.difficulty = generator_->difficulty();
2339 data.deterministic_limit = generator_->deterministic_limit();
2342 CpSolverResponse base_response;
2344 const SharedSolutionRepository<int64_t>& repo =
2345 shared_->response->SolutionsRepository();
2346 if (repo.NumSolutions() > 0) {
2348 const SharedSolutionRepository<int64_t>::Solution solution =
2349 repo.GetRandomBiasedSolution(random);
2350 for (
const int64_t
value : solution.variable_values) {
2351 base_response.add_solution(
value);
2355 data.initial_best_objective = repo.GetSolution(0).rank;
2356 data.base_objective = solution.rank;
2365 data.initial_best_objective =
2366 shared_->response->GetInnerObjectiveUpperBound();
2367 data.base_objective = data.initial_best_objective;
2371 Neighborhood neighborhood =
2372 generator_->Generate(base_response, data.difficulty, random);
2374 if (!neighborhood.is_generated)
return;
2376 data.neighborhood_id = neighborhood.id;
2378 const int64_t num_calls =
std::max(int64_t{1}, generator_->num_calls());
2379 const double fully_solved_proportion =
2380 static_cast<double>(generator_->num_fully_solved_calls()) /
2381 static_cast<double>(num_calls);
2382 std::string source_info =
name();
2383 if (!neighborhood.source_info.empty()) {
2384 absl::StrAppend(&source_info,
"_", neighborhood.source_info);
2386 const std::string solution_info = absl::StrFormat(
2387 "%s(d=%0.2f s=%i t=%0.2f p=%0.2f)", source_info, data.difficulty,
2388 task_id, data.deterministic_limit, fully_solved_proportion);
2390 SatParameters local_params(parameters_);
2391 local_params.set_max_deterministic_time(data.deterministic_limit);
2392 local_params.set_stop_after_first_solution(
false);
2393 local_params.set_log_search_progress(
false);
2394 local_params.set_cp_model_probing_level(0);
2395 local_params.set_symmetry_level(0);
2396 local_params.set_solution_pool_size(1);
2398 Model local_model(solution_info);
2399 *(local_model.GetOrCreate<SatParameters>()) = local_params;
2400 TimeLimit* local_time_limit = local_model.GetOrCreate<TimeLimit>();
2401 local_time_limit->ResetLimitFromParameters(local_params);
2402 shared_->time_limit->UpdateLocalLimit(local_time_limit);
2405 CpModelProto lns_fragment;
2406 CpModelProto mapping_proto;
2407 auto context = absl::make_unique<PresolveContext>(
2408 &local_model, &lns_fragment, &mapping_proto);
2410 *lns_fragment.mutable_variables() = neighborhood.delta.variables();
2412 ModelCopy copier(
context.get());
2415 if (!copier.ImportAndSimplifyConstraints(
2416 helper_->ModelProto(), neighborhood.constraints_to_ignore)) {
2421 if (!neighborhood.delta.constraints().empty() &&
2422 !copier.ImportAndSimplifyConstraints(neighborhood.delta, {})) {
2429 helper_->ModelProto(),
context.get());
2430 lns_fragment.set_name(absl::StrCat(
"lns_", task_id));
2433 if (neighborhood.delta.has_solution_hint()) {
2434 *lns_fragment.mutable_solution_hint() =
2435 neighborhood.delta.solution_hint();
2438 CpModelProto debug_copy;
2439 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
2442 debug_copy = lns_fragment;
2445#if !defined(__PORTABLE_PLATFORM__)
2448 if (absl::GetFlag(FLAGS_cp_model_dump_lns)) {
2450 const std::string lns_name =
2451 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2452 lns_fragment.name(),
".pb.txt");
2453 LOG(
INFO) <<
"Dumping LNS model to '" << lns_name <<
"'.";
2457 std::vector<int> postsolve_mapping;
2462 neighborhood.delta.Clear();
2467 auto* local_response_manager =
2468 local_model.GetOrCreate<SharedResponseManager>();
2469 local_response_manager->InitializeObjective(lns_fragment);
2470 LoadCpModel(lns_fragment, &local_model);
2471 QuickSolveWithHint(lns_fragment, &local_model);
2472 SolveLoadedCpModel(lns_fragment, &local_model);
2473 CpSolverResponse local_response = local_response_manager->GetResponse();
2477 PostsolveResponseWrapper(
2478 local_params, helper_->ModelProto().variables_size(), mapping_proto,
2479 postsolve_mapping, shared_->wall_timer, &local_response);
2480 data.status = local_response.status();
2481 data.deterministic_time = local_time_limit->GetElapsedDeterministicTime();
2483 if (generator_->IsRelaxationGenerator()) {
2484 bool has_feasible_solution =
false;
2487 has_feasible_solution =
true;
2491 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2492 local_response.solution_info());
2495 if (shared_->model_proto->has_objective()) {
2498 const IntegerValue current_obj_lb =
2499 shared_->response->GetInnerObjectiveLowerBound();
2501 const IntegerValue local_obj_lb =
2502 local_response_manager->GetInnerObjectiveLowerBound();
2505 lns_fragment.objective(), local_obj_lb.value());
2508 const IntegerValue new_inner_obj_lb = IntegerValue(
2510 scaled_local_obj_bound) -
2512 data.new_objective_bound = new_inner_obj_lb;
2513 data.initial_best_objective_bound = current_obj_lb;
2514 if (new_inner_obj_lb > current_obj_lb) {
2515 shared_->response->UpdateInnerObjectiveBounds(
2522 if (has_feasible_solution) {
2524 *shared_->model_proto,
2525 std::vector<int64_t>(local_response.solution().begin(),
2526 local_response.solution().end()))) {
2527 shared_->response->NewSolution(local_response,
2532 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2533 local_response.solution_info());
2534 shared_->time_limit->Stop();
2537 shared_->relaxation_solutions->NewRelaxationSolution(local_response);
2540 if (!local_response.solution().empty()) {
2547 *shared_->model_proto,
2548 std::vector<int64_t>(local_response.solution().begin(),
2549 local_response.solution().end()));
2551 if (absl::GetFlag(FLAGS_cp_model_dump_problematic_lns)) {
2552 const std::string
name =
2553 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
2554 debug_copy.name(),
".pb.txt");
2555 LOG(
INFO) <<
"Dumping problematic LNS model to '" <<
name <<
"'.";
2558 LOG(
FATAL) <<
"Infeasible LNS solution! " << solution_info
2559 <<
" solved with params "
2560 << local_params.ShortDebugString();
2565 data.new_objective = data.base_objective;
2569 shared_->model_proto->objective(), local_response));
2575 shared_->response->NewSolution(local_response,
2578 if (!neighborhood.is_reduced &&
2581 shared_->response->NotifyThatImprovingProblemIsInfeasible(
2582 local_response.solution_info());
2583 shared_->time_limit->Stop();
2587 generator_->AddSolveData(data);
2590 const int64_t total_num_calls = task_id;
2591 VLOG(2) <<
name() <<
": [difficulty: " << data.difficulty
2592 <<
", id: " << task_id
2593 <<
", deterministic_time: " << data.deterministic_time <<
" / "
2594 << data.deterministic_limit
2595 <<
", status: " << ProtoEnumToString<CpSolverStatus>(data.status)
2596 <<
", num calls: " << generator_->num_calls()
2597 <<
", UCB1 Score: " << generator_->GetUCBScore(total_num_calls)
2598 <<
", p: " << fully_solved_proportion <<
"]";
2602 void Synchronize()
override {
2603 generator_->Synchronize();
2604 const double old = deterministic_time_;
2605 deterministic_time_ = generator_->deterministic_time();
2606 shared_->time_limit->AdvanceDeterministicTime(deterministic_time_ - old);
2612 std::unique_ptr<NeighborhoodGenerator> generator_;
2613 NeighborhoodGeneratorHelper* helper_;
2614 const SatParameters parameters_;
2615 SharedClasses* shared_;
2618void SolveCpModelParallel(
const CpModelProto&
model_proto,
2623 <<
"Enumerating all solutions in parallel is not supported.";
2625 std::unique_ptr<SharedBoundsManager> shared_bounds_manager;
2627 shared_bounds_manager = absl::make_unique<SharedBoundsManager>(
model_proto);
2630 std::unique_ptr<SharedRelaxationSolutionRepository>
2631 shared_relaxation_solutions;
2633 shared_relaxation_solutions =
2634 absl::make_unique<SharedRelaxationSolutionRepository>(
2637 shared_relaxation_solutions.get());
2640 auto shared_lp_solutions = absl::make_unique<SharedLPSolutionRepository>(
2646 std::unique_ptr<SharedIncompleteSolutionManager> shared_incomplete_solutions;
2651 if (use_feasibility_pump) {
2652 shared_incomplete_solutions =
2653 absl::make_unique<SharedIncompleteSolutionManager>();
2655 shared_incomplete_solutions.get());
2658 SharedClasses shared;
2662 shared.bounds = shared_bounds_manager.get();
2664 shared.relaxation_solutions = shared_relaxation_solutions.get();
2665 shared.lp_solutions = shared_lp_solutions.get();
2666 shared.incomplete_solutions = shared_incomplete_solutions.get();
2670 std::vector<std::unique_ptr<SubSolver>> subsolvers;
2673 subsolvers.push_back(absl::make_unique<SynchronizationPoint>([&shared]() {
2674 shared.response->Synchronize();
2675 shared.response->MutableSolutionsRepository()->Synchronize();
2676 if (shared.bounds !=
nullptr) {
2677 shared.bounds->Synchronize();
2679 if (shared.relaxation_solutions !=
nullptr) {
2680 shared.relaxation_solutions->Synchronize();
2682 if (shared.lp_solutions !=
nullptr) {
2683 shared.lp_solutions->Synchronize();
2693 local_params.set_linearization_level(0);
2694 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2695 "first_solution", local_params,
2703 subsolvers.push_back(absl::make_unique<FullProblemSolver>(
2704 local_params.name(), local_params,
2710 if (use_feasibility_pump) {
2711 subsolvers.push_back(
2712 absl::make_unique<FeasibilityPumpSolver>(
parameters, &shared));
2719 auto unique_helper = absl::make_unique<NeighborhoodGeneratorHelper>(
2722 NeighborhoodGeneratorHelper* helper = unique_helper.get();
2723 subsolvers.push_back(std::move(unique_helper));
2726 std::vector<SatParameters> lns_params = {
parameters};
2727 lns_params.back().
set_name(
"default");
2729 std::vector<SatParameters> lns_params =
2732 for (
const SatParameters& local_params : lns_params) {
2737 subsolvers.push_back(absl::make_unique<LnsSolver>(
2738 absl::make_unique<RelaxRandomVariablesGenerator>(
2739 helper, absl::StrCat(
"rnd_var_lns_", local_params.name())),
2740 local_params, helper, &shared));
2741 subsolvers.push_back(absl::make_unique<LnsSolver>(
2742 absl::make_unique<RelaxRandomConstraintsGenerator>(
2743 helper, absl::StrCat(
"rnd_cst_lns_", local_params.name())),
2744 local_params, helper, &shared));
2745 subsolvers.push_back(absl::make_unique<LnsSolver>(
2746 absl::make_unique<VariableGraphNeighborhoodGenerator>(
2747 helper, absl::StrCat(
"graph_var_lns_", local_params.name())),
2748 local_params, helper, &shared));
2749 subsolvers.push_back(absl::make_unique<LnsSolver>(
2750 absl::make_unique<ConstraintGraphNeighborhoodGenerator>(
2751 helper, absl::StrCat(
"graph_cst_lns_", local_params.name())),
2752 local_params, helper, &shared));
2759 subsolvers.push_back(absl::make_unique<LnsSolver>(
2760 absl::make_unique<SchedulingTimeWindowNeighborhoodGenerator>(
2761 helper, absl::StrCat(
"scheduling_time_window_lns_",
2762 local_params.name())),
2763 local_params, helper, &shared));
2764 subsolvers.push_back(absl::make_unique<LnsSolver>(
2765 absl::make_unique<SchedulingNeighborhoodGenerator>(
2767 absl::StrCat(
"scheduling_random_lns_", local_params.name())),
2768 local_params, helper, &shared));
2772 const int num_circuit =
2774 const int num_routes =
2776 if (num_circuit + num_routes > 0) {
2777 subsolvers.push_back(absl::make_unique<LnsSolver>(
2778 absl::make_unique<RoutingRandomNeighborhoodGenerator>(
2779 helper, absl::StrCat(
"routing_random_lns_", local_params.name())),
2780 local_params, helper, &shared));
2782 subsolvers.push_back(absl::make_unique<LnsSolver>(
2783 absl::make_unique<RoutingPathNeighborhoodGenerator>(
2784 helper, absl::StrCat(
"routing_path_lns_", local_params.name())),
2785 local_params, helper, &shared));
2787 if (num_routes > 0 || num_circuit > 1) {
2788 subsolvers.push_back(absl::make_unique<LnsSolver>(
2789 absl::make_unique<RoutingFullPathNeighborhoodGenerator>(
2791 absl::StrCat(
"routing_full_path_lns_", local_params.name())),
2792 local_params, helper, &shared));
2804 subsolvers.push_back(absl::make_unique<LnsSolver>(
2805 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2806 helper, shared.response, shared.relaxation_solutions,
2807 shared.lp_solutions,
nullptr,
2808 absl::StrCat(
"rins_lns_", local_params.name())),
2809 local_params, helper, &shared));
2812 subsolvers.push_back(absl::make_unique<LnsSolver>(
2813 absl::make_unique<RelaxationInducedNeighborhoodGenerator>(
2814 helper,
nullptr, shared.relaxation_solutions,
2815 shared.lp_solutions, shared.incomplete_solutions,
2816 absl::StrCat(
"rens_lns_", local_params.name())),
2817 local_params, helper, &shared));
2821 subsolvers.push_back(absl::make_unique<LnsSolver>(
2823 ConsecutiveConstraintsRelaxationNeighborhoodGenerator>(
2824 helper, absl::StrCat(
"rnd_rel_lns_", local_params.name())),
2825 local_params, helper, &shared));
2827 subsolvers.push_back(absl::make_unique<LnsSolver>(
2828 absl::make_unique<WeightedRandomRelaxationNeighborhoodGenerator>(
2829 helper, absl::StrCat(
"wgt_rel_lns_", local_params.name())),
2830 local_params, helper, &shared));
2837 subsolvers.push_back(absl::make_unique<SynchronizationPoint>(
2838 [&shared]() { shared.response->UpdatePrimalIntegral(); }));
2842 if (logger->LoggingIsEnabled()) {
2843 std::vector<std::string> names;
2844 for (
const auto& subsolver : subsolvers) {
2845 if (!subsolver->name().empty()) names.push_back(subsolver->name());
2849 absl::StrFormat(
"Starting Search at %.2fs with %i "
2850 "workers and subsolvers: [ %s ]",
2851 shared.wall_timer->Get(), num_search_workers,
2852 absl::StrJoin(names,
", ")));
2865 SOLVER_LOG(logger,
"Sub-solver search statistics:");
2866 for (
const auto& subsolver : subsolvers) {
2867 const std::string stats = subsolver->StatisticsString();
2868 if (stats.empty())
continue;
2869 SOLVER_LOG(logger, absl::StrCat(
" '", subsolver->name(),
"':\n", stats));
2879void AddPostsolveClauses(
const std::vector<int>& postsolve_mapping,
2880 Model*
model, CpModelProto* mapping_proto) {
2881 auto* mapping =
model->GetOrCreate<CpModelMapping>();
2882 auto* postsolve =
model->GetOrCreate<PostsolveClauses>();
2883 for (
const auto& clause : postsolve->clauses) {
2884 auto*
ct = mapping_proto->add_constraints()->mutable_bool_or();
2885 for (
const Literal l : clause) {
2886 int var = mapping->GetProtoVariableFromBooleanVariable(l.Variable());
2888 var = postsolve_mapping[
var];
2892 postsolve->clauses.clear();
2901 user_timer->
Start();
2903#if !defined(__PORTABLE_PLATFORM__)
2906#if !defined(__PORTABLE_PLATFORM__)
2908 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
2909 const std::string
file =
2910 absl::StrCat(absl::GetFlag(FLAGS_cp_model_dump_prefix),
"model.pb.txt");
2911 LOG(
INFO) <<
"Dumping cp model proto to '" <<
file <<
"'.";
2916#if !defined(__PORTABLE_PLATFORM__)
2918 if (!absl::GetFlag(FLAGS_cp_model_params).empty()) {
2921 CHECK(google::protobuf::TextFormat::ParseFromString(
2922 absl::GetFlag(FLAGS_cp_model_params), &flag_params));
2933 std::string log_string;
2935 logger->AddInfoLoggingCallback([&log_string](
const std::string&
message) {
2936 absl::StrAppend(&log_string,
message,
"\n");
2942 absl::GetFlag(FLAGS_cp_model_dump_prefix));
2944#if !defined(__PORTABLE_PLATFORM__)
2948 if (absl::GetFlag(FLAGS_cp_model_dump_response)) {
2949 shared_response_manager->AddFinalSolutionPostprocessor(
2951 const std::string
file = absl::StrCat(
2952 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"response.pbtxt");
2953 LOG(
INFO) <<
"Dumping response proto to '" <<
file <<
"'.";
2961 shared_response_manager->AddFinalSolutionPostprocessor(
2966 if (!log_string.empty()) {
2967 response->set_solve_log(log_string);
2975 shared_response_manager->AddSolutionPostprocessor(
2981 shared_time_limit->GetElapsedDeterministicTime());
2990 if (!error.empty()) {
2991 SOLVER_LOG(logger,
"Invalid parameters: ", error);
2996 shared_response_manager->MutableResponse()->set_status(
2998 return shared_response_manager->GetResponse();
3003 model->GetOrCreate<
TimeLimit>()->ResetLimitFromParameters(params);
3005#if !defined(__PORTABLE_PLATFORM__)
3009 [&shared_time_limit]() { shared_time_limit->Stop(); });
3014 SOLVER_LOG(logger,
"Starting CP-SAT solver.");
3018#if !defined(__PORTABLE_PLATFORM__)
3020 const int num_cores =
3023 : std::max<int>(std::thread::hardware_concurrency(), 1);
3025 const int num_cores = 1;
3027 SOLVER_LOG(logger,
"Setting number of workers to ", num_cores);
3031 SOLVER_LOG(logger,
"Parameters: ", params.ShortDebugString());
3040 if (!error.empty()) {
3041 SOLVER_LOG(logger,
"Invalid model: ", error);
3042 shared_response_manager->MutableResponse()->set_status(
3044 return shared_response_manager->GetResponse();
3059 bool is_pure_sat =
true;
3061 if (
var.domain_size() != 2 ||
var.domain(0) < 0 ||
var.domain(1) > 1) {
3062 is_pure_sat =
false;
3068 if (
ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolOr &&
3069 ct.constraint_case() != ConstraintProto::ConstraintCase::kBoolAnd) {
3070 is_pure_sat =
false;
3078 *shared_response_manager->MutableResponse() =
3081 *shared_response_manager->MutableResponse()
3084 return shared_response_manager->GetResponse();
3091 absl::StrFormat(
"Starting presolve at %.2fs",
wall_timer->
Get()));
3094 auto context = absl::make_unique<PresolveContext>(
model, &new_cp_model_proto,
3100 VLOG(1) <<
"Model found infeasible during copy";
3112 " variables to their value in the solution hints.");
3119 const std::string var_name = var_proto.
name().empty()
3120 ? absl::StrCat(
"var(",
var,
")")
3125 "Hint found infeasible when assigning variable '", var_name,
3126 "' with domain", var_domain.
ToString(),
" the value ",
3150 "The solution hint is complete and is feasible.");
3154 "The solution hint is complete, but it is infeasible! we "
3155 "will try to repair it.");
3167 shared_response_manager->AddFinalSolutionPostprocessor(
3172 *
response->mutable_sufficient_assumptions_for_infeasibility() =
3176 context->InitializeNewDomains();
3178 if (!
context->SetLiteralToTrue(ref)) {
3179 shared_response_manager->MutableResponse()->set_status(
3181 shared_response_manager->MutableResponse()
3182 ->add_sufficient_assumptions_for_infeasibility(ref);
3183 return shared_response_manager->GetResponse();
3189 std::vector<int> postsolve_mapping;
3192 LOG(
ERROR) <<
"Error while presolving, likely due to integer overflow.";
3193 shared_response_manager->MutableResponse()->set_status(
3195 return shared_response_manager->GetResponse();
3204 shared_response_manager->AddSolutionPostprocessor(
3207 AddPostsolveClauses(postsolve_mapping,
model, &mapping_proto);
3209 mapping_proto, postsolve_mapping,
wall_timer,
3211 if (!
response->solution().empty()) {
3214 std::vector<int64_t>(
response->solution().begin(),
3216 &mapping_proto, &postsolve_mapping))
3217 <<
"postsolved solution";
3225 *
response->add_tightened_variables() =
3232 shared_response_manager->AddFinalSolutionPostprocessor(
3234 if (!
response->solution().empty()) {
3240 shared_response_manager->AddSolutionPostprocessor(
3244 if (
response->solution_size() > 0) {
3245 response->mutable_solution()->Truncate(initial_size);
3247 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
3250 std::vector<int64_t>(
response->solution().begin(),
3253 }
else if (
response->solution_lower_bounds_size() > 0) {
3254 response->mutable_solution_lower_bounds()->Truncate(initial_size);
3255 response->mutable_solution_upper_bounds()->Truncate(initial_size);
3271 if (!observers.empty()) {
3272 shared_response_manager->AddSolutionCallback(
3274 for (
const auto& observer : observers) {
3285 shared_response_manager->InitializeObjective(new_cp_model_proto);
3286 shared_response_manager->SetGapLimitsFromParameters(params);
3291 shared_response_manager->UpdatePrimalIntegral();
3293#if !defined(__PORTABLE_PLATFORM__)
3294 if (absl::GetFlag(FLAGS_cp_model_dump_models)) {
3295 const std::string presolved_file = absl::StrCat(
3296 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"presolved_model.pb.txt");
3297 LOG(
INFO) <<
"Dumping presolved cp model proto to '" << presolved_file
3302 const std::string mapping_file = absl::StrCat(
3303 absl::GetFlag(FLAGS_cp_model_dump_prefix),
"mapping_model.pb.txt");
3304 LOG(
INFO) <<
"Dumping mapping cp model proto to '" << mapping_file <<
"'.";
3310 int64_t num_terms = 0;
3315 logger,
"Stopped after presolve.",
3316 "\nPresolvedNumVariables: ", new_cp_model_proto.
variables().size(),
3317 "\nPresolvedNumConstraints: ", new_cp_model_proto.
constraints().size(),
3318 "\nPresolvedNumTerms: ", num_terms);
3320 shared_response_manager->SetStatsFromModel(
model);
3321 return shared_response_manager->GetResponse();
3326 shared_response_manager->AddSolutionCallback(
3328 shared_time_limit->Stop();
3332#if defined(__PORTABLE_PLATFORM__)
3337 SolveCpModelParallel(new_cp_model_proto,
model);
3341 SOLVER_LOG(logger, absl::StrFormat(
"Starting to load the model at %.2fs",
3343 shared_response_manager->SetUpdatePrimalIntegralOnEachChange(
true);
3344 LoadCpModel(new_cp_model_proto,
model);
3345 shared_response_manager->LoadDebugSolution(
model);
3348 SOLVER_LOG(logger, absl::StrFormat(
"Starting sequential search at %.2fs",
3351 MinimizeL1DistanceWithHint(new_cp_model_proto,
model);
3353 QuickSolveWithHint(new_cp_model_proto,
model);
3355 SolveLoadedCpModel(new_cp_model_proto,
model);
3358 if (logger->LoggingIsEnabled()) {
3364 for (
const auto* lp : lps) {
3372 shared_response_manager->DisplayImprovementStatistics();
3376 return shared_response_manager->GetResponse();
3391#if !defined(__PORTABLE_PLATFORM__)
3393 const std::string& params) {
#define CHECK_NE(val1, val2)
#define DCHECK(condition)
#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...
const ::operations_research::sat::IntervalConstraintProto & interval() const
::PROTOBUF_NAMESPACE_ID::int32 enforcement_literal(int index) const
const std::string & name() const
const ::operations_research::sat::CpObjectiveProto & objective() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::DecisionStrategyProto & search_strategy(int index) const
const ::operations_research::sat::PartialVariableAssignment & solution_hint() const
bool has_objective() const
bool has_solution_hint() const
int variables_size() const
::PROTOBUF_NAMESPACE_ID::int32 assumptions(int index) const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
::PROTOBUF_NAMESPACE_ID::int64 coeffs(int index) const
double scaling_factor() const
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
const std::string & name() const
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
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).
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
::PROTOBUF_NAMESPACE_ID::int64 values(int index) const
::PROTOBUF_NAMESPACE_ID::int32 interleave_batch_size() const
bool optimize_with_core() const
bool interleave_search() const
::PROTOBUF_NAMESPACE_ID::int32 symmetry_level() const
bool use_lns_only() const
void MergeFrom(const SatParameters &from)
void set_search_branching(::operations_research::sat::SatParameters_SearchBranching value)
bool log_search_progress() const
bool enumerate_all_solutions() const
bool optimize_with_lb_tree_search() const
::PROTOBUF_NAMESPACE_ID::int32 binary_search_num_conflicts() const
::operations_research::sat::SatParameters_SearchBranching search_branching() const
bool use_feasibility_pump() const
bool fix_variables_to_their_hinted_value() const
bool stop_after_first_solution() const
::PROTOBUF_NAMESPACE_ID::int32 linearization_level() const
void set_optimize_with_core(bool value)
::PROTOBUF_NAMESPACE_ID::int32 cp_model_probing_level() const
bool use_relaxation_lns() const
bool use_rins_lns() const
::PROTOBUF_NAMESPACE_ID::int32 num_search_workers() const
bool catch_sigint_signal() const
bool use_sat_inprocessing() const
::PROTOBUF_NAMESPACE_ID::int32 hint_conflict_limit() const
void set_max_number_of_conflicts(::PROTOBUF_NAMESPACE_ID::int64 value)
void set_name(ArgT0 &&arg0, ArgT... args)
bool log_subsolver_statistics() const
void set_stop_after_first_solution(bool value)
bool cp_model_presolve() const
bool fill_tightened_domains_in_response() const
bool use_absl_random() const
bool auto_detect_greater_than_at_least_one_of() const
bool use_probing_search() const
bool stop_after_presolve() const
static constexpr SearchBranching HINT_SEARCH
bool optimize_with_max_hs() const
bool log_to_response() const
bool log_to_stdout() const
bool share_level_zero_bounds() const
static constexpr SearchBranching FIXED_SEARCH
bool diversify_lns_params() const
void set_dump_prefix(const std::string &dump_prefix)
void NewSolution(const CpSolverResponse &response, Model *model)
SharedBoundsManager * bounds
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)
double UnscaleObjectiveValue(const CpObjectiveProto &proto, double value)
bool RefIsPositive(int ref)
std::string ValidateParameters(const SatParameters ¶ms)
void MaybeFullyEncodeMoreVariables(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
void ComputeLinearRelaxation(const CpModelProto &model_proto, int linearization_level, Model *m, LinearRelaxation *relaxation)
void PostsolveResponse(const int64_t num_variables_in_original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, CpSolverResponse *response)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
bool PresolveCpModel(PresolveContext *context, std::vector< int > *postsolve_mapping)
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)
SatSolver::Status MinimizeWithHittingSetAndLazyEncoding(const ObjectiveDefinition &objective_definition, const std::function< void()> &feasible_solution_observer, Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
void CopyEverythingExceptVariablesAndConstraintsFieldsIntoContext(const CpModelProto &in_model, PresolveContext *context)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
std::function< int64_t(const Model &)> Value(IntegerVariable v)
bool ImportConstraintsWithBasicPresolveIntoContext(const CpModelProto &in_model, PresolveContext *context)
std::string CpModelStats(const CpModelProto &model_proto)
Returns a string with some statistics on the given CpModelProto.
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)
SatSolver::Status ContinuousProbing(const std::vector< BooleanVariable > &bool_vars, const std::vector< IntegerVariable > &int_vars, const std::function< void()> &feasible_solution_observer, Model *model)
CutGenerator CreateKnapsackCoverCutGenerator(const std::vector< LinearConstraint > &base_constraints, const std::vector< IntegerVariable > &vars, Model *model)
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)
std::vector< SatParameters > GetDiverseSetOfParameters(const SatParameters &base_params, const CpModelProto &cp_model, const int num_workers)
std::string ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
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::string ValidateCpModel(const CpModelProto &model)
std::function< BooleanOrIntegerLiteral()> ConstructSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, IntegerVariable objective_var, Model *model)
CpSolverResponse SolveWithParameters(const CpModelProto &model_proto, const SatParameters ¶ms)
Solves the given CpModelProto with the given parameters.
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.
std::mt19937 random_engine_t
std::string ProtobufDebugString(const P &message)
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)