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"
40#include "ortools/sat/sat_parameters.pb.h"
51 : name_(
model->Name()),
64 parameters_(*
model->GetOrCreate<SatParameters>()) {
71 DCHECK(objective !=
nullptr);
79 if (lp->ObjectiveVariable() == objective_var_) {
88 if (SaveLpBasisOption()) {
98bool LbTreeSearch::NodeHasBasis(
const Node& node)
const {
99 return !node.basis.IsEmpty();
102bool LbTreeSearch::NodeHasUpToDateBasis(
const Node& node)
const {
103 if (node.basis.IsEmpty())
return false;
113void LbTreeSearch::EnableLpAndLoadBestBasis() {
114 DCHECK(lp_constraint_ !=
nullptr);
118 if (current_branch_.empty())
return;
121 int basis_level = -1;
123 for (
int i = 0;
i < level; ++
i) {
124 if (n >= nodes_.size())
break;
125 if (NodeHasBasis(nodes_[n])) {
127 last_node_with_basis = n;
129 const Literal decision = sat_solver_->
Decisions()[
i].literal;
130 if (nodes_[n].literal_index == decision.Index()) {
131 n = nodes_[n].true_child;
133 DCHECK_EQ(nodes_[n].literal_index, decision.NegatedIndex());
134 n = nodes_[n].false_child;
137 if (n < nodes_.size()) {
138 if (NodeHasBasis(nodes_[n])) {
140 last_node_with_basis = n;
144 if (last_node_with_basis == -1) {
145 VLOG(1) <<
"no basis?";
148 VLOG(1) <<
"load " << basis_level <<
" / " << level;
150 if (!NodeHasUpToDateBasis(nodes_[last_node_with_basis])) {
153 VLOG(1) <<
"Skipping potentially bad basis.";
157 lp_constraint_->
LoadBasisState(nodes_[last_node_with_basis].basis);
160void LbTreeSearch::SaveLpBasisInto(Node& node) {
165void LbTreeSearch::UpdateParentObjective(
int level) {
167 DCHECK_LT(level, current_branch_.size());
168 if (level == 0)
return;
169 const NodeIndex parent_index = current_branch_[level - 1];
170 Node& parent = nodes_[parent_index];
171 const NodeIndex child_index = current_branch_[level];
172 const Node& child = nodes_[child_index];
173 if (parent.true_child == child_index) {
174 parent.UpdateTrueObjective(child.MinObjective());
176 DCHECK_EQ(parent.false_child, child_index);
177 parent.UpdateFalseObjective(child.MinObjective());
181void LbTreeSearch::UpdateObjectiveFromParent(
int level) {
183 DCHECK_LT(level, current_branch_.size());
184 if (level == 0)
return;
185 const NodeIndex parent_index = current_branch_[level - 1];
186 const Node& parent = nodes_[parent_index];
187 DCHECK_GE(parent.MinObjective(), current_objective_lb_);
188 const NodeIndex child_index = current_branch_[level];
189 Node& child = nodes_[child_index];
190 if (parent.true_child == child_index) {
191 child.UpdateObjective(parent.true_objective);
193 DCHECK_EQ(parent.false_child, child_index);
194 child.UpdateObjective(parent.false_objective);
198std::string LbTreeSearch::NodeDebugString(
NodeIndex n)
const {
199 const IntegerValue root_lb = current_objective_lb_;
200 const auto shifted_lb = [root_lb](IntegerValue lb) {
201 return std::max<int64_t>(0, (lb - root_lb).
value());
205 absl::StrAppend(&s,
"#", n.value());
207 const Node& node = nodes_[n];
208 std::string true_letter =
"t";
209 std::string false_letter =
"f";
211 const Literal decision = node.Decision();
220 if (node.true_child < nodes_.size()) {
221 absl::StrAppend(&s,
" [", true_letter,
":#", node.true_child.value(),
" ",
222 shifted_lb(node.true_objective),
"]");
224 absl::StrAppend(&s,
" [", true_letter,
":## ",
225 shifted_lb(node.true_objective),
"]");
227 if (node.false_child < nodes_.size()) {
228 absl::StrAppend(&s,
" [", false_letter,
":#", node.false_child.value(),
" ",
229 shifted_lb(node.false_objective),
"]");
231 absl::StrAppend(&s,
" [", false_letter,
":## ",
232 shifted_lb(node.false_objective),
"]");
235 if (node.is_deleted) {
236 absl::StrAppend(&s,
" <D>");
238 if (NodeHasBasis(node)) {
239 absl::StrAppend(&s,
" <B>");
245void LbTreeSearch::DebugDisplayTree(
NodeIndex root)
const {
249 std::vector<NodeIndex> to_explore = {root};
250 while (!to_explore.empty()) {
252 to_explore.pop_back();
255 const Node& node = nodes_[n];
257 std::string s(level[n],
' ');
258 absl::StrAppend(&s, NodeDebugString(n));
261 if (node.true_child < nodes_.size()) {
262 to_explore.push_back(node.true_child);
263 level[node.true_child] = level[n] + 1;
265 if (node.false_child < nodes_.size()) {
266 to_explore.push_back(node.false_child);
267 level[node.false_child] = level[n] + 1;
270 LOG(INFO) <<
"num_nodes: " << num_nodes;
277bool LbTreeSearch::FullRestart() {
278 ++num_full_restarts_;
279 num_decisions_taken_at_last_restart_ = num_decisions_taken_;
280 num_nodes_in_tree_ = 0;
282 current_branch_.clear();
286void LbTreeSearch::MarkAsDeletedNodeAndUnreachableSubtree(Node& node) {
287 --num_nodes_in_tree_;
288 DCHECK(!node.is_deleted);
289 node.is_deleted =
true;
291 if (assignment_.
LiteralIsTrue(Literal(node.literal_index))) {
292 MarkSubtreeAsDeleted(node.false_child);
294 MarkSubtreeAsDeleted(node.true_child);
298void LbTreeSearch::MarkBranchAsInfeasible(Node& node,
bool true_branch) {
301 MarkSubtreeAsDeleted(node.true_child);
302 node.true_child =
NodeIndex(std::numeric_limits<int32_t>::max());
305 MarkSubtreeAsDeleted(node.false_child);
306 node.false_child =
NodeIndex(std::numeric_limits<int32_t>::max());
310void LbTreeSearch::MarkSubtreeAsDeleted(
NodeIndex root) {
311 std::vector<NodeIndex> to_delete{root};
312 for (
int i = 0;
i < to_delete.size(); ++
i) {
314 if (n >= nodes_.size())
continue;
315 if (nodes_[n].is_deleted)
continue;
317 --num_nodes_in_tree_;
318 nodes_[n].is_deleted =
true;
320 to_delete.
push_back(nodes_[n].true_child);
321 to_delete.push_back(nodes_[n].false_child);
325std::string LbTreeSearch::SmallProgressString()
const {
327 "nodes=", num_nodes_in_tree_,
"/", nodes_.size(),
328 " rc=", num_rc_detected_,
" decisions=", num_decisions_taken_,
329 " @root=", num_back_to_root_node_,
" restarts=", num_full_restarts_,
330 " lp_iters=[",
FormatCounter(num_lp_iters_at_level_zero_),
", ",
336std::function<void()> LbTreeSearch::UpdateLpIters(int64_t* counter) {
337 if (lp_constraint_ ==
nullptr)
return []() {};
339 return [old_num, counter,
this]() {
341 *counter += new_num - old_num;
345bool LbTreeSearch::LevelZeroLogic() {
346 ++num_back_to_root_node_;
347 num_decisions_taken_at_last_level_zero_ = num_decisions_taken_;
350 if (SaveLpBasisOption() && !current_branch_.empty()) {
352 absl::MakeCleanup(UpdateLpIters(&num_lp_iters_at_level_zero_));
353 EnableLpAndLoadBestBasis();
357 SaveLpBasisInto(nodes_[current_branch_[0]]);
367 if (integer_trail_->
UpperBound(objective_var_) > ub) {
385 if (num_nodes_in_tree_ > 50) {
388 const IntegerValue latest_lb =
391 int num_nodes_with_lower_objective = 0;
392 for (
const Node& node : nodes_) {
393 if (node.is_deleted)
continue;
395 if (node.MinObjective() < latest_lb) num_nodes_with_lower_objective++;
397 DCHECK_EQ(num_nodes_in_tree_, num_nodes);
398 if (num_nodes_with_lower_objective * 2 > num_nodes) {
399 VLOG(2) <<
"lb_tree_search restart nodes: "
400 << num_nodes_with_lower_objective <<
"/" << num_nodes <<
" : "
401 << 100.0 * num_nodes_with_lower_objective / num_nodes <<
"%"
402 <<
", decisions:" << num_decisions_taken_;
403 if (!FullRestart())
return false;
411 const std::function<
void()>& feasible_solution_observer) {
435 const int kMaxNumInitialRestarts = 10;
436 const int64_t kNumDecisionsBeforeInitialRestarts = 1000;
441 return integer_trail_->
LowerBound(objective_var_) > current_objective_lb_;
457 const IntegerValue obj_diff =
461 std::vector<Literal> reason =
463 objective_var_, integer_trail_->
LowerBound(objective_var_)));
472 if (!current_branch_.empty()) {
477 const IntegerValue current_objective_lb =
480 CHECK_LE(current_level, current_branch_.size());
481 for (
int i = 0;
i < current_level; ++
i) {
482 CHECK(!nodes_[current_branch_[
i]].is_deleted);
484 nodes_[current_branch_[
i]].Decision()));
487 if (current_level < current_branch_.size()) {
488 nodes_[current_branch_[current_level]].UpdateObjective(
489 current_objective_lb);
500 if (current_objective_lb >
502 const std::vector<Literal> reason =
504 objective_var_, current_objective_lb));
506 for (
const Literal l : reason) {
507 max_level = std::max<int>(
511 if (max_level < current_level) {
512 nodes_[current_branch_[max_level]].UpdateObjective(
513 current_objective_lb);
519 for (
int level = current_branch_.size(); --level > 0;) {
520 UpdateParentObjective(level);
524 if (SaveLpBasisOption()) {
541 if (!current_branch_.empty()) {
542 nodes_[current_branch_[0]].UpdateObjective(current_objective_lb_);
543 for (
int i = 1;
i < current_branch_.size(); ++
i) {
544 UpdateObjectiveFromParent(
i);
548 const IntegerValue
bound = nodes_[current_branch_[0]].MinObjective();
549 if (
bound > current_objective_lb_) {
551 absl::StrCat(name_,
" (", SmallProgressString(),
") "),
bound,
553 current_objective_lb_ =
bound;
554 if (VLOG_IS_ON(3)) DebugDisplayTree(current_branch_[0]);
562 if (num_decisions_taken_ >= num_decisions_taken_at_last_restart_ +
563 kNumDecisionsBeforeInitialRestarts &&
564 num_full_restarts_ < kMaxNumInitialRestarts) {
565 VLOG(2) <<
"lb_tree_search (initial_restart " << SmallProgressString()
567 if (!FullRestart())
return sat_solver_->
UnsatStatus();
572 if (num_decisions_taken_ >=
573 num_decisions_taken_at_last_level_zero_ + 10000) {
588 (current_branch_.size() > 1 &&
589 nodes_[current_branch_.back()].MinObjective() >
590 current_objective_lb_)) {
591 current_branch_.pop_back();
596 const int backtrack_level =
597 std::max(0,
static_cast<int>(current_branch_.size()) - 1);
606 if (!LevelZeroLogic()) {
618 const int size = current_branch_.size();
625 DCHECK(
size == level ||
size == level + 1);
626 if (
size == level)
break;
628 const NodeIndex node_index = current_branch_[level];
629 Node& node = nodes_[node_index];
630 DCHECK_GT(node.true_child, node_index);
631 DCHECK_GT(node.false_child, node_index);
634 node.UpdateObjective(std::max(
635 current_objective_lb_, integer_trail_->
LowerBound(objective_var_)));
636 if (node.MinObjective() > current_objective_lb_)
break;
637 DCHECK_EQ(node.MinObjective(), current_objective_lb_) << level;
641 DCHECK(!node.is_deleted);
642 const Literal node_literal = node.Decision();
650 new_lb = node.true_objective;
652 n = node.false_child;
653 new_lb = node.false_objective;
655 MarkAsDeletedNodeAndUnreachableSubtree(node);
659 current_branch_.pop_back();
660 if (!current_branch_.empty()) {
661 const NodeIndex parent = current_branch_.back();
662 DCHECK(!nodes_[parent].is_deleted);
663 const Literal parent_literal = nodes_[parent].Decision();
665 nodes_[parent].true_child = n;
666 nodes_[parent].UpdateTrueObjective(new_lb);
669 nodes_[parent].false_child = n;
670 nodes_[parent].UpdateFalseObjective(new_lb);
672 if (new_lb > current_objective_lb_) {
674 if (n < nodes_.size() && !nodes_[n].IsLeaf()) {
675 current_branch_.push_back(n);
676 nodes_[n].UpdateObjective(new_lb);
681 if (n >= nodes_.size()) {
684 num_nodes_in_tree_ = 0;
686 }
else if (nodes_[n].IsLeaf()) {
688 num_nodes_in_tree_ = 1;
689 Node root = std::move(nodes_[n]);
691 nodes_.push_back(std::move(root));
701 ExploitReducedCosts(current_branch_[level]);
702 if (node.MinObjective() > current_objective_lb_)
break;
708 num_decisions_taken_++;
709 const bool choose_true = node.true_objective <= node.false_objective;
713 next_decision = node_literal;
715 n = node.false_child;
716 next_decision = node_literal.
Negated();
725 if (SaveLpBasisOption() &&
726 (n >= nodes_.size() || !NodeHasBasis(node))) {
728 absl::MakeCleanup(UpdateLpIters(&num_lp_iters_save_basis_));
731 EnableLpAndLoadBestBasis();
746 SaveLpBasisInto(node);
749 if (n < nodes_.size()) {
756 absl::MakeCleanup(UpdateLpIters(&num_lp_iters_first_branch_));
762 MarkBranchAsInfeasible(node, choose_true);
768 const IntegerValue lb = integer_trail_->
LowerBound(objective_var_);
770 node.UpdateTrueObjective(lb);
772 node.UpdateFalseObjective(lb);
775 if (n < nodes_.size()) {
776 nodes_[n].UpdateObjective(lb);
777 }
else if (SaveLpBasisOption()) {
778 SaveLpBasisInto(nodes_[CreateNewEmptyNodeIfNeeded()]);
781 if (lb > current_objective_lb_)
break;
786 "TreeS", absl::StrCat(
" (", SmallProgressString(),
")"));
789 if (n < nodes_.size() && !nodes_[n].IsLeaf()) {
790 current_branch_.push_back(n);
803 if (!current_branch_.empty()) {
804 const Node& final_node = nodes_[current_branch_.back()];
806 if (final_node.true_objective > current_objective_lb_) {
811 if (final_node.false_objective > current_objective_lb_) {
830 if (integer_trail_->
LowerBound(objective_var_) > current_objective_lb_) {
834 const auto cleanup = absl::MakeCleanup(UpdateLpIters(&num_lp_iters_dive_));
836 if (current_branch_.empty()) {
837 VLOG(2) <<
"DIVE from empty tree";
839 VLOG(2) <<
"DIVE from " << NodeDebugString(current_branch_.back());
846 EnableLpAndLoadBestBasis();
854 Node& node = nodes_[
index];
859 SaveLpBasisInto(nodes_[
index]);
861 const IntegerValue obj_lb = integer_trail_->
LowerBound(objective_var_);
862 if (obj_lb > current_objective_lb_) {
863 nodes_[
index].UpdateObjective(obj_lb);
864 if (!current_branch_.empty()) {
865 Node& parent_node = nodes_[current_branch_.back()];
866 const Literal node_literal = parent_node.Decision();
869 parent_node.UpdateTrueObjective(obj_lb);
871 parent_node.UpdateFalseObjective(obj_lb);
882 CHECK_EQ(base_level, current_branch_.size());
884 CHECK(!nodes_[
index].is_deleted);
887 CHECK_EQ(nodes_[
index].true_objective, current_objective_lb_);
890 CHECK_EQ(nodes_[
index].false_objective, current_objective_lb_);
908 LiteralIndex decision;
909 if (!search_helper_->
GetDecision(search_heuristic_, &decision)) {
916 feasible_solution_observer();
920 num_decisions_taken_++;
931 if (integer_trail_->
LowerBound(objective_var_) > current_objective_lb_) {
942 const std::vector<Literal> reason =
944 objective_var_, integer_trail_->
LowerBound(objective_var_)));
945 std::vector<Literal> decisions = ExtractDecisions(base_level, reason);
953 DCHECK_EQ(current_branch_.size(), base_level);
954 for (
const Literal d : decisions) {
955 AppendNewNodeToCurrentBranch(d);
959 if (SaveLpBasisOption() && decisions.size() == 1) {
960 SaveLpBasisInto(nodes_[CreateNewEmptyNodeIfNeeded()]);
965 if (!current_branch_.empty()) {
966 Node& n = nodes_[current_branch_.back()];
968 n.UpdateTrueObjective(integer_trail_->
LowerBound(objective_var_));
970 n.UpdateFalseObjective(integer_trail_->
LowerBound(objective_var_));
979 int backtrack_level = base_level;
981 while (backtrack_level < current_branch_.size() &&
982 sat_solver_->
Decisions()[backtrack_level].literal.Index() ==
983 nodes_[current_branch_[backtrack_level]].literal_index) {
994 for (
int i = backtrack_level;
i < current_branch_.size(); ++
i) {
995 ExploitReducedCosts(current_branch_[
i]);
1002std::vector<Literal> LbTreeSearch::ExtractDecisions(
1003 int base_level, absl::Span<const Literal> conflict) {
1005 std::vector<bool> is_marked;
1006 for (
const Literal l : conflict) {
1008 if (info.
level <= base_level)
continue;
1009 num_per_level[info.
level]++;
1016 std::vector<Literal> result;
1017 if (is_marked.empty())
return result;
1018 for (
int i = is_marked.size() - 1;
i >= 0; --
i) {
1019 if (!is_marked[
i])
continue;
1021 const Literal l = (*trail_)[
i];
1022 const AssignmentInfo& info = trail_->
Info(l.Variable());
1023 if (info.level <= base_level)
break;
1024 if (num_per_level[info.level] == 1) {
1025 result.push_back(l);
1030 num_per_level[info.level]--;
1031 for (
const Literal new_l : trail_->
Reason(l.Variable())) {
1032 const AssignmentInfo& new_info = trail_->
Info(new_l.Variable());
1033 if (new_info.level <= base_level)
continue;
1034 if (is_marked[new_info.trail_index])
continue;
1035 is_marked[new_info.trail_index] =
true;
1036 num_per_level[new_info.level]++;
1041 std::reverse(result.begin(), result.end());
1045LbTreeSearch::NodeIndex LbTreeSearch::CreateNewEmptyNodeIfNeeded() {
1047 if (current_branch_.empty()) {
1048 if (nodes_.empty()) {
1049 ++num_nodes_in_tree_;
1050 nodes_.emplace_back(current_objective_lb_);
1052 DCHECK_EQ(nodes_.size(), 1);
1055 const NodeIndex parent = current_branch_.back();
1056 DCHECK(!nodes_[parent].is_deleted);
1057 const Literal parent_literal = nodes_[parent].Decision();
1059 if (nodes_[parent].true_child >= nodes_.size()) {
1061 ++num_nodes_in_tree_;
1062 nodes_[parent].true_child =
NodeIndex(nodes_.size());
1063 nodes_.emplace_back(current_objective_lb_);
1065 n = nodes_[parent].true_child;
1067 nodes_[parent].UpdateTrueObjective(current_objective_lb_);
1070 if (nodes_[parent].false_child >= nodes_.size()) {
1072 ++num_nodes_in_tree_;
1073 nodes_[parent].false_child =
NodeIndex(nodes_.size());
1074 nodes_.emplace_back(current_objective_lb_);
1076 n = nodes_[parent].false_child;
1078 nodes_[parent].UpdateFalseObjective(current_objective_lb_);
1081 DCHECK(!nodes_[n].is_deleted);
1086void LbTreeSearch::AppendNewNodeToCurrentBranch(Literal decision) {
1088 if (current_branch_.empty()) {
1089 if (nodes_.empty()) {
1090 ++num_nodes_in_tree_;
1091 nodes_.emplace_back(current_objective_lb_);
1093 DCHECK_EQ(nodes_.size(), 1);
1097 const NodeIndex parent = current_branch_.back();
1098 DCHECK(!nodes_[parent].is_deleted);
1099 const Literal parent_literal = nodes_[parent].Decision();
1101 if (nodes_[parent].true_child < nodes_.size()) {
1102 n = nodes_[parent].true_child;
1104 ++num_nodes_in_tree_;
1105 nodes_.emplace_back(current_objective_lb_);
1106 nodes_[parent].true_child = n;
1108 nodes_[parent].UpdateTrueObjective(current_objective_lb_);
1111 if (nodes_[parent].false_child < nodes_.size()) {
1112 n = nodes_[parent].false_child;
1114 ++num_nodes_in_tree_;
1115 nodes_.emplace_back(current_objective_lb_);
1116 nodes_[parent].false_child = n;
1118 nodes_[parent].UpdateFalseObjective(current_objective_lb_);
1121 DCHECK_LT(n, nodes_.size());
1122 DCHECK_EQ(nodes_[n].literal_index,
kNoLiteralIndex) <<
" issue " << n;
1123 nodes_[n].SetDecision(decision);
1124 nodes_[n].UpdateObjective(current_objective_lb_);
1125 current_branch_.push_back(n);
1140void LbTreeSearch::ExploitReducedCosts(
NodeIndex n) {
1141 if (lp_constraint_ ==
nullptr)
return;
1147 if (cts.empty())
return;
1148 const std::unique_ptr<IntegerSumLE128>& rc = cts.back();
1153 Node& node = nodes_[n];
1154 DCHECK(!node.is_deleted);
1155 const Literal node_literal = node.Decision();
1157 for (
const IntegerLiteral integer_literal :
1160 if (++num_tests > 10)
break;
1162 const std::pair<IntegerValue, IntegerValue> bounds =
1163 rc->ConditionalLb(integer_literal, objective_var_);
1164 if (bounds.first > node.false_objective) {
1166 node.UpdateFalseObjective(bounds.first);
1168 if (bounds.second > node.true_objective) {
1170 node.UpdateTrueObjective(bounds.second);
void SetStopPropagationCallback(std::function< bool()> callback)
const InlinedIntegerLiteralVector & GetIntegerLiterals(Literal lit) const
Returns the IntegerLiterals that were associated with the given Literal.
An helper class to share the code used by the different kind of search.
bool TakeDecision(Literal decision)
bool GetDecision(const std::function< BooleanOrIntegerLiteral()> &f, LiteralIndex *decision)
bool BeforeTakingDecision()
IntegerValue LowerBound(IntegerVariable i) const
Returns the current lower/upper bound of the given integer variable.
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Returns globally valid lower/upper bound on the given integer variable.
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit)
std::vector< Literal > ReasonFor(IntegerLiteral literal) const
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.
const std::vector< std::unique_ptr< IntegerSumLE128 > > & OptimalConstraints() const
void EnablePropagation(bool enable)
int64_t total_num_simplex_iterations() const
Stats.
void LoadBasisState(const glop::BasisState &state)
const glop::BasisState & GetBasisState() const
bool PropagationIsEnabled() const
void UpdateBoolPseudoCosts(absl::Span< const Literal > reason, IntegerValue objective_increase)
void BumpVariableActivities(absl::Span< const Literal > literals)
void UpdateVariableActivityIncrement()
Status UnsatStatus() const
void NotifyThatModelIsUnsat()
bool RestoreSolverToAssumptionLevel()
bool ModelIsUnsat() const
void Backtrack(int target_level)
int CurrentDecisionLevel() const
ABSL_MUST_USE_RESULT bool ResetToLevelZero()
const std::vector< Decision > & Decisions() const
ABSL_MUST_USE_RESULT bool FinishPropagation()
const Trail & LiteralTrail() const
IntegerValue GetInnerObjectiveLowerBound()
void LogMessageWithThrottling(const std::string &prefix, const std::string &message)
bool ProblemIsSolved() const
IntegerValue GetInnerObjectiveUpperBound()
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
Updates the inner objective bounds.
absl::Span< const Literal > Reason(BooleanVariable var, int64_t conflict_id=-1) const
const AssignmentInfo & Info(BooleanVariable var) const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void push_back(const value_type &val)
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.