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;
166 std::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();
323 CHECK(!sat_solver->Assignment().LiteralIsAssigned(result));
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;
633 std::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;
977 if (!prober->ProbeOneVariable(
Literal(decision).Variable())) {
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)) {
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool HasPendingRootLevelDeduction() const
static constexpr SearchBranching LP_SEARCH
void RandomizeDecisionHeuristic(URBG *random, SatParameters *parameters)
Class that owns everything related to a particular optimization model.
ModelSharedTimeLimit * time_limit
bool SolutionIsInteger() const
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
bool exploit_all_lp_solution() const
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
#define VLOG(verboselevel)
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
IntegerValue LowerBound(IntegerVariable i) const
std::string DebugString() const
bool use_optimization_hints() const
LiteralIndex Index() const
SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model *model)
std::vector< std::function< BooleanOrIntegerLiteral()> > CompleteHeuristics(const std::vector< std::function< BooleanOrIntegerLiteral()>> &incomplete_heuristics, const std::function< BooleanOrIntegerLiteral()> &completion_heuristic)
bool exploit_relaxation_solution() const
::operations_research::sat::SatParameters_SearchBranching search_branching() const
IntegerSearchHelper(Model *model)
int64_t num_failures() const
IntegerLiteral SplitUsingBestSolutionValueInRepository(IntegerVariable var, const SharedSolutionRepository< int64_t > &solution_repo, Model *model)
static constexpr SearchBranching PORTFOLIO_SEARCH
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
SatSolver::Status ContinuousProbing(const std::vector< BooleanVariable > &bool_vars, const std::vector< IntegerVariable > &int_vars, const std::function< void()> &feasible_solution_observer, Model *model)
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
static constexpr SearchBranching PSEUDO_COST_SEARCH
bool CurrentBranchHadAnIncompletePropagation()
static constexpr SearchBranching PORTFOLIO_WITH_QUICK_RESTART_SEARCH
#define CHECK_LT(val1, val2)
IntegerVariable PositiveVariable(IntegerVariable i)
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
std::function< bool()> RestartEveryKFailures(int k, SatSolver *solver)
std::function< BooleanOrIntegerLiteral()> fixed_search
bool ReapplyAssumptionsIfNeeded()
std::function< BooleanOrIntegerLiteral()> FollowHint(const std::vector< BooleanOrIntegerVariable > &vars, const std::vector< IntegerValue > &values, Model *model)
LiteralIndex boolean_literal_index
void ConfigureSearchHeuristics(Model *model)
Status UnsatStatus() const
std::function< bool()> SatSolverRestartPolicy(Model *model)
const BooleanVariable kNoBooleanVariable(-1)
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
std::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies
bool ResetWithGivenAssumptions(const std::vector< Literal > &assumptions)
bool VariableIsAssigned(BooleanVariable var) const
bool LimitReached() const
void ProcessIntegerTrail(Literal first_decision)
std::function< BooleanOrIntegerLiteral()> hint_search
bool LiteralIsAssigned(Literal literal) const
bool exploit_objective() const
IntegerVariable objective_var
bool exploit_best_solution() const
void AdvanceDeterministicTime(TimeLimit *limit)
IntegerLiteral ChooseBestObjectiveValue(IntegerVariable var, Model *model)
bool use_sat_inprocessing() const
std::function< BooleanOrIntegerLiteral()> SequentialValueSelection(std::vector< std::function< IntegerLiteral(IntegerVariable)>> value_selection_heuristics, std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
bool InPropagationLoop() const
void Backtrack(int target_level)
bool BeforeTakingDecision()
IntegerLiteral GreaterOrEqualToMiddleValue(IntegerVariable var, IntegerTrail *integer_trail)
static constexpr SearchBranching AUTOMATIC_SEARCH
bool IsOptional(IntegerVariable i) const
#define CHECK_EQ(val1, val2)
std::function< BooleanOrIntegerLiteral()> SchedulingSearchHeuristic(Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
bool RestoreSolverToAssumptionLevel()
IntegerLiteral AtMinValue(IntegerVariable var, IntegerTrail *integer_trail)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
const VariablesAssignment & Assignment() const
static constexpr SearchBranching HINT_SEARCH
#define DCHECK(condition)
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)
SatSolver::Status SolveIntegerProblem(Model *model)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
void UpdateCost(const std::vector< VariableBoundChange > &bound_changes, IntegerValue obj_bound_improvement)
#define DCHECK_EQ(val1, val2)
bool IsCurrentlyIgnored(IntegerVariable i) const
#define DCHECK_LE(val1, val2)
IntegerLiteral integer_literal
IntegerValue UpperBound(IntegerVariable i) const
int CurrentDecisionLevel() const
IntegerLiteral SplitAroundGivenValue(IntegerVariable var, IntegerValue value, Model *model)
static constexpr SearchBranching FIXED_SEARCH
std::function< BooleanOrIntegerLiteral()> next_decision_override
SharedRelaxationSolutionRepository * relaxation_solutions
Collection of objects used to extend the Constraint Solver library.
const IntegerVariable kNoIntegerVariable(-1)
void RecordLPRelaxationValues(Model *model)
std::function< BooleanOrIntegerLiteral()> IntegerValueSelectionHeuristic(std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
void NotifyThatModelIsUnsat()
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::function< BooleanOrIntegerLiteral()> PseudoCost(Model *model)
std::vector< PseudoCosts::VariableBoundChange > GetBoundChanges(LiteralIndex decision, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
const VariablesAssignment & Assignment() const
IntegerVariable FirstUnassignedVariable() const
IntegerLiteral SplitAroundLpValue(IntegerVariable var, Model *model)
bool TakeDecision(Literal decision)
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(Model *model)
std::function< BooleanOrIntegerLiteral()> UnassignedVarWithLowestMinAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
bool randomize_search() const
std::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
bool LinearizedPartIsLarge(Model *model)
bool EnqueueNewDeductions()
std::vector< std::function< bool()> > restart_policies
#define CHECK_NE(val1, val2)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()>> heuristics)
LiteralIndex GetDecision(const std::function< BooleanOrIntegerLiteral()> &f)
double GetSolutionValue(IntegerVariable variable) const
bool exploit_integer_lp_solution() const