25#include "absl/container/flat_hash_set.h"
26#include "absl/log/check.h"
27#include "absl/log/log.h"
28#include "absl/log/vlog_is_on.h"
29#include "absl/random/distributions.h"
30#include "absl/strings/str_cat.h"
31#include "absl/types/span.h"
62 const IntegerValue lb = integer_trail->
LowerBound(var);
72 if (variables.contains(var)) {
74 }
else if (variables.contains(
NegationOf(var))) {
82 const IntegerValue var_lb = integer_trail->
LowerBound(var);
83 const IntegerValue var_ub = integer_trail->
UpperBound(var);
84 CHECK_LT(var_lb, var_ub);
86 const IntegerValue chosen_value =
87 var_lb + std::max(IntegerValue(1), (var_ub - var_lb) / IntegerValue(2));
94 const IntegerValue lb = integer_trail->
LowerBound(var);
95 const IntegerValue ub = integer_trail->UpperBound(var);
97 const absl::flat_hash_set<IntegerVariable>& variables =
105 const bool branch_down_feasible = value >= lb && value < ub;
106 const bool branch_up_feasible = value > lb && value <= ub;
107 if (variables.contains(var) && branch_down_feasible) {
109 }
else if (variables.contains(
NegationOf(var)) && branch_up_feasible) {
111 }
else if (branch_down_feasible) {
113 }
else if (branch_up_feasible) {
124 const auto& it = lp_dispatcher->find(positive_var);
126 it == lp_dispatcher->end() ? nullptr : it->second;
139 const IntegerValue value = IntegerValue(
156 const int proto_var =
174 absl::Span<const IntegerVariable> vars,
Model* model) {
177 [ vars = std::vector<IntegerVariable>(vars.begin(), vars.end()),
179 for (
const IntegerVariable var : vars) {
190 return [lp_values, integer_trail, model]() {
191 double best_fractionality = 0.0;
193 for (IntegerVariable var(0); var < lp_values->size(); var += 2) {
194 if (integer_trail->IsFixed(var))
continue;
195 const double lp_value = (*lp_values)[var];
196 const double fractionality = std::abs(lp_value - std::round(lp_value));
197 if (fractionality > best_fractionality) {
198 best_fractionality = fractionality;
202 var, IntegerValue(std::floor(lp_value)), model));
214 return [lp_values, encoder, pseudo_costs, integer_trail]() {
215 double best_score = 0.0;
217 for (IntegerVariable var(0); var < lp_values->size(); var += 2) {
219 const IntegerValue lb = integer_trail->LowerBound(var);
220 const IntegerValue ub = integer_trail->UpperBound(var);
221 if (lb != 0 || ub != 1)
continue;
224 const LiteralIndex index =
228 const double lp_value = (*lp_values)[var];
230 pseudo_costs->BoolPseudoCost(
Literal(index), lp_value);
231 if (score > best_score) {
243 return [lp_values, pseudo_costs]() {
244 double best_score = 0.0;
246 for (IntegerVariable var(0); var < lp_values->size(); var += 2) {
248 pseudo_costs->EvaluateVar(var, *lp_values);
260 if (info.
score > best_score) {
261 best_score = info.
score;
276std::function<BooleanOrIntegerLiteral()>
278 absl::Span<const IntegerVariable> vars,
Model* model) {
281 [ vars = std::vector<IntegerVariable>(vars.begin(), vars.end()),
284 IntegerValue candidate_lb;
285 for (
const IntegerVariable var : vars) {
286 const IntegerValue lb = integer_trail->LowerBound(var);
301 for (
const auto& h : heuristics) {
302 DCHECK(h !=
nullptr);
305 return [heuristics]() {
306 for (
const auto& h : heuristics) {
308 if (decision.
HasValue())
return decision;
316 value_selection_heuristics,
324 if (!current_decision.
HasValue())
return current_decision;
329 sat_policy->InStablePhase()) {
330 return current_decision;
335 for (
const auto& value_heuristic : value_selection_heuristics) {
340 return current_decision;
347 for (
const IntegerVariable var : encoder->GetAllAssociatedVariables(
350 for (
const auto& value_heuristic : value_selection_heuristics) {
356 return current_decision;
361 auto* lp_constraints =
363 int num_lp_variables = 0;
365 num_lp_variables += lp->NumVariables();
367 const int num_integer_variables =
369 return (num_integer_variables <= 2 * num_lp_variables);
381 value_selection_heuristics;
390 value_selection_heuristics.push_back([model](IntegerVariable var) {
398 if (response_manager !=
nullptr) {
399 VLOG(3) <<
"Using best solution value selection heuristic.";
400 value_selection_heuristics.push_back(
401 [model, response_manager](IntegerVariable var) {
403 var, response_manager->SolutionPool().BestSolutions(), model);
412 value_selection_heuristics.push_back([integer_trail, objective_definition](
413 IntegerVariable var) {
419 var_selection_heuristic, model);
426 return [sat_solver, trail, decision_policy] {
427 const bool all_assigned = trail->Index() == sat_solver->
NumVariables();
429 const Literal result = decision_policy->NextBranch();
439 const IntegerVariable obj_var = objective_definition->
objective_var;
444 return [obj_var, integer_trail, sat_solver, random]() {
446 const int level = sat_solver->CurrentDecisionLevel();
449 const IntegerValue obj_lb = integer_trail->LowerBound(obj_var);
450 const IntegerValue obj_ub = integer_trail->UpperBound(obj_var);
451 if (obj_lb == obj_ub)
return result;
452 const IntegerValue mid = (obj_ub - obj_lb) / 2;
453 const IntegerValue new_ub =
454 obj_lb + absl::LogUniform<int64_t>(*random, 0, mid.value());
463 const bool has_objective =
465 if (!has_objective) {
471 return [pseudo_costs, integer_trail]() {
472 const IntegerVariable chosen_var = pseudo_costs->GetBestDecisionVar();
490 const int64_t randomization_size = std::max<int64_t>(
497 const int num_intervals = repo->NumIntervals();
499 bool rev_is_in_dive =
false;
500 std::vector<IntervalVariable> intervals(num_intervals);
501 std::vector<IntegerValue> cached_start_mins(num_intervals);
502 for (IntervalVariable
i(0);
i < num_intervals; ++
i) {
503 intervals[
i.value()] =
i;
507 return [=]()
mutable {
523 bool operator<(
const ToSchedule& other)
const {
524 return std::tie(start_min, start_max, size_min, noise) <
525 std::tie(other.start_min, other.start_max, other.size_min,
531 bool MightBeBetter(
const ToSchedule& other)
const {
532 return std::tie(start_min, start_max) <=
533 std::tie(other.start_min, other.start_max);
536 std::vector<ToSchedule> top_decisions;
537 top_decisions.reserve(randomization_size);
538 top_decisions.resize(1);
541 rev_int_repo->SaveState(&rev_fixed);
545 for (
int i = rev_fixed;
i < num_intervals; ++
i) {
546 const ToSchedule& worst = top_decisions.back();
547 if (rev_is_in_dive && cached_start_mins[
i] > worst.start_min) {
551 const IntervalVariable interval = intervals[
i];
552 if (repo->IsAbsent(interval)) {
553 std::swap(intervals[
i], intervals[rev_fixed]);
554 std::swap(cached_start_mins[
i], cached_start_mins[rev_fixed]);
561 if (repo->IsPresent(interval) && integer_trail->IsFixed(start) &&
562 integer_trail->IsFixed(
end)) {
563 std::swap(intervals[
i], intervals[rev_fixed]);
564 std::swap(cached_start_mins[
i], cached_start_mins[rev_fixed]);
569 ToSchedule candidate;
570 if (repo->IsOptional(interval)) {
575 const Literal lit = repo->PresenceLiteral(interval);
576 candidate.start_min = integer_trail->ConditionalLowerBound(lit, start);
577 candidate.start_max = integer_trail->ConditionalUpperBound(lit, start);
579 candidate.start_min = integer_trail->LowerBound(start);
580 candidate.start_max = integer_trail->UpperBound(start);
582 cached_start_mins[
i] = candidate.start_min;
583 if (top_decisions.size() < randomization_size ||
584 candidate.MightBeBetter(worst)) {
591 candidate.start = start;
593 candidate.presence = repo->IsOptional(interval)
594 ? repo->PresenceLiteral(interval).Index()
597 std::max(integer_trail->LowerBound(repo->Size(interval)),
598 integer_trail->LowerBound(
end) - candidate.start_min);
599 candidate.noise = absl::Uniform(*random, 0.0, 1.0);
601 if (top_decisions.size() == randomization_size) {
603 if (worst < candidate)
continue;
604 top_decisions.pop_back();
606 top_decisions.push_back(candidate);
607 if (top_decisions.size() > 1) {
608 std::sort(top_decisions.begin(), top_decisions.end());
615 watcher->SetUntilNextBacktrack(&rev_is_in_dive);
617 const ToSchedule best =
618 top_decisions.size() == 1
619 ? top_decisions.front()
620 : top_decisions[absl::Uniform(
621 *random, 0,
static_cast<int>(top_decisions.size()))];
622 if (top_decisions.size() > 1) {
623 VLOG(2) <<
"Choose among " << top_decisions.size() <<
" "
624 << best.start_min <<
" " << best.size_min
625 <<
"[t=" << top_decisions.front().start_min
626 <<
", s=" << top_decisions.front().size_min
627 <<
", t=" << top_decisions.back().start_min
628 <<
", s=" << top_decisions.back().size_min <<
"]";
635 heuristic->next_decision_override = [trail, integer_trail, best,
636 num_times]()
mutable {
637 if (++num_times > 5) {
641 VLOG(3) <<
"Skipping ... ";
647 if (!trail->Assignment().LiteralIsAssigned(
Literal(best.presence))) {
648 VLOG(3) <<
"assign " << best.presence;
651 if (trail->Assignment().LiteralIsFalse(
Literal(best.presence))) {
652 VLOG(2) <<
"unperformed.";
658 if (!integer_trail->IsFixed(best.start)) {
659 const IntegerValue start_min = integer_trail->LowerBound(best.start);
660 VLOG(3) <<
"start == " << start_min;
665 if (!integer_trail->IsFixed(best.end)) {
666 const IntegerValue end_min = integer_trail->LowerBound(best.end);
667 VLOG(3) <<
"end == " << end_min;
672 const IntegerValue start = integer_trail->LowerBound(best.start);
673 VLOG(2) <<
"Fixed @[" << start <<
","
674 << integer_trail->LowerBound(best.end) <<
"]"
676 ? absl::StrCat(
" presence=",
679 << (best.start_min < start ? absl::StrCat(
" start_at_selection=",
680 best.start_min.value())
685 return heuristic->next_decision_override();
691bool PrecedenceIsBetter(SchedulingConstraintHelper* helper,
int a,
692 SchedulingConstraintHelper* other_helper,
int other_a) {
693 return std::make_tuple(helper->StartMin(a), helper->StartMax(a),
694 helper->SizeMin(a)) <
695 std::make_tuple(other_helper->StartMin(other_a),
696 other_helper->StartMax(other_a),
697 other_helper->SizeMin(other_a));
714 if (!helper->SynchronizeAndSetTimeDirection(
true)) {
723 for (
auto [
b, time] : helper->TaskByIncreasingStartMin()) {
724 if (helper->IsAbsent(
b))
continue;
725 if (a == -1 || helper->EndMin(a) <= helper->StartMin(
b)) {
731 if (PrecedenceIsBetter(helper,
b, helper, a)) {
735 if (helper->EndMin(a) <= helper->StartMin(
b)) {
743 if (best_helper ==
nullptr ||
744 PrecedenceIsBetter(helper, a, best_helper, best_before)) {
745 best_helper = helper;
753 if (best_helper !=
nullptr) {
755 for (
const int t : {best_before, best_after}) {
762 VLOG(2) <<
"New disjunctive precedence: "
768 repo->GetOrCreateDisjunctivePrecedenceLiteralIfNonTrivial(a,
b));
789 return [repo, integer_trail, trail, search_helper]() {
793 for (
const auto h : repo->AllCumulativeHelpers()) {
794 auto* helper = h.task_helper;
795 if (!helper->SynchronizeAndSetTimeDirection(
true)) {
799 const int num_tasks = helper->NumTasks();
800 std::vector<IntegerValue> added_demand(num_tasks, 0);
803 const auto& by_smin = helper->TaskByIncreasingStartMin();
804 const auto& by_emin = helper->TaskByIncreasingEndMin();
805 const IntegerValue capacity_max = integer_trail->UpperBound(h.capacity);
808 IntegerValue current_height = 0;
809 int first_skipped_task = -1;
814 while (!found && next_end < num_tasks) {
815 IntegerValue time = by_emin[next_end].time;
816 if (next_start < num_tasks) {
817 time = std::min(time, by_smin[next_start].time);
822 for (; next_end < num_tasks && by_emin[next_end].time == time;
824 const int t = by_emin[next_end].task_index;
825 if (!helper->IsPresent(t))
continue;
826 if (added_demand[t] > 0) {
827 current_height -= added_demand[t];
831 added_demand[t] = -1;
838 for (; next_start < num_tasks && by_smin[next_start].time == time;
840 const int t = by_smin[next_start].task_index;
841 if (!helper->IsPresent(t))
continue;
842 if (added_demand[t] == -1)
continue;
843 const IntegerValue demand_min = h.demand_helper->DemandMin(t);
844 if (current_height + demand_min <= capacity_max) {
845 added_demand[t] = demand_min;
846 current_height += demand_min;
847 }
else if (first_skipped_task == -1) {
849 first_skipped_task = t;
857 if (first_skipped_task == -1) {
865 std::vector<int> open_tasks;
866 for (
int t = 0; t < num_tasks; ++t) {
867 if (added_demand[t] <= 0)
continue;
868 open_tasks.push_back(t);
870 open_tasks.push_back(first_skipped_task);
877 bool found_precedence_to_add =
false;
878 helper->ResetReason();
879 for (
const int s : open_tasks) {
880 for (
const int t : open_tasks) {
881 if (s == t)
continue;
884 if (helper->TaskIsBeforeOrIsOverlapping(t, s)) {
885 helper->AddReasonForBeingBeforeAssumingNoOverlap(t, s);
891 CHECK_LT(helper->StartMin(s), helper->EndMin(t));
892 CHECK_LT(helper->StartMin(t), helper->EndMin(s));
895 const LiteralIndex existing = repo->GetPrecedenceLiteral(
896 helper->Ends()[s], helper->Starts()[t]);
898 if (trail->Assignment().LiteralIsTrue(
Literal(existing))) {
908 if (trail->Assignment().LiteralIsFalse(
Literal(existing))) {
909 helper->AddLiteralReason(
Literal(existing));
914 CHECK(repo->CreatePrecedenceLiteralIfNonTrivial(
915 helper->Ends()[s], helper->Starts()[t]));
919 best_helper = helper;
922 found_precedence_to_add =
true;
925 if (found_precedence_to_add)
break;
927 if (found_precedence_to_add)
break;
934 if (!h.capacity.IsConstant()) {
935 helper->AddIntegerReason(
936 integer_trail->UpperBoundAsLiteral(h.capacity));
938 const auto& demands = h.demand_helper->Demands();
939 for (
const int t : open_tasks) {
940 if (helper->IsOptional(t)) {
941 CHECK(trail->Assignment().LiteralIsTrue(helper->PresenceLiteral(t)));
942 helper->AddLiteralReason(helper->PresenceLiteral(t).Negated());
946 helper->AddIntegerReason(integer_trail->LowerBoundAsLiteral(d));
949 (void)helper->ReportConflict();
950 search_helper->NotifyThatConflictWasFoundDuringGetDecision();
952 LOG(INFO) <<
"Conflict between precedences !";
953 for (
const int t : open_tasks) LOG(INFO) << helper->TaskDebugString(t);
960 if (best_helper !=
nullptr) {
961 VLOG(2) <<
"New precedence: " << best_helper->
TaskDebugString(best_before)
965 repo->CreatePrecedenceLiteralIfNonTrivial(end_a, start_b);
967 repo->GetPrecedenceLiteral(end_a, start_b));
975 bool lns_mode,
Model* model) {
984 std::vector<double> weights;
987 policies.push_back(
SequentialSearch({sat_policy, heuristics.fixed_search}));
988 weights.push_back(5);
991 if (heuristics.user_search !=
nullptr) {
993 {heuristics.user_search, sat_policy, heuristics.fixed_search}));
994 weights.push_back(1);
998 if (heuristics.heuristic_search !=
nullptr) {
1001 heuristics.integer_completion_search}));
1002 weights.push_back(1);
1003 }
else if (heuristics.user_search ==
nullptr) {
1006 {
PseudoCost(model), sat_policy, heuristics.integer_completion_search}));
1007 weights.push_back(1);
1013 std::discrete_distribution<int> var_dist(weights.begin(), weights.end());
1017 value_selection_heuristics;
1018 std::vector<int> value_selection_weight;
1021 const int linearization_level =
1024 value_selection_heuristics.push_back([model](IntegerVariable var) {
1027 value_selection_weight.push_back(linearization_level == 2 ? 4 : 2);
1033 CHECK(response_manager !=
nullptr);
1034 value_selection_heuristics.push_back(
1035 [model, response_manager](IntegerVariable var) {
1037 var, response_manager->SolutionPool().BestSolutions(), model);
1039 value_selection_weight.push_back(5);
1044 value_selection_heuristics.push_back([integer_trail](IntegerVariable var) {
1047 value_selection_weight.push_back(1);
1050 value_selection_weight.push_back(10);
1054 std::discrete_distribution<int> val_dist(value_selection_weight.begin(),
1055 value_selection_weight.end());
1057 int policy_index = 0;
1058 int val_policy_index = 0;
1061 return [=]()
mutable {
1065 decision_policy->ResetDecisionHeuristic();
1069 if (objective !=
nullptr && absl::Bernoulli(*random, 0.2)) {
1071 IntegerValue max_abs_weight = 0;
1072 for (
const IntegerValue coeff : objective->coeffs) {
1073 max_abs_weight = std::max(max_abs_weight,
IntTypeAbs(coeff));
1075 const double max_abs_weight_double =
ToDouble(max_abs_weight);
1077 const int objective_size = objective->vars.size();
1078 for (
int i = 0;
i < objective_size; ++
i) {
1079 const IntegerVariable var = objective->vars[
i];
1080 if (integer_trail->LowerBound(var) != 0)
continue;
1081 if (integer_trail->UpperBound(var) != 1)
continue;
1087 const IntegerValue coeff = objective->coeffs[
i];
1088 const double abs_weight =
1089 std::abs(
ToDouble(objective->coeffs[
i])) / max_abs_weight_double;
1094 decision_policy->SetAssignmentPreference(
1095 coeff > 0 ? literal.
Negated() : literal, abs_weight);
1100 policy_index = var_dist(*(random));
1103 val_policy_index = val_dist(*(random));
1108 if (!current_decision.
HasValue())
return current_decision;
1111 if (val_policy_index >= value_selection_heuristics.size()) {
1112 return current_decision;
1117 value_selection_heuristics[val_policy_index](
1120 return current_decision;
1124 for (
const IntegerVariable var : encoder->GetAllAssociatedVariables(
1128 value_selection_heuristics[val_policy_index](var);
1133 return current_decision;
1138 absl::Span<const BooleanOrIntegerVariable> vars,
1139 absl::Span<const IntegerValue> values,
Model* model) {
1150 *rev_start_index = 0;
1154 std::vector<BooleanOrIntegerVariable>(vars.begin(), vars.end()),
1155 values = std::vector<IntegerValue>(values.begin(), values.end())]() {
1156 rev_int_repo->SaveState(rev_start_index);
1157 for (
int i = *rev_start_index;
i < vars.size(); ++
i) {
1158 const IntegerValue value = values[
i];
1160 if (trail->Assignment().VariableIsAssigned(vars[
i].bool_var))
continue;
1163 *rev_start_index =
i;
1165 Literal(vars[
i].bool_var, value == 1).Index());
1167 const IntegerVariable integer_var = vars[
i].int_var;
1168 if (integer_trail->IsFixed(integer_var))
continue;
1172 positive_var, positive_var != integer_var ? -value : value, model);
1175 *rev_start_index =
i;
1188 bool reset_at_next_call =
true;
1189 int next_num_failures = 0;
1190 return [=]()
mutable {
1191 if (reset_at_next_call) {
1193 reset_at_next_call =
false;
1194 }
else if (solver->
num_failures() >= next_num_failures) {
1195 reset_at_next_call =
true;
1197 return reset_at_next_call;
1203 return [policy]() {
return policy->ShouldRestart(); };
1208std::function<BooleanOrIntegerLiteral()> WrapIntegerLiteralHeuristic(
1209 std::function<IntegerLiteral()> f) {
1210 return [f]() {
return BooleanOrIntegerLiteral(f()); };
1252 auto no_restart = []() {
return false; };
1278 auto no_restart = []() {
return false; };
1290 for (
const auto& ct :
1292 lp_heuristics.push_back(WrapIntegerLiteralHeuristic(
1293 ct->HeuristicLpReducedCostAverageBranching()));
1295 if (lp_heuristics.empty()) {
1339 incomplete_heuristics,
1342 complete_heuristics.reserve(incomplete_heuristics.size());
1343 for (
const auto& incomplete : incomplete_heuristics) {
1344 complete_heuristics.push_back(
1347 return complete_heuristics;
1353 sat_solver_(model->GetOrCreate<
SatSolver>()),
1358 prober_(model->GetOrCreate<
Prober>()),
1360 time_limit_(model->GetOrCreate<
TimeLimit>()),
1365 DCHECK(sat_solver_->PropagationIsDone());
1371 if (integer_trail_->HasPendingRootLevelDeduction()) {
1372 sat_solver_->Backtrack(0);
1373 if (!sat_solver_->Propagate()) {
1375 sat_solver_->ProcessCurrentConflict();
1376 sat_solver_->NotifyThatModelIsUnsat();
1382 if (sat_solver_->CurrentDecisionLevel() != 0)
return true;
1386 const int saved_bool_index = sat_solver_->LiteralTrail().Index();
1387 const int saved_integer_index = integer_trail_->num_enqueues();
1390 for (
const auto& cb : level_zero_callbacks->callbacks) {
1392 sat_solver_->NotifyThatModelIsUnsat();
1398 if (sat_solver_->LiteralTrail().Index() > saved_bool_index ||
1399 integer_trail_->num_enqueues() > saved_integer_index ||
1400 integer_trail_->HasPendingRootLevelDeduction()) {
1401 if (!sat_solver_->Propagate()) {
1403 sat_solver_->ProcessCurrentConflict();
1404 sat_solver_->NotifyThatModelIsUnsat();
1408 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0)
1409 <<
"Level zero callback left decisions on the trail ?";
1412 if (parameters_.use_sat_inprocessing() &&
1413 !inprocessing_->InprocessingRound()) {
1414 sat_solver_->NotifyThatModelIsUnsat();
1417 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0)
1418 <<
"Inprocessing left decisions on the trail ?";
1423 return sat_solver_->ReapplyAssumptionsIfNeeded();
1439 if (sat_solver_->Assignment().LiteralIsAssigned(literal)) {
1444 VLOG(1) <<
"Trying to take a decision that is already assigned!"
1445 <<
" Fix this. Continuing for now...";
1448 return literal.
Index();
1456 if (integer_trail_->InPropagationLoop()) {
1457 const IntegerVariable var =
1458 integer_trail_->NextVariableToBranchOnInPropagationLoop();
1466 if (must_process_conflict_) {
1467 must_process_conflict_ =
false;
1468 sat_solver_->ProcessCurrentConflict();
1469 (void)sat_solver_->FinishPropagation();
1474 integer_trail_->CurrentBranchHadAnIncompletePropagation()) {
1475 const IntegerVariable var = integer_trail_->FirstUnassignedVariable();
1480 if (!new_decision.
HasValue())
break;
1484 }
while (!time_limit_->LimitReached());
1489 bool use_representative) {
1496 if (use_representative) {
1497 decision = binary_implication_graph_->RepresentativeOf(decision);
1498 CHECK(!sat_solver_->Assignment().LiteralIsAssigned(decision));
1501 pseudo_costs_->BeforeTakingDecision(decision);
1507 const int old_level = sat_solver_->CurrentDecisionLevel();
1508 const int index = sat_solver_->EnqueueDecisionAndBackjumpOnConflict(decision);
1513 if (old_level == 0 && sat_solver_->CurrentDecisionLevel() == 1) {
1514 if (!implied_bounds_->ProcessIntegerTrail(decision))
return false;
1515 product_detector_->ProcessTrailAtLevelOne();
1519 pseudo_costs_->AfterTakingDecision(
1520 sat_solver_->CurrentDecisionLevel() <= old_level);
1522 sat_solver_->AdvanceDeterministicTime(time_limit_);
1523 return sat_solver_->ReapplyAssumptionsIfNeeded();
1531 CHECK_NE(num_policies, 0);
1544 if (!sat_solver_->FinishPropagation())
return sat_solver_->UnsatStatus();
1547 const int64_t old_num_conflicts = sat_solver_->num_failures();
1548 const int64_t conflict_limit = parameters_.max_number_of_conflicts();
1549 int64_t num_decisions_since_last_lp_record_ = 0;
1550 while (!time_limit_->LimitReached() &&
1551 (sat_solver_->num_failures() - old_num_conflicts < conflict_limit)) {
1558 sat_solver_->IncreaseNumRestarts();
1559 sat_solver_->Backtrack(0);
1560 if (!sat_solver_->FinishPropagation()) {
1561 return sat_solver_->UnsatStatus();
1571 if (sat_solver_->ModelIsUnsat())
return sat_solver_->UnsatStatus();
1608 if (parameters_.use_optimization_hints()) {
1610 const auto& trail = *model_->GetOrCreate<
Trail>();
1611 for (
int i = 0;
i < trail.Index(); ++
i) {
1612 sat_decision->SetAssignmentPreference(trail[
i], 0.0);
1619 return sat_solver_->UnsatStatus();
1635 parameters_.linearization_level() >= 2) {
1636 num_decisions_since_last_lp_record_++;
1637 if (num_decisions_since_last_lp_record_ >= 100) {
1642 num_decisions_since_last_lp_record_ = 0;
1650 const std::vector<Literal>& assumptions,
Model* model) {
1653 if (!sat_solver->ResetToLevelZero()) {
1654 return sat_solver->UnsatStatus();
1660 DCHECK(sat_solver->PropagationIsDone());
1661 if (!helper->BeforeTakingDecision()) {
1662 return sat_solver->UnsatStatus();
1666 if (!sat_solver->ResetWithGivenAssumptions(assumptions)) {
1667 return sat_solver->UnsatStatus();
1670 return helper->SolveIntegerProblem();
1674 const IntegerVariable num_vars =
1676 std::vector<IntegerVariable> all_variables;
1677 for (IntegerVariable var(0); var < num_vars; ++var) {
1678 all_variables.push_back(var);
1690#define RETURN_IF_NOT_FEASIBLE(test) \
1691 const SatSolver::Status status = (test); \
1692 if (status != SatSolver::FEASIBLE) return status;
1697 sat_solver_(model->GetOrCreate<
SatSolver>()),
1698 time_limit_(model->GetOrCreate<
TimeLimit>()),
1701 trail_(model->GetOrCreate<
Trail>()),
1707 prober_(model->GetOrCreate<
Prober>()),
1711 active_limit_(parameters_.shaving_search_deterministic_time()) {
1713 absl::flat_hash_set<BooleanVariable> visited;
1715 trail_index_at_start_of_iteration_ = trail_->Index();
1716 integer_trail_index_at_start_of_iteration_ = integer_trail_->Index();
1724 if (mapping->IsBoolean(v)) {
1725 const BooleanVariable bool_var = mapping->Literal(v).Variable();
1726 const auto [_, inserted] = visited.insert(bool_var);
1728 bool_vars_.push_back(bool_var);
1731 IntegerVariable var = mapping->Integer(v);
1732 if (integer_trail_->IsFixed(var))
continue;
1733 int_vars_.push_back(var);
1737 VLOG(2) <<
"Start continuous probing with " << bool_vars_.size()
1738 <<
" Boolean variables, " << int_vars_.size()
1739 <<
" integer variables, deterministic time limit = "
1740 << time_limit_->GetDeterministicLimit() <<
" on " << model_->Name();
1754 while (!time_limit_->LimitReached()) {
1755 if (parameters_.use_sat_inprocessing() &&
1756 !inprocessing_->InprocessingRound()) {
1757 sat_solver_->NotifyThatModelIsUnsat();
1758 return sat_solver_->UnsatStatus();
1762 const int64_t initial_num_literals_fixed =
1763 prober_->num_new_literals_fixed();
1764 const int64_t initial_num_bounds_shaved = num_bounds_shaved_;
1765 const auto& assignment = sat_solver_->Assignment();
1769 for (; current_int_var_ < int_vars_.size(); ++current_int_var_) {
1770 const IntegerVariable int_var = int_vars_[current_int_var_];
1771 if (integer_trail_->IsFixed(int_var))
continue;
1773 const Literal shave_lb_literal =
1775 int_var, integer_trail_->LowerBound(int_var)));
1776 const BooleanVariable shave_lb_var = shave_lb_literal.
Variable();
1777 const auto [_lb, lb_inserted] = probed_bool_vars_.insert(shave_lb_var);
1779 if (!prober_->ProbeOneVariable(shave_lb_var)) {
1782 num_literals_probed_++;
1785 const Literal shave_ub_literal =
1787 int_var, integer_trail_->UpperBound(int_var)));
1788 const BooleanVariable shave_ub_var = shave_ub_literal.
Variable();
1789 const auto [_ub, ub_inserted] = probed_bool_vars_.insert(shave_ub_var);
1791 if (!prober_->ProbeOneVariable(shave_ub_var)) {
1794 num_literals_probed_++;
1799 if (ReportStatus(lb_status))
return lb_status;
1802 if (ReportStatus(ub_status))
return ub_status;
1809 for (; current_bool_var_ < bool_vars_.size(); ++current_bool_var_) {
1810 const BooleanVariable& bool_var = bool_vars_[current_bool_var_];
1812 if (assignment.VariableIsAssigned(bool_var))
continue;
1814 const auto [_, inserted] = probed_bool_vars_.insert(bool_var);
1815 if (!inserted)
continue;
1817 if (!prober_->ProbeOneVariable(bool_var)) {
1820 num_literals_probed_++;
1822 const Literal literal(bool_var,
true);
1823 if (use_shaving_ && !assignment.LiteralIsAssigned(literal)) {
1825 if (ReportStatus(true_status))
return true_status;
1829 if (ReportStatus(false_status))
return false_status;
1835 if (parameters_.use_extended_probing()) {
1836 const auto at_least_one_literal_is_true =
1837 [&assignment](absl::Span<const Literal> literals) {
1838 for (
const Literal literal : literals) {
1839 if (assignment.LiteralIsTrue(literal)) {
1848 const SatClause* clause = clause_manager_->NextClauseToProbe();
1849 if (clause ==
nullptr)
break;
1850 if (at_least_one_literal_is_true(clause->
AsSpan()))
continue;
1854 if (assignment.LiteralIsAssigned(literal))
continue;
1855 tmp_dnf_.push_back({literal});
1857 ++num_at_least_one_probed_;
1858 if (!prober_->ProbeDnf(
"at_least_one", tmp_dnf_,
1868 const absl::Span<const Literal> at_most_one =
1869 binary_implication_graph_->NextAtMostOne();
1870 if (at_most_one.empty())
break;
1871 if (at_least_one_literal_is_true(at_most_one))
continue;
1874 tmp_literals_.clear();
1875 for (
const Literal literal : at_most_one) {
1876 if (assignment.LiteralIsAssigned(literal))
continue;
1877 tmp_dnf_.push_back({literal});
1878 tmp_literals_.push_back(literal.Negated());
1880 tmp_dnf_.push_back(tmp_literals_);
1881 ++num_at_most_one_probed_;
1882 if (!prober_->ProbeDnf(
"at_most_one", tmp_dnf_,
1891 const int limit = parameters_.probing_num_combinations_limit();
1892 const int max_num_bool_vars_for_pairs_probing =
1893 static_cast<int>(std::sqrt(2 * limit));
1894 const int num_bool_vars = bool_vars_.size();
1896 if (num_bool_vars < max_num_bool_vars_for_pairs_probing) {
1897 for (; current_bv1_ + 1 < bool_vars_.size(); ++current_bv1_) {
1898 const BooleanVariable bv1 = bool_vars_[current_bv1_];
1899 if (assignment.VariableIsAssigned(bv1))
continue;
1900 current_bv2_ = std::max(current_bv1_ + 1, current_bv2_);
1901 for (; current_bv2_ < bool_vars_.size(); ++current_bv2_) {
1902 const BooleanVariable& bv2 = bool_vars_[current_bv2_];
1903 if (assignment.VariableIsAssigned(bv2))
continue;
1904 if (!prober_->ProbeDnf(
"pair_of_bool_vars",
1905 {{Literal(bv1, false), Literal(bv2, false)},
1906 {Literal(bv1, false), Literal(bv2, true)},
1907 {Literal(bv1, true), Literal(bv2, false)},
1908 {Literal(bv1, true), Literal(bv2, true)}},
1917 for (; random_pair_of_bool_vars_probed_ < 10000;
1918 ++random_pair_of_bool_vars_probed_) {
1919 const BooleanVariable bv1 =
1920 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1921 if (assignment.VariableIsAssigned(bv1))
continue;
1922 const BooleanVariable bv2 =
1923 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1924 if (assignment.VariableIsAssigned(bv2) || bv1 == bv2) {
1927 if (!prober_->ProbeDnf(
"rnd_pair_of_bool_vars",
1928 {{Literal(bv1, false), Literal(bv2, false)},
1929 {Literal(bv1, false), Literal(bv2, true)},
1930 {Literal(bv1, true), Literal(bv2, false)},
1931 {Literal(bv1, true), Literal(bv2, true)}},
1941 const int max_num_bool_vars_for_triplet_probing =
1942 static_cast<int>(std::cbrt(2 * limit));
1944 const int loop_limit =
1945 num_bool_vars < max_num_bool_vars_for_triplet_probing
1946 ? num_bool_vars * (num_bool_vars - 1) * (num_bool_vars - 2) / 2
1948 for (; random_triplet_of_bool_vars_probed_ < loop_limit;
1949 ++random_triplet_of_bool_vars_probed_) {
1950 const BooleanVariable bv1 =
1951 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1952 if (assignment.VariableIsAssigned(bv1))
continue;
1953 const BooleanVariable bv2 =
1954 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1955 if (assignment.VariableIsAssigned(bv2) || bv1 == bv2) {
1958 const BooleanVariable bv3 =
1959 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1960 if (assignment.VariableIsAssigned(bv3) || bv1 == bv3 || bv2 == bv3) {
1964 for (
int i = 0;
i < 8; ++
i) {
1965 tmp_dnf_.push_back({
Literal(bv1, (
i & 4) > 0),
1970 if (!prober_->ProbeDnf(
"rnd_triplet_of_bool_vars", tmp_dnf_,
1981 const double dtime = parameters_.shaving_search_deterministic_time();
1982 const bool something_has_been_detected =
1983 num_bounds_shaved_ != initial_num_bounds_shaved ||
1984 prober_->num_new_literals_fixed() != initial_num_literals_fixed;
1985 if (something_has_been_detected) {
1986 active_limit_ = dtime;
1987 }
else if (active_limit_ <= 128 * dtime) {
1994 current_bool_var_ = 0;
1995 current_int_var_ = 0;
1998 random_pair_of_bool_vars_probed_ = 0;
1999 random_triplet_of_bool_vars_probed_ = 0;
2000 binary_implication_graph_->ResetAtMostOneIterator();
2001 clause_manager_->ResetToProbeIndex();
2002 probed_bool_vars_.clear();
2003 shaved_literals_.clear();
2005 const int new_trail_index = trail_->Index();
2006 const int new_integer_trail_index = integer_trail_->Index();
2012 parameters_.shaving_deterministic_time_in_probing_search() > 0.0;
2013 trail_index_at_start_of_iteration_ = new_trail_index;
2014 integer_trail_index_at_start_of_iteration_ = new_integer_trail_index;
2018 for (
int i = 0;
i < bool_vars_.size(); ++
i) {
2019 if (!sat_solver_->Assignment().VariableIsAssigned(bool_vars_[
i])) {
2020 bool_vars_[new_size++] = bool_vars_[
i];
2023 bool_vars_.resize(new_size);
2027 for (
int i = 0;
i < int_vars_.size(); ++
i) {
2028 if (!integer_trail_->IsFixed(int_vars_[
i])) {
2029 int_vars_[new_size++] = int_vars_[
i];
2032 int_vars_.resize(new_size);
2034 return SatSolver::LIMIT_REACHED;
2037#undef RETURN_IF_NOT_FEASIBLE
2039SatSolver::Status ContinuousProber::PeriodicSyncAndCheck() {
2041 if (--num_test_limit_remaining_ <= 0) {
2042 num_test_limit_remaining_ = kTestLimitPeriod;
2043 if (time_limit_->LimitReached())
return SatSolver::LIMIT_REACHED;
2047 if (--num_logs_remaining_ <= 0) {
2048 num_logs_remaining_ = kLogPeriod;
2053 if (--num_syncs_remaining_ <= 0) {
2054 num_syncs_remaining_ = kSyncPeriod;
2055 if (!sat_solver_->ResetToLevelZero())
return SatSolver::INFEASIBLE;
2056 for (
const auto& cb : level_zero_callbacks_->callbacks) {
2058 sat_solver_->NotifyThatModelIsUnsat();
2059 return SatSolver::INFEASIBLE;
2064 return SatSolver::FEASIBLE;
2067SatSolver::Status ContinuousProber::ShaveLiteral(Literal literal) {
2068 const auto [_, inserted] = shaved_literals_.insert(literal.Index());
2069 if (trail_->Assignment().LiteralIsAssigned(literal) || !inserted) {
2070 return SatSolver::LIMIT_REACHED;
2072 num_bounds_tried_++;
2074 const double original_dtime_limit = time_limit_->GetDeterministicLimit();
2075 time_limit_->ChangeDeterministicLimit(
2076 std::min(original_dtime_limit,
2077 time_limit_->GetElapsedDeterministicTime() + active_limit_));
2078 const SatSolver::Status status =
2080 time_limit_->ChangeDeterministicLimit(original_dtime_limit);
2081 if (ReportStatus(status))
return status;
2083 if (status == SatSolver::ASSUMPTIONS_UNSAT) {
2084 num_bounds_shaved_++;
2089 if (!sat_solver_->ResetToLevelZero())
return SatSolver::INFEASIBLE;
2093bool ContinuousProber::ReportStatus(
const SatSolver::Status status) {
2094 return status == SatSolver::INFEASIBLE || status == SatSolver::FEASIBLE;
2097void ContinuousProber::LogStatistics() {
2098 if (shared_response_manager_ ==
nullptr ||
2099 shared_bounds_manager_ ==
nullptr) {
2102 if (VLOG_IS_ON(1)) {
2103 shared_response_manager_->LogMessageWithThrottling(
2106 " (iterations=", iteration_,
" linearization_level=",
2107 parameters_.linearization_level(),
" active_limit=", active_limit_,
2108 " shaving=", use_shaving_,
" active_bool_vars=", bool_vars_.size(),
2109 " active_int_vars=", integer_trail_->NumIntegerVariables(),
2110 " literals fixed/probed=", prober_->num_new_literals_fixed(),
"/",
2111 num_literals_probed_,
" bounds shaved/tried=", num_bounds_shaved_,
2112 "/", num_bounds_tried_,
" new_integer_bounds=",
2113 shared_bounds_manager_->NumBoundsExported(
"probing"),
2114 " new_binary_clause=", prober_->num_new_binary_clauses(),
2115 " num_at_least_one_probed=", num_at_least_one_probed_,
2116 " num_at_most_one_probed=", num_at_most_one_probed_,
")"));
SatSolver::Status Probe()
ContinuousProber(const CpModelProto &model_proto, Model *model)
int variables_size() const
LiteralIndex GetAssociatedLiteral(IntegerLiteral i_lit) const
IntegerSearchHelper(Model *model)
bool TakeDecision(Literal decision, bool use_representative=true)
LiteralIndex GetDecisionLiteral(const BooleanOrIntegerLiteral &decision)
bool GetDecision(const std::function< BooleanOrIntegerLiteral()> &f, LiteralIndex *decision)
ABSL_MUST_USE_RESULT bool BeforeTakingDecision()
SatSolver::Status SolveIntegerProblem()
IntegerValue LowerBound(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
bool SolutionIsInteger() const
double GetSolutionValue(IntegerVariable variable) const
std::string DebugString() const
LiteralIndex Index() const
BooleanVariable Variable() const
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
absl::Span< const Literal > AsSpan() const
bool exploit_best_solution() const
static constexpr SearchBranching PARTIAL_FIXED_SEARCH
static constexpr SearchBranching PORTFOLIO_SEARCH
bool exploit_all_lp_solution() const
::int64_t search_random_variable_pool_size() const
static constexpr SearchBranching AUTOMATIC_SEARCH
static constexpr SearchBranching RANDOMIZED_SEARCH
bool exploit_integer_lp_solution() const
bool use_objective_lb_search() const
static constexpr SearchBranching FIXED_SEARCH
static constexpr SearchBranching PSEUDO_COST_SEARCH
bool randomize_search() const
static constexpr SearchBranching HINT_SEARCH
::operations_research::sat::SatParameters_SearchBranching search_branching() const
bool exploit_objective() const
static constexpr SearchBranching PORTFOLIO_WITH_QUICK_RESTART_SEARCH
static constexpr SearchBranching LP_SEARCH
int64_t num_failures() const
const VariablesAssignment & Assignment() const
int CurrentDecisionLevel() const
IntervalDefinition GetIntervalDefinition(int index) const
bool IsPresent(int t) const
Literal PresenceLiteral(int index) const
absl::Span< const AffineExpression > Starts() const
absl::Span< const AffineExpression > Ends() const
std::string TaskDebugString(int t) const
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
bool LiteralIsAssigned(Literal literal) const
#define RETURN_IF_NOT_FEASIBLE(test)
void ConfigureSearchHeuristics(Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntType IntTypeAbs(IntType t)
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
std::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(absl::Span< const IntegerVariable > vars, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
std::function< BooleanOrIntegerLiteral()> DisjunctivePrecedenceSearchHeuristic(Model *model)
std::vector< std::function< BooleanOrIntegerLiteral()> > CompleteHeuristics(absl::Span< const std::function< BooleanOrIntegerLiteral()> > incomplete_heuristics, const std::function< BooleanOrIntegerLiteral()> &completion_heuristic)
std::function< BooleanOrIntegerLiteral()> BoolPseudoCostHeuristic(Model *model)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
std::function< BooleanOrIntegerLiteral()> SequentialValueSelection(std::vector< std::function< IntegerLiteral(IntegerVariable)> > value_selection_heuristics, std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
std::function< BooleanOrIntegerLiteral()> ShaveObjectiveLb(Model *model)
std::function< BooleanOrIntegerLiteral()> IntegerValueSelectionHeuristic(std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
IntegerLiteral SplitAroundLpValue(IntegerVariable var, Model *model)
const IntegerVariable kNoIntegerVariable(-1)
std::function< BooleanOrIntegerLiteral()> UnassignedVarWithLowestMinAtItsMinHeuristic(absl::Span< const IntegerVariable > vars, Model *model)
IntegerLiteral ChooseBestObjectiveValue(IntegerVariable var, IntegerTrail *integer_trail, ObjectiveDefinition *objective_definition)
IntegerLiteral AtMinValue(IntegerVariable var, IntegerTrail *integer_trail)
void RecordLPRelaxationValues(Model *model)
SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model *model)
std::function< BooleanOrIntegerLiteral()> PseudoCost(Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
std::function< BooleanOrIntegerLiteral()> SchedulingSearchHeuristic(Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
std::function< bool()> RestartEveryKFailures(int k, SatSolver *solver)
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(bool lns_mode, Model *model)
IntegerLiteral SplitUsingBestSolutionValueInRepository(IntegerVariable var, const SharedSolutionRepository< int64_t > &solution_repo, Model *model)
std::function< BooleanOrIntegerLiteral()> FollowHint(absl::Span< const BooleanOrIntegerVariable > vars, absl::Span< const IntegerValue > values, Model *model)
bool LinearizedPartIsLarge(Model *model)
const int kUnsatTrailIndex
IntegerLiteral SplitAroundGivenValue(IntegerVariable var, IntegerValue value, Model *model)
const BooleanVariable kNoBooleanVariable(-1)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
std::function< bool()> SatSolverRestartPolicy(Model *model)
std::function< BooleanOrIntegerLiteral()> LpPseudoCostHeuristic(Model *model)
IntegerLiteral GreaterOrEqualToMiddleValue(IntegerVariable var, IntegerTrail *integer_trail)
double ToDouble(IntegerValue value)
std::function< BooleanOrIntegerLiteral()> MostFractionalHeuristic(Model *model)
std::function< BooleanOrIntegerLiteral()> CumulativePrecedenceSearchHeuristic(Model *model)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()> > heuristics)
ClosedInterval::Iterator end(ClosedInterval interval)
LiteralIndex boolean_literal_index
IntegerLiteral integer_literal
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
IntegerLiteral Negated() const
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
IntegerVariable objective_var
absl::flat_hash_set< IntegerVariable > objective_impacting_variables
IntegerLiteral down_branch
std::function< BooleanOrIntegerLiteral()> fixed_search
std::vector< std::function< bool()> > restart_policies
std::function< BooleanOrIntegerLiteral()> user_search
std::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies
std::function< BooleanOrIntegerLiteral()> hint_search
std::function< BooleanOrIntegerLiteral()> next_decision_override