25#include "absl/cleanup/cleanup.h"
26#include "absl/log/check.h"
27#include "absl/strings/str_cat.h"
28#include "absl/types/span.h"
41#include "ortools/sat/sat_parameters.pb.h"
52 : name_(model->Name()),
53 time_limit_(model->GetOrCreate<
TimeLimit>()),
55 sat_solver_(model->GetOrCreate<
SatSolver>()),
57 trail_(model->GetOrCreate<
Trail>()),
65 parameters_(*model->GetOrCreate<SatParameters>()) {
72 DCHECK(objective !=
nullptr);
80 if (lp->ObjectiveVariable() == objective_var_) {
89 if (SaveLpBasisOption()) {
99bool LbTreeSearch::NodeHasBasis(
const Node& node)
const {
100 return !node.basis.IsEmpty();
103bool LbTreeSearch::NodeHasUpToDateBasis(
const Node& node)
const {
104 if (node.basis.IsEmpty())
return false;
114void LbTreeSearch::EnableLpAndLoadBestBasis() {
115 DCHECK(lp_constraint_ !=
nullptr);
116 lp_constraint_->EnablePropagation(
true);
118 const int level = sat_solver_->CurrentDecisionLevel();
119 if (current_branch_.empty())
return;
122 int basis_level = -1;
124 for (
int i = 0;
i < level; ++
i) {
125 if (n >= nodes_.size())
break;
126 if (NodeHasBasis(nodes_[n])) {
128 last_node_with_basis = n;
130 const Literal decision = sat_solver_->Decisions()[
i].literal;
131 if (nodes_[n].literal_index == decision.Index()) {
132 n = nodes_[n].true_child;
134 DCHECK_EQ(nodes_[n].literal_index, decision.NegatedIndex());
135 n = nodes_[n].false_child;
138 if (n < nodes_.size()) {
139 if (NodeHasBasis(nodes_[n])) {
141 last_node_with_basis = n;
145 if (last_node_with_basis == -1) {
146 VLOG(1) <<
"no basis?";
149 VLOG(1) <<
"load " << basis_level <<
" / " << level;
151 if (!NodeHasUpToDateBasis(nodes_[last_node_with_basis])) {
154 VLOG(1) <<
"Skipping potentially bad basis.";
158 lp_constraint_->LoadBasisState(nodes_[last_node_with_basis].basis);
161void LbTreeSearch::SaveLpBasisInto(Node& node) {
162 node.basis_timestamp = lp_constraint_->num_lp_changes();
163 node.basis = lp_constraint_->GetBasisState();
166void LbTreeSearch::UpdateParentObjective(
int level) {
168 DCHECK_LT(level, current_branch_.size());
169 if (level == 0)
return;
170 const NodeIndex parent_index = current_branch_[level - 1];
171 Node& parent = nodes_[parent_index];
172 const NodeIndex child_index = current_branch_[level];
173 const Node& child = nodes_[child_index];
174 if (parent.true_child == child_index) {
175 parent.UpdateTrueObjective(child.MinObjective());
177 DCHECK_EQ(parent.false_child, child_index);
178 parent.UpdateFalseObjective(child.MinObjective());
182void LbTreeSearch::UpdateObjectiveFromParent(
int level) {
184 DCHECK_LT(level, current_branch_.size());
185 if (level == 0)
return;
186 const NodeIndex parent_index = current_branch_[level - 1];
187 const Node& parent = nodes_[parent_index];
188 DCHECK_GE(parent.MinObjective(), current_objective_lb_);
189 const NodeIndex child_index = current_branch_[level];
190 Node& child = nodes_[child_index];
191 if (parent.true_child == child_index) {
192 child.UpdateObjective(parent.true_objective);
194 DCHECK_EQ(parent.false_child, child_index);
195 child.UpdateObjective(parent.false_objective);
199std::string LbTreeSearch::NodeDebugString(
NodeIndex n)
const {
200 const IntegerValue root_lb = current_objective_lb_;
201 const auto shifted_lb = [root_lb](IntegerValue lb) {
202 return std::max<int64_t>(0, (lb - root_lb).value());
206 absl::StrAppend(&s,
"#", n.value());
208 const Node& node = nodes_[n];
209 std::string true_letter =
"t";
210 std::string false_letter =
"f";
212 const Literal decision = node.Decision();
213 if (assignment_.LiteralIsTrue(decision)) {
216 if (assignment_.LiteralIsFalse(decision)) {
221 if (node.true_child < nodes_.size()) {
222 absl::StrAppend(&s,
" [", true_letter,
":#", node.true_child.value(),
" ",
223 shifted_lb(node.true_objective),
"]");
225 absl::StrAppend(&s,
" [", true_letter,
":## ",
226 shifted_lb(node.true_objective),
"]");
228 if (node.false_child < nodes_.size()) {
229 absl::StrAppend(&s,
" [", false_letter,
":#", node.false_child.value(),
" ",
230 shifted_lb(node.false_objective),
"]");
232 absl::StrAppend(&s,
" [", false_letter,
":## ",
233 shifted_lb(node.false_objective),
"]");
236 if (node.is_deleted) {
237 absl::StrAppend(&s,
" <D>");
239 if (NodeHasBasis(node)) {
240 absl::StrAppend(&s,
" <B>");
246void LbTreeSearch::DebugDisplayTree(
NodeIndex root)
const {
249 util_intops::StrongVector<NodeIndex, int> level(nodes_.size(), 0);
250 std::vector<NodeIndex> to_explore = {root};
251 while (!to_explore.empty()) {
253 to_explore.pop_back();
256 const Node& node = nodes_[n];
258 std::string s(level[n],
' ');
259 absl::StrAppend(&s, NodeDebugString(n));
262 if (node.true_child < nodes_.size()) {
263 to_explore.push_back(node.true_child);
264 level[node.true_child] = level[n] + 1;
266 if (node.false_child < nodes_.size()) {
267 to_explore.push_back(node.false_child);
268 level[node.false_child] = level[n] + 1;
271 LOG(INFO) <<
"num_nodes: " << num_nodes;
278bool LbTreeSearch::FullRestart() {
279 ++num_full_restarts_;
280 num_decisions_taken_at_last_restart_ = num_decisions_taken_;
281 num_nodes_in_tree_ = 0;
283 current_branch_.clear();
284 return sat_solver_->RestoreSolverToAssumptionLevel();
287void LbTreeSearch::MarkAsDeletedNodeAndUnreachableSubtree(Node& node) {
288 --num_nodes_in_tree_;
289 DCHECK(!node.is_deleted);
290 node.is_deleted =
true;
292 if (assignment_.LiteralIsTrue(Literal(node.literal_index))) {
293 MarkSubtreeAsDeleted(node.false_child);
295 MarkSubtreeAsDeleted(node.true_child);
299void LbTreeSearch::MarkBranchAsInfeasible(Node& node,
bool true_branch) {
302 MarkSubtreeAsDeleted(node.true_child);
303 node.true_child =
NodeIndex(std::numeric_limits<int32_t>::max());
306 MarkSubtreeAsDeleted(node.false_child);
307 node.false_child =
NodeIndex(std::numeric_limits<int32_t>::max());
311void LbTreeSearch::MarkSubtreeAsDeleted(
NodeIndex root) {
312 std::vector<NodeIndex> to_delete{root};
313 for (
int i = 0;
i < to_delete.size(); ++
i) {
315 if (n >= nodes_.size())
continue;
316 if (nodes_[n].is_deleted)
continue;
318 --num_nodes_in_tree_;
319 nodes_[n].is_deleted =
true;
321 to_delete.push_back(nodes_[n].true_child);
322 to_delete.push_back(nodes_[n].false_child);
326std::string LbTreeSearch::SmallProgressString()
const {
328 "nodes=", num_nodes_in_tree_,
"/", nodes_.size(),
329 " rc=", num_rc_detected_,
" decisions=", num_decisions_taken_,
330 " @root=", num_back_to_root_node_,
" restarts=", num_full_restarts_,
331 " lp_iters=[",
FormatCounter(num_lp_iters_at_level_zero_),
", ",
337std::function<void()> LbTreeSearch::UpdateLpIters(int64_t* counter) {
338 if (lp_constraint_ ==
nullptr)
return []() {};
339 const int64_t old_num = lp_constraint_->total_num_simplex_iterations();
340 return [old_num, counter,
this]() {
341 const int64_t new_num = lp_constraint_->total_num_simplex_iterations();
342 *counter += new_num - old_num;
346bool LbTreeSearch::LevelZeroLogic() {
347 ++num_back_to_root_node_;
348 num_decisions_taken_at_last_level_zero_ = num_decisions_taken_;
351 if (SaveLpBasisOption() && !current_branch_.empty()) {
353 absl::MakeCleanup(UpdateLpIters(&num_lp_iters_at_level_zero_));
354 EnableLpAndLoadBestBasis();
355 if (!sat_solver_->FinishPropagation()) {
358 SaveLpBasisInto(nodes_[current_branch_[0]]);
359 lp_constraint_->EnablePropagation(
false);
367 const IntegerValue ub = shared_response_->GetInnerObjectiveUpperBound();
368 if (integer_trail_->UpperBound(objective_var_) > ub) {
369 if (!integer_trail_->Enqueue(
371 sat_solver_->NotifyThatModelIsUnsat();
374 if (!sat_solver_->FinishPropagation()) {
386 if (num_nodes_in_tree_ > 50) {
389 const IntegerValue latest_lb =
390 shared_response_->GetInnerObjectiveLowerBound();
392 int num_nodes_with_lower_objective = 0;
393 for (
const Node& node : nodes_) {
394 if (node.is_deleted)
continue;
396 if (node.MinObjective() < latest_lb) num_nodes_with_lower_objective++;
398 DCHECK_EQ(num_nodes_in_tree_, num_nodes);
399 if (num_nodes_with_lower_objective * 2 > num_nodes) {
400 VLOG(2) <<
"lb_tree_search restart nodes: "
401 << num_nodes_with_lower_objective <<
"/" << num_nodes <<
" : "
402 << 100.0 * num_nodes_with_lower_objective / num_nodes <<
"%"
403 <<
", decisions:" << num_decisions_taken_;
404 if (!FullRestart())
return false;
412 const std::function<
void()>& feasible_solution_observer) {
413 if (!sat_solver_->RestoreSolverToAssumptionLevel()) {
414 return sat_solver_->UnsatStatus();
436 const int kMaxNumInitialRestarts = 10;
437 const int64_t kNumDecisionsBeforeInitialRestarts = 1000;
441 watcher_->SetStopPropagationCallback([
this] {
442 return integer_trail_->LowerBound(objective_var_) > current_objective_lb_;
445 while (!time_limit_->LimitReached() && !shared_response_->ProblemIsSolved()) {
446 VLOG(2) <<
"LOOP " << sat_solver_->CurrentDecisionLevel();
458 const IntegerValue obj_diff =
459 integer_trail_->LowerBound(objective_var_) -
460 integer_trail_->LevelZeroLowerBound(objective_var_);
462 std::vector<Literal> reason =
464 objective_var_, integer_trail_->LowerBound(objective_var_)));
467 pseudo_costs_->UpdateBoolPseudoCosts(reason, obj_diff);
468 sat_decision_->BumpVariableActivities(reason);
469 sat_decision_->UpdateVariableActivityIncrement();
473 if (!current_branch_.empty()) {
477 const int current_level = sat_solver_->CurrentDecisionLevel();
478 const IntegerValue current_objective_lb =
479 integer_trail_->LowerBound(objective_var_);
481 CHECK_LE(current_level, current_branch_.size());
482 for (
int i = 0;
i < current_level; ++
i) {
483 CHECK(!nodes_[current_branch_[
i]].is_deleted);
484 CHECK(assignment_.LiteralIsAssigned(
485 nodes_[current_branch_[
i]].Decision()));
488 if (current_level < current_branch_.size()) {
489 nodes_[current_branch_[current_level]].UpdateObjective(
490 current_objective_lb);
501 if (current_objective_lb >
502 integer_trail_->LevelZeroLowerBound(objective_var_)) {
503 const std::vector<Literal> reason =
505 objective_var_, current_objective_lb));
507 for (
const Literal l : reason) {
508 max_level = std::max<int>(
510 sat_solver_->LiteralTrail().Info(l.Variable()).level);
512 if (max_level < current_level) {
513 nodes_[current_branch_[max_level]].UpdateObjective(
514 current_objective_lb);
520 for (
int level = current_branch_.size(); --level > 0;) {
521 UpdateParentObjective(level);
525 if (SaveLpBasisOption()) {
529 lp_constraint_->EnablePropagation(
false);
531 if (sat_solver_->ModelIsUnsat())
return sat_solver_->UnsatStatus();
535 if (!search_helper_->BeforeTakingDecision()) {
536 return sat_solver_->UnsatStatus();
542 current_objective_lb_ = shared_response_->GetInnerObjectiveLowerBound();
543 if (!current_branch_.empty()) {
544 nodes_[current_branch_[0]].UpdateObjective(current_objective_lb_);
545 for (
int i = 1;
i < current_branch_.size(); ++
i) {
546 UpdateObjectiveFromParent(
i);
550 const IntegerValue bound = nodes_[current_branch_[0]].MinObjective();
551 if (bound > current_objective_lb_) {
552 shared_response_->UpdateInnerObjectiveBounds(
553 absl::StrCat(name_,
" (", SmallProgressString(),
") "), bound,
554 integer_trail_->LevelZeroUpperBound(objective_var_));
555 current_objective_lb_ = bound;
556 if (VLOG_IS_ON(3)) DebugDisplayTree(current_branch_[0]);
564 if (num_decisions_taken_ >= num_decisions_taken_at_last_restart_ +
565 kNumDecisionsBeforeInitialRestarts &&
566 num_full_restarts_ < kMaxNumInitialRestarts) {
567 VLOG(2) <<
"lb_tree_search (initial_restart " << SmallProgressString()
569 if (!FullRestart())
return sat_solver_->UnsatStatus();
574 if (num_decisions_taken_ >=
575 num_decisions_taken_at_last_level_zero_ + 10000) {
576 if (!sat_solver_->ResetToLevelZero()) {
577 return sat_solver_->UnsatStatus();
589 while (current_branch_.size() > sat_solver_->CurrentDecisionLevel() + 1 ||
590 (current_branch_.size() > 1 &&
591 nodes_[current_branch_.back()].MinObjective() >
592 current_objective_lb_)) {
593 current_branch_.pop_back();
598 const int backtrack_level =
599 std::max(0,
static_cast<int>(current_branch_.size()) - 1);
600 sat_solver_->Backtrack(backtrack_level);
601 if (!sat_solver_->FinishPropagation()) {
602 return sat_solver_->UnsatStatus();
604 if (sat_solver_->CurrentDecisionLevel() < backtrack_level)
continue;
607 if (sat_solver_->CurrentDecisionLevel() == 0) {
608 if (!LevelZeroLogic()) {
609 return sat_solver_->UnsatStatus();
620 const int size = current_branch_.size();
621 const int level = sat_solver_->CurrentDecisionLevel();
627 DCHECK(size == level || size == level + 1);
628 if (size == level)
break;
630 const NodeIndex node_index = current_branch_[level];
631 Node& node = nodes_[node_index];
632 DCHECK_GT(node.true_child, node_index);
633 DCHECK_GT(node.false_child, node_index);
636 node.UpdateObjective(std::max(
637 current_objective_lb_, integer_trail_->LowerBound(objective_var_)));
638 if (node.MinObjective() > current_objective_lb_)
break;
639 DCHECK_EQ(node.MinObjective(), current_objective_lb_) << level;
643 DCHECK(!node.is_deleted);
644 const Literal node_literal = node.Decision();
648 if (assignment_.LiteralIsAssigned(node_literal)) {
650 if (assignment_.LiteralIsTrue(node_literal)) {
652 new_lb = node.true_objective;
654 n = node.false_child;
655 new_lb = node.false_objective;
657 MarkAsDeletedNodeAndUnreachableSubtree(node);
661 current_branch_.pop_back();
662 if (!current_branch_.empty()) {
663 const NodeIndex parent = current_branch_.back();
664 DCHECK(!nodes_[parent].is_deleted);
665 const Literal parent_literal = nodes_[parent].Decision();
666 if (assignment_.LiteralIsTrue(parent_literal)) {
667 nodes_[parent].true_child = n;
668 nodes_[parent].UpdateTrueObjective(new_lb);
670 DCHECK(assignment_.LiteralIsFalse(parent_literal));
671 nodes_[parent].false_child = n;
672 nodes_[parent].UpdateFalseObjective(new_lb);
674 if (new_lb > current_objective_lb_) {
676 if (n < nodes_.size() && !nodes_[n].IsLeaf()) {
677 current_branch_.push_back(n);
678 nodes_[n].UpdateObjective(new_lb);
683 if (n >= nodes_.size()) {
686 num_nodes_in_tree_ = 0;
688 }
else if (nodes_[n].IsLeaf()) {
690 num_nodes_in_tree_ = 1;
691 Node root = std::move(nodes_[n]);
693 nodes_.push_back(std::move(root));
703 ExploitReducedCosts(current_branch_[level]);
704 if (node.MinObjective() > current_objective_lb_)
break;
710 num_decisions_taken_++;
711 const bool choose_true = node.true_objective <= node.false_objective;
715 next_decision = node_literal;
717 n = node.false_child;
718 next_decision = node_literal.
Negated();
727 if (SaveLpBasisOption() &&
728 (n >= nodes_.size() || !NodeHasBasis(node))) {
730 absl::MakeCleanup(UpdateLpIters(&num_lp_iters_save_basis_));
733 EnableLpAndLoadBestBasis();
734 const int level = sat_solver_->CurrentDecisionLevel();
735 if (!sat_solver_->FinishPropagation()) {
736 return sat_solver_->UnsatStatus();
738 if (sat_solver_->CurrentDecisionLevel() < level) {
744 if (assignment_.LiteralIsAssigned(next_decision)) {
748 SaveLpBasisInto(node);
751 if (n < nodes_.size()) {
752 lp_constraint_->EnablePropagation(
false);
758 absl::MakeCleanup(UpdateLpIters(&num_lp_iters_first_branch_));
759 DCHECK(!assignment_.LiteralIsAssigned(next_decision));
760 search_helper_->TakeDecision(next_decision);
763 if (current_branch_.size() != sat_solver_->CurrentDecisionLevel()) {
764 MarkBranchAsInfeasible(node, choose_true);
770 const IntegerValue lb = integer_trail_->LowerBound(objective_var_);
772 node.UpdateTrueObjective(lb);
774 node.UpdateFalseObjective(lb);
777 if (n < nodes_.size()) {
778 nodes_[n].UpdateObjective(lb);
779 }
else if (SaveLpBasisOption()) {
780 SaveLpBasisInto(nodes_[CreateNewEmptyNodeIfNeeded()]);
783 if (lb > current_objective_lb_)
break;
787 shared_response_->LogMessageWithThrottling(
788 "TreeS", absl::StrCat(
" (", SmallProgressString(),
")"));
791 if (n < nodes_.size() && !nodes_[n].IsLeaf()) {
792 current_branch_.push_back(n);
799 if (current_branch_.size() != sat_solver_->CurrentDecisionLevel()) {
805 if (!current_branch_.empty()) {
806 const Node& final_node = nodes_[current_branch_.back()];
807 if (assignment_.LiteralIsTrue(final_node.Decision())) {
808 if (final_node.true_objective > current_objective_lb_) {
812 DCHECK(assignment_.LiteralIsFalse(final_node.Decision()));
813 if (final_node.false_objective > current_objective_lb_) {
832 if (integer_trail_->LowerBound(objective_var_) > current_objective_lb_) {
836 const auto cleanup = absl::MakeCleanup(UpdateLpIters(&num_lp_iters_dive_));
838 if (current_branch_.empty()) {
839 VLOG(2) <<
"DIVE from empty tree";
841 VLOG(2) <<
"DIVE from " << NodeDebugString(current_branch_.back());
844 if (SaveLpBasisOption() && !lp_constraint_->PropagationIsEnabled()) {
846 const NodeIndex index = CreateNewEmptyNodeIfNeeded();
848 EnableLpAndLoadBestBasis();
849 const int level = sat_solver_->CurrentDecisionLevel();
850 if (!sat_solver_->FinishPropagation()) {
851 return sat_solver_->UnsatStatus();
855 if (sat_solver_->CurrentDecisionLevel() < level) {
856 Node& node = nodes_[index];
861 SaveLpBasisInto(nodes_[index]);
863 const IntegerValue obj_lb = integer_trail_->LowerBound(objective_var_);
864 if (obj_lb > current_objective_lb_) {
865 nodes_[index].UpdateObjective(obj_lb);
866 if (!current_branch_.empty()) {
867 Node& parent_node = nodes_[current_branch_.back()];
868 const Literal node_literal = parent_node.Decision();
869 DCHECK(assignment_.LiteralIsAssigned(node_literal));
870 if (assignment_.LiteralIsTrue(node_literal)) {
871 parent_node.UpdateTrueObjective(obj_lb);
873 parent_node.UpdateFalseObjective(obj_lb);
882 const int base_level = sat_solver_->CurrentDecisionLevel();
884 CHECK_EQ(base_level, current_branch_.size());
885 for (
const NodeIndex index : current_branch_) {
886 CHECK(!nodes_[index].is_deleted);
887 const Literal decision = nodes_[index].Decision();
888 if (assignment_.LiteralIsTrue(decision)) {
889 CHECK_EQ(nodes_[index].true_objective, current_objective_lb_);
891 CHECK(assignment_.LiteralIsFalse(decision));
892 CHECK_EQ(nodes_[index].false_objective, current_objective_lb_);
909 if (sat_solver_->ModelIsUnsat())
return sat_solver_->UnsatStatus();
910 LiteralIndex decision;
911 if (!search_helper_->GetDecision(search_heuristic_, &decision)) {
918 feasible_solution_observer();
922 num_decisions_taken_++;
923 if (!search_helper_->TakeDecision(
Literal(decision))) {
924 return sat_solver_->UnsatStatus();
926 if (sat_solver_->CurrentDecisionLevel() < base_level) {
933 if (integer_trail_->LowerBound(objective_var_) > current_objective_lb_) {
938 if (sat_solver_->CurrentDecisionLevel() <= base_level)
continue;
944 const std::vector<Literal> reason =
946 objective_var_, integer_trail_->LowerBound(objective_var_)));
947 std::vector<Literal> decisions = ExtractDecisions(base_level, reason);
950 sat_decision_->BumpVariableActivities(reason);
951 sat_decision_->BumpVariableActivities(decisions);
952 sat_decision_->UpdateVariableActivityIncrement();
955 DCHECK_EQ(current_branch_.size(), base_level);
956 for (
const Literal d : decisions) {
957 AppendNewNodeToCurrentBranch(d);
961 if (SaveLpBasisOption() && decisions.size() == 1) {
962 SaveLpBasisInto(nodes_[CreateNewEmptyNodeIfNeeded()]);
967 if (!current_branch_.empty()) {
968 Node& n = nodes_[current_branch_.back()];
969 if (assignment_.LiteralIsTrue(n.Decision())) {
970 n.UpdateTrueObjective(integer_trail_->LowerBound(objective_var_));
972 n.UpdateFalseObjective(integer_trail_->LowerBound(objective_var_));
981 int backtrack_level = base_level;
982 DCHECK_LE(current_branch_.size(), sat_solver_->CurrentDecisionLevel());
983 while (backtrack_level < current_branch_.size() &&
984 sat_solver_->Decisions()[backtrack_level].literal.Index() ==
985 nodes_[current_branch_[backtrack_level]].literal_index) {
988 sat_solver_->Backtrack(backtrack_level);
996 for (
int i = backtrack_level;
i < current_branch_.size(); ++
i) {
997 ExploitReducedCosts(current_branch_[
i]);
1004std::vector<Literal> LbTreeSearch::ExtractDecisions(
1005 int base_level, absl::Span<const Literal> conflict) {
1007 std::vector<bool> is_marked;
1008 for (
const Literal l : conflict) {
1010 if (info.
level <= base_level)
continue;
1011 num_per_level[info.
level]++;
1018 std::vector<Literal> result;
1019 if (is_marked.empty())
return result;
1020 for (
int i = is_marked.size() - 1;
i >= 0; --
i) {
1021 if (!is_marked[
i])
continue;
1023 const Literal l = (*trail_)[
i];
1024 const AssignmentInfo& info = trail_->
Info(l.Variable());
1025 if (info.level <= base_level)
break;
1026 if (num_per_level[info.level] == 1) {
1027 result.push_back(l);
1032 num_per_level[info.level]--;
1033 for (
const Literal new_l : trail_->Reason(l.Variable())) {
1034 const AssignmentInfo& new_info = trail_->Info(new_l.Variable());
1035 if (new_info.level <= base_level)
continue;
1036 if (is_marked[new_info.trail_index])
continue;
1037 is_marked[new_info.trail_index] =
true;
1038 num_per_level[new_info.level]++;
1043 std::reverse(result.begin(), result.end());
1047LbTreeSearch::NodeIndex LbTreeSearch::CreateNewEmptyNodeIfNeeded() {
1049 if (current_branch_.empty()) {
1050 if (nodes_.empty()) {
1051 ++num_nodes_in_tree_;
1052 nodes_.emplace_back(current_objective_lb_);
1054 DCHECK_EQ(nodes_.size(), 1);
1057 const NodeIndex parent = current_branch_.back();
1058 DCHECK(!nodes_[parent].is_deleted);
1059 const Literal parent_literal = nodes_[parent].Decision();
1060 if (assignment_.LiteralIsTrue(parent_literal)) {
1061 if (nodes_[parent].true_child >= nodes_.size()) {
1063 ++num_nodes_in_tree_;
1064 nodes_[parent].true_child =
NodeIndex(nodes_.size());
1065 nodes_.emplace_back(current_objective_lb_);
1067 n = nodes_[parent].true_child;
1069 nodes_[parent].UpdateTrueObjective(current_objective_lb_);
1071 DCHECK(assignment_.LiteralIsFalse(parent_literal));
1072 if (nodes_[parent].false_child >= nodes_.size()) {
1074 ++num_nodes_in_tree_;
1075 nodes_[parent].false_child =
NodeIndex(nodes_.size());
1076 nodes_.emplace_back(current_objective_lb_);
1078 n = nodes_[parent].false_child;
1080 nodes_[parent].UpdateFalseObjective(current_objective_lb_);
1083 DCHECK(!nodes_[n].is_deleted);
1088void LbTreeSearch::AppendNewNodeToCurrentBranch(
Literal decision) {
1090 if (current_branch_.empty()) {
1091 if (nodes_.empty()) {
1092 ++num_nodes_in_tree_;
1093 nodes_.emplace_back(current_objective_lb_);
1095 DCHECK_EQ(nodes_.size(), 1);
1099 const NodeIndex parent = current_branch_.back();
1100 DCHECK(!nodes_[parent].is_deleted);
1101 const Literal parent_literal = nodes_[parent].Decision();
1102 if (assignment_.LiteralIsTrue(parent_literal)) {
1103 if (nodes_[parent].true_child < nodes_.size()) {
1104 n = nodes_[parent].true_child;
1106 ++num_nodes_in_tree_;
1107 nodes_.emplace_back(current_objective_lb_);
1108 nodes_[parent].true_child = n;
1110 nodes_[parent].UpdateTrueObjective(current_objective_lb_);
1112 DCHECK(assignment_.LiteralIsFalse(parent_literal));
1113 if (nodes_[parent].false_child < nodes_.size()) {
1114 n = nodes_[parent].false_child;
1116 ++num_nodes_in_tree_;
1117 nodes_.emplace_back(current_objective_lb_);
1118 nodes_[parent].false_child = n;
1120 nodes_[parent].UpdateFalseObjective(current_objective_lb_);
1123 DCHECK_LT(n, nodes_.size());
1124 DCHECK_EQ(nodes_[n].literal_index,
kNoLiteralIndex) <<
" issue " << n;
1125 nodes_[n].SetDecision(decision);
1126 nodes_[n].UpdateObjective(current_objective_lb_);
1127 current_branch_.push_back(n);
1142void LbTreeSearch::ExploitReducedCosts(
NodeIndex n) {
1143 if (lp_constraint_ ==
nullptr)
return;
1148 const auto& cts = lp_constraint_->OptimalConstraints();
1149 if (cts.empty())
return;
1150 const std::unique_ptr<IntegerSumLE128>& rc = cts.back();
1155 Node& node = nodes_[n];
1156 DCHECK(!node.is_deleted);
1157 const Literal node_literal = node.Decision();
1158 DCHECK(!assignment_.LiteralIsAssigned(node_literal));
1159 for (
const IntegerLiteral integer_literal :
1160 integer_encoder_->GetIntegerLiterals(node_literal)) {
1162 if (++num_tests > 10)
break;
1164 const std::pair<IntegerValue, IntegerValue> bounds =
1165 rc->ConditionalLb(integer_literal, objective_var_);
1166 if (bounds.first > node.false_objective) {
1168 node.UpdateFalseObjective(bounds.first);
1170 if (bounds.second > node.true_objective) {
1172 node.UpdateTrueObjective(bounds.second);
An helper class to share the code used by the different kind of search.
LbTreeSearch(Model *model)
SatSolver::Status Search(const std::function< void()> &feasible_solution_observer)
Explores the search space.
A class that stores the collection of all LP constraints in a model.
int64_t num_lp_changes() const
This can serve as a timestamp to know if a saved basis is out of date.
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
int CurrentDecisionLevel() const
const AssignmentInfo & Info(BooleanVariable var) const
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
const LiteralIndex kNoLiteralIndex(-1)
std::function< BooleanOrIntegerLiteral()> IntegerValueSelectionHeuristic(std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
std::string FormatCounter(int64_t num)
Prints a positive number with separators for easier reading (ex: 1'348'065).
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
Returns the BooleanOrIntegerLiteral advised by the underlying SAT solver.
std::function< BooleanOrIntegerLiteral()> LpPseudoCostHeuristic(Model *model)
std::function< BooleanOrIntegerLiteral()> MostFractionalHeuristic(Model *model)
Choose the variable with most fractional LP value.
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()> > heuristics)
In SWIG mode, we don't want anything besides these top-level includes.
Information about a variable assignment.
int32_t trail_index
The index of this assignment in the trail.
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
IntegerVariable objective_var
std::function< BooleanOrIntegerLiteral()> fixed_search
Fixed search, built from above building blocks.