30#if !defined(__PORTABLE_PLATFORM__)
34#include "absl/cleanup/cleanup.h"
35#include "absl/container/flat_hash_set.h"
36#include "absl/flags/flag.h"
37#include "absl/log/check.h"
38#include "absl/strings/escaping.h"
39#include "absl/strings/str_cat.h"
40#include "absl/strings/string_view.h"
41#include "absl/time/time.h"
42#include "absl/types/span.h"
43#include "google/protobuf/arena.h"
49#include "ortools/sat/cp_model.pb.h"
73#include "ortools/sat/sat_parameters.pb.h"
79#if !defined(__PORTABLE_PLATFORM__)
87 "DEBUG ONLY. When set to true, SolveCpModel() will dump its model "
88 "protos (original model, presolved model, mapping model) in text "
89 "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|"
90 "mapping_model}.pb.txt.");
93ABSL_FLAG(std::string, cp_model_dump_prefix,
".\\",
94 "Prefix filename for all dumped files");
96ABSL_FLAG(std::string, cp_model_dump_prefix,
"/tmp/",
97 "Prefix filename for all dumped files");
101 "DEBUG ONLY. When set to true, solve will dump all "
102 "lns or objective_shaving submodels proto in text format to "
103 "'FLAGS_cp_model_dump_prefix'xxx.pb.txt.");
106 std::string, cp_model_load_debug_solution,
"",
107 "DEBUG ONLY. When this is set to a non-empty file name, "
108 "we will interpret this as an internal solution which can be used for "
109 "debugging. For instance we use it to identify wrong cuts/reasons.");
111ABSL_FLAG(
bool, cp_model_check_intermediate_solutions,
false,
112 "When true, all intermediate solutions found by the solver will be "
113 "checked. This can be expensive, therefore it is off by default.");
122#if !defined(__PORTABLE_PLATFORM__)
123 if (absl::GetFlag(FLAGS_cp_model_load_debug_solution).empty())
return;
125 CpSolverResponse response;
127 "Reading debug solution from '",
128 absl::GetFlag(FLAGS_cp_model_load_debug_solution),
"'.");
134 CHECK_EQ(response.solution().size(), model_proto.variables().size());
136 response.solution());
144 if (shared_response ==
nullptr)
return;
145 if (shared_response->DebugSolution().empty())
return;
149 debug_sol.
proto_values = shared_response->DebugSolution();
152 const int num_integers =
157 std::vector<Literal> boolean_solution;
161 if (mapping.IsBoolean(
i)) {
166 boolean_solution.push_back(l);
169 if (!mapping.IsInteger(
i))
continue;
170 const IntegerVariable
var = mapping.Integer(
i);
180 if (boolean_solution.size() == debug_sol.
proto_values.size() &&
181 !model_proto.has_objective()) {
182 LOG(INFO) <<
"Loaded pure Boolean debugging solution.";
189 if (objective_def !=
nullptr) {
190 const IntegerVariable objective_var = objective_def->objective_var;
201 const auto checker = [mapping, encoder, debug_sol,
model](
202 absl::Span<const Literal> clause,
203 absl::Span<const IntegerLiteral> integers) {
204 bool is_satisfied =
false;
207 std::vector<std::tuple<Literal, IntegerLiteral, int>> to_print;
208 for (
const Literal l : clause) {
211 const int proto_var =
212 mapping.GetProtoVariableFromBooleanVariable(l.Variable());
213 if (proto_var != -1) {
215 if (debug_sol.
proto_values[proto_var] == (l.IsPositive() ? 1 : 0)) {
226 bool all_true =
true;
227 for (
const IntegerLiteral associated : encoder->GetIntegerLiterals(l)) {
228 const int proto_var = mapping.GetProtoVariableFromIntegerVariable(
230 if (proto_var == -1)
break;
232 to_print.push_back({l, associated, proto_var});
235 if (
value < associated.bound) {
247 const int proto_var = mapping.GetProtoVariableFromIntegerVariable(
249 if (proto_var == -1) {
260 if (
value >= i_lit.bound) {
266 LOG(INFO) <<
"Reason clause is not satisfied by loaded solution:";
267 LOG(INFO) <<
"Worker '" <<
model->Name() <<
"', level="
269 LOG(INFO) <<
"literals (neg): " << clause;
270 LOG(INFO) <<
"integer literals: " << integers;
271 for (
const auto [l, i_lit, proto_var] : to_print) {
272 LOG(INFO) << l <<
" " << i_lit <<
" var=" << proto_var
273 <<
" value_in_sol=" << debug_sol.
proto_values[proto_var];
278 const auto lit_checker = [checker](absl::Span<const Literal> clause) {
279 return checker(clause, {});
282 model->GetOrCreate<
Trail>()->RegisterDebugChecker(lit_checker);
292 for (
int i = 0;
i < model_proto.variables_size(); ++
i) {
293 if (mapping->IsInteger(
i)) {
294 const IntegerVariable
var = mapping->Integer(
i);
300 DCHECK(mapping->IsBoolean(
i));
302 if (trail->Assignment().LiteralIsAssigned(
literal)) {
312 absl::GetFlag(FLAGS_cp_model_check_intermediate_solutions)) {
321IntegerVariable GetOrCreateVariableWithTightBound(
322 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
325 if (terms.size() == 1 && terms.front().second == 1) {
326 return terms.front().first;
328 if (terms.size() == 1 && terms.front().second == -1) {
334 for (
const std::pair<IntegerVariable, int64_t>& var_coeff : terms) {
337 const int64_t coeff = var_coeff.second;
338 const int64_t prod1 = min_domain * coeff;
339 const int64_t prod2 = max_domain * coeff;
340 sum_min += std::min(prod1, prod2);
341 sum_max += std::max(prod1, prod2);
346IntegerVariable GetOrCreateVariableLinkedToSumOf(
347 const std::vector<std::pair<IntegerVariable, int64_t>>& terms,
348 bool lb_required,
bool ub_required, Model*
model) {
350 if (terms.size() == 1 && terms.front().second == 1) {
351 return terms.front().first;
353 if (terms.size() == 1 && terms.front().second == -1) {
357 const IntegerVariable new_var =
358 GetOrCreateVariableWithTightBound(terms,
model);
361 std::vector<IntegerVariable> vars;
362 std::vector<int64_t> coeffs;
363 for (
const auto [
var, coeff] : terms) {
365 coeffs.push_back(coeff);
367 vars.push_back(new_var);
368 coeffs.push_back(-1);
371 if (vars.size() >
model->GetOrCreate<SatParameters>()->linear_split_size()) {
391 const CpModelProto& model_proto,
Model* m) {
402 const int num_lp_constraints =
404 const int num_lp_cut_generators =
406 const int num_integer_variables =
411 num_integer_variables);
413 auto get_cut_generator_index = [num_lp_constraints](
int cut_index) {
414 return num_lp_constraints + cut_index;
416 auto get_var_index = [num_lp_constraints,
417 num_lp_cut_generators](IntegerVariable
var) {
418 return num_lp_constraints + num_lp_cut_generators +
421 for (
int i = 0;
i < num_lp_constraints;
i++) {
422 for (
const IntegerVariable
var :
424 components.
AddEdge(get_constraint_index(
i), get_var_index(
var));
427 for (
int i = 0;
i < num_lp_cut_generators; ++
i) {
429 components.
AddEdge(get_cut_generator_index(
i), get_var_index(
var));
434 std::vector<int> component_sizes(num_components, 0);
435 const std::vector<int> index_to_component = components.
GetComponentIds();
436 for (
int i = 0;
i < num_lp_constraints;
i++) {
437 ++component_sizes[index_to_component[get_constraint_index(
i)]];
439 for (
int i = 0;
i < num_lp_cut_generators;
i++) {
440 ++component_sizes[index_to_component[get_cut_generator_index(
i)]];
444 std::vector<std::vector<IntegerVariable>> component_to_var(num_components);
445 for (IntegerVariable
var(0);
var < num_integer_variables;
var += 2) {
447 component_to_var[index_to_component[get_var_index(
var)]].push_back(
var);
456 for (
int i = 0;
i < model_proto.objective().coeffs_size(); ++
i) {
457 const IntegerVariable
var =
458 mapping->Integer(model_proto.objective().vars(
i));
459 ++component_sizes[index_to_component[get_var_index(
var)]];
463 std::vector<LinearProgrammingConstraint*> lp_constraints(num_components,
465 for (
int i = 0;
i < num_lp_constraints;
i++) {
466 const int c = index_to_component[get_constraint_index(
i)];
467 if (component_sizes[c] <= 1)
continue;
468 if (lp_constraints[c] ==
nullptr) {
474 lp_constraints[c]->AddLinearConstraint(
479 for (
int i = 0;
i < num_lp_cut_generators;
i++) {
480 const int c = index_to_component[get_cut_generator_index(
i)];
481 if (lp_constraints[c] ==
nullptr) {
486 lp_constraints[c]->AddCutGenerator(std::move(relaxation.
cut_generators[
i]));
490 std::vector<std::vector<std::pair<IntegerVariable, int64_t>>>
491 component_to_cp_terms(num_components);
492 std::vector<std::pair<IntegerVariable, int64_t>> top_level_cp_terms;
493 int num_components_containing_objective = 0;
494 if (model_proto.has_objective()) {
497 for (
int i = 0;
i < model_proto.objective().coeffs_size(); ++
i) {
498 const IntegerVariable
var =
499 mapping->Integer(model_proto.objective().vars(
i));
500 const int64_t coeff = model_proto.objective().coeffs(
i);
501 const int c = index_to_component[get_var_index(
var)];
502 if (lp_constraints[c] !=
nullptr) {
503 lp_constraints[c]->SetObjectiveCoefficient(
var, IntegerValue(coeff));
504 component_to_cp_terms[c].push_back(std::make_pair(
var, coeff));
507 top_level_cp_terms.push_back(std::make_pair(
var, coeff));
511 for (
int c = 0; c < num_components; ++c) {
512 if (component_to_cp_terms[c].empty())
continue;
513 const IntegerVariable sub_obj_var = GetOrCreateVariableLinkedToSumOf(
514 component_to_cp_terms[c], objective_need_to_be_tight,
true, m);
515 top_level_cp_terms.push_back(std::make_pair(sub_obj_var, 1));
516 lp_constraints[c]->SetMainObjectiveVariable(sub_obj_var);
517 num_components_containing_objective++;
521 const IntegerVariable main_objective_var =
522 model_proto.has_objective()
523 ? GetOrCreateVariableLinkedToSumOf(
524 top_level_cp_terms, objective_need_to_be_tight,
true, m)
530 if (lp_constraint ==
nullptr)
continue;
531 lp_constraint->RegisterWith(m);
532 VLOG(3) <<
"LP constraint: " << lp_constraint->DimensionString() <<
".";
535 VLOG(3) << top_level_cp_terms.size()
536 <<
" terms in the main objective linear equation ("
537 << num_components_containing_objective <<
" from LP constraints).";
538 return main_objective_var;
544 const CpModelProto& ,
546 CHECK(shared_bounds_manager !=
nullptr);
552 int saved_trail_index = 0;
553 std::vector<int> model_variables;
554 std::vector<int64_t> new_lower_bounds;
555 std::vector<int64_t> new_upper_bounds;
556 absl::flat_hash_set<int> visited_variables;
559 auto broadcast_level_zero_bounds =
560 [=](
const std::vector<IntegerVariable>& modified_vars)
mutable {
562 for (
const IntegerVariable&
var : modified_vars) {
565 mapping->GetProtoVariableFromIntegerVariable(positive_var);
568 const auto [_, inserted] = visited_variables.insert(
model_var);
569 if (!inserted)
continue;
571 const int64_t new_lb =
572 integer_trail->LevelZeroLowerBound(positive_var).value();
573 const int64_t new_ub =
574 integer_trail->LevelZeroUpperBound(positive_var).value();
579 new_lower_bounds.push_back(new_lb);
580 new_upper_bounds.push_back(new_ub);
584 for (; saved_trail_index < trail->Index(); ++saved_trail_index) {
585 const Literal fixed_literal = (*trail)[saved_trail_index];
586 const int model_var = mapping->GetProtoVariableFromBooleanVariable(
590 const auto [_, inserted] = visited_variables.insert(
model_var);
591 if (!inserted)
continue;
595 new_lower_bounds.push_back(1);
596 new_upper_bounds.push_back(1);
598 new_lower_bounds.push_back(0);
599 new_upper_bounds.push_back(0);
603 if (!model_variables.empty()) {
605 model->Name(), model_variables, new_lower_bounds,
609 model_variables.clear();
610 new_lower_bounds.clear();
611 new_upper_bounds.clear();
612 visited_variables.clear();
615 if (!
model->Get<SatParameters>()->interleave_search()) {
628 const IntegerVariable num_vars =
630 std::vector<IntegerVariable> all_variables;
631 all_variables.reserve(num_vars.value());
632 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
633 all_variables.push_back(
var);
635 broadcast_level_zero_bounds(all_variables);
638 ->RegisterLevelZeroModifiedVariablesCallback(broadcast_level_zero_bounds);
647 CHECK(shared_bounds_manager !=
nullptr);
655 const auto& import_level_zero_bounds = [&model_proto, shared_bounds_manager,
656 name, sat_solver, integer_trail,
657 trail, id, mapping]() {
658 std::vector<int> model_variables;
659 std::vector<int64_t> new_lower_bounds;
660 std::vector<int64_t> new_upper_bounds;
662 id, &model_variables, &new_lower_bounds, &new_upper_bounds);
663 bool new_bounds_have_been_imported =
false;
664 for (
int i = 0;
i < model_variables.size(); ++
i) {
672 if (new_upper_bounds[
i] == 0)
lit =
lit.Negated();
673 if (trail->Assignment().LiteralIsTrue(
lit))
continue;
674 if (trail->Assignment().LiteralIsFalse(
lit)) {
675 sat_solver->NotifyThatModelIsUnsat();
678 new_bounds_have_been_imported =
true;
679 trail->EnqueueWithUnitReason(
lit);
684 if (!mapping->IsInteger(
model_var))
continue;
685 const IntegerVariable
var = mapping->Integer(
model_var);
686 const IntegerValue new_lb(new_lower_bounds[
i]);
687 const IntegerValue new_ub(new_upper_bounds[
i]);
688 const IntegerValue old_lb = integer_trail->LowerBound(
var);
689 const IntegerValue old_ub = integer_trail->UpperBound(
var);
690 const bool changed_lb = new_lb > old_lb;
691 const bool changed_ub = new_ub < old_ub;
692 if (!changed_lb && !changed_ub)
continue;
694 new_bounds_have_been_imported =
true;
696 const IntegerVariableProto& var_proto =
698 const std::string& var_name =
699 var_proto.name().empty()
700 ? absl::StrCat(
"anonymous_var(",
model_var,
")")
702 LOG(INFO) <<
" '" <<
name <<
"' imports new bounds for " << var_name
703 <<
": from [" << old_lb <<
", " << old_ub <<
"] to ["
704 << new_lb <<
", " << new_ub <<
"]";
718 if (new_bounds_have_been_imported && !sat_solver->FinishPropagation()) {
724 import_level_zero_bounds);
730 IntegerVariable objective_var,
733 const auto broadcast_objective_lower_bound =
734 [objective_var, integer_trail, shared_response_manager,
model,
737 const IntegerValue objective_lb =
738 integer_trail->LevelZeroLowerBound(objective_var);
739 if (objective_lb > best_obj_lb) {
740 best_obj_lb = objective_lb;
742 model->Name(), objective_lb,
743 integer_trail->LevelZeroUpperBound(objective_var));
745 if (!
model->Get<SatParameters>()->interleave_search()) {
751 ->RegisterLevelZeroModifiedVariablesCallback(
752 broadcast_objective_lower_bound);
764 const auto import_objective_bounds = [
name, solver, integer_trail, objective,
765 shared_response_manager]() {
766 if (solver->AssumptionLevel() != 0)
return true;
767 bool propagate =
false;
769 const IntegerValue external_lb =
771 const IntegerValue current_lb =
772 integer_trail->LowerBound(objective->objective_var);
773 if (external_lb > current_lb) {
775 objective->objective_var, external_lb),
782 const IntegerValue external_ub =
784 const IntegerValue current_ub =
785 integer_trail->UpperBound(objective->objective_var);
786 if (external_ub < current_ub) {
788 objective->objective_var, external_ub),
795 if (!propagate)
return true;
797 VLOG(3) <<
"'" <<
name <<
"' imports objective bounds: external ["
798 << objective->ScaleIntegerObjective(external_lb) <<
", "
799 << objective->ScaleIntegerObjective(external_ub) <<
"], current ["
800 << objective->ScaleIntegerObjective(current_lb) <<
", "
801 << objective->ScaleIntegerObjective(current_ub) <<
"]";
803 return solver->FinishPropagation();
807 import_objective_bounds);
814 const auto& share_binary_clause = [mapping, id, shared_clauses_manager](
817 mapping->GetProtoVariableFromBooleanVariable(l1.Variable());
818 if (var1 == -1)
return;
820 mapping->GetProtoVariableFromBooleanVariable(l2.Variable());
821 if (var2 == -1)
return;
822 const int lit1 = l1.IsPositive() ? var1 :
NegatedRef(var1);
823 const int lit2 = l2.IsPositive() ? var2 :
NegatedRef(var2);
827 share_binary_clause);
828 if (!
model->GetOrCreate<SatParameters>()->share_glue_clauses()) {
833 model->GetOrCreate<SatParameters>()->clause_cleanup_lbd_bound();
837 auto share_clause = [mapping, clause_stream, max_lbd,
838 clause = std::vector<int>()](
839 int lbd, absl::Span<const Literal> literals)
mutable {
840 if (lbd <= 0 || lbd > max_lbd ||
841 !clause_stream->CanAccept(literals.size(), lbd)) {
847 mapping->GetProtoVariableFromBooleanVariable(
lit.Variable());
848 if (
var == -1)
return;
851 clause_stream->Add(clause);
854 std::move(share_clause));
866 CHECK(shared_clauses_manager !=
nullptr);
870 bool share_glue_clauses =
871 model->GetOrCreate<SatParameters>()->share_glue_clauses();
872 auto* clause_stream = share_glue_clauses
875 const auto& import_level_zero_clauses = [shared_clauses_manager, id, mapping,
876 sat_solver, implications,
878 std::vector<std::pair<int, int>> new_binary_clauses;
880 implications->EnableSharing(
false);
881 for (
const auto& [ref1, ref2] : new_binary_clauses) {
884 if (!sat_solver->AddBinaryClause(l1, l2)) {
888 implications->EnableSharing(
true);
889 if (clause_stream ==
nullptr)
return true;
891 std::array<Literal, UniqueClauseStream::kMaxClauseSize> local_clause;
892 for (
const absl::Span<const int> shared_clause :
899 if (clause_stream->Delete(shared_clause))
continue;
900 for (
int i = 0;
i < shared_clause.size(); ++
i) {
901 local_clause[
i] = mapping->
Literal(shared_clause[
i]);
903 if (!sat_solver->AddProblemClause(
904 absl::MakeSpan(local_clause).subspan(0, shared_clause.size()))) {
908 clause_stream->RemoveWorstClauses();
912 import_level_zero_clauses);
918 CHECK(shared_response_manager !=
nullptr);
922 const auto unsat = [shared_response_manager, sat_solver,
model] {
923 sat_solver->NotifyThatModelIsUnsat();
924 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
925 absl::StrCat(
model->Name(),
" [loading]"));
932 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
933 const bool view_all_booleans_as_integers =
935 (
parameters.search_branching() == SatParameters::FIXED_SEARCH &&
936 model_proto.search_strategy().empty()) ||
960 if (sat_solver->ModelIsUnsat())
return unsat();
964 if (sat_solver->ModelIsUnsat())
return unsat();
971 int num_ignored_constraints = 0;
972 absl::flat_hash_set<ConstraintProto::ConstraintCase> unsupported_types;
973 for (
const ConstraintProto&
ct : model_proto.constraints()) {
974 if (mapping->ConstraintIsAlreadyLoaded(&
ct)) {
975 ++num_ignored_constraints;
980 unsupported_types.insert(
ct.constraint_case());
990 if (sat_solver->FinishPropagation()) {
992 const int old_num_fixed = trail->
Index();
993 if (trail->
Index() > old_num_fixed) {
994 VLOG(3) <<
"Constraint fixed " << trail->
Index() - old_num_fixed
999 if (sat_solver->ModelIsUnsat()) {
1000 VLOG(2) <<
"UNSAT during extraction (after adding '"
1006 if (num_ignored_constraints > 0) {
1007 VLOG(3) << num_ignored_constraints <<
" constraints were skipped.";
1009 if (!unsupported_types.empty()) {
1010 VLOG(1) <<
"There is unsupported constraints types in this model: ";
1011 std::vector<absl::string_view> names;
1012 for (
const ConstraintProto::ConstraintCase type : unsupported_types) {
1015 std::sort(names.begin(), names.end());
1016 for (
const absl::string_view
name : names) {
1017 VLOG(1) <<
" - " <<
name;
1023 ->AddAllImplicationsBetweenAssociatedLiterals();
1024 if (!sat_solver->FinishPropagation())
return unsat();
1035 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1036 if (
parameters.linearization_level() == 0)
return;
1043 const int num_lp_constraints =
1045 if (num_lp_constraints == 0)
return;
1047 for (
int i = 0;
i < num_lp_constraints;
i++) {
1051 if (model_proto.has_objective()) {
1052 for (
int i = 0;
i < model_proto.objective().coeffs_size(); ++
i) {
1053 const IntegerVariable
var =
1054 mapping->Integer(model_proto.objective().vars(
i));
1055 const int64_t coeff = model_proto.objective().coeffs(
i);
1056 feasibility_pump->SetObjectiveCoefficient(
var, IntegerValue(coeff));
1074 const auto unsat = [shared_response_manager, sat_solver,
model] {
1075 sat_solver->NotifyThatModelIsUnsat();
1076 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1077 absl::StrCat(
model->Name(),
" [loading]"));
1081 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1087 if (
parameters.auto_detect_greater_than_at_least_one_of()) {
1089 ->AddGreaterThanAtLeastOneOfConstraints(
model);
1090 if (!sat_solver->FinishPropagation())
return unsat();
1100 if (
parameters.cp_model_probing_level() > 1) {
1108 if (sat_solver->ModelIsUnsat())
return unsat();
1115 if (repository !=
nullptr) {
1121 bool objective_need_to_be_tight =
false;
1122 if (model_proto.has_objective() &&
1123 !model_proto.objective().domain().empty()) {
1124 int64_t min_value = 0;
1125 int64_t max_value = 0;
1127 const CpObjectiveProto& obj = model_proto.objective();
1128 for (
int i = 0;
i < obj.vars_size(); ++
i) {
1129 const int64_t coeff = obj.coeffs(
i);
1130 const IntegerVariable
var = mapping->Integer(obj.vars(
i));
1132 min_value += coeff * integer_trail->LowerBound(
var).value();
1133 max_value += coeff * integer_trail->UpperBound(
var).value();
1135 min_value += coeff * integer_trail->UpperBound(
var).value();
1136 max_value += coeff * integer_trail->LowerBound(
var).value();
1140 const Domain automatic_domain =
Domain(min_value, max_value);
1141 objective_need_to_be_tight = !automatic_domain.
IsIncludedIn(user_domain);
1151 if (sat_solver->ModelIsUnsat())
return unsat();
1152 }
else if (model_proto.has_objective()) {
1153 const CpObjectiveProto& obj = model_proto.objective();
1154 std::vector<std::pair<IntegerVariable, int64_t>> terms;
1155 terms.reserve(obj.vars_size());
1156 for (
int i = 0;
i < obj.vars_size(); ++
i) {
1158 std::make_pair(mapping->Integer(obj.vars(
i)), obj.coeffs(
i)));
1161 if (objective_need_to_be_tight) {
1171 GetOrCreateVariableLinkedToSumOf(terms,
true,
false,
model);
1173 objective_var = GetOrCreateVariableWithTightBound(terms,
model);
1176 objective_var = GetOrCreateVariableLinkedToSumOf(
1177 terms, objective_need_to_be_tight,
true,
model);
1184 const CpObjectiveProto& objective_proto = model_proto.objective();
1187 objective_definition->
scaling_factor = objective_proto.scaling_factor();
1188 if (objective_definition->scaling_factor == 0.0) {
1189 objective_definition->scaling_factor = 1.0;
1191 objective_definition->offset = objective_proto.offset();
1192 objective_definition->objective_var = objective_var;
1194 const int size = objective_proto.vars_size();
1195 objective_definition->vars.resize(
size);
1196 objective_definition->coeffs.resize(
size);
1197 for (
int i = 0;
i < objective_proto.vars_size(); ++
i) {
1200 objective_definition->vars[
i] = mapping->Integer(objective_proto.vars(
i));
1201 objective_definition->coeffs[
i] = IntegerValue(objective_proto.coeffs(
i));
1204 const int ref = objective_proto.vars(
i);
1205 if (mapping->IsInteger(ref)) {
1206 const IntegerVariable
var = mapping->Integer(objective_proto.vars(
i));
1207 objective_definition->objective_impacting_variables.insert(
1213 model->TakeOwnership(
1215 objective_definition->coeffs,
model));
1219 if (!model_proto.objective().domain().empty()) {
1222 const Domain automatic_domain =
1223 integer_trail->InitialVariableDomain(objective_var);
1224 VLOG(3) <<
"Objective offset:" << model_proto.objective().offset()
1225 <<
" scaling_factor:" << model_proto.objective().scaling_factor();
1226 VLOG(3) <<
"Automatic internal objective domain: " << automatic_domain;
1227 VLOG(3) <<
"User specified internal objective domain: " << user_domain;
1229 if (!integer_trail->UpdateInitialDomain(objective_var, user_domain)) {
1230 VLOG(2) <<
"UNSAT due to the objective domain.";
1238 "Initial num_bool: ", sat_solver->NumVariables());
1239 if (!sat_solver->FinishPropagation())
return unsat();
1241 if (model_proto.has_objective()) {
1244 shared_response_manager->UpdateInnerObjectiveBounds(
1245 absl::StrCat(
model->Name(),
" (initial_propagation)"),
1246 integer_trail->LowerBound(objective_var),
1247 integer_trail->UpperBound(objective_var));
1256 if (
model->GetOrCreate<SatParameters>()->share_objective_bounds()) {
1265 search_heuristics->heuristic_search =
1267 search_heuristics->integer_completion_search =
1269 objective_var,
model);
1271 search_heuristics->user_search, search_heuristics->heuristic_search,
1272 search_heuristics->integer_completion_search);
1273 if (VLOG_IS_ON(3)) {
1274 search_heuristics->fixed_search =
1276 search_heuristics->fixed_search,
model);
1278 search_heuristics->hint_search =
1285 const auto solution_observer = [&model_proto,
model,
1286 shared_response_manager,
1288 const std::vector<int64_t>
solution =
1290 const IntegerValue obj_ub =
1292 if (obj_ub < best_obj_ub) {
1293 best_obj_ub = obj_ub;
1301 model_proto, objective, solution_observer,
model);
1303 model->TakeOwnership(max_hs);
1307 objective.coeffs, solution_observer,
model);
1309 model->TakeOwnership(core);
1324 if (shared_response_manager->ProblemIsSolved())
return;
1326 const SatParameters&
parameters = *
model->GetOrCreate<SatParameters>();
1327 if (
parameters.stop_after_root_propagation())
return;
1329 auto solution_observer = [&model_proto,
model, shared_response_manager,
1331 const std::vector<int64_t>
solution =
1333 if (model_proto.has_objective()) {
1334 const IntegerValue obj_ub =
1336 if (obj_ub < best_obj_ub) {
1337 best_obj_ub = obj_ub;
1347 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1363 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1368 solution_observer();
1373 }
else if (!model_proto.has_objective()) {
1380 mapping.Literals(model_proto.assumptions()),
model);
1383 solution_observer();
1384 if (!
parameters.enumerate_all_solutions())
break;
1388 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1392 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1398 std::vector<Literal> core = sat_solver->GetLastIncompatibleDecisions();
1400 std::vector<int> core_in_proto_format;
1401 for (
const Literal l : core) {
1402 core_in_proto_format.push_back(
1403 mapping.GetProtoVariableFromBooleanVariable(l.Variable()));
1404 if (!l.IsPositive()) {
1405 core_in_proto_format.back() =
NegatedRef(core_in_proto_format.back());
1408 shared_response_manager->AddUnsatCore(core_in_proto_format);
1413 const IntegerVariable objective_var = objective.
objective_var;
1416 if (
parameters.optimize_with_lb_tree_search()) {
1419 }
else if (
parameters.optimize_with_core()) {
1427 }
else if (
parameters.use_shared_tree_search()) {
1433 if (
parameters.binary_search_num_conflicts() >= 0) {
1435 solution_observer,
model);
1438 objective_var, solution_observer,
model);
1446 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1455 if (!model_proto.has_solution_hint())
return;
1458 if (shared_response_manager->ProblemIsSolved())
return;
1469 if (
parameters->optimize_with_core())
return;
1471 const SatParameters saved_params = *
parameters;
1473 parameters->set_search_branching(SatParameters::HINT_SEARCH);
1476 auto cleanup = ::absl::MakeCleanup(
1483 mapping.Literals(model_proto.assumptions()),
model);
1485 const std::string& solution_info =
model->Name();
1487 const std::vector<int64_t>
solution =
1489 shared_response_manager->NewSolution(
1492 if (!model_proto.has_objective()) {
1498 const IntegerVariable objective_var =
1505 shared_response_manager->GetInnerObjectiveUpperBound()),
1507 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1508 absl::StrCat(solution_info,
" [hint]"));
1521 shared_response_manager->SolutionsRepository().NumSolutions() == 0 &&
1524 LOG(FATAL) <<
"QuickSolveWithHint() didn't find a feasible solution."
1525 <<
" The model name is '" << model_proto.name() <<
"'."
1526 <<
" Status: " <<
status <<
".";
1530 shared_response_manager->NotifyThatImprovingProblemIsInfeasible(
1531 absl::StrCat(solution_info,
" [hint]"));
1547 if (!model_proto.has_solution_hint())
return;
1550 if (shared_response_manager->ProblemIsSolved())
return;
1556 if (
parameters->enumerate_all_solutions())
return;
1559 const SatParameters saved_params = *
model->GetOrCreate<SatParameters>();
1565 CpModelProto updated_model_proto = model_proto;
1566 updated_model_proto.clear_objective();
1569 for (
int i = 0;
i < model_proto.solution_hint().vars_size(); ++
i) {
1570 const int var = model_proto.solution_hint().vars(
i);
1571 const int64_t
value = model_proto.solution_hint().values(
i);
1574 const int new_var_index = updated_model_proto.variables_size();
1575 IntegerVariableProto* var_proto = updated_model_proto.add_variables();
1576 const int64_t min_domain = model_proto.variables(
var).domain(0) -
value;
1577 const int64_t max_domain =
1578 model_proto.variables(
var).domain(
1579 model_proto.variables(
var).domain_size() - 1) -
1581 var_proto->add_domain(min_domain);
1582 var_proto->add_domain(max_domain);
1585 ConstraintProto*
const linear_constraint_proto =
1586 updated_model_proto.add_constraints();
1587 LinearConstraintProto* linear = linear_constraint_proto->mutable_linear();
1588 linear->add_vars(new_var_index);
1589 linear->add_coeffs(1);
1590 linear->add_vars(
var);
1591 linear->add_coeffs(-1);
1592 linear->add_domain(-
value);
1593 linear->add_domain(-
value);
1596 const int abs_var_index = updated_model_proto.variables_size();
1597 IntegerVariableProto* abs_var_proto = updated_model_proto.add_variables();
1598 const int64_t abs_min_domain = 0;
1599 const int64_t abs_max_domain =
1600 std::max(std::abs(min_domain), std::abs(max_domain));
1601 abs_var_proto->add_domain(abs_min_domain);
1602 abs_var_proto->add_domain(abs_max_domain);
1603 auto* abs_ct = updated_model_proto.add_constraints()->mutable_lin_max();
1604 abs_ct->mutable_target()->add_vars(abs_var_index);
1605 abs_ct->mutable_target()->add_coeffs(1);
1606 LinearExpressionProto* left = abs_ct->add_exprs();
1607 left->add_vars(new_var_index);
1608 left->add_coeffs(1);
1609 LinearExpressionProto* right = abs_ct->add_exprs();
1610 right->add_vars(new_var_index);
1611 right->add_coeffs(-1);
1613 updated_model_proto.mutable_objective()->add_vars(abs_var_index);
1614 updated_model_proto.mutable_objective()->add_coeffs(1);
1617 auto* local_response_manager =
1627 mapping.Literals(updated_model_proto.assumptions()), &local_model);
1629 const std::string& solution_info =
model->Name();
1631 const std::vector<int64_t>
solution =
1634 const std::vector<int64_t> updated_solution =
1636 LOG(INFO) <<
"Found solution with repaired hint penalty = "
1640 shared_response_manager->NewSolution(
1641 solution, absl::StrCat(solution_info,
" [repaired]"), &local_model);
1650 CpModelProto mapping_proto,
1651 const std::vector<int>& postsolve_mapping,
1658 auto* var_proto = mapping_proto.mutable_variables(postsolve_mapping[
i]);
1659 var_proto->clear_domain();
1667 Model postsolve_model;
1670 SatParameters& params = *postsolve_model.
GetOrCreate<SatParameters>();
1671 params.set_linearization_level(0);
1672 params.set_cp_model_probing_level(0);
1680 const CpSolverResponse postsolve_response = response_manager->GetResponse();
1681 CHECK(postsolve_response.status() == CpSolverStatus::FEASIBLE ||
1682 postsolve_response.status() == CpSolverStatus::OPTIMAL)
1683 << postsolve_response.status();
1686 CHECK_LE(num_variables_in_original_model,
1687 postsolve_response.solution().size());
1689 postsolve_response.solution().begin(),
1690 postsolve_response.solution().begin() + num_variables_in_original_model);
1694 int num_variable_in_original_model,
1695 const CpModelProto& mapping_proto,
1696 const std::vector<int>& postsolve_mapping,
1698 if (params.debug_postsolve_with_full_solver()) {
1700 mapping_proto, postsolve_mapping,
solution);
1708 auto* params =
model->GetOrCreate<SatParameters>();
1712 if (params->num_workers() == 0) {
1713 params->set_num_workers(params->num_search_workers());
1716 if (params->enumerate_all_solutions()) {
1717 if (params->num_workers() >= 1) {
1719 "Forcing sequential search as enumerating all solutions is "
1720 "not supported in multi-thread.");
1722 params->set_num_workers(1);
1725 if (!model_proto.assumptions().empty()) {
1726 if (params->num_workers() >= 1) {
1728 "Forcing sequential search as assumptions are not supported "
1729 "in multi-thread.");
1731 params->set_num_workers(1);
1734 if (params->num_workers() == 0) {
1736#if !defined(__PORTABLE_PLATFORM__)
1738 const int num_cores = std::max<int>(std::thread::hardware_concurrency(), 1);
1740 const int num_cores = 1;
1742 SOLVER_LOG(logger,
"Setting number of workers to ", num_cores);
1743 params->set_num_workers(num_cores);
1751 if (params->interleave_search() || params->num_workers() == 1 ||
1752 !params->use_lns()) {
1753 params->set_use_rins_lns(
false);
1754 params->set_use_feasibility_pump(
false);
1758 if (params->linearization_level() == 0) {
1759 params->set_use_feasibility_pump(
false);
1764 if (!params->fill_tightened_domains_in_response() &&
1765 params->num_workers() == 1) {
1766 params->set_share_level_zero_bounds(
false);
1771 : model_proto(*
proto),
1772 wall_timer(global_model->GetOrCreate<
WallTimer>()),
1778 const SatParameters& params = *global_model->
GetOrCreate<SatParameters>();
1780 if (params.share_level_zero_bounds()) {
1781 bounds = std::make_unique<SharedBoundsManager>(*proto);
1782 bounds->set_dump_prefix(absl::GetFlag(FLAGS_cp_model_dump_prefix));
1783 bounds->LoadDebugSolution(response->DebugSolution());
1792 if (params.use_rins_lns() || params.use_feasibility_pump()) {
1793 lp_solutions = std::make_unique<SharedLPSolutionRepository>(
1795 global_model->Register<SharedLPSolutionRepository>(lp_solutions.get());
1797 incomplete_solutions = std::make_unique<SharedIncompleteSolutionManager>();
1798 global_model->Register<SharedIncompleteSolutionManager>(
1799 incomplete_solutions.get());
1803 const bool always_synchronize =
1804 !params.interleave_search() || params.num_workers() <= 1;
1806 if (params.share_binary_clauses() && params.num_workers() > 1) {
1807 clauses = std::make_unique<SharedClausesManager>(always_synchronize,
A connected components finder that only works on dense ints.
bool AddEdge(int node1, int node2)
void SetNumberOfNodes(int num_nodes)
int GetNumberOfComponents() const
std::vector< int > GetComponentIds()
Returns the same as GetConnectedComponents().
void Start()
When Start() is called multiple times, only the most recent is used.
bool IsIncludedIn(const Domain &domain) const
bool LimitReached() const
bool ComputeTransitiveReduction(bool log_info=false)
SatSolver::Status Probe()
sat::Literal Literal(int ref) const
IntegerVariable NumIntegerVariables() const
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
void InitAllDecomposedEnergies()
Calls InitDecomposedEnergies on all SchedulingDemandHelper created.
SatSolver::Status Search(const std::function< void()> &feasible_solution_observer)
Explores the search space.
BooleanVariable Variable() const
Literal(int signed_value)
The model "singleton" shared time limit.
void Register(T *non_owned_class)
bool ProbeBooleanVariables(double deterministic_time_limit)
bool ModelIsUnsat() const
ABSL_MUST_USE_RESULT bool ResetToLevelZero()
void GetChangedBounds(int id, std::vector< int > *variables, std::vector< int64_t > *new_lower_bounds, std::vector< int64_t > *new_upper_bounds)
void ReportPotentialNewBounds(const std::string &worker_name, const std::vector< int > &variables, const std::vector< int64_t > &new_lower_bounds, const std::vector< int64_t > &new_upper_bounds)
void AddBinaryClause(int id, int lit1, int lit2)
UniqueClauseStream * GetClauseStream(int id)
void GetUnseenBinaryClauses(int id, std::vector< std::pair< int, int > > *new_clauses)
std::vector< absl::Span< const int > > GetUnseenClauses(int id)
IntegerValue GetInnerObjectiveLowerBound()
bool ProblemIsSolved() const
void SetSynchronizationMode(bool always_synchronize)
void InitializeObjective(const CpModelProto &cp_model)
IntegerValue GetInnerObjectiveUpperBound()
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
Updates the inner objective bounds.
Simple class to add statistics by name and print them at the end.
SatSolver::Status Search(const std::function< void()> &feasible_solution_observer)
void assign(size_type n, const value_type &val)
CpModelProto proto
The output proto.
ABSL_FLAG(bool, cp_model_dump_models, false, "DEBUG ONLY. When set to true, SolveCpModel() will dump its model " "protos (original model, presolved model, mapping model) in text " "format to 'FLAGS_cp_model_dump_prefix'{model|presolved_model|" "mapping_model}.pb.txt.")
const std::string name
A name for logging purposes.
absl::Status GetTextProto(absl::string_view filename, google::protobuf::Message *proto, Options options)
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Weighted sum >= constant.
void ConfigureSearchHeuristics(Model *model)
std::vector< int64_t > GetSolutionValues(const CpModelProto &model_proto, const Model &model)
void SolveLoadedCpModel(const CpModelProto &model_proto, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void LoadBaseModel(const CpModelProto &model_proto, Model *model)
int64_t ComputeInnerObjective(const CpObjectiveProto &objective, absl::Span< const int64_t > solution)
void SplitAndLoadIntermediateConstraints(bool lb_required, bool ub_required, std::vector< IntegerVariable > *vars, std::vector< int64_t > *coeffs, Model *m)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Weighted sum <= constant.
const LiteralIndex kNoLiteralIndex(-1)
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
Automatically detect optional variables.
bool SolutionIsFeasible(const CpModelProto &model, absl::Span< const int64_t > variable_values, const CpModelProto *mapping_proto, const std::vector< int > *postsolve_mapping)
LinearRelaxation ComputeLinearRelaxation(const CpModelProto &model_proto, Model *m)
Builds the linear relaxation of a CpModelProto.
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
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 LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
void PostsolveResponseWrapper(const SatParameters ¶ms, int num_variable_in_original_model, const CpModelProto &mapping_proto, const std::vector< int > &postsolve_mapping, std::vector< int64_t > *solution)
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
void RegisterObjectiveBoundsImport(SharedResponseManager *shared_response_manager, Model *model)
std::function< BooleanOrIntegerLiteral()> ConstructFixedSearchStrategy(std::function< BooleanOrIntegerLiteral()> user_search, std::function< BooleanOrIntegerLiteral()> heuristic_search, std::function< BooleanOrIntegerLiteral()> integer_completion)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
std::function< BooleanOrIntegerLiteral()> ConstructHintSearchStrategy(const CpModelProto &cp_model_proto, CpModelMapping *mapping, Model *model)
Constructs a search strategy that follow the hint from the model.
void InitializeDebugSolution(const CpModelProto &model_proto, Model *model)
void PostsolveResponseWithFullSolver(int num_variables_in_original_model, CpModelProto mapping_proto, const std::vector< int > &postsolve_mapping, std::vector< int64_t > *solution)
const IntegerVariable kNoIntegerVariable(-1)
void RegisterClausesExport(int id, SharedClausesManager *shared_clauses_manager, Model *model)
Registers a callback that will export good clauses discovered during search.
void MinimizeL1DistanceWithHint(const CpModelProto &model_proto, Model *model)
std::function< IntegerVariable(Model *)> ConstantIntegerVariable(int64_t value)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
IntegerVariable AddLPConstraints(bool objective_need_to_be_tight, const CpModelProto &model_proto, Model *m)
Adds one LinearProgrammingConstraint per connected component of the model.
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
IntegerVariable PositiveVariable(IntegerVariable i)
std::function< BooleanOrIntegerLiteral()> ConstructHeuristicSearchStrategy(const CpModelProto &cp_model_proto, Model *model)
Constructs a search strategy tailored for the current model.
void LoadFeasibilityPump(const CpModelProto &model_proto, Model *model)
std::function< BooleanOrIntegerLiteral()> InstrumentSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, std::function< BooleanOrIntegerLiteral()> instrumented_strategy, Model *model)
void ExtractElementEncoding(const CpModelProto &model_proto, Model *m)
std::function< BooleanOrIntegerLiteral()> ConstructIntegerCompletionSearchStrategy(const std::vector< IntegerVariable > &variable_mapping, IntegerVariable objective_var, Model *model)
Constructs an integer completion search strategy.
std::function< int64_t(const Model &)> Value(IntegerVariable v)
This checks that the variable is fixed.
void AdaptGlobalParameters(const CpModelProto &model_proto, Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Reads a Domain from the domain field of a proto.
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
SatSolver::Status MinimizeIntegerVariableWithLinearScanAndLazyEncoding(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
void QuickSolveWithHint(const CpModelProto &model_proto, Model *model)
std::function< BooleanOrIntegerLiteral()> ConstructUserSearchStrategy(const CpModelProto &cp_model_proto, Model *model)
Constructs the search strategy specified in the given CpModelProto.
absl::string_view ConstraintCaseName(ConstraintProto::ConstraintCase constraint_case)
void LoadDebugSolution(const CpModelProto &model_proto, Model *model)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
void RegisterVariableBoundsLevelZeroExport(const CpModelProto &, SharedBoundsManager *shared_bounds_manager, Model *model)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
void RegisterVariableBoundsLevelZeroImport(const CpModelProto &model_proto, SharedBoundsManager *shared_bounds_manager, Model *model)
void LoadCpModel(const CpModelProto &model_proto, Model *model)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
bool VariableIsPositive(IntegerVariable i)
void RegisterObjectiveBestBoundExport(IntegerVariable objective_var, SharedResponseManager *shared_response_manager, Model *model)
std::function< void(Model *)> ExcludeCurrentSolutionAndBacktrack()
int RegisterClausesLevelZeroImport(int id, SharedClausesManager *shared_clauses_manager, Model *model)
void RestrictObjectiveDomainWithBinarySearch(IntegerVariable objective_var, const std::function< void()> &feasible_solution_observer, Model *model)
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
In SWIG mode, we don't want anything besides these top-level includes.
std::string ProtobufDebugString(const P &message)
std::vector< int64_t > proto_values
util_intops::StrongVector< IntegerVariable, bool > ivar_has_value
util_intops::StrongVector< IntegerVariable, IntegerValue > ivar_values
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< CutGenerator > cut_generators
std::vector< LinearConstraint > linear_constraints
IntegerVariable objective_var
std::function< BooleanOrIntegerLiteral()> user_search
Contains the search specified by the user in CpModelProto.
ModelSharedTimeLimit *const time_limit
SharedResponseManager *const response
SharedClasses(const CpModelProto *proto, Model *global_model)
double objective_value
The value objective_vector^T * (solution - center_point).
#define SOLVER_LOG(logger,...)