21 #include "absl/container/flat_hash_set.h"
22 #include "absl/types/span.h"
49 const auto& variables =
52 if (variables.contains(
var)) {
66 const IntegerValue chosen_value =
67 var_lb +
std::max(IntegerValue(1), (var_ub - var_lb) / IntegerValue(2));
75 const IntegerValue ub = integer_trail->UpperBound(
var);
77 const absl::flat_hash_set<IntegerVariable>& variables =
85 const bool branch_down_feasible =
value >= lb &&
value < ub;
86 const bool branch_up_feasible =
value > lb &&
value <= ub;
87 if (variables.contains(
var) && branch_down_feasible) {
89 }
else if (variables.contains(
NegationOf(
var)) && branch_up_feasible) {
91 }
else if (branch_down_feasible) {
93 }
else if (branch_up_feasible) {
103 DCHECK(!integer_trail->IsCurrentlyIgnored(
var));
120 const IntegerValue
value = IntegerValue(
137 const int proto_var =
155 const std::vector<IntegerVariable>& vars,
Model*
model) {
157 return [ vars, integer_trail]() {
158 for (
const IntegerVariable
var : vars) {
160 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
168 std::function<BooleanOrIntegerLiteral()>
170 const std::vector<IntegerVariable>& vars,
Model*
model) {
172 return [ vars, integer_trail]() {
174 IntegerValue candidate_lb;
175 for (
const IntegerVariable
var : vars) {
176 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
177 const IntegerValue lb = integer_trail->LowerBound(
var);
191 return [heuristics]() {
192 for (
const auto& h : heuristics) {
194 if (decision.
HasValue())
return decision;
202 value_selection_heuristics,
211 if (!current_decision.
HasValue())
return current_decision;
216 sat_policy->InStablePhase()) {
217 return current_decision;
222 for (
const auto& value_heuristic : value_selection_heuristics) {
227 return current_decision;
234 if (integer_trail->IsCurrentlyIgnored(l.var))
continue;
237 for (
const auto& value_heuristic : value_selection_heuristics) {
243 return current_decision;
248 auto* lp_constraints =
250 int num_lp_variables = 0;
252 num_lp_variables += lp->NumVariables();
254 const int num_integer_variables =
256 return (num_integer_variables <= 2 * num_lp_variables);
263 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
265 value_selection_heuristics;
274 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
282 if (response_manager !=
nullptr) {
283 VLOG(2) <<
"Using best solution value selection heuristic.";
284 value_selection_heuristics.push_back(
285 [
model, response_manager](IntegerVariable
var) {
287 var, response_manager->SolutionsRepository(),
model);
293 if (
parameters.exploit_relaxation_solution()) {
297 value_selection_heuristics.push_back(
299 VLOG(2) <<
"Using relaxation solution value selection heuristic.";
308 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
314 var_selection_heuristic,
model);
321 return [sat_solver, trail, decision_policy] {
322 const bool all_assigned = trail->Index() == sat_solver->
NumVariables();
324 const Literal result = decision_policy->NextBranch();
332 const bool has_objective =
334 if (!has_objective) {
340 return [pseudo_costs, integer_trail]() {
341 const IntegerVariable chosen_var = pseudo_costs->GetBestDecisionVar();
364 std::discrete_distribution<int> var_dist{3 , 1 };
368 value_selection_heuristics;
369 std::vector<int> value_selection_weight;
372 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
375 value_selection_weight.push_back(8);
379 if (response_manager !=
nullptr) {
380 value_selection_heuristics.push_back(
381 [
model, response_manager](IntegerVariable
var) {
383 var, response_manager->SolutionsRepository(),
model);
385 value_selection_weight.push_back(5);
391 value_selection_heuristics.push_back(
396 value_selection_weight.push_back(3);
401 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
404 value_selection_weight.push_back(1);
407 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
410 value_selection_weight.push_back(1);
413 value_selection_weight.push_back(10);
417 std::discrete_distribution<int> val_dist(value_selection_weight.begin(),
418 value_selection_weight.end());
420 int policy_index = 0;
421 int val_policy_index = 0;
423 return [=]()
mutable {
427 decision_policy->ResetDecisionHeuristic();
430 policy_index = var_dist(*(random));
433 val_policy_index = val_dist(*(random));
438 if (!current_decision.
HasValue())
return current_decision;
441 if (val_policy_index >= value_selection_heuristics.size()) {
442 return current_decision;
447 value_selection_heuristics[val_policy_index](
450 return current_decision;
456 if (integer_trail->IsCurrentlyIgnored(l.var))
continue;
460 value_selection_heuristics[val_policy_index](l.var);
465 return current_decision;
471 const std::vector<BooleanOrIntegerVariable>& vars,
472 const std::vector<IntegerValue>& values,
Model*
model) {
476 for (
int i = 0; i < vars.size(); ++i) {
477 const IntegerValue
value = values[i];
483 const IntegerVariable integer_var = vars[i].int_var;
484 if (integer_trail->IsCurrentlyIgnored(integer_var))
continue;
485 if (integer_trail->IsFixed(integer_var))
continue;
501 bool reset_at_next_call =
true;
502 int next_num_failures = 0;
503 return [=]()
mutable {
504 if (reset_at_next_call) {
506 reset_at_next_call =
false;
507 }
else if (solver->
num_failures() >= next_num_failures) {
508 reset_at_next_call =
true;
510 return reset_at_next_call;
521 std::function<BooleanOrIntegerLiteral()> WrapIntegerLiteralHeuristic(
522 std::function<IntegerLiteral()> f) {
523 return [f]() {
return BooleanOrIntegerLiteral(f()); };
535 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
537 case SatParameters::AUTOMATIC_SEARCH: {
551 case SatParameters::FIXED_SEARCH: {
564 auto no_restart = []() {
return false; };
568 case SatParameters::HINT_SEARCH: {
573 auto no_restart = []() {
return false; };
577 case SatParameters::PORTFOLIO_SEARCH: {
583 for (
const auto&
ct :
585 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
586 ct->HeuristicLpReducedCostBinary(
model)));
587 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
588 ct->HeuristicLpMostInfeasibleBinary(
model)));
600 case SatParameters::LP_SEARCH: {
602 for (
const auto&
ct :
604 lp_heuristics.push_back(WrapIntegerLiteralHeuristic(
605 ct->HeuristicLpReducedCostAverageBranching()));
607 if (lp_heuristics.empty()) {
620 case SatParameters::PSEUDO_COST_SEARCH: {
629 case SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH: {
642 incomplete_heuristics,
645 complete_heuristics.reserve(incomplete_heuristics.size());
646 for (
const auto& incomplete : incomplete_heuristics) {
647 complete_heuristics.push_back(
650 return complete_heuristics;
666 if (objective !=
nullptr) objective_var = objective->
objective_var;
692 const SatParameters& sat_parameters = *(
model->GetOrCreate<SatParameters>());
695 const int64_t old_num_conflicts = sat_solver->
num_failures();
696 const int64_t conflict_limit = sat_parameters.max_number_of_conflicts();
697 int64_t num_decisions_since_last_lp_record_ = 0;
698 int64_t num_decisions_without_probing = 0;
700 (sat_solver->
num_failures() - old_num_conflicts < conflict_limit)) {
713 if (integer_trail->HasPendingRootLevelDeduction()) {
721 if (!implied_bounds->EnqueueNewDeductions()) {
725 auto* level_zero_callbacks =
727 for (
const auto& cb : level_zero_callbacks->callbacks) {
733 if (sat_parameters.use_sat_inprocessing() &&
742 if (integer_trail->InPropagationLoop()) {
743 const IntegerVariable
var =
744 integer_trail->NextVariableToBranchOnInPropagationLoop();
754 integer_trail->CurrentBranchHadAnIncompletePropagation()) {
755 const IntegerVariable
var = integer_trail->FirstUnassignedVariable();
760 if (!new_decision.
HasValue())
break;
780 VLOG(1) <<
"Trying to take a decision that is already assigned!"
781 <<
" Fix this. Continuing for now...";
787 sat_parameters.probing_period_at_root() > 0 &&
788 ++num_decisions_without_probing >=
789 sat_parameters.probing_period_at_root()) {
790 num_decisions_without_probing = 0;
793 if (!prober->ProbeOneVariable(
Literal(decision).Variable())) {
824 if (
model->GetOrCreate<SatParameters>()->use_optimization_hints()) {
826 const auto& trail = *
model->GetOrCreate<
Trail>();
827 for (
int i = 0; i < trail.Index(); ++i) {
828 sat_decision->SetAssignmentPreference(trail[i], 0.0);
835 const std::vector<PseudoCosts::VariableBoundChange> bound_changes =
840 old_obj_lb = integer_trail->LowerBound(objective_var);
841 old_obj_ub = integer_trail->UpperBound(objective_var);
852 implied_bounds->ProcessIntegerTrail(
Literal(decision));
858 const IntegerValue new_obj_lb = integer_trail->LowerBound(objective_var);
859 const IntegerValue new_obj_ub = integer_trail->UpperBound(objective_var);
860 const IntegerValue objective_bound_change =
861 (new_obj_lb - old_obj_lb) + (old_obj_ub - new_obj_ub);
862 pseudo_costs->
UpdateCost(bound_changes, objective_bound_change);
879 num_decisions_since_last_lp_record_++;
880 if (num_decisions_since_last_lp_record_ >= 100) {
885 num_decisions_since_last_lp_record_ = 0;
889 return SatSolver::Status::LIMIT_REACHED;
893 const std::vector<Literal>& assumptions,
Model*
model) {
899 for (
const auto& cb : level_zero_callbacks->callbacks) {
911 const IntegerVariable num_vars =
913 std::vector<IntegerVariable> all_variables;
914 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
915 all_variables.push_back(
var);
928 const std::vector<BooleanVariable>& bool_vars,
929 const std::vector<IntegerVariable>& int_vars,
930 const std::function<
void()>& feasible_solution_observer,
Model*
model) {
931 VLOG(1) <<
"Start continuous probing with " << bool_vars.size()
932 <<
" Boolean variables, and " << int_vars.size()
933 <<
" integer variables";
939 const SatParameters& sat_parameters = *(
model->GetOrCreate<SatParameters>());
943 std::vector<BooleanVariable> active_vars;
944 std::vector<BooleanVariable> integer_bounds;
945 absl::flat_hash_set<BooleanVariable> integer_bounds_set;
949 VLOG(1) <<
"Probing loop " << loop++;
952 auto SyncBounds = [solver, &level_zero_callbacks]() {
954 for (
const auto& cb : level_zero_callbacks->callbacks) {
955 if (!cb())
return false;
964 if (sat_parameters.use_sat_inprocessing() &&
972 absl::flat_hash_set<BooleanVariable> probed;
976 for (
const IntegerVariable int_var : int_vars) {
977 if (integer_trail->IsFixed(int_var) ||
978 integer_trail->IsOptional(int_var)) {
982 const BooleanVariable shave_lb =
985 int_var, integer_trail->LowerBound(int_var)))
987 if (!probed.contains(shave_lb)) {
988 probed.insert(shave_lb);
989 if (!prober->ProbeOneVariable(shave_lb)) {
994 const BooleanVariable shave_ub =
997 int_var, integer_trail->UpperBound(int_var)))
999 if (!probed.contains(shave_ub)) {
1000 probed.insert(shave_ub);
1001 if (!prober->ProbeOneVariable(shave_ub)) {
1006 if (!SyncBounds()) {
1015 for (
const BooleanVariable& bool_var : bool_vars) {
1020 if (!SyncBounds()) {
1023 if (!probed.contains(bool_var)) {
1024 probed.insert(bool_var);
1025 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)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool IsCurrentlyIgnored(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LowerBound(IntegerVariable i) const
double GetSolutionValue(IntegerVariable variable) const
bool SolutionIsInteger() const
LiteralIndex Index() const
Class that owns everything related to a particular optimization model.
void UpdateCost(const std::vector< VariableBoundChange > &bound_changes, IntegerValue obj_bound_improvement)
Status UnsatStatus() const
void AdvanceDeterministicTime(TimeLimit *limit)
const VariablesAssignment & Assignment() const
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
SharedTimeLimit * 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::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
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)
IntegerLiteral ChooseBestObjectiveValue(IntegerVariable var, Model *model)
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(Model *model)
void ConfigureSearchHeuristics(Model *model)
std::vector< std::function< BooleanOrIntegerLiteral()> > CompleteHeuristics(const std::vector< std::function< BooleanOrIntegerLiteral()>> &incomplete_heuristics, const std::function< BooleanOrIntegerLiteral()> &completion_heuristic)
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::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)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()>> heuristics)
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()> SequentialValueSelection(std::vector< std::function< IntegerLiteral(IntegerVariable)>> value_selection_heuristics, std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, 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::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies