24#include "absl/random/distributions.h"
25#include "absl/strings/str_cat.h"
26#include "absl/time/clock.h"
27#include "absl/time/time.h"
38#include "ortools/sat/sat_parameters.pb.h"
57 parameters_(*
model->GetOrCreate<SatParameters>()) {
64 CHECK(objective !=
nullptr);
72 if (lp->ObjectiveVariable() == objective_var_) {
84 last_logging_time_ = absl::Now();
87void LbTreeSearch::UpdateParentObjective(
int level) {
89 CHECK_LT(level, current_branch_.size());
90 if (level == 0)
return;
91 const NodeIndex parent_index = current_branch_[level - 1];
92 Node& parent = nodes_[parent_index];
93 const NodeIndex child_index = current_branch_[level];
94 const Node& child = nodes_[child_index];
95 if (parent.true_child == child_index) {
96 parent.UpdateTrueObjective(child.MinObjective());
98 CHECK_EQ(parent.false_child, child_index);
99 parent.UpdateFalseObjective(child.MinObjective());
103void LbTreeSearch::UpdateObjectiveFromParent(
int level) {
105 CHECK_LT(level, current_branch_.size());
106 if (level == 0)
return;
107 const NodeIndex parent_index = current_branch_[level - 1];
108 const Node& parent = nodes_[parent_index];
109 CHECK_GE(parent.MinObjective(), current_objective_lb_);
110 const NodeIndex child_index = current_branch_[level];
111 Node& child = nodes_[child_index];
112 if (parent.true_child == child_index) {
113 child.UpdateObjective(parent.true_objective);
115 CHECK_EQ(parent.false_child, child_index);
116 child.UpdateObjective(parent.false_objective);
120void LbTreeSearch::DebugDisplayTree(
NodeIndex root)
const {
122 const IntegerValue root_lb = nodes_[root].MinObjective();
123 const auto shifted_lb = [root_lb](IntegerValue lb) {
124 return std::max<int64_t>(0, (lb - root_lb).
value());
128 std::vector<NodeIndex> to_explore = {root};
129 while (!to_explore.empty()) {
131 to_explore.pop_back();
134 const Node& node = nodes_[n];
136 std::string s(level[n],
' ');
137 absl::StrAppend(&s,
"#", n.value());
139 if (node.true_child < nodes_.
size()) {
140 absl::StrAppend(&s,
" [t:#", node.true_child.value(),
" ",
141 shifted_lb(node.true_objective),
"]");
142 to_explore.push_back(node.true_child);
143 level[node.true_child] = level[n] + 1;
145 absl::StrAppend(&s,
" [t:## ", shifted_lb(node.true_objective),
"]");
147 if (node.false_child < nodes_.
size()) {
148 absl::StrAppend(&s,
" [f:#", node.false_child.value(),
" ",
149 shifted_lb(node.false_objective),
"]");
150 to_explore.push_back(node.false_child);
151 level[node.false_child] = level[n] + 1;
153 absl::StrAppend(&s,
" [f:## ", shifted_lb(node.false_objective),
"]");
157 LOG(
INFO) <<
"num_nodes: " << num_nodes;
161 const std::function<
void()>& feasible_solution_observer) {
185 const int64_t kNumBranchesBeforePeriodicRestarts = 1000;
186 int64_t num_restarts = 0;
187 const int kMaxNumInitialRestarts = 10;
196 if (!current_branch_.empty()) {
201 CHECK_GE(current_branch_.size(), current_level);
202 for (
int i = 0; i < current_level; ++i) {
204 nodes_[current_branch_[i]].literal));
206 if (current_level < current_branch_.size()) {
207 nodes_[current_branch_[current_level]].UpdateObjective(
219 if (integer_trail_->
LowerBound(objective_var_) >
221 const std::vector<Literal> reason =
223 objective_var_, integer_trail_->
LowerBound(objective_var_)));
225 for (
const Literal l : reason) {
226 max_level = std::max<int>(
230 if (max_level < current_level) {
231 nodes_[current_branch_[max_level]].UpdateObjective(
238 for (
int level = current_branch_.size(); --level > 0;) {
239 UpdateParentObjective(level);
241 nodes_[current_branch_[0]].UpdateObjective(current_objective_lb_);
242 for (
int level = 1; level < current_branch_.size(); ++level) {
243 UpdateObjectiveFromParent(level);
247 const IntegerValue
bound = nodes_[current_branch_[0]].MinObjective();
248 if (
bound > current_objective_lb_) {
250 absl::StrCat(
"lb_tree_search #nodes:", nodes_.
size(),
251 " #rc:", num_rc_detected_,
" #imports:", num_imports_,
252 " #restarts:", num_restarts),
254 current_objective_lb_ =
bound;
255 if (
VLOG_IS_ON(3)) DebugDisplayTree(current_branch_[0]);
269 if (integer_trail_->
LowerBound(objective_var_) >
271 std::vector<Literal> reason =
273 objective_var_, integer_trail_->
LowerBound(objective_var_)));
282 if (num_decisions_taken_ >= num_decisions_taken_at_last_restart_ +
283 kNumBranchesBeforePeriodicRestarts &&
284 num_restarts < kMaxNumInitialRestarts) {
286 num_decisions_taken_at_last_restart_ = num_decisions_taken_;
287 VLOG(2) <<
"lb_tree_search initial_restart nodes: " << nodes_.
size()
288 <<
", branches:" << num_decisions_taken_
289 <<
", restarts: " << num_restarts;
291 current_branch_.clear();
306 (current_branch_.size() > 1 &&
307 nodes_[current_branch_.back()].MinObjective() >
308 current_objective_lb_)) {
309 current_branch_.pop_back();
314 int backtrack_level =
315 std::max(0,
static_cast<int>(current_branch_.size()) - 1);
318 if (num_decisions_taken_ >= num_decisions_taken_at_last_import_ + 10000) {
331 num_decisions_taken_at_last_import_ = num_decisions_taken_;
346 const IntegerValue latest_lb =
348 int num_nodes_with_lower_objective = 0;
349 for (
const Node& node : nodes_) {
350 if (node.MinObjective() < latest_lb) num_nodes_with_lower_objective++;
352 if (num_nodes_with_lower_objective * 2 > nodes_.size()) {
354 num_decisions_taken_at_last_restart_ = num_decisions_taken_;
355 VLOG(2) <<
"lb_tree_search restart nodes: "
356 << num_nodes_with_lower_objective <<
"/" << nodes_.size()
358 << 100.0 * num_nodes_with_lower_objective / nodes_.size() <<
"%"
359 <<
", branches:" << num_decisions_taken_
360 <<
", restarts: " << num_restarts;
362 current_branch_.clear();
369 const int level = current_branch_.size() - 1;
371 Node& node = nodes_[current_branch_[level]];
373 current_objective_lb_, integer_trail_->
LowerBound(objective_var_)));
374 if (node.MinObjective() > current_objective_lb_) {
377 CHECK_EQ(node.MinObjective(), current_objective_lb_) << level;
388 new_lb = node.true_objective;
390 n = node.false_child;
391 new_lb = node.false_objective;
396 current_branch_.pop_back();
397 if (!current_branch_.empty()) {
398 const NodeIndex parent = current_branch_.back();
400 nodes_[parent].true_child = n;
401 nodes_[parent].UpdateTrueObjective(new_lb);
404 nodes_[parent].literal));
405 nodes_[parent].false_child = n;
406 nodes_[parent].UpdateFalseObjective(new_lb);
408 if (nodes_[parent].MinObjective() > current_objective_lb_)
break;
412 bool choose_true = node.true_objective < node.false_objective;
413 if (node.true_objective == node.false_objective) {
414 choose_true = absl::Bernoulli(*random_, 0.5);
420 n = node.false_child;
424 num_decisions_taken_++;
438 const IntegerValue lb = integer_trail_->
LowerBound(objective_var_);
440 node.UpdateTrueObjective(lb);
442 node.UpdateFalseObjective(lb);
444 if (lb > current_objective_lb_)
break;
449 absl::StrCat(
"#nodes:", nodes_.
size(),
" #branches:",
450 num_decisions_taken_,
" #imports:", num_imports_),
451 &last_logging_time_);
453 if (n < nodes_.
size()) {
454 current_branch_.push_back(n);
478 if (integer_trail_->
LowerBound(objective_var_) > current_objective_lb_) {
483 const LiteralIndex decision =
489 feasible_solution_observer();
499 if (!current_branch_.empty()) {
500 const NodeIndex parent = current_branch_.back();
502 nodes_[parent].true_child = n;
503 nodes_[parent].UpdateTrueObjective(nodes_.
back().MinObjective());
506 nodes_[parent].false_child = n;
507 nodes_[parent].UpdateFalseObjective(nodes_.
back().MinObjective());
510 current_branch_.push_back(n);
524 if (lp_constraint_ !=
nullptr) {
533 if (++num_tests > 10)
break;
539 if (cts.empty())
continue;
541 const std::unique_ptr<IntegerSumLE>& rc = cts.back();
542 const std::pair<IntegerValue, IntegerValue>
bounds =
543 rc->ConditionalLb(integer_literal, objective_var_);
544 Node& node = nodes_[n];
545 if (
bounds.first > node.false_objective) {
547 node.UpdateFalseObjective(
bounds.first);
549 if (
bounds.second > node.true_objective) {
551 node.UpdateTrueObjective(
bounds.second);
#define CHECK_LT(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define VLOG(verboselevel)
void emplace_back(Args &&... args)
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...
const InlinedIntegerLiteralVector & GetIntegerLiterals(Literal lit) const
bool BeforeTakingDecision()
bool TakeDecision(Literal decision)
LiteralIndex GetDecision(const std::function< BooleanOrIntegerLiteral()> &f)
bool IsCurrentlyIgnored(IntegerVariable i) const
std::vector< Literal > ReasonFor(IntegerLiteral literal) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
IntegerValue LowerBound(IntegerVariable i) const
LbTreeSearch(Model *model)
SatSolver::Status Search(const std::function< void()> &feasible_solution_observer)
const std::vector< std::unique_ptr< IntegerSumLE > > & OptimalConstraints() const
Class that owns everything related to a particular optimization model.
void UpdateVariableActivityIncrement()
void BumpVariableActivities(const std::vector< Literal > &literals)
const VariablesAssignment & Assignment() const
const Trail & LiteralTrail() const
Status UnsatStatus() const
void Backtrack(int target_level)
bool RestoreSolverToAssumptionLevel()
int CurrentDecisionLevel() const
bool ProblemIsSolved() const
void LogPeriodicMessage(const std::string &prefix, const std::string &message, absl::Time *last_logging_time)
IntegerValue GetInnerObjectiveLowerBound()
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
const AssignmentInfo & Info(BooleanVariable var) const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
SharedBoundsManager * bounds
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()> > heuristics)
const LiteralIndex kNoLiteralIndex(-1)
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
Collection of objects used to extend the Constraint Solver library.
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
IntegerVariable objective_var
#define VLOG_IS_ON(verboselevel)