23#include "absl/container/flat_hash_set.h"
24#include "absl/strings/str_cat.h"
25#include "absl/time/clock.h"
26#include "absl/time/time.h"
29#include "ortools/sat/cp_model.pb.h"
43#include "ortools/sat/sat_parameters.pb.h"
62 const auto& variables =
65 if (variables.contains(
var)) {
79 const IntegerValue chosen_value =
80 var_lb +
std::max(IntegerValue(1), (var_ub - var_lb) / IntegerValue(2));
88 const IntegerValue ub = integer_trail->UpperBound(
var);
90 const absl::flat_hash_set<IntegerVariable>& variables =
98 const bool branch_down_feasible =
value >= lb &&
value < ub;
99 const bool branch_up_feasible =
value > lb &&
value <= ub;
100 if (variables.contains(
var) && branch_down_feasible) {
102 }
else if (variables.contains(
NegationOf(
var)) && branch_up_feasible) {
104 }
else if (branch_down_feasible) {
106 }
else if (branch_up_feasible) {
116 DCHECK(!integer_trail->IsCurrentlyIgnored(
var));
133 const IntegerValue
value = IntegerValue(
150 const int proto_var =
168 const std::vector<IntegerVariable>& vars,
Model*
model) {
170 return [ vars, integer_trail]() {
171 for (
const IntegerVariable
var : vars) {
173 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
181std::function<BooleanOrIntegerLiteral()>
183 const std::vector<IntegerVariable>& vars,
Model*
model) {
185 return [ vars, integer_trail]() {
187 IntegerValue candidate_lb;
188 for (
const IntegerVariable
var : vars) {
189 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
190 const IntegerValue lb = integer_trail->LowerBound(
var);
204 return [heuristics]() {
205 for (
const auto& h : heuristics) {
207 if (decision.
HasValue())
return decision;
215 value_selection_heuristics,
224 if (!current_decision.
HasValue())
return current_decision;
229 sat_policy->InStablePhase()) {
230 return current_decision;
235 for (
const auto& value_heuristic : value_selection_heuristics) {
240 return current_decision;
247 if (integer_trail->IsCurrentlyIgnored(l.var))
continue;
250 for (
const auto& value_heuristic : value_selection_heuristics) {
256 return current_decision;
261 auto* lp_constraints =
263 int num_lp_variables = 0;
265 num_lp_variables += lp->NumVariables();
267 const int num_integer_variables =
269 return (num_integer_variables <= 2 * num_lp_variables);
276 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
278 value_selection_heuristics;
287 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
295 if (response_manager !=
nullptr) {
296 VLOG(3) <<
"Using best solution value selection heuristic.";
297 value_selection_heuristics.push_back(
298 [
model, response_manager](IntegerVariable
var) {
300 var, response_manager->SolutionsRepository(),
model);
306 if (
parameters.exploit_relaxation_solution()) {
310 value_selection_heuristics.push_back(
312 VLOG(3) <<
"Using relaxation solution value selection heuristic.";
321 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
327 var_selection_heuristic,
model);
334 return [sat_solver, trail, decision_policy] {
335 const bool all_assigned = trail->Index() == sat_solver->
NumVariables();
337 const Literal result = decision_policy->NextBranch();
345 const bool has_objective =
347 if (!has_objective) {
353 return [pseudo_costs, integer_trail]() {
354 const IntegerVariable chosen_var = pseudo_costs->GetBestDecisionVar();
371 return [repo, heuristic, trail, integer_trail]() {
386 const int num_intervals = repo->NumIntervals();
387 for (IntervalVariable i(0); i < num_intervals; ++i) {
388 if (repo->IsAbsent(i))
continue;
389 if (!repo->IsPresent(i) || !integer_trail->IsFixed(repo->Start(i)) ||
390 !integer_trail->IsFixed(repo->End(i))) {
391 IntegerValue
time = integer_trail->LowerBound(repo->Start(i));
398 repo->PresenceLiteral(i), repo->Start(i)));
405 const IntegerValue size_min =
406 std::max(integer_trail->LowerBound(repo->Size(i)),
407 integer_trail->LowerBound(repo->End(i)) -
time);
408 if (
time < best.time ||
409 (
time == best.time && size_min < best.size_min)) {
412 best.start = repo->Start(i);
413 best.end = repo->End(i);
415 best.size_min = size_min;
424 heuristic->next_decision_override = [trail, integer_trail, best,
425 num_times]()
mutable {
426 if (++num_times > 5) {
430 VLOG(3) <<
"Skipping ... ";
436 if (!trail->Assignment().LiteralIsAssigned(
Literal(best.presence))) {
437 VLOG(3) <<
"assign " << best.presence;
440 if (trail->Assignment().LiteralIsFalse(
Literal(best.presence))) {
441 VLOG(2) <<
"unperformed.";
447 if (!integer_trail->IsFixed(best.start)) {
448 const IntegerValue
start_min = integer_trail->LowerBound(best.start);
454 if (!integer_trail->IsFixed(best.end)) {
455 const IntegerValue
end_min = integer_trail->LowerBound(best.end);
461 const IntegerValue
start = integer_trail->LowerBound(best.start);
463 << integer_trail->LowerBound(best.end) <<
"]"
465 ? absl::StrCat(
" presence=",
468 << (best.time <
start
469 ? absl::StrCat(
" start_at_selection=", best.time.value())
474 return heuristic->next_decision_override();
491 std::discrete_distribution<int> var_dist{3 , 1 };
495 value_selection_heuristics;
496 std::vector<int> value_selection_weight;
499 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
502 value_selection_weight.push_back(8);
506 if (response_manager !=
nullptr) {
507 value_selection_heuristics.push_back(
508 [
model, response_manager](IntegerVariable
var) {
510 var, response_manager->SolutionsRepository(),
model);
512 value_selection_weight.push_back(5);
518 value_selection_heuristics.push_back(
523 value_selection_weight.push_back(3);
528 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
531 value_selection_weight.push_back(1);
534 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
537 value_selection_weight.push_back(1);
540 value_selection_weight.push_back(10);
544 std::discrete_distribution<int> val_dist(value_selection_weight.begin(),
545 value_selection_weight.end());
547 int policy_index = 0;
548 int val_policy_index = 0;
550 return [=]()
mutable {
554 decision_policy->ResetDecisionHeuristic();
557 policy_index = var_dist(*(random));
560 val_policy_index = val_dist(*(random));
565 if (!current_decision.
HasValue())
return current_decision;
568 if (val_policy_index >= value_selection_heuristics.size()) {
569 return current_decision;
574 value_selection_heuristics[val_policy_index](
577 return current_decision;
583 if (integer_trail->IsCurrentlyIgnored(l.var))
continue;
587 value_selection_heuristics[val_policy_index](l.var);
592 return current_decision;
598 const std::vector<BooleanOrIntegerVariable>& vars,
599 const std::vector<IntegerValue>& values,
Model*
model) {
603 for (
int i = 0; i < vars.size(); ++i) {
604 const IntegerValue
value = values[i];
610 const IntegerVariable integer_var = vars[i].int_var;
611 if (integer_trail->IsCurrentlyIgnored(integer_var))
continue;
612 if (integer_trail->IsFixed(integer_var))
continue;
628 bool reset_at_next_call =
true;
629 int next_num_failures = 0;
630 return [=]()
mutable {
631 if (reset_at_next_call) {
633 reset_at_next_call =
false;
634 }
else if (solver->
num_failures() >= next_num_failures) {
635 reset_at_next_call =
true;
637 return reset_at_next_call;
648std::function<BooleanOrIntegerLiteral()> WrapIntegerLiteralHeuristic(
649 std::function<IntegerLiteral()> f) {
650 return [f]() {
return BooleanOrIntegerLiteral(f()); };
662 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
664 case SatParameters::AUTOMATIC_SEARCH: {
678 case SatParameters::FIXED_SEARCH: {
691 auto no_restart = []() {
return false; };
695 case SatParameters::HINT_SEARCH: {
700 auto no_restart = []() {
return false; };
704 case SatParameters::PORTFOLIO_SEARCH: {
710 for (
const auto&
ct :
712 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
713 ct->HeuristicLpReducedCostBinary(
model)));
714 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
715 ct->HeuristicLpMostInfeasibleBinary(
model)));
727 case SatParameters::LP_SEARCH: {
729 for (
const auto&
ct :
731 lp_heuristics.push_back(WrapIntegerLiteralHeuristic(
732 ct->HeuristicLpReducedCostAverageBranching()));
734 if (lp_heuristics.empty()) {
747 case SatParameters::PSEUDO_COST_SEARCH: {
756 case SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH: {
769 incomplete_heuristics,
772 complete_heuristics.reserve(incomplete_heuristics.size());
773 for (
const auto& incomplete : incomplete_heuristics) {
774 complete_heuristics.push_back(
777 return complete_heuristics;
790 if (objective !=
nullptr) objective_var_ = objective->
objective_var;
812 for (
const auto& cb : level_zero_callbacks->callbacks) {
819 if (model_->
GetOrCreate<SatParameters>()->use_sat_inprocessing() &&
834 const IntegerVariable
var =
851 if (!new_decision.
HasValue())
break;
870 VLOG(1) <<
"Trying to take a decision that is already assigned!"
871 <<
" Fix this. Continuing for now...";
881 const std::vector<PseudoCosts::VariableBoundChange> bound_changes =
886 old_obj_lb = integer_trail_->
LowerBound(objective_var_);
887 old_obj_ub = integer_trail_->
UpperBound(objective_var_);
907 const IntegerValue new_obj_lb = integer_trail_->
LowerBound(objective_var_);
908 const IntegerValue new_obj_ub = integer_trail_->
UpperBound(objective_var_);
909 const IntegerValue objective_bound_change =
910 (new_obj_lb - old_obj_lb) + (old_obj_ub - new_obj_ub);
911 pseudo_costs_->
UpdateCost(bound_changes, objective_bound_change);
933 if (objective !=
nullptr) objective_var = objective->
objective_var;
951 const SatParameters& sat_parameters = *(
model->GetOrCreate<SatParameters>());
954 const int64_t old_num_conflicts = sat_solver->
num_failures();
955 const int64_t conflict_limit = sat_parameters.max_number_of_conflicts();
956 int64_t num_decisions_since_last_lp_record_ = 0;
957 int64_t num_decisions_without_probing = 0;
959 (sat_solver->
num_failures() - old_num_conflicts < conflict_limit)) {
968 if (!helper->BeforeTakingDecision())
return sat_solver->
UnsatStatus();
981 decision = helper->GetDecision(
991 sat_parameters.probing_period_at_root() > 0 &&
992 ++num_decisions_without_probing >=
993 sat_parameters.probing_period_at_root()) {
994 num_decisions_without_probing = 0;
1026 if (
model->GetOrCreate<SatParameters>()->use_optimization_hints()) {
1028 const auto& trail = *
model->GetOrCreate<
Trail>();
1029 for (
int i = 0; i < trail.Index(); ++i) {
1030 sat_decision->SetAssignmentPreference(trail[i], 0.0);
1036 if (!helper->TakeDecision(
Literal(decision))) {
1049 num_decisions_since_last_lp_record_++;
1050 if (num_decisions_since_last_lp_record_ >= 100) {
1055 num_decisions_since_last_lp_record_ = 0;
1059 return SatSolver::Status::LIMIT_REACHED;
1063 const std::vector<Literal>& assumptions,
Model*
model) {
1069 for (
const auto& cb : level_zero_callbacks->callbacks) {
1084 const IntegerVariable num_vars =
1086 std::vector<IntegerVariable> all_variables;
1087 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
1088 all_variables.push_back(
var);
1108 parameters_(*(
model->GetOrCreate<SatParameters>())),
1113 active_limit_(parameters_.shaving_search_deterministic_time()) {
1115 absl::flat_hash_set<BooleanVariable> visited;
1116 for (
int v = 0; v <
model_proto.variables_size(); ++v) {
1117 if (mapping->IsBoolean(v)) {
1119 const auto [_, inserted] = visited.insert(bool_var);
1121 bool_vars_.push_back(bool_var);
1124 IntegerVariable
var = mapping->Integer(v);
1126 int_vars_.push_back(
var);
1129 VLOG(2) <<
"Start continuous probing with " << bool_vars_.size()
1130 <<
" Boolean variables, and " << int_vars_.size()
1131 <<
" integer variables"
1132 <<
", deterministic time limit = "
1134 last_logging_time_ = absl::Now();
1150 if (parameters_.use_sat_inprocessing() &&
1156 probed_bool_vars_.clear();
1157 probed_literals_.clear();
1160 const int64_t initial_num_literals_fixed =
1162 const int64_t initial_num_bounds_shaved = num_bounds_shaved_;
1166 for (; current_int_var_ < int_vars_.size(); ++current_int_var_) {
1167 const IntegerVariable int_var = int_vars_[current_int_var_];
1168 if (integer_trail_->
IsFixed(int_var) ||
1173 if (!ImportFromSharedClasses()) {
1181 const BooleanVariable shave_lb =
1184 int_var, integer_trail_->
LowerBound(int_var)))
1186 const auto [_lb, lb_inserted] = probed_bool_vars_.insert(shave_lb);
1191 num_literals_probed_++;
1194 const BooleanVariable shave_ub =
1197 int_var, integer_trail_->
UpperBound(int_var)))
1199 const auto [_ub, ub_inserted] = probed_bool_vars_.insert(shave_ub);
1204 num_literals_probed_++;
1207 if (parameters_.use_shaving_in_probing_search()) {
1209 ShaveLiteral(
Literal(shave_lb,
true));
1210 if (ReportStatus(lb_status))
return lb_status;
1213 ShaveLiteral(
Literal(shave_ub,
true));
1214 if (ReportStatus(ub_status))
return ub_status;
1221 for (; current_bool_var_ < bool_vars_.size(); ++current_bool_var_) {
1222 const BooleanVariable& bool_var = bool_vars_[current_bool_var_];
1226 if (!ImportFromSharedClasses()) {
1234 const auto [_, inserted] = probed_bool_vars_.insert(bool_var);
1239 num_literals_probed_++;
1243 if (parameters_.use_shaving_in_probing_search() &&
1246 if (ReportStatus(true_status))
return true_status;
1250 if (ReportStatus(false_status))
return false_status;
1258 const double deterministic_time =
1259 parameters_.shaving_search_deterministic_time();
1260 const bool something_has_been_detected =
1261 num_bounds_shaved_ != initial_num_bounds_shaved ||
1263 if (something_has_been_detected) {
1264 active_limit_ = deterministic_time;
1265 }
else if (active_limit_ < 25 * deterministic_time) {
1266 active_limit_ += deterministic_time;
1271 current_bool_var_ = 0;
1272 current_int_var_ = 0;
1277bool ContinuousProber::ImportFromSharedClasses() {
1279 for (
const auto& cb : level_zero_callbacks_->
callbacks) {
1289 const auto [_, inserted] = probed_literals_.insert(
literal.Index());
1293 num_bounds_tried_++;
1304 num_bounds_shaved_++;
1314void ContinuousProber::LogStatistics() {
1315 if (shared_response_manager_ ==
nullptr ||
1316 shared_bounds_manager_ ==
nullptr) {
1321 absl::StrCat(
"#iterations:", iteration_,
" #literals fixed/probed:",
1323 " #bounds shaved/tried:", num_bounds_shaved_,
"/",
1324 num_bounds_tried_,
" #new_integer_bounds:",
1327 &last_logging_time_);
#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...
double GetDeterministicLimit() const
Queries the deterministic time limit.
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
void ChangeDeterministicLimit(double new_limit)
Overwrites the deterministic time limit with the new value.
SatSolver::Status Probe()
ContinuousProber(const CpModelProto &model_proto, Model *model)
sat::Literal Literal(int ref) const
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 IsFixed(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.
const std::string & Name() const
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
int num_new_binary_clauses() const
bool ProbeOneVariable(BooleanVariable b)
int num_new_literals_fixed() const
void UpdateCost(const std::vector< VariableBoundChange > &bound_changes, IntegerValue obj_bound_improvement)
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
int NumBoundsExported(const std::string &worker_name)
void LogPeriodicMessage(const std::string &prefix, const std::string &message, absl::Time *last_logging_time)
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
CpModelProto const * model_proto
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)
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
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)
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)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
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)
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)
const int kUnsatTrailIndex
std::function< BooleanOrIntegerLiteral()> PseudoCost(Model *model)
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(Model *model)
Collection of objects used to extend the Constraint Solver library.
std::optional< int64_t > end
LiteralIndex boolean_literal_index
IntegerLiteral integer_literal
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< std::function< bool()> > callbacks
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