29#include "absl/container/flat_hash_set.h"
30#include "absl/log/check.h"
31#include "absl/strings/str_cat.h"
32#include "absl/synchronization/mutex.h"
33#include "absl/types/span.h"
43#include "ortools/sat/sat_parameters.pb.h"
56const int kSyncsPerWorkerPerRestart = 2;
57const int kNumInitialRestarts = 10;
66int MaxAllowedDiscrepancyPlusDepth(
int num_leaves) {
70 while (
b < num_leaves) {
71 std::tie(
a,
b) = std::make_pair(
b,
a +
b);
82 return mapping->
Literal(proto_var_);
98std::optional<ProtoLiteral> ProtoLiteral::EncodeInteger(
99 IntegerLiteral
literal, CpModelMapping* mapping) {
102 mapping->GetProtoVariableFromIntegerVariable(positive_var);
109 DCHECK_EQ(result.DecodeInteger(mapping),
literal);
110 DCHECK_EQ(result.Negated().DecodeInteger(mapping),
literal.Negated());
126 DCHECK_EQ(result.Decode(mapping, encoder),
literal);
127 DCHECK_EQ(result.Negated().Decode(mapping, encoder),
literal.Negated());
131 auto result = EncodeInteger(int_lit, mapping);
132 if (result.has_value()) {
133 DCHECK_EQ(result->DecodeInteger(mapping), int_lit);
134 DCHECK_EQ(result->Negated().DecodeInteger(mapping), int_lit.Negated());
142 IntegerValue objective_lb,
int node_id) {
143 CHECK_GT(node_id, 0);
144 decision_indexes_.push_back(literals_.size());
145 literals_.push_back(decision);
146 node_ids_.push_back(node_id);
147 implications_.push_back({});
148 if (!level_to_objective_lbs_.empty()) {
149 objective_lb = std::max(level_to_objective_lbs_.back(), objective_lb);
151 level_to_objective_lbs_.push_back(objective_lb);
156 DCHECK_LE(level, decision_indexes_.size());
157 DCHECK_LE(level, implications_.size());
160 implication_level_[decision] = level - 1;
164 MutableImplications(level - 1).push_back(decision);
167 implication_level_[implication] = level - 1;
169 MutableImplications(level - 1).push_back(implication);
174 implications_.erase(implications_.begin() + level - 1);
175 decision_indexes_.erase(decision_indexes_.begin() + level - 1);
176 level_to_objective_lbs_.erase(level_to_objective_lbs_.begin() + level - 1);
180 decision_indexes_.clear();
182 level_to_objective_lbs_.clear();
184 target_phase_.clear();
185 implication_level_.clear();
186 implications_.clear();
190 if (level == 0)
return;
191 level_to_objective_lbs_[level - 1] =
192 std::max(objective_lb, level_to_objective_lbs_[level - 1]);
196 DCHECK_LE(level, decision_indexes_.size());
197 int start = level == 0 ? 0 : decision_indexes_[level - 1];
198 int end = level == decision_indexes_.size() ? node_ids_.size()
199 : decision_indexes_[level];
200 return absl::MakeSpan(node_ids_.data() +
start,
end -
start);
204 if (level > implications_.size() || level <= 0) {
205 return absl::MakeSpan(literals_.data(), 0);
207 return absl::MakeSpan(implications_[level - 1]);
211 : params_(*
model->GetOrCreate<SatParameters>()),
212 num_workers_(
std::
max(1, params_.shared_tree_num_workers())),
215 num_workers_ * params_.shared_tree_open_leaves_per_worker() - 1),
216 max_nodes_(params_.shared_tree_max_nodes_per_worker() >=
217 std::numeric_limits<int>::
max() / num_workers_
218 ?
std::numeric_limits<int>::
max()
220 params_.shared_tree_max_nodes_per_worker()) {
225 .trail_info = std::make_unique<NodeTrailInfo>()});
226 unassigned_leaves_.reserve(num_workers_);
227 unassigned_leaves_.push_back(&nodes_.back());
231 absl::MutexLock mutex_lock(&mu_);
232 return nodes_.size();
236 absl::MutexLock mutex_lock(&mu_);
237 std::vector<std::pair<Node*, int>>
nodes = GetAssignedNodes(path);
238 if (!IsValid(path)) {
243 DCHECK(to_close_.empty());
244 DCHECK(to_update_.empty());
246 for (
const auto& [node, level] :
nodes) {
247 if (level == prev_level) {
248 to_close_.push_back(GetSibling(node));
249 }
else if (level > 0 && node->objective_lb < path.
ObjectiveLb(level)) {
251 to_update_.push_back(node->parent);
253 if (level > 0 && !node->closed) {
254 NodeTrailInfo* trail_info = GetTrailInfo(node);
256 trail_info->implications.insert(implication);
261 ProcessNodeChanges();
262 if (
nodes.back().first->closed) {
267 if (++num_syncs_since_restart_ / num_workers_ > kSyncsPerWorkerPerRestart &&
268 num_restarts_ < kNumInitialRestarts) {
274 AssignLeaf(path,
nodes.back().first);
279 absl::MutexLock mutex_lock(&mu_);
280 if (!IsValid(path))
return;
281 std::vector<std::pair<Node*, int>>
nodes = GetAssignedNodes(path);
282 if (
nodes.back().first->closed) {
283 VLOG(2) <<
"Cannot split closed node";
286 if (
nodes.back().first->children[0] !=
nullptr) {
287 LOG_IF(WARNING,
nodes.size() > 1)
288 <<
"Cannot resplit previously split node @ " <<
nodes.back().second
289 <<
"/" <<
nodes.size();
292 if (nodes_.size() + 2 > max_nodes_) {
293 VLOG(2) <<
"Too many nodes to accept split";
296 if (num_splits_wanted_ <= 0) {
297 VLOG(2) <<
"Enough splits for now";
300 const int num_desired_leaves =
301 params_.shared_tree_open_leaves_per_worker() * num_workers_;
302 if (params_.shared_tree_split_strategy() ==
303 SatParameters::SPLIT_STRATEGY_DISCREPANCY ||
304 params_.shared_tree_split_strategy() ==
305 SatParameters::SPLIT_STRATEGY_AUTO) {
307 for (
const auto& [node, level] :
nodes) {
308 if (node->parent ==
nullptr || node->implied)
continue;
309 IntegerValue sibling_bound = GetSibling(node)->objective_lb;
310 discrepancy += (node->objective_lb == sibling_bound
311 ? node != node->parent->children[0]
312 : node->objective_lb > sibling_bound);
317 MaxAllowedDiscrepancyPlusDepth(num_desired_leaves)) {
318 VLOG(2) <<
"Too high discrepancy to accept split";
321 }
else if (params_.shared_tree_split_strategy() ==
322 SatParameters::SPLIT_STRATEGY_OBJECTIVE_LB) {
323 if (
nodes.back().first->objective_lb >
nodes.front().first->objective_lb) {
324 VLOG(2) <<
"Can only split nodes with minimum objective lb, "
325 <<
nodes.back().first->objective_lb <<
" > "
326 <<
nodes.front().first->objective_lb;
329 }
else if (params_.shared_tree_split_strategy() ==
330 SatParameters::SPLIT_STRATEGY_BALANCED_TREE) {
331 if (path.
MaxLevel() + 1 > log2(num_desired_leaves)) {
332 VLOG(2) <<
"Tree too unbalanced to accept split";
336 VLOG_EVERY_N(2, 10) << unassigned_leaves_.size() <<
" unassigned leaves, "
337 << nodes_.size() <<
" subtrees, " << num_splits_wanted_
339 Split(
nodes, decision);
340 auto [new_leaf, level] =
nodes.back();
341 path.
PushLevel(new_leaf->literal, new_leaf->objective_lb, new_leaf->id);
345 absl::MutexLock mutex_lock(&mu_);
346 std::vector<std::pair<Node*, int>>
nodes = GetAssignedNodes(path);
347 if (
nodes.back().first->children[0] ==
nullptr &&
348 !
nodes.back().first->closed &&
nodes.size() > 1) {
349 Node* leaf =
nodes.back().first;
350 VLOG(2) <<
"Returning leaf to be replaced";
351 GetTrailInfo(leaf)->phase.assign(path.
TargetPhase().begin(),
353 unassigned_leaves_.push_back(leaf);
356 while (!unassigned_leaves_.empty()) {
357 const int i = num_leaves_assigned_++ % unassigned_leaves_.size();
358 std::swap(unassigned_leaves_[
i], unassigned_leaves_.back());
359 Node* leaf = unassigned_leaves_.back();
360 unassigned_leaves_.pop_back();
361 if (!leaf->closed && leaf->children[0] ==
nullptr) {
362 AssignLeaf(path, leaf);
366 VLOG(2) <<
"Assigning root because no unassigned leaves are available";
371SharedTreeManager::NodeTrailInfo* SharedTreeManager::GetTrailInfo(Node* node) {
372 CHECK(node !=
nullptr && !node->closed);
373 while (node->trail_info ==
nullptr) {
376 CHECK_NE(node,
nullptr);
377 return node->trail_info.get();
380SharedTreeManager::Node* SharedTreeManager::GetSibling(Node* node) {
381 if (node ==
nullptr || node->parent ==
nullptr)
return nullptr;
382 if (node->parent->children[0] != node) {
383 return node->parent->children[0];
385 return node->parent->children[1];
388void SharedTreeManager::Split(std::vector<std::pair<Node*, int>>&
nodes,
390 const auto [parent, level] =
nodes.back();
391 DCHECK(parent->children[0] ==
nullptr);
392 DCHECK(parent->children[1] ==
nullptr);
393 parent->children[0] = MakeSubtree(parent,
lit);
394 parent->children[1] = MakeSubtree(parent,
lit.Negated());
395 NodeTrailInfo* trail_info = GetTrailInfo(parent);
396 if (trail_info !=
nullptr) {
397 parent->children[0]->trail_info = std::make_unique<NodeTrailInfo>(
398 NodeTrailInfo{.phase = trail_info->phase});
399 parent->children[1]->trail_info = std::make_unique<NodeTrailInfo>(
400 NodeTrailInfo{.phase = std::move(trail_info->phase)});
402 nodes.push_back(std::make_pair(parent->children[0], level + 1));
403 unassigned_leaves_.push_back(parent->children[1]);
404 --num_splits_wanted_;
407SharedTreeManager::Node* SharedTreeManager::MakeSubtree(Node* parent,
411 .objective_lb = parent->objective_lb,
413 .id =
static_cast<int>(nodes_.size() + node_id_offset_)});
414 return &nodes_.back();
417void SharedTreeManager::ProcessNodeChanges() {
418 int num_newly_closed = 0;
419 while (!to_close_.empty()) {
420 Node* node = to_close_.back();
421 CHECK_NE(node,
nullptr);
422 to_close_.pop_back();
424 while (node !=
nullptr && !node->closed) {
429 if (node->parent !=
nullptr) node->trail_info.reset();
432 num_splits_wanted_ += (node->children[0] ==
nullptr);
433 for (Node* child : node->children) {
434 if (child ==
nullptr || child->closed)
continue;
435 to_close_.push_back(child);
437 Node* sibling = GetSibling(node);
438 if (sibling !=
nullptr) {
439 sibling->implied =
true;
440 if (!sibling->closed) {
446 DCHECK(node ==
nullptr || node->closed);
447 if (node ==
nullptr) {
450 }
else if (node->parent !=
nullptr) {
451 to_update_.push_back(node->parent);
454 if (num_newly_closed > 0) {
456 "Tree", absl::StrCat(
"nodes:", nodes_.size(),
"/", max_nodes_,
457 " closed:", num_closed_nodes_,
458 " unassigned:", unassigned_leaves_.size(),
459 " restarts:", num_restarts_));
463 bool root_updated =
false;
464 while (!to_update_.empty()) {
465 Node* node = to_update_.back();
466 to_update_.pop_back();
468 while (node !=
nullptr && !node->closed) {
469 DCHECK(node->children[0] !=
nullptr);
470 DCHECK(node->children[1] !=
nullptr);
471 NodeTrailInfo* trail_info = GetTrailInfo(node);
472 for (Node* child : node->children) {
473 if (child->implied && child->trail_info !=
nullptr) {
474 trail_info->implications.merge(child->trail_info->implications);
475 child->trail_info.reset();
478 IntegerValue child_bound = std::min(node->children[0]->objective_lb,
479 node->children[1]->objective_lb);
480 if (child_bound <= node->objective_lb)
break;
481 node->objective_lb = child_bound;
484 if (node ==
nullptr) root_updated =
true;
491 nodes_[0].trail_info->implications.clear();
494std::vector<std::pair<SharedTreeManager::Node*, int>>
495SharedTreeManager::GetAssignedNodes(
const ProtoTrail& path) {
496 std::vector<std::pair<Node*, int>>
nodes({std::make_pair(&nodes_[0], 0)});
497 if (!IsValid(path)) {
502 for (
int i = 0;
i <= path.MaxLevel(); ++
i) {
503 for (
int id : path.NodeIds(
i)) {
504 const int index =
id - node_id_offset_;
505 CHECK_GE(
index, 0) <<
" in path.NodeIds(" <<
i
506 <<
"), max_level=" << path.MaxLevel();
507 CHECK_LT(
index, nodes_.size());
508 DCHECK_EQ(
nodes.back().first, nodes_[
index].parent);
509 nodes.push_back(std::make_pair(&nodes_[
index],
i));
516 absl::MutexLock mutex_lock(&mu_);
517 const int node_id_to_close = path.
NodeIds(level).front();
519 if (node_id_to_close < node_id_offset_)
return;
520 Node* node = &nodes_[node_id_to_close - node_id_offset_];
521 VLOG(2) <<
"Closing subtree at level " << level;
522 DCHECK(to_close_.empty());
523 to_close_.push_back(node);
524 ProcessNodeChanges();
527void SharedTreeManager::AssignLeaf(
ProtoTrail& path, Node* leaf) {
529 std::vector<Node*> reversed_path;
530 while (leaf != &nodes_[0]) {
531 reversed_path.push_back(&nodes_[leaf->id - node_id_offset_]);
534 while (!reversed_path.empty()) {
535 Node* leaf = reversed_path.back();
536 reversed_path.pop_back();
537 path.
PushLevel(leaf->literal, leaf->objective_lb, leaf->id);
541 if (params_.shared_tree_worker_enable_trail_sharing()) {
542 for (
const ProtoLiteral& implication : GetTrailInfo(leaf)->implications) {
549bool SharedTreeManager::IsValid(
const ProtoTrail& path)
const {
550 auto node_ids = path.NodeIds(path.MaxLevel());
551 if (node_ids.empty())
return true;
552 if (node_ids.back() < node_id_offset_)
return false;
556void SharedTreeManager::RestartLockHeld() {
557 node_id_offset_ += nodes_.size();
559 nodes_[0].id = node_id_offset_;
560 nodes_[0].children = {
nullptr,
nullptr};
561 unassigned_leaves_.clear();
563 num_workers_ * params_.shared_tree_open_leaves_per_worker() - 1;
564 num_closed_nodes_ = 0;
566 num_syncs_since_restart_ = 0;
569std::string SharedTreeManager::ShortStatus()
const {
570 return absl::StrCat(
"shared_tree_manager(r=", num_restarts_,
571 " n=", nodes_.size(),
")");
575 : parameters_(
model->GetOrCreate<SatParameters>()),
592 assigned_tree_lbds_(8) {}
594const std::vector<Literal>& SharedTreeWorker::DecisionReason(
int level) {
595 CHECK_LE(level, assigned_tree_literals_.size());
597 for (
int i = 0;
i < level; ++
i) {
598 reason_.push_back(assigned_tree_literals_[
i].Negated());
603bool SharedTreeWorker::AddDecisionImplication(Literal
lit,
int level) {
607 VLOG(2) <<
"Closing subtree via impl at " << level + 1
608 <<
" assigned=" << assigned_tree_.
MaxLevel();
610 manager_->
CloseTree(assigned_tree_, level);
611 assigned_tree_literals_.clear();
615 VLOG(2) <<
"Learned shared clause";
619bool SharedTreeWorker::AddImplications() {
622 if (level == 0)
return false;
623 if (level > assigned_tree_.
MaxLevel()) {
626 rev_num_processed_implications_.resize(level + 1, 0);
627 auto& num_processed_implications = rev_num_processed_implications_[level];
628 reversible_int_repository_->
SaveState(&num_processed_implications);
629 absl::Span<const Literal> implied_literals =
630 absl::MakeConstSpan(assigned_tree_implications_[level - 1])
631 .subspan(num_processed_implications);
632 bool added_clause =
false;
633 for (Literal impl : implied_literals) {
634 ++num_processed_implications;
637 if (!AddDecisionImplication(impl, level))
return true;
639 if (objective_ !=
nullptr &&
641 const IntegerValue obj_lb =
644 const Literal obj_lit =
648 AddDecisionImplication(obj_lit, level);
655bool SharedTreeWorker::SyncWithLocalTrail() {
660 if (AddImplications())
continue;
664 if (parameters_->shared_tree_worker_enable_trail_sharing() && level > 0 &&
665 level <= assigned_tree_.
MaxLevel()) {
667 reversible_int_repository_->
SaveState(&reversible_trail_index_);
668 for (
int i = trail_->
Index() - 1;
i >= reversible_trail_index_; --
i) {
669 const Literal
lit = (*trail_)[
i];
674 std::optional<ProtoLiteral> encoded = EncodeDecision(
lit);
675 if (!encoded.has_value())
continue;
678 reversible_trail_index_ = trail_->
Index();
680 if (level >= assigned_tree_.
MaxLevel())
break;
682 const Literal next_decision = assigned_tree_literals_[level];
686 VLOG(2) <<
"Closing subtree at " << level + 1
687 <<
" assigned=" << assigned_tree_.
MaxLevel();
688 manager_->
CloseTree(assigned_tree_, level + 1);
689 assigned_tree_literals_.clear();
690 assigned_tree_implications_.clear();
696 assigned_tree_implications_[level - 1].insert(
697 assigned_tree_implications_[level - 1].
end(),
698 assigned_tree_implications_[level].begin(),
699 assigned_tree_implications_[level].
end());
701 assigned_tree_implications_.erase(assigned_tree_implications_.begin() +
703 assigned_tree_literals_.erase(assigned_tree_literals_.begin() + level);
709bool SharedTreeWorker::NextDecision(LiteralIndex* decision_index) {
710 const auto& decision_policy =
713 new_split_available_ = next_level == assigned_tree_.
MaxLevel() + 1;
715 CHECK_EQ(assigned_tree_literals_.size(), assigned_tree_.
MaxLevel());
716 if (next_level <= assigned_tree_.
MaxLevel()) {
717 VLOG(2) <<
"Following shared trail depth=" << next_level <<
" "
718 << parameters_->name();
719 const Literal decision = assigned_tree_literals_[next_level - 1];
721 <<
" at depth " << next_level <<
" " << parameters_->name();
723 *decision_index = decision.Index();
726 if (objective_ ==
nullptr ||
728 return helper_->
GetDecision(decision_policy, decision_index);
732 const IntegerValue root_obj_lb =
734 const IntegerValue root_obj_ub =
736 const IntegerValue obj_split =
737 root_obj_lb + absl::LogUniform<int64_t>(
738 *random_, 0, (root_obj_ub - root_obj_lb).
value());
739 const double objective_split_probability =
740 parameters_->shared_tree_worker_objective_split_probability();
742 [&]() -> BooleanOrIntegerLiteral {
743 IntegerValue obj_lb =
745 IntegerValue obj_ub =
747 if (obj_lb > obj_split || obj_ub <= obj_split ||
748 next_level > assigned_tree_.
MaxLevel() + 1 ||
749 absl::Bernoulli(*random_, 1 - objective_split_probability)) {
750 return decision_policy();
752 return BooleanOrIntegerLiteral(
758void SharedTreeWorker::MaybeProposeSplit() {
759 if (!new_split_available_ ||
763 new_split_available_ =
false;
764 const Literal split_decision =
766 const std::optional<ProtoLiteral> encoded = EncodeDecision(split_decision);
767 if (encoded.has_value()) {
768 CHECK_EQ(assigned_tree_literals_.size(), assigned_tree_.
MaxLevel());
770 if (assigned_tree_.
MaxLevel() > assigned_tree_literals_.size()) {
771 assigned_tree_literals_.push_back(split_decision);
772 assigned_tree_implications_.push_back({});
774 CHECK_EQ(assigned_tree_literals_.size(), assigned_tree_.
MaxLevel());
778bool SharedTreeWorker::ShouldReplaceSubtree() {
780 if (assigned_tree_.
MaxLevel() == 0)
return true;
782 parameters_->shared_tree_worker_min_restarts_per_subtree()) {
789bool SharedTreeWorker::SyncWithSharedTree() {
791 if (ShouldReplaceSubtree()) {
793 VLOG(2) << parameters_->name() <<
" acquiring tree #" << num_trees_
794 <<
" after " << num_restarts_ - tree_assignment_restart_
795 <<
" restarts prev depth: " << assigned_tree_.
MaxLevel()
798 if (parameters_->shared_tree_worker_enable_trail_sharing()) {
799 std::vector<ProtoLiteral> phase_out;
802 if (!encoded.has_value())
continue;
803 phase_out.push_back(*encoded);
808 tree_assignment_restart_ = num_restarts_;
810 restart_policy_->
Reset();
811 if (parameters_->shared_tree_worker_enable_trail_sharing()) {
818 VLOG(2) <<
"Assigned level: " << assigned_tree_.
MaxLevel() <<
" "
819 << parameters_->name();
820 assigned_tree_literals_.clear();
821 assigned_tree_implications_.clear();
822 for (
int i = 1;
i <= assigned_tree_.
MaxLevel(); ++
i) {
823 assigned_tree_literals_.push_back(
824 DecodeDecision(assigned_tree_.
Decision(
i)));
825 std::vector<Literal> implications;
826 for (
const ProtoLiteral& impl : assigned_tree_.
Implications(
i)) {
827 implications.push_back(DecodeDecision(impl));
829 assigned_tree_implications_.push_back(std::move(implications));
835 const std::function<
void()>& feasible_solution_observer) {
843 level_zero_callbacks_->
callbacks.push_back(
844 [
this]() {
return SyncWithSharedTree(); });
845 const bool has_objective =
857 if (!SyncWithLocalTrail())
return sat_solver_->
UnsatStatus();
858 LiteralIndex decision_index;
859 if (!NextDecision(&decision_index))
continue;
862 feasible_solution_observer();
864 const IntegerValue objective =
876 const Literal decision(decision_index);
889 return lit.Decode(mapping_, encoder_);
892std::optional<ProtoLiteral> SharedTreeWorker::EncodeDecision(Literal decision) {
void SaveState(T *object)
double WindowAverage() const
void Add(int value)
Adds the next integer of the stream.
sat::Literal Literal(int ref) const
bool IsBoolean(int ref) const
bool IsInteger(int ref) const
int GetProtoVariableFromBooleanVariable(BooleanVariable var) const
int NumProtoVariables() const
Returns the number of variables in the loaded proto.
IntegerVariable Integer(int ref) const
Literal GetFalseLiteral()
Literal GetTrueLiteral()
Gets the literal always set to true, make it if it does not exist.
const InlinedIntegerLiteralVector & GetIntegerLiterals(Literal lit) const
Returns the IntegerLiterals that were associated with the given Literal.
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
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
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
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)
static std::optional< ProtoLiteral > Encode(Literal, CpModelMapping *, IntegerEncoder *)
Literal Decode(CpModelMapping *, IntegerEncoder *) const
Note you should only decode integer literals at the root level.
int MaxLevel() const
Returns the maximum decision level stored in the trail.
void SetPhase(absl::Span< const ProtoLiteral > phase)
void Clear()
Clear the trail, removing all levels.
ProtoLiteral Decision(int level) const
Returns the decision assigned at level.
void SetObjectiveLb(int level, IntegerValue objective_lb)
Set a lower bound on the objective at level.
absl::Span< const ProtoLiteral > Implications(int level) const
absl::Span< const int > NodeIds(int level) const
Returns the node ids for decisions and implications at level.
void AddImplication(int level, ProtoLiteral implication)
IntegerValue ObjectiveLb(int level) const
void PushLevel(const ProtoLiteral &decision, IntegerValue objective_lb, int node_id)
Adds a new assigned level to the trail.
const std::vector< ProtoLiteral > & TargetPhase() const
void SetLevelImplied(int level)
Asserts that the decision at level is implied by earlier decisions.
Contain the logic to decide when to restart a SAT tree search.
int NumRestarts() const
Returns the number of restarts since the last Reset().
void Reset()
Resets the policy using the current model parameters.
double LbdAverageSinceReset() const
void SetTargetPolarity(Literal l)
Like SetAssignmentPreference() but it can be overridden by phase-saving.
void ClearBestPartialAssignment()
absl::Span< const Literal > GetBestPartialAssignment() const
Status UnsatStatus() const
void Backtrack(int target_level)
const VariablesAssignment & Assignment() const
int CurrentDecisionLevel() const
const std::vector< Decision > & Decisions() const
ABSL_MUST_USE_RESULT bool FinishPropagation()
IntegerValue GetInnerObjectiveLowerBound()
void LogMessageWithThrottling(const std::string &prefix, const std::string &message)
void NotifyThatImprovingProblemIsInfeasible(const std::string &worker_info)
void UpdateInnerObjectiveBounds(const std::string &update_info, IntegerValue lb, IntegerValue ub)
Updates the inner objective bounds.
void CloseTree(ProtoTrail &path, int level)
SharedTreeManager(Model *model)
void ProposeSplit(ProtoTrail &path, ProtoLiteral decision)
void ReplaceTree(ProtoTrail &path)
Assigns a path prefix that the worker should explore.
bool SyncTree(ProtoTrail &path) ABSL_LOCKS_EXCLUDED(mu_)
int NumNodes() const ABSL_LOCKS_EXCLUDED(mu_)
SharedTreeWorker(Model *model)
SatSolver::Status Search(const std::function< void()> &feasible_solution_observer)
int AssignmentType(BooleanVariable var) const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
const LiteralIndex kNoLiteralIndex(-1)
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
int NegatedRef(int ref)
Small utility functions to deal with negative variable/literal references.
std::optional< int64_t > end
static constexpr int kSearchDecision
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< std::function< bool()> > callbacks
IntegerVariable objective_var
std::vector< std::function< bool()> > restart_policies
std::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies
int policy_index
Index in the vectors above that indicate the current configuration.