24#include "absl/container/flat_hash_set.h"
25#include "absl/log/check.h"
26#include "absl/meta/type_traits.h"
27#include "absl/random/distributions.h"
28#include "absl/strings/str_cat.h"
29#include "absl/types/span.h"
32#include "ortools/sat/cp_model.pb.h"
47#include "ortools/sat/sat_parameters.pb.h"
65 const auto& variables =
68 if (variables.contains(
var)) {
80 CHECK_LT(var_lb, var_ub);
82 const IntegerValue chosen_value =
83 var_lb + std::max(IntegerValue(1), (var_ub - var_lb) / IntegerValue(2));
91 const IntegerValue ub = integer_trail->UpperBound(
var);
93 const absl::flat_hash_set<IntegerVariable>& variables =
101 const bool branch_down_feasible =
value >= lb &&
value < ub;
102 const bool branch_up_feasible =
value > lb &&
value <= ub;
103 if (variables.contains(
var) && branch_down_feasible) {
105 }
else if (variables.contains(
NegationOf(
var)) && branch_up_feasible) {
107 }
else if (branch_down_feasible) {
109 }
else if (branch_up_feasible) {
120 const auto& it = lp_dispatcher->find(positive_var);
122 it == lp_dispatcher->end() ? nullptr : it->second;
135 const IntegerValue
value = IntegerValue(
152 const int proto_var =
170 const std::vector<IntegerVariable>& vars,
Model*
model) {
172 return [ vars, integer_trail]() {
173 for (
const IntegerVariable
var : vars) {
184 return [lp_values, integer_trail,
model]() {
185 double best_fractionality = 0.0;
187 for (IntegerVariable
var(0);
var < lp_values->size();
var += 2) {
188 if (integer_trail->IsFixed(
var))
continue;
189 const double lp_value = (*lp_values)[
var];
190 const double fractionality = std::abs(lp_value - std::round(lp_value));
191 if (fractionality > best_fractionality) {
192 best_fractionality = fractionality;
196 var, IntegerValue(std::floor(lp_value)),
model));
208 return [lp_values, encoder, pseudo_costs, integer_trail]() {
209 double best_score = 0.0;
211 for (IntegerVariable
var(0);
var < lp_values->size();
var += 2) {
213 const IntegerValue lb = integer_trail->LowerBound(
var);
214 const IntegerValue ub = integer_trail->UpperBound(
var);
215 if (lb != 0 || ub != 1)
continue;
218 const LiteralIndex
index =
222 const double lp_value = (*lp_values)[
var];
225 if (score > best_score) {
237 return [lp_values, pseudo_costs]() {
238 double best_score = 0.0;
240 for (IntegerVariable
var(0);
var < lp_values->size();
var += 2) {
242 pseudo_costs->EvaluateVar(
var, *lp_values);
254 if (info.
score > best_score) {
255 best_score = info.
score;
270std::function<BooleanOrIntegerLiteral()>
272 const std::vector<IntegerVariable>& vars,
Model*
model) {
274 return [ vars, integer_trail]() {
276 IntegerValue candidate_lb;
277 for (
const IntegerVariable
var : vars) {
278 const IntegerValue lb = integer_trail->LowerBound(
var);
292 return [heuristics]() {
293 for (
const auto& h : heuristics) {
295 if (decision.
HasValue())
return decision;
303 value_selection_heuristics,
311 if (!current_decision.
HasValue())
return current_decision;
316 sat_policy->InStablePhase()) {
317 return current_decision;
322 for (
const auto& value_heuristic : value_selection_heuristics) {
327 return current_decision;
334 for (
const IntegerVariable
var : encoder->GetAllAssociatedVariables(
337 for (
const auto& value_heuristic : value_selection_heuristics) {
343 return current_decision;
348 auto* lp_constraints =
350 int num_lp_variables = 0;
352 num_lp_variables += lp->NumVariables();
354 const int num_integer_variables =
356 return (num_integer_variables <= 2 * num_lp_variables);
366 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
368 value_selection_heuristics;
377 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
385 if (response_manager !=
nullptr) {
386 VLOG(3) <<
"Using best solution value selection heuristic.";
387 value_selection_heuristics.push_back(
388 [
model, response_manager](IntegerVariable
var) {
390 var, response_manager->SolutionsRepository(),
model);
397 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
403 var_selection_heuristic,
model);
410 return [sat_solver, trail, decision_policy] {
411 const bool all_assigned = trail->Index() == sat_solver->
NumVariables();
413 const Literal result = decision_policy->NextBranch();
423 const IntegerVariable obj_var = objective_definition->
objective_var;
428 return [obj_var, integer_trail, sat_solver, random]() {
430 const int level = sat_solver->CurrentDecisionLevel();
433 const IntegerValue obj_lb = integer_trail->LowerBound(obj_var);
434 const IntegerValue obj_ub = integer_trail->UpperBound(obj_var);
435 if (obj_lb == obj_ub)
return result;
436 const IntegerValue mid = (obj_ub - obj_lb) / 2;
437 const IntegerValue new_ub =
438 obj_lb + absl::LogUniform<int64_t>(*random, 0, mid.value());
447 const bool has_objective =
449 if (!has_objective) {
455 return [pseudo_costs, integer_trail]() {
456 const IntegerVariable chosen_var = pseudo_costs->GetBestDecisionVar();
474 const int64_t randomization_size = std::max<int64_t>(
476 model->GetOrCreate<SatParameters>()->search_random_variable_pool_size());
481 const int num_intervals = repo->NumIntervals();
483 bool rev_is_in_dive =
false;
484 std::vector<IntervalVariable> intervals(num_intervals);
485 std::vector<IntegerValue> cached_start_mins(num_intervals);
486 for (IntervalVariable
i(0);
i < num_intervals; ++
i) {
487 intervals[
i.value()] =
i;
491 return [=]()
mutable {
507 bool operator<(
const ToSchedule& other)
const {
509 std::tie(other.start_min, other.start_max, other.size_min,
515 bool MightBeBetter(
const ToSchedule& other)
const {
517 std::tie(other.start_min, other.start_max);
520 std::vector<ToSchedule> top_decisions;
521 top_decisions.reserve(randomization_size);
522 top_decisions.resize(1);
525 rev_int_repo->SaveState(&rev_fixed);
529 for (
int i = rev_fixed;
i < num_intervals; ++
i) {
530 const ToSchedule& worst = top_decisions.back();
531 if (rev_is_in_dive && cached_start_mins[
i] > worst.start_min) {
535 const IntervalVariable
interval = intervals[
i];
537 std::swap(intervals[
i], intervals[rev_fixed]);
538 std::swap(cached_start_mins[
i], cached_start_mins[rev_fixed]);
545 if (repo->IsPresent(
interval) && integer_trail->IsFixed(
start) &&
546 integer_trail->IsFixed(
end)) {
547 std::swap(intervals[
i], intervals[rev_fixed]);
548 std::swap(cached_start_mins[
i], cached_start_mins[rev_fixed]);
553 ToSchedule candidate;
560 candidate.start_min = integer_trail->ConditionalLowerBound(
lit,
start);
561 candidate.start_max = integer_trail->ConditionalUpperBound(
lit,
start);
563 candidate.start_min = integer_trail->LowerBound(
start);
564 candidate.start_max = integer_trail->UpperBound(
start);
566 cached_start_mins[
i] = candidate.start_min;
567 if (top_decisions.size() < randomization_size ||
568 candidate.MightBeBetter(worst)) {
575 candidate.start =
start;
577 candidate.presence = repo->IsOptional(
interval)
578 ? repo->PresenceLiteral(
interval).Index()
581 std::max(integer_trail->LowerBound(repo->Size(
interval)),
582 integer_trail->LowerBound(
end) - candidate.start_min);
583 candidate.noise = absl::Uniform(*random, 0.0, 1.0);
585 if (top_decisions.size() == randomization_size) {
587 if (worst < candidate)
continue;
588 top_decisions.pop_back();
590 top_decisions.push_back(candidate);
591 if (top_decisions.size() > 1) {
592 std::sort(top_decisions.begin(), top_decisions.end());
599 watcher->SetUntilNextBacktrack(&rev_is_in_dive);
601 const ToSchedule best =
602 top_decisions.size() == 1
603 ? top_decisions.front()
604 : top_decisions[absl::Uniform(
605 *random, 0,
static_cast<int>(top_decisions.size()))];
606 if (top_decisions.size() > 1) {
607 VLOG(2) <<
"Choose among " << top_decisions.size() <<
" "
608 << best.start_min <<
" " << best.size_min
609 <<
"[t=" << top_decisions.front().start_min
610 <<
", s=" << top_decisions.front().size_min
611 <<
", t=" << top_decisions.back().start_min
612 <<
", s=" << top_decisions.back().size_min <<
"]";
619 heuristic->next_decision_override = [trail, integer_trail, best,
620 num_times]()
mutable {
621 if (++num_times > 5) {
625 VLOG(3) <<
"Skipping ... ";
631 if (!trail->Assignment().LiteralIsAssigned(
Literal(best.presence))) {
632 VLOG(3) <<
"assign " << best.presence;
635 if (trail->Assignment().LiteralIsFalse(
Literal(best.presence))) {
636 VLOG(2) <<
"unperformed.";
642 if (!integer_trail->IsFixed(best.start)) {
643 const IntegerValue
start_min = integer_trail->LowerBound(best.start);
649 if (!integer_trail->IsFixed(best.end)) {
650 const IntegerValue
end_min = integer_trail->LowerBound(best.end);
651 VLOG(3) <<
"end == " <<
end_min;
656 const IntegerValue
start = integer_trail->LowerBound(best.start);
657 VLOG(2) <<
"Fixed @[" <<
start <<
","
658 << integer_trail->LowerBound(best.end) <<
"]"
660 ? absl::StrCat(
" presence=",
663 << (best.start_min <
start ? absl::StrCat(
" start_at_selection=",
664 best.start_min.value())
669 return heuristic->next_decision_override();
675bool PrecedenceIsBetter(SchedulingConstraintHelper* helper,
int a,
676 SchedulingConstraintHelper* other_helper,
int other_a) {
677 return std::make_tuple(helper->StartMin(
a), helper->StartMax(
a),
678 helper->SizeMin(
a)) <
679 std::make_tuple(other_helper->StartMin(other_a),
680 other_helper->StartMax(other_a),
681 other_helper->SizeMin(other_a));
698 if (!helper->SynchronizeAndSetTimeDirection(
true)) {
707 for (
auto [
b,
time] : helper->TaskByIncreasingStartMin()) {
708 if (helper->IsAbsent(
b))
continue;
709 if (
a == -1 || helper->EndMin(
a) <= helper->StartMin(
b)) {
715 if (PrecedenceIsBetter(helper,
b, helper,
a)) {
719 if (helper->EndMin(
a) <= helper->StartMin(
b)) {
727 if (best_helper ==
nullptr ||
728 PrecedenceIsBetter(helper,
a, best_helper, best_before)) {
729 best_helper = helper;
737 if (best_helper !=
nullptr) {
738 VLOG(2) <<
"New disjunctive precedence: "
743 repo->CreateDisjunctivePrecedenceLiteral(
a,
b);
765 return [repo, integer_trail, trail, search_helper]() {
769 for (
const auto h : repo->AllCumulativeHelpers()) {
770 auto* helper = h.task_helper;
771 if (!helper->SynchronizeAndSetTimeDirection(
true)) {
775 const int num_tasks = helper->NumTasks();
776 std::vector<IntegerValue> added_demand(num_tasks, 0);
779 const auto& by_smin = helper->TaskByIncreasingStartMin();
780 const auto& by_emin = helper->TaskByIncreasingEndMin();
781 const IntegerValue capacity_max = integer_trail->UpperBound(h.capacity);
784 IntegerValue current_height = 0;
785 int first_skipped_task = -1;
791 while (!found && next_end < num_tasks) {
792 IntegerValue
time = by_emin[next_end].time;
793 if (next_start < num_tasks) {
799 while (next_end < num_tasks && by_emin[next_end].
time ==
time) {
800 const int t = by_emin[next_end].task_index;
801 if (!helper->IsPresent(t))
continue;
802 if (added_demand[t] > 0) {
803 current_height -= added_demand[t];
807 added_demand[t] = -1;
815 while (next_start < num_tasks && by_smin[next_start].
time ==
time) {
816 const int t = by_smin[next_start].task_index;
817 if (!helper->IsPresent(t))
continue;
818 if (added_demand[t] == -1)
continue;
819 const IntegerValue demand_min = h.demand_helper->DemandMin(t);
820 if (current_height + demand_min <= capacity_max) {
822 added_demand[t] = demand_min;
823 current_height += demand_min;
824 }
else if (first_skipped_task == -1) {
826 first_skipped_task = t;
835 if (first_skipped_task == -1) {
836 CHECK_EQ(num_added, num_tasks);
844 std::vector<int> open_tasks;
845 for (
int t = 0; t < num_tasks; ++t) {
846 if (added_demand[t] <= 0)
continue;
847 open_tasks.push_back(t);
849 open_tasks.push_back(first_skipped_task);
856 bool found_precedence_to_add =
false;
857 std::vector<Literal> conflict;
858 helper->ClearReason();
859 for (
const int s : open_tasks) {
860 for (
const int t : open_tasks) {
861 if (s == t)
continue;
865 CHECK_LT(helper->StartMin(s), helper->EndMin(t));
866 CHECK_LT(helper->StartMin(t), helper->EndMin(s));
869 const IntervalVariable
a = helper->IntervalVariables()[s];
870 const IntervalVariable
b = helper->IntervalVariables()[t];
871 const LiteralIndex existing = repo->GetPrecedenceLiteral(
a,
b);
875 CHECK(!trail->Assignment().LiteralIsTrue(
Literal(existing)))
876 << helper->TaskDebugString(s) <<
" ( <= ?) "
877 << helper->TaskDebugString(t);
882 if (trail->Assignment().LiteralIsFalse(
Literal(existing))) {
883 conflict.push_back(
Literal(existing));
888 if (helper->EndMin(s) > helper->StartMax(t)) {
889 helper->AddReasonForBeingBefore(t, s);
894 CHECK(repo->CreatePrecedenceLiteral(
a,
b));
898 best_helper = helper;
901 found_precedence_to_add =
true;
904 if (found_precedence_to_add)
break;
906 if (found_precedence_to_add)
break;
914 std::vector<IntegerLiteral> integer_reason =
915 *helper->MutableIntegerReason();
916 if (!h.capacity.IsConstant()) {
917 integer_reason.push_back(
918 integer_trail->UpperBoundAsLiteral(h.capacity));
920 const auto& demands = h.demand_helper->Demands();
921 for (
const int t : open_tasks) {
922 if (helper->IsOptional(t)) {
923 CHECK(trail->Assignment().LiteralIsTrue(helper->PresenceLiteral(t)));
924 conflict.push_back(helper->PresenceLiteral(t).Negated());
928 integer_reason.push_back(integer_trail->LowerBoundAsLiteral(d));
931 integer_trail->ReportConflict(conflict, integer_reason);
932 search_helper->NotifyThatConflictWasFoundDuringGetDecision();
934 LOG(INFO) <<
"Conflict between precedences !";
935 for (
const int t : open_tasks) LOG(INFO) << helper->TaskDebugString(t);
942 if (best_helper !=
nullptr) {
943 VLOG(2) <<
"New precedence: " << best_helper->
TaskDebugString(best_before)
947 repo->CreatePrecedenceLiteral(
a,
b);
965 std::vector<double> weights;
968 policies.push_back(
SequentialSearch({sat_policy, heuristics.fixed_search}));
969 weights.push_back(5);
972 if (heuristics.user_search !=
nullptr) {
974 {heuristics.user_search, sat_policy, heuristics.fixed_search}));
975 weights.push_back(1);
979 policies.push_back(
SequentialSearch({heuristics.heuristic_search, sat_policy,
980 heuristics.integer_completion_search}));
981 weights.push_back(1);
986 std::discrete_distribution<int> var_dist(weights.begin(), weights.end());
990 value_selection_heuristics;
991 std::vector<int> value_selection_weight;
994 const int linearization_level =
995 model->GetOrCreate<SatParameters>()->linearization_level();
997 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
1000 value_selection_weight.push_back(linearization_level == 2 ? 4 : 2);
1006 CHECK(response_manager !=
nullptr);
1007 value_selection_heuristics.push_back(
1008 [
model, response_manager](IntegerVariable
var) {
1010 var, response_manager->SolutionsRepository(),
model);
1012 value_selection_weight.push_back(5);
1017 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
1020 value_selection_weight.push_back(1);
1023 value_selection_weight.push_back(10);
1027 std::discrete_distribution<int> val_dist(value_selection_weight.begin(),
1028 value_selection_weight.end());
1030 int policy_index = 0;
1031 int val_policy_index = 0;
1034 return [=]()
mutable {
1038 decision_policy->ResetDecisionHeuristic();
1042 if (objective !=
nullptr && absl::Bernoulli(*random, 0.2)) {
1044 IntegerValue max_abs_weight = 0;
1045 for (
const IntegerValue coeff : objective->coeffs) {
1046 max_abs_weight = std::max(max_abs_weight,
IntTypeAbs(coeff));
1048 const double max_abs_weight_double =
ToDouble(max_abs_weight);
1050 const int objective_size = objective->vars.size();
1051 for (
int i = 0;
i < objective_size; ++
i) {
1052 const IntegerVariable
var = objective->vars[
i];
1053 if (integer_trail->LowerBound(
var) != 0)
continue;
1054 if (integer_trail->UpperBound(
var) != 1)
continue;
1060 const IntegerValue coeff = objective->coeffs[
i];
1061 const double abs_weight =
1062 std::abs(
ToDouble(objective->coeffs[
i])) / max_abs_weight_double;
1067 decision_policy->SetAssignmentPreference(
1073 policy_index = var_dist(*(random));
1076 val_policy_index = val_dist(*(random));
1081 if (!current_decision.
HasValue())
return current_decision;
1084 if (val_policy_index >= value_selection_heuristics.size()) {
1085 return current_decision;
1090 value_selection_heuristics[val_policy_index](
1093 return current_decision;
1097 for (
const IntegerVariable
var : encoder->GetAllAssociatedVariables(
1101 value_selection_heuristics[val_policy_index](
var);
1106 return current_decision;
1111 const std::vector<BooleanOrIntegerVariable>& vars,
1112 const std::vector<IntegerValue>& values,
Model*
model) {
1122 int* rev_start_index =
model->TakeOwnership(
new int);
1123 *rev_start_index = 0;
1126 rev_int_repo->SaveState(rev_start_index);
1127 for (
int i = *rev_start_index;
i < vars.size(); ++
i) {
1128 const IntegerValue
value = values[
i];
1130 if (trail->Assignment().VariableIsAssigned(vars[
i].bool_var))
continue;
1133 *rev_start_index =
i;
1137 const IntegerVariable integer_var = vars[
i].int_var;
1138 if (integer_trail->IsFixed(integer_var))
continue;
1145 *rev_start_index =
i;
1158 bool reset_at_next_call =
true;
1159 int next_num_failures = 0;
1160 return [=]()
mutable {
1161 if (reset_at_next_call) {
1163 reset_at_next_call =
false;
1164 }
else if (solver->
num_failures() >= next_num_failures) {
1165 reset_at_next_call =
true;
1167 return reset_at_next_call;
1173 return [policy]() {
return policy->ShouldRestart(); };
1178std::function<BooleanOrIntegerLiteral()> WrapIntegerLiteralHeuristic(
1179 std::function<IntegerLiteral()> f) {
1180 return [f]() {
return BooleanOrIntegerLiteral(f()); };
1192 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
1194 case SatParameters::AUTOMATIC_SEARCH: {
1209 case SatParameters::FIXED_SEARCH: {
1222 auto no_restart = []() {
return false; };
1226 case SatParameters::PARTIAL_FIXED_SEARCH: {
1243 case SatParameters::HINT_SEARCH: {
1248 auto no_restart = []() {
return false; };
1252 case SatParameters::PORTFOLIO_SEARCH: {
1258 case SatParameters::LP_SEARCH: {
1260 for (
const auto&
ct :
1262 lp_heuristics.push_back(WrapIntegerLiteralHeuristic(
1263 ct->HeuristicLpReducedCostAverageBranching()));
1265 if (lp_heuristics.empty()) {
1280 case SatParameters::PSEUDO_COST_SEARCH: {
1289 case SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH: {
1298 case SatParameters::RANDOMIZED_SEARCH: {
1309 incomplete_heuristics,
1312 complete_heuristics.reserve(incomplete_heuristics.size());
1313 for (
const auto& incomplete : incomplete_heuristics) {
1314 complete_heuristics.push_back(
1317 return complete_heuristics;
1321 : parameters_(*
model->GetOrCreate<SatParameters>()),
1346 for (
const auto& cb : level_zero_callbacks->callbacks) {
1353 if (parameters_.use_sat_inprocessing() &&
1380 VLOG(1) <<
"Trying to take a decision that is already assigned!"
1381 <<
" Fix this. Continuing for now...";
1393 const IntegerVariable
var =
1402 if (must_process_conflict_) {
1403 must_process_conflict_ =
false;
1416 if (!new_decision.
HasValue())
break;
1455 CHECK_NE(num_policies, 0);
1471 const int64_t old_num_conflicts = sat_solver_->
num_failures();
1472 const int64_t conflict_limit = parameters_.max_number_of_conflicts();
1473 int64_t num_decisions_since_last_lp_record_ = 0;
1475 (sat_solver_->
num_failures() - old_num_conflicts < conflict_limit)) {
1525 if (parameters_.use_optimization_hints()) {
1528 for (
int i = 0;
i < trail.Index(); ++
i) {
1529 sat_decision->SetAssignmentPreference(trail[
i], 0.0);
1552 parameters_.linearization_level() >= 2) {
1553 num_decisions_since_last_lp_record_++;
1554 if (num_decisions_since_last_lp_record_ >= 100) {
1559 num_decisions_since_last_lp_record_ = 0;
1567 const std::vector<Literal>& assumptions,
Model*
model) {
1570 if (!sat_solver->ResetToLevelZero())
return sat_solver->
UnsatStatus();
1575 if (!helper->BeforeTakingDecision())
return sat_solver->UnsatStatus();
1578 if (!sat_solver->ResetWithGivenAssumptions(assumptions)) {
1579 return sat_solver->UnsatStatus();
1585 const IntegerVariable num_vars =
1587 std::vector<IntegerVariable> all_variables;
1588 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
1589 all_variables.push_back(
var);
1601#define RETURN_IF_NOT_FEASIBLE(test) \
1602 const SatSolver::Status status = (test); \
1603 if (status != SatSolver::FEASIBLE) return status;
1616 parameters_(*(
model->GetOrCreate<SatParameters>())),
1622 active_limit_(parameters_.shaving_search_deterministic_time()) {
1624 absl::flat_hash_set<BooleanVariable> visited;
1626 trail_index_at_start_of_iteration_ = trail_->
Index();
1627 integer_trail_index_at_start_of_iteration_ = integer_trail_->
Index();
1634 for (
int v = 0; v < model_proto.variables_size(); ++v) {
1635 if (mapping->IsBoolean(v)) {
1636 const BooleanVariable bool_var = mapping->Literal(v).Variable();
1637 const auto [_, inserted] = visited.insert(bool_var);
1639 bool_vars_.push_back(bool_var);
1642 IntegerVariable
var = mapping->Integer(v);
1644 int_vars_.push_back(
var);
1648 VLOG(2) <<
"Start continuous probing with " << bool_vars_.size()
1649 <<
" Boolean variables, " << int_vars_.size()
1650 <<
" integer variables, deterministic time limit = "
1666 if (parameters_.use_sat_inprocessing() &&
1673 const int64_t initial_num_literals_fixed =
1675 const int64_t initial_num_bounds_shaved = num_bounds_shaved_;
1676 const auto& assignment = sat_solver_->
Assignment();
1680 for (; current_int_var_ < int_vars_.size(); ++current_int_var_) {
1681 const IntegerVariable int_var = int_vars_[current_int_var_];
1682 if (integer_trail_->
IsFixed(int_var))
continue;
1684 const Literal shave_lb_literal =
1686 int_var, integer_trail_->
LowerBound(int_var)));
1687 const BooleanVariable shave_lb_var = shave_lb_literal.
Variable();
1688 const auto [_lb, lb_inserted] = probed_bool_vars_.insert(shave_lb_var);
1693 num_literals_probed_++;
1696 const Literal shave_ub_literal =
1698 int_var, integer_trail_->
UpperBound(int_var)));
1699 const BooleanVariable shave_ub_var = shave_ub_literal.
Variable();
1700 const auto [_ub, ub_inserted] = probed_bool_vars_.insert(shave_ub_var);
1705 num_literals_probed_++;
1710 if (ReportStatus(lb_status))
return lb_status;
1713 if (ReportStatus(ub_status))
return ub_status;
1720 for (; current_bool_var_ < bool_vars_.size(); ++current_bool_var_) {
1721 const BooleanVariable& bool_var = bool_vars_[current_bool_var_];
1723 if (assignment.VariableIsAssigned(bool_var))
continue;
1725 const auto [_, inserted] = probed_bool_vars_.insert(bool_var);
1726 if (!inserted)
continue;
1731 num_literals_probed_++;
1734 if (use_shaving_ && !assignment.LiteralIsAssigned(
literal)) {
1736 if (ReportStatus(true_status))
return true_status;
1740 if (ReportStatus(false_status))
return false_status;
1746 if (parameters_.use_extended_probing()) {
1747 const auto at_least_one_literal_is_true =
1748 [&assignment](absl::Span<const Literal> literals) {
1750 if (assignment.LiteralIsTrue(
literal)) {
1760 if (clause ==
nullptr)
break;
1761 if (at_least_one_literal_is_true(clause->
AsSpan()))
continue;
1765 if (assignment.LiteralIsAssigned(
literal))
continue;
1766 tmp_dnf_.push_back({
literal});
1768 ++num_at_least_one_probed_;
1769 if (!prober_->
ProbeDnf(
"at_least_one", tmp_dnf_)) {
1778 const absl::Span<const Literal> at_most_one =
1780 if (at_most_one.empty())
break;
1781 if (at_least_one_literal_is_true(at_most_one))
continue;
1784 tmp_literals_.clear();
1786 if (assignment.LiteralIsAssigned(
literal))
continue;
1787 tmp_dnf_.push_back({
literal});
1788 tmp_literals_.push_back(
literal.Negated());
1790 tmp_dnf_.push_back(tmp_literals_);
1791 ++num_at_most_one_probed_;
1792 if (!prober_->
ProbeDnf(
"at_most_one", tmp_dnf_)) {
1800 const int limit = parameters_.probing_num_combinations_limit();
1801 const bool max_num_bool_vars_for_pairs_probing =
1802 static_cast<int>(std::sqrt(2 * limit));
1803 const int num_bool_vars = bool_vars_.size();
1805 if (num_bool_vars < max_num_bool_vars_for_pairs_probing) {
1806 for (; current_bv1_ + 1 < bool_vars_.size(); ++current_bv1_) {
1807 const BooleanVariable bv1 = bool_vars_[current_bv1_];
1808 if (assignment.VariableIsAssigned(bv1))
continue;
1809 current_bv2_ = std::max(current_bv1_ + 1, current_bv2_);
1810 for (; current_bv2_ < bool_vars_.size(); ++current_bv2_) {
1811 const BooleanVariable& bv2 = bool_vars_[current_bv2_];
1812 if (assignment.VariableIsAssigned(bv2))
continue;
1814 "pair_of_bool_vars",
1815 {{Literal(bv1, true), Literal(bv2, true)},
1816 {Literal(bv1, true), Literal(bv2, false)},
1817 {Literal(bv1, false), Literal(bv2, true)},
1818 {Literal(bv1, false), Literal(bv2, false)}})) {
1826 for (; random_pair_of_bool_vars_probed_ < 10000;
1827 ++random_pair_of_bool_vars_probed_) {
1828 const BooleanVariable bv1 =
1829 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1830 if (assignment.VariableIsAssigned(bv1))
continue;
1831 const BooleanVariable bv2 =
1832 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1833 if (assignment.VariableIsAssigned(bv2) || bv1 == bv2) {
1837 "rnd_pair_of_bool_vars",
1838 {{Literal(bv1, true), Literal(bv2, true)},
1839 {Literal(bv1, true), Literal(bv2, false)},
1840 {Literal(bv1, false), Literal(bv2, true)},
1841 {Literal(bv1, false), Literal(bv2, false)}})) {
1850 const bool max_num_bool_vars_for_triplet_probing =
1851 static_cast<int>(std::cbrt(2 * limit));
1853 const int loop_limit =
1854 num_bool_vars < max_num_bool_vars_for_triplet_probing
1855 ? num_bool_vars * (num_bool_vars - 1) * (num_bool_vars - 2) / 2
1857 for (; random_triplet_of_bool_vars_probed_ < loop_limit;
1858 ++random_triplet_of_bool_vars_probed_) {
1859 const BooleanVariable bv1 =
1860 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1861 if (assignment.VariableIsAssigned(bv1))
continue;
1862 const BooleanVariable bv2 =
1863 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1864 if (assignment.VariableIsAssigned(bv2) || bv1 == bv2) {
1867 const BooleanVariable bv3 =
1868 bool_vars_[absl::Uniform<int>(*random_, 0, bool_vars_.size())];
1869 if (assignment.VariableIsAssigned(bv3) || bv1 == bv3 || bv2 == bv3) {
1873 for (
int i = 0;
i < 8; ++
i) {
1874 tmp_dnf_.push_back({
Literal(bv1, (
i & 1) > 0),
1879 if (!prober_->
ProbeDnf(
"rnd_triplet_of_bool_vars", tmp_dnf_)) {
1889 const double deterministic_time =
1890 parameters_.shaving_search_deterministic_time();
1891 const bool something_has_been_detected =
1892 num_bounds_shaved_ != initial_num_bounds_shaved ||
1893 prober_->num_new_literals_fixed() != initial_num_literals_fixed;
1894 if (something_has_been_detected) {
1895 active_limit_ = deterministic_time;
1896 }
else if (active_limit_ < 25 * deterministic_time) {
1897 active_limit_ += deterministic_time;
1903 current_bool_var_ = 0;
1904 current_int_var_ = 0;
1907 random_pair_of_bool_vars_probed_ = 0;
1908 random_triplet_of_bool_vars_probed_ = 0;
1909 binary_implication_graph_->ResetAtMostOneIterator();
1910 clause_manager_->ResetToProbeIndex();
1911 probed_bool_vars_.clear();
1912 shaved_literals_.clear();
1914 const int new_trail_index = trail_->Index();
1915 const int new_integer_trail_index = integer_trail_->Index();
1921 parameters_.use_shaving_in_probing_search() ? !use_shaving_ :
false;
1922 trail_index_at_start_of_iteration_ = new_trail_index;
1923 integer_trail_index_at_start_of_iteration_ = new_integer_trail_index;
1927 for (
int i = 0;
i < bool_vars_.size(); ++
i) {
1928 if (!sat_solver_->Assignment().VariableIsAssigned(bool_vars_[
i])) {
1929 bool_vars_[new_size++] = bool_vars_[
i];
1932 bool_vars_.resize(new_size);
1936 for (
int i = 0;
i < int_vars_.size(); ++
i) {
1937 if (!integer_trail_->IsFixed(int_vars_[
i])) {
1938 int_vars_[new_size++] = int_vars_[
i];
1941 int_vars_.resize(new_size);
1943 return SatSolver::LIMIT_REACHED;
1946#undef RETURN_IF_NOT_FEASIBLE
1948SatSolver::Status ContinuousProber::PeriodicSyncAndCheck() {
1950 if (--num_test_limit_remaining_ <= 0) {
1951 num_test_limit_remaining_ = kTestLimitPeriod;
1952 if (time_limit_->LimitReached())
return SatSolver::LIMIT_REACHED;
1956 if (--num_logs_remaining_ <= 0) {
1957 num_logs_remaining_ = kLogPeriod;
1962 if (--num_syncs_remaining_ <= 0) {
1963 num_syncs_remaining_ = kSyncPeriod;
1964 if (!sat_solver_->ResetToLevelZero())
return SatSolver::INFEASIBLE;
1965 for (
const auto& cb : level_zero_callbacks_->callbacks) {
1967 sat_solver_->NotifyThatModelIsUnsat();
1968 return SatSolver::INFEASIBLE;
1973 return SatSolver::FEASIBLE;
1976SatSolver::Status ContinuousProber::ShaveLiteral(Literal
literal) {
1977 const auto [_, inserted] = shaved_literals_.insert(
literal.Index());
1978 if (trail_->Assignment().LiteralIsAssigned(
literal) || !inserted) {
1979 return SatSolver::LIMIT_REACHED;
1981 num_bounds_tried_++;
1983 const double original_dtime_limit = time_limit_->GetDeterministicLimit();
1984 time_limit_->ChangeDeterministicLimit(
1985 std::min(original_dtime_limit,
1986 time_limit_->GetElapsedDeterministicTime() + active_limit_));
1987 const SatSolver::Status
status =
1989 time_limit_->ChangeDeterministicLimit(original_dtime_limit);
1992 if (
status == SatSolver::ASSUMPTIONS_UNSAT) {
1993 num_bounds_shaved_++;
1998 if (!sat_solver_->ResetToLevelZero())
return SatSolver::INFEASIBLE;
2002bool ContinuousProber::ReportStatus(
const SatSolver::Status
status) {
2003 return status == SatSolver::INFEASIBLE ||
status == SatSolver::FEASIBLE;
2006void ContinuousProber::LogStatistics() {
2007 if (shared_response_manager_ ==
nullptr ||
2008 shared_bounds_manager_ ==
nullptr) {
2011 if (VLOG_IS_ON(1)) {
2012 shared_response_manager_->LogMessageWithThrottling(
2015 " (iterations=", iteration_,
2016 " linearization_level=", parameters_.linearization_level(),
2017 " shaving=", use_shaving_,
" active_bool_vars=", bool_vars_.size(),
2018 " active_int_vars=", integer_trail_->NumIntegerVariables(),
2019 " literals fixed/probed=", prober_->num_new_literals_fixed(),
"/",
2020 num_literals_probed_,
" bounds shaved/tried=", num_bounds_shaved_,
2021 "/", num_bounds_tried_,
" new_integer_bounds=",
2022 shared_bounds_manager_->NumBoundsExported(
"probing"),
2023 " new_binary_clause=", prober_->num_new_binary_clauses(),
2024 " num_at_least_one_probed=", num_at_least_one_probed_,
2025 " num_at_most_one_probed=", num_at_most_one_probed_,
")"));
double GetDeterministicLimit() const
absl::Span< const Literal > NextAtMostOne()
Returns the next at_most_one, or a span of size 0 when finished.
SatClause * NextClauseToProbe()
SatSolver::Status Probe()
ContinuousProber(const CpModelProto &model_proto, Model *model)
The model_proto is just used to construct the lists of variable to probe.
bool ProcessIntegerTrail(Literal first_decision)
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
LiteralIndex GetAssociatedLiteral(IntegerLiteral i_lit) const
An helper class to share the code used by the different kind of search.
IntegerSearchHelper(Model *model)
bool TakeDecision(Literal decision)
LiteralIndex GetDecisionLiteral(const BooleanOrIntegerLiteral &decision)
bool GetDecision(const std::function< BooleanOrIntegerLiteral()> &f, LiteralIndex *decision)
bool BeforeTakingDecision()
SatSolver::Status SolveIntegerProblem()
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerValue UpperBound(IntegerVariable i) const
bool HasPendingRootLevelDeduction() const
Return true if we can fix new fact at level zero.
bool InPropagationLoop() const
bool IsFixed(IntegerVariable i) const
Checks if the variable is fixed.
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
IntegerVariable FirstUnassignedVariable() const
bool CurrentBranchHadAnIncompletePropagation()
A class that stores the collection of all LP constraints in a model.
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.
const std::string & Name() const
bool ProbeDnf(absl::string_view name, absl::Span< const std::vector< Literal > > dnf)
bool ProbeOneVariable(BooleanVariable b)
int num_new_literals_fixed() const
void ProcessTrailAtLevelOne()
void AfterTakingDecision(bool conflict=false)
void BeforeTakingDecision(Literal decision)
Contain the logic to decide when to restart a SAT tree search.
absl::Span< const Literal > AsSpan() const
Returns a Span<> representation of the clause.
void ProcessCurrentConflict()
Status UnsatStatus() const
void NotifyThatModelIsUnsat()
bool RestoreSolverToAssumptionLevel()
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
int64_t num_failures() const
bool ModelIsUnsat() const
bool ReapplyAssumptionsIfNeeded()
void AdvanceDeterministicTime(TimeLimit *limit)
const VariablesAssignment & Assignment() const
int CurrentDecisionLevel() const
ABSL_MUST_USE_RESULT bool ResetToLevelZero()
ABSL_MUST_USE_RESULT bool FinishPropagation()
std::string TaskDebugString(int t) const
Returns a string with the current task bounds.
absl::Span< const IntervalVariable > IntervalVariables() const
Returns the underlying affine expressions.
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)
Randomizes the decision heuristic of the given SatParameters.
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::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Returns the vector of the negated variables.
std::function< BooleanOrIntegerLiteral()> BoolPseudoCostHeuristic(Model *model)
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)
IntegerLiteral AtMinValue(IntegerVariable var, IntegerTrail *integer_trail)
void RecordLPRelaxationValues(Model *model)
Adds the current LP solution to the pool.
SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model *model)
std::function< BooleanOrIntegerLiteral()> PseudoCost(Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
std::function< BooleanOrIntegerLiteral()> SchedulingSearchHeuristic(Model *model)
A simple heuristic for scheduling models.
std::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
Returns the BooleanOrIntegerLiteral advised by the underlying SAT solver.
std::function< bool()> RestartEveryKFailures(int k, SatSolver *solver)
A restart policy that restarts every k failures.
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(bool lns_mode, Model *model)
IntegerLiteral SplitUsingBestSolutionValueInRepository(IntegerVariable var, const SharedSolutionRepository< int64_t > &solution_repo, Model *model)
bool LinearizedPartIsLarge(Model *model)
const int kUnsatTrailIndex
A constant used by the EnqueueDecision*() API.
IntegerLiteral SplitAroundGivenValue(IntegerVariable var, IntegerValue value, Model *model)
IntegerLiteral ChooseBestObjectiveValue(IntegerVariable var, Model *model)
If a variable appear in the objective, branch on its best objective value.
const BooleanVariable kNoBooleanVariable(-1)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
std::function< bool()> SatSolverRestartPolicy(Model *model)
A restart policy that uses the underlying sat solver's policy.
std::function< BooleanOrIntegerLiteral()> LpPseudoCostHeuristic(Model *model)
IntegerLiteral GreaterOrEqualToMiddleValue(IntegerVariable var, IntegerTrail *integer_trail)
double ToDouble(IntegerValue value)
std::function< BooleanOrIntegerLiteral()> MostFractionalHeuristic(Model *model)
Choose the variable with most fractional LP value.
std::function< BooleanOrIntegerLiteral()> CumulativePrecedenceSearchHeuristic(Model *model)
std::function< BooleanOrIntegerLiteral()> UnassignedVarWithLowestMinAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
std::function< BooleanOrIntegerLiteral()> FollowHint(const std::vector< BooleanOrIntegerVariable > &vars, const std::vector< IntegerValue > &values, Model *model)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()> > heuristics)
In SWIG mode, we don't want anything besides these top-level includes.
std::optional< int64_t > end
LiteralIndex boolean_literal_index
IntegerLiteral integer_literal
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
IntegerLiteral Negated() const
The negation of x >= bound is x <= bound - 1.
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
IntegerVariable objective_var
IntegerLiteral down_branch
std::function< BooleanOrIntegerLiteral()> fixed_search
Fixed search, built from above building blocks.
std::vector< std::function< bool()> > restart_policies
std::function< BooleanOrIntegerLiteral()> user_search
Contains the search specified by the user in CpModelProto.
std::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies
std::function< BooleanOrIntegerLiteral()> hint_search
std::function< BooleanOrIntegerLiteral()> next_decision_override
int policy_index
Index in the vectors above that indicate the current configuration.