21#include "absl/container/flat_hash_set.h"
22#include "absl/types/span.h"
47 const auto& variables =
50 if (variables.contains(
var)) {
64 const IntegerValue chosen_value =
65 var_lb +
std::max(IntegerValue(1), (var_ub - var_lb) / IntegerValue(2));
73 const IntegerValue ub = integer_trail->UpperBound(
var);
75 const absl::flat_hash_set<IntegerVariable>& variables =
83 const bool branch_down_feasible =
value >= lb &&
value < ub;
84 const bool branch_up_feasible =
value > lb &&
value <= ub;
85 if (variables.contains(
var) && branch_down_feasible) {
87 }
else if (variables.contains(
NegationOf(
var)) && branch_up_feasible) {
89 }
else if (branch_down_feasible) {
91 }
else if (branch_up_feasible) {
101 DCHECK(!integer_trail->IsCurrentlyIgnored(
var));
118 const IntegerValue
value = IntegerValue(
135 const int proto_var =
153 const std::vector<IntegerVariable>& vars,
Model*
model) {
155 return [ vars, integer_trail]() {
156 for (
const IntegerVariable
var : vars) {
158 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
166std::function<BooleanOrIntegerLiteral()>
168 const std::vector<IntegerVariable>& vars,
Model*
model) {
170 return [ vars, integer_trail]() {
172 IntegerValue candidate_lb;
173 for (
const IntegerVariable
var : vars) {
174 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
175 const IntegerValue lb = integer_trail->LowerBound(
var);
189 return [heuristics]() {
190 for (
const auto& h : heuristics) {
192 if (decision.
HasValue())
return decision;
200 value_selection_heuristics,
209 if (!current_decision.
HasValue())
return current_decision;
214 sat_policy->InStablePhase()) {
215 return current_decision;
220 for (
const auto& value_heuristic : value_selection_heuristics) {
225 return current_decision;
232 if (integer_trail->IsCurrentlyIgnored(l.var))
continue;
235 for (
const auto& value_heuristic : value_selection_heuristics) {
241 return current_decision;
246 auto* lp_constraints =
248 int num_lp_variables = 0;
250 num_lp_variables += lp->NumVariables();
252 const int num_integer_variables =
254 return (num_integer_variables <= 2 * num_lp_variables);
263 value_selection_heuristics;
272 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
280 if (response_manager !=
nullptr) {
281 VLOG(3) <<
"Using best solution value selection heuristic.";
282 value_selection_heuristics.push_back(
283 [
model, response_manager](IntegerVariable
var) {
285 var, response_manager->SolutionsRepository(),
model);
295 value_selection_heuristics.push_back(
297 VLOG(3) <<
"Using relaxation solution value selection heuristic.";
306 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
312 var_selection_heuristic,
model);
319 return [sat_solver, trail, decision_policy] {
320 const bool all_assigned = trail->Index() == sat_solver->
NumVariables();
322 const Literal result = decision_policy->NextBranch();
330 const bool has_objective =
332 if (!has_objective) {
338 return [pseudo_costs, integer_trail]() {
339 const IntegerVariable chosen_var = pseudo_costs->GetBestDecisionVar();
356 return [repo, heuristic, trail, integer_trail]() {
371 const int num_intervals = repo->NumIntervals();
372 for (IntervalVariable i(0); i < num_intervals; ++i) {
373 if (repo->IsAbsent(i))
continue;
374 if (!repo->IsPresent(i) || !integer_trail->IsFixed(repo->Start(i)) ||
375 !integer_trail->IsFixed(repo->End(i))) {
376 IntegerValue
time = integer_trail->LowerBound(repo->Start(i));
383 repo->PresenceLiteral(i), repo->Start(i)));
390 const IntegerValue size_min =
391 std::max(integer_trail->LowerBound(repo->Size(i)),
392 integer_trail->LowerBound(repo->End(i)) -
time);
393 if (
time < best.time ||
394 (
time == best.time && size_min < best.size_min)) {
397 best.start = repo->Start(i);
398 best.end = repo->End(i);
400 best.size_min = size_min;
409 heuristic->next_decision_override = [trail, integer_trail, best,
410 num_times]()
mutable {
411 if (++num_times > 5) {
415 VLOG(3) <<
"Skipping ... ";
421 if (!trail->Assignment().LiteralIsAssigned(
Literal(best.presence))) {
422 VLOG(3) <<
"assign " << best.presence;
425 if (trail->Assignment().LiteralIsFalse(
Literal(best.presence))) {
426 VLOG(2) <<
"unperformed.";
432 if (!integer_trail->IsFixed(best.start)) {
433 const IntegerValue
start_min = integer_trail->LowerBound(best.start);
439 if (!integer_trail->IsFixed(best.end)) {
440 const IntegerValue
end_min = integer_trail->LowerBound(best.end);
446 const IntegerValue start = integer_trail->LowerBound(best.start);
447 VLOG(2) <<
"Fixed @[" << start <<
","
448 << integer_trail->LowerBound(best.end) <<
"]"
450 ? absl::StrCat(
" presence=",
453 << (best.time < start
454 ? absl::StrCat(
" start_at_selection=", best.time.value())
459 return heuristic->next_decision_override();
476 std::discrete_distribution<int> var_dist{3 , 1 };
480 value_selection_heuristics;
481 std::vector<int> value_selection_weight;
484 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
487 value_selection_weight.push_back(8);
491 if (response_manager !=
nullptr) {
492 value_selection_heuristics.push_back(
493 [
model, response_manager](IntegerVariable
var) {
495 var, response_manager->SolutionsRepository(),
model);
497 value_selection_weight.push_back(5);
503 value_selection_heuristics.push_back(
508 value_selection_weight.push_back(3);
513 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
516 value_selection_weight.push_back(1);
519 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
522 value_selection_weight.push_back(1);
525 value_selection_weight.push_back(10);
529 std::discrete_distribution<int> val_dist(value_selection_weight.begin(),
530 value_selection_weight.end());
532 int policy_index = 0;
533 int val_policy_index = 0;
535 return [=]()
mutable {
539 decision_policy->ResetDecisionHeuristic();
542 policy_index = var_dist(*(random));
545 val_policy_index = val_dist(*(random));
550 if (!current_decision.
HasValue())
return current_decision;
553 if (val_policy_index >= value_selection_heuristics.size()) {
554 return current_decision;
559 value_selection_heuristics[val_policy_index](
562 return current_decision;
568 if (integer_trail->IsCurrentlyIgnored(l.var))
continue;
572 value_selection_heuristics[val_policy_index](l.var);
577 return current_decision;
583 const std::vector<BooleanOrIntegerVariable>& vars,
584 const std::vector<IntegerValue>& values,
Model*
model) {
588 for (
int i = 0; i < vars.size(); ++i) {
589 const IntegerValue
value = values[i];
595 const IntegerVariable integer_var = vars[i].int_var;
596 if (integer_trail->IsCurrentlyIgnored(integer_var))
continue;
597 if (integer_trail->IsFixed(integer_var))
continue;
613 bool reset_at_next_call =
true;
614 int next_num_failures = 0;
615 return [=]()
mutable {
616 if (reset_at_next_call) {
618 reset_at_next_call =
false;
619 }
else if (solver->
num_failures() >= next_num_failures) {
620 reset_at_next_call =
true;
622 return reset_at_next_call;
633std::function<BooleanOrIntegerLiteral()> WrapIntegerLiteralHeuristic(
634 std::function<IntegerLiteral()> f) {
635 return [f]() {
return BooleanOrIntegerLiteral(f()); };
676 auto no_restart = []() {
return false; };
685 auto no_restart = []() {
return false; };
695 for (
const auto&
ct :
697 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
698 ct->HeuristicLpReducedCostBinary(
model)));
699 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
700 ct->HeuristicLpMostInfeasibleBinary(
model)));
714 for (
const auto&
ct :
716 lp_heuristics.push_back(WrapIntegerLiteralHeuristic(
717 ct->HeuristicLpReducedCostAverageBranching()));
719 if (lp_heuristics.empty()) {
754 incomplete_heuristics,
757 complete_heuristics.reserve(incomplete_heuristics.size());
758 for (
const auto& incomplete : incomplete_heuristics) {
759 complete_heuristics.push_back(
762 return complete_heuristics;
775 if (objective !=
nullptr) objective_var_ = objective->
objective_var;
797 for (
const auto& cb : level_zero_callbacks->callbacks) {
819 const IntegerVariable
var =
836 if (!new_decision.
HasValue())
break;
855 VLOG(1) <<
"Trying to take a decision that is already assigned!"
856 <<
" Fix this. Continuing for now...";
866 const std::vector<PseudoCosts::VariableBoundChange> bound_changes =
871 old_obj_lb = integer_trail_->
LowerBound(objective_var_);
872 old_obj_ub = integer_trail_->
UpperBound(objective_var_);
889 const IntegerValue new_obj_lb = integer_trail_->
LowerBound(objective_var_);
890 const IntegerValue new_obj_ub = integer_trail_->
UpperBound(objective_var_);
891 const IntegerValue objective_bound_change =
892 (new_obj_lb - old_obj_lb) + (old_obj_ub - new_obj_ub);
893 pseudo_costs_->
UpdateCost(bound_changes, objective_bound_change);
915 if (objective !=
nullptr) objective_var = objective->
objective_var;
936 const int64_t old_num_conflicts = sat_solver->
num_failures();
937 const int64_t conflict_limit = sat_parameters.max_number_of_conflicts();
938 int64_t num_decisions_since_last_lp_record_ = 0;
939 int64_t num_decisions_without_probing = 0;
941 (sat_solver->
num_failures() - old_num_conflicts < conflict_limit)) {
950 if (!helper->BeforeTakingDecision())
return sat_solver->
UnsatStatus();
963 decision = helper->GetDecision(
973 sat_parameters.probing_period_at_root() > 0 &&
974 ++num_decisions_without_probing >=
975 sat_parameters.probing_period_at_root()) {
976 num_decisions_without_probing = 0;
1010 const auto& trail = *
model->GetOrCreate<
Trail>();
1011 for (
int i = 0; i < trail.Index(); ++i) {
1012 sat_decision->SetAssignmentPreference(trail[i], 0.0);
1018 if (!helper->TakeDecision(
Literal(decision))) {
1031 num_decisions_since_last_lp_record_++;
1032 if (num_decisions_since_last_lp_record_ >= 100) {
1037 num_decisions_since_last_lp_record_ = 0;
1041 return SatSolver::Status::LIMIT_REACHED;
1045 const std::vector<Literal>& assumptions,
Model*
model) {
1051 for (
const auto& cb : level_zero_callbacks->callbacks) {
1066 const IntegerVariable num_vars =
1068 std::vector<IntegerVariable> all_variables;
1069 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
1070 all_variables.push_back(
var);
1083 const std::vector<BooleanVariable>& bool_vars,
1084 const std::vector<IntegerVariable>& int_vars,
1085 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
1086 VLOG(2) <<
"Start continuous probing with " << bool_vars.size()
1087 <<
" Boolean variables, and " << int_vars.size()
1088 <<
" integer variables";
1098 std::vector<BooleanVariable> active_vars;
1099 std::vector<BooleanVariable> integer_bounds;
1100 absl::flat_hash_set<BooleanVariable> integer_bounds_set;
1104 VLOG(2) <<
"Probing loop " << loop++;
1107 auto SyncBounds = [solver, &level_zero_callbacks]() {
1109 for (
const auto& cb : level_zero_callbacks->callbacks) {
1117 if (!SyncBounds()) {
1122 if (sat_parameters.use_sat_inprocessing() &&
1130 absl::flat_hash_set<BooleanVariable> probed;
1134 for (
const IntegerVariable int_var : int_vars) {
1135 if (integer_trail->IsFixed(int_var) ||
1136 integer_trail->IsOptional(int_var)) {
1140 const BooleanVariable shave_lb =
1143 int_var, integer_trail->LowerBound(int_var)))
1145 if (!probed.contains(shave_lb)) {
1146 probed.insert(shave_lb);
1147 if (!prober->ProbeOneVariable(shave_lb)) {
1152 const BooleanVariable shave_ub =
1155 int_var, integer_trail->UpperBound(int_var)))
1157 if (!probed.contains(shave_ub)) {
1158 probed.insert(shave_ub);
1159 if (!prober->ProbeOneVariable(shave_ub)) {
1164 if (!SyncBounds()) {
1173 for (
const BooleanVariable& bool_var : bool_vars) {
1178 if (!SyncBounds()) {
1181 if (!probed.contains(bool_var)) {
1182 probed.insert(bool_var);
1183 if (!prober->ProbeOneVariable(bool_var)) {
#define DCHECK_LE(val1, val2)
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
bool LimitReached() const
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
bool EnqueueNewDeductions()
void ProcessIntegerTrail(Literal first_decision)
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
IntegerSearchHelper(Model *model)
bool BeforeTakingDecision()
bool TakeDecision(Literal decision)
LiteralIndex GetDecision(const std::function< BooleanOrIntegerLiteral()> &f)
IntegerVariable FirstUnassignedVariable() const
bool IsCurrentlyIgnored(IntegerVariable i) const
bool CurrentBranchHadAnIncompletePropagation()
bool InPropagationLoop() const
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LowerBound(IntegerVariable i) const
bool HasPendingRootLevelDeduction() const
bool IsOptional(IntegerVariable i) const
double GetSolutionValue(IntegerVariable variable) const
bool SolutionIsInteger() const
LiteralIndex Index() const
BooleanVariable Variable() const
std::string DebugString() const
Class that owns everything related to a particular optimization model.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
void UpdateCost(const std::vector< VariableBoundChange > &bound_changes, IntegerValue obj_bound_improvement)
bool exploit_all_lp_solution() const
bool use_optimization_hints() const
static constexpr SearchBranching PSEUDO_COST_SEARCH
::operations_research::sat::SatParameters_SearchBranching search_branching() const
static constexpr SearchBranching PORTFOLIO_WITH_QUICK_RESTART_SEARCH
static constexpr SearchBranching PORTFOLIO_SEARCH
bool use_sat_inprocessing() const
bool exploit_best_solution() const
bool exploit_integer_lp_solution() const
static constexpr SearchBranching LP_SEARCH
static constexpr SearchBranching AUTOMATIC_SEARCH
bool exploit_objective() const
static constexpr SearchBranching HINT_SEARCH
bool randomize_search() const
bool exploit_relaxation_solution() const
static constexpr SearchBranching FIXED_SEARCH
const VariablesAssignment & Assignment() const
void NotifyThatModelIsUnsat()
Status UnsatStatus() const
void AdvanceDeterministicTime(TimeLimit *limit)
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
void Backtrack(int target_level)
bool RestoreSolverToAssumptionLevel()
int64_t num_failures() const
bool ReapplyAssumptionsIfNeeded()
bool ResetWithGivenAssumptions(const std::vector< Literal > &assumptions)
int CurrentDecisionLevel() const
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
const VariablesAssignment & Assignment() const
bool LiteralIsAssigned(Literal literal) const
bool VariableIsAssigned(BooleanVariable var) const
SharedRelaxationSolutionRepository * relaxation_solutions
ModelSharedTimeLimit * time_limit
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
std::vector< std::function< BooleanOrIntegerLiteral()> > CompleteHeuristics(const std::vector< std::function< BooleanOrIntegerLiteral()> > &incomplete_heuristics, const std::function< BooleanOrIntegerLiteral()> &completion_heuristic)
std::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, 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()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()> > heuristics)
const LiteralIndex kNoLiteralIndex(-1)
IntegerLiteral AtMinValue(IntegerVariable var, IntegerTrail *integer_trail)
void RecordLPRelaxationValues(Model *model)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
IntegerLiteral GreaterOrEqualToMiddleValue(IntegerVariable var, IntegerTrail *integer_trail)
IntegerLiteral SplitAroundGivenValue(IntegerVariable var, IntegerValue value, Model *model)
std::function< BooleanOrIntegerLiteral()> UnassignedVarWithLowestMinAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model *model)
std::function< bool()> SatSolverRestartPolicy(Model *model)
const IntegerVariable kNoIntegerVariable(-1)
std::function< BooleanOrIntegerLiteral()> FollowHint(const std::vector< BooleanOrIntegerVariable > &vars, const std::vector< IntegerValue > &values, Model *model)
std::function< bool()> RestartEveryKFailures(int k, SatSolver *solver)
std::function< BooleanOrIntegerLiteral()> SchedulingSearchHeuristic(Model *model)
IntegerLiteral ChooseBestObjectiveValue(IntegerVariable var, Model *model)
void ConfigureSearchHeuristics(Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
std::function< BooleanOrIntegerLiteral()> IntegerValueSelectionHeuristic(std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
SatSolver::Status SolveIntegerProblem(Model *model)
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
std::vector< PseudoCosts::VariableBoundChange > GetBoundChanges(LiteralIndex decision, Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
SatSolver::Status ContinuousProbing(const std::vector< BooleanVariable > &bool_vars, const std::vector< IntegerVariable > &int_vars, const std::function< void()> &feasible_solution_observer, Model *model)
IntegerLiteral SplitAroundLpValue(IntegerVariable var, Model *model)
IntegerLiteral SplitUsingBestSolutionValueInRepository(IntegerVariable var, const SharedSolutionRepository< int64_t > &solution_repo, Model *model)
const BooleanVariable kNoBooleanVariable(-1)
bool LinearizedPartIsLarge(Model *model)
void RandomizeDecisionHeuristic(URBG *random, SatParameters *parameters)
std::function< BooleanOrIntegerLiteral()> PseudoCost(Model *model)
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(Model *model)
Collection of objects used to extend the Constraint Solver library.
LiteralIndex boolean_literal_index
IntegerLiteral integer_literal
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
IntegerVariable objective_var
std::vector< std::function< bool()> > restart_policies
std::function< BooleanOrIntegerLiteral()> hint_search
std::function< BooleanOrIntegerLiteral()> fixed_search
std::function< BooleanOrIntegerLiteral()> next_decision_override
std::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies