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"
44#include "ortools/sat/sat_parameters.pb.h"
57const int kSyncsPerWorkerPerRestart = 2;
58const int kNumInitialRestarts = 10;
67int MaxAllowedDiscrepancyPlusDepth(
int num_leaves) {
71 while (
b < num_leaves) {
72 std::tie(a,
b) = std::make_pair(
b, a +
b);
83 return mapping->
Literal(proto_var_);
99std::optional<ProtoLiteral> ProtoLiteral::EncodeInteger(
102 const int model_var =
103 mapping->GetProtoVariableFromIntegerVariable(positive_var);
104 if (model_var == -1) {
108 literal.var == positive_var ? model_var :
NegatedRef(model_var),
110 DCHECK_EQ(result.DecodeInteger(mapping), literal);
111 DCHECK_EQ(result.Negated().DecodeInteger(mapping), literal.Negated());
117 const std::optional<ProtoLiteral> result =
EncodeLiteral(literal, mapping);
118 if (result.has_value())
return result;
121 auto result = EncodeInteger(int_lit, mapping);
122 if (result.has_value()) {
123 DCHECK_EQ(result->DecodeInteger(mapping), int_lit);
124 DCHECK_EQ(result->Negated().DecodeInteger(mapping), int_lit.Negated());
138 if (model_var == -1) {
150 IntegerValue objective_lb,
int node_id) {
151 CHECK_GT(node_id, 0);
152 decision_indexes_.push_back(literals_.size());
153 literals_.push_back(decision);
154 node_ids_.push_back(node_id);
155 implications_.push_back({});
156 if (!level_to_objective_lbs_.empty()) {
157 objective_lb = std::max(level_to_objective_lbs_.back(), objective_lb);
159 level_to_objective_lbs_.push_back(objective_lb);
164 DCHECK_LE(level, decision_indexes_.size());
165 DCHECK_LE(level, implications_.size());
168 implication_level_[decision] = level - 1;
172 MutableImplications(level - 1).push_back(decision);
175 implication_level_[implication] = level - 1;
177 MutableImplications(level - 1).push_back(implication);
182 implications_.erase(implications_.begin() + level - 1);
183 decision_indexes_.erase(decision_indexes_.begin() + level - 1);
184 level_to_objective_lbs_.erase(level_to_objective_lbs_.begin() + level - 1);
188 decision_indexes_.clear();
190 level_to_objective_lbs_.clear();
192 target_phase_.clear();
193 implication_level_.clear();
194 implications_.clear();
198 if (level == 0)
return;
199 level_to_objective_lbs_[level - 1] =
200 std::max(objective_lb, level_to_objective_lbs_[level - 1]);
204 DCHECK_LE(level, decision_indexes_.size());
205 int start = level == 0 ? 0 : decision_indexes_[level - 1];
206 int end = level == decision_indexes_.size() ? node_ids_.size()
207 : decision_indexes_[level];
208 return absl::MakeSpan(node_ids_.data() + start, end - start);
212 if (level > implications_.size() || level <= 0) {
213 return absl::MakeSpan(literals_.data(), 0);
215 return absl::MakeSpan(implications_[level - 1]);
219 : params_(*model->GetOrCreate<SatParameters>()),
220 num_workers_(params_.shared_tree_num_workers()),
223 num_workers_ * params_.shared_tree_open_leaves_per_worker() - 1),
225 params_.shared_tree_max_nodes_per_worker() >=
226 std::numeric_limits<int>::max() /
std::max(num_workers_, 1)
227 ?
std::numeric_limits<int>::max()
228 : num_workers_ * params_.shared_tree_max_nodes_per_worker()) {
229 CHECK_GE(num_workers_, 0);
233 .objective_lb = shared_response_manager_->GetInnerObjectiveLowerBound(),
234 .trail_info = std::make_unique<NodeTrailInfo>()});
235 unassigned_leaves_.reserve(num_workers_);
236 unassigned_leaves_.push_back(&nodes_.back());
240 absl::MutexLock mutex_lock(&mu_);
241 return nodes_.size();
245 absl::MutexLock mutex_lock(&mu_);
246 std::vector<std::pair<Node*, int>> nodes = GetAssignedNodes(path);
247 if (!IsValid(path)) {
252 DCHECK(to_close_.empty());
253 DCHECK(to_update_.empty());
255 for (
const auto& [node, level] : nodes) {
256 if (level == prev_level) {
257 to_close_.push_back(GetSibling(node));
258 }
else if (level > 0 && node->objective_lb < path.
ObjectiveLb(level)) {
260 to_update_.push_back(node->parent);
262 if (level > 0 && !node->closed) {
263 NodeTrailInfo* trail_info = GetTrailInfo(node);
265 auto it = trail_info->implications
266 .emplace(implication.proto_var(), implication.lb())
268 if (it->second < implication.lb()) {
269 it->second = implication.lb();
275 ProcessNodeChanges();
276 if (nodes.back().first->closed) {
281 if (++num_syncs_since_restart_ / num_workers_ > kSyncsPerWorkerPerRestart &&
282 num_restarts_ < kNumInitialRestarts) {
288 AssignLeaf(path, nodes.back().first);
293 absl::MutexLock mutex_lock(&mu_);
294 if (!IsValid(path))
return;
295 std::vector<std::pair<Node*, int>> nodes = GetAssignedNodes(path);
296 if (nodes.back().first->closed) {
297 VLOG(2) <<
"Cannot split closed node";
300 if (nodes.back().first->children[0] !=
nullptr) {
301 LOG_IF(WARNING, nodes.size() > 1)
302 <<
"Cannot resplit previously split node @ " << nodes.back().second
303 <<
"/" << nodes.size();
306 if (nodes_.size() + 2 > max_nodes_) {
307 VLOG(2) <<
"Too many nodes to accept split";
310 if (num_splits_wanted_ <= 0) {
311 VLOG(2) <<
"Enough splits for now";
314 const int num_desired_leaves =
315 params_.shared_tree_open_leaves_per_worker() * num_workers_;
316 if (params_.shared_tree_split_strategy() ==
317 SatParameters::SPLIT_STRATEGY_DISCREPANCY ||
318 params_.shared_tree_split_strategy() ==
319 SatParameters::SPLIT_STRATEGY_AUTO) {
321 for (
const auto& [node, level] : nodes) {
322 if (node->parent ==
nullptr || node->implied)
continue;
323 IntegerValue sibling_bound = GetSibling(node)->objective_lb;
324 discrepancy += (node->objective_lb == sibling_bound
325 ? node != node->parent->children[0]
326 : node->objective_lb > sibling_bound);
331 MaxAllowedDiscrepancyPlusDepth(num_desired_leaves) +
332 params_.shared_tree_balance_tolerance()) {
333 VLOG(2) <<
"Too high discrepancy to accept split";
336 }
else if (params_.shared_tree_split_strategy() ==
337 SatParameters::SPLIT_STRATEGY_OBJECTIVE_LB) {
338 if (nodes.back().first->objective_lb > nodes.front().first->objective_lb) {
339 VLOG(2) <<
"Can only split nodes with minimum objective lb, "
340 << nodes.back().first->objective_lb <<
" > "
341 << nodes.front().first->objective_lb;
344 }
else if (params_.shared_tree_split_strategy() ==
345 SatParameters::SPLIT_STRATEGY_BALANCED_TREE) {
347 log2(num_desired_leaves) + params_.shared_tree_balance_tolerance()) {
348 VLOG(2) <<
"Tree too unbalanced to accept split";
352 VLOG_EVERY_N(2, 10) << unassigned_leaves_.size() <<
" unassigned leaves, "
353 << nodes_.size() <<
" subtrees, " << num_splits_wanted_
355 Split(nodes, decision);
356 auto [new_leaf, level] = nodes.back();
357 path.
PushLevel(new_leaf->literal, new_leaf->objective_lb, new_leaf->id);
361 absl::MutexLock mutex_lock(&mu_);
362 std::vector<std::pair<Node*, int>> nodes = GetAssignedNodes(path);
363 if (nodes.back().first->children[0] ==
nullptr &&
364 !nodes.back().first->closed && nodes.size() > 1) {
365 Node* leaf = nodes.back().first;
366 VLOG(2) <<
"Returning leaf to be replaced";
367 GetTrailInfo(leaf)->phase.assign(path.
TargetPhase().begin(),
369 unassigned_leaves_.push_back(leaf);
372 while (!unassigned_leaves_.empty()) {
373 const int i = num_leaves_assigned_++ % unassigned_leaves_.size();
374 std::swap(unassigned_leaves_[
i], unassigned_leaves_.back());
375 Node* leaf = unassigned_leaves_.back();
376 unassigned_leaves_.pop_back();
377 if (!leaf->closed && leaf->children[0] ==
nullptr) {
378 AssignLeaf(path, leaf);
383 VLOG(2) <<
"Assigning root because no unassigned leaves are available";
388SharedTreeManager::NodeTrailInfo* SharedTreeManager::GetTrailInfo(Node* node) {
389 CHECK(node !=
nullptr && !node->closed);
390 while (node->trail_info ==
nullptr) {
393 CHECK_NE(node,
nullptr);
394 return node->trail_info.get();
397SharedTreeManager::Node* SharedTreeManager::GetSibling(Node* node) {
398 if (node ==
nullptr || node->parent ==
nullptr)
return nullptr;
399 if (node->parent->children[0] != node) {
400 return node->parent->children[0];
402 return node->parent->children[1];
405void SharedTreeManager::Split(std::vector<std::pair<Node*, int>>& nodes,
407 const auto [parent, level] = nodes.back();
408 DCHECK(parent->children[0] ==
nullptr);
409 DCHECK(parent->children[1] ==
nullptr);
410 parent->children[0] = MakeSubtree(parent, lit);
411 parent->children[1] = MakeSubtree(parent, lit.Negated());
412 NodeTrailInfo* trail_info = GetTrailInfo(parent);
413 if (trail_info !=
nullptr) {
414 parent->children[0]->trail_info = std::make_unique<NodeTrailInfo>(
415 NodeTrailInfo{.phase = trail_info->phase});
416 parent->children[1]->trail_info = std::make_unique<NodeTrailInfo>(
417 NodeTrailInfo{.phase = std::move(trail_info->phase)});
419 nodes.push_back(std::make_pair(parent->children[0], level + 1));
420 unassigned_leaves_.push_back(parent->children[1]);
421 --num_splits_wanted_;
424SharedTreeManager::Node* SharedTreeManager::MakeSubtree(Node* parent,
427 Node{.literal = literal,
428 .objective_lb = parent->objective_lb,
430 .id =
static_cast<int>(nodes_.size() + node_id_offset_)});
431 return &nodes_.back();
434void SharedTreeManager::ProcessNodeChanges() {
435 int num_newly_closed = 0;
436 while (!to_close_.empty()) {
437 Node* node = to_close_.back();
438 CHECK_NE(node,
nullptr);
439 to_close_.pop_back();
441 while (node !=
nullptr && !node->closed) {
446 if (node->parent !=
nullptr) node->trail_info.reset();
449 num_splits_wanted_ += (node->children[0] ==
nullptr);
450 for (Node* child : node->children) {
451 if (child ==
nullptr || child->closed)
continue;
452 to_close_.push_back(child);
454 Node* sibling = GetSibling(node);
455 if (sibling !=
nullptr) {
456 sibling->implied =
true;
457 if (!sibling->closed) {
463 DCHECK(node ==
nullptr || node->closed);
464 if (node ==
nullptr) {
465 shared_response_manager_->NotifyThatImprovingProblemIsInfeasible(
467 }
else if (node->parent !=
nullptr) {
468 to_update_.push_back(node->parent);
471 if (num_newly_closed > 0) {
472 shared_response_manager_->LogMessageWithThrottling(
473 "Tree", absl::StrCat(
"nodes:", nodes_.size(),
"/", max_nodes_,
474 " closed:", num_closed_nodes_,
475 " unassigned:", unassigned_leaves_.size(),
476 " restarts:", num_restarts_));
480 bool root_updated =
false;
481 while (!to_update_.empty()) {
482 Node* node = to_update_.back();
483 to_update_.pop_back();
485 while (node !=
nullptr && !node->closed) {
486 DCHECK(node->children[0] !=
nullptr);
487 DCHECK(node->children[1] !=
nullptr);
488 NodeTrailInfo* trail_info = GetTrailInfo(node);
489 for (Node* child : node->children) {
490 if (child->implied && child->trail_info !=
nullptr) {
491 trail_info->implications.merge(child->trail_info->implications);
492 child->trail_info.reset();
495 IntegerValue child_bound = std::min(node->children[0]->objective_lb,
496 node->children[1]->objective_lb);
497 if (child_bound <= node->objective_lb)
break;
498 node->objective_lb = child_bound;
501 if (node ==
nullptr) root_updated =
true;
504 shared_response_manager_->UpdateInnerObjectiveBounds(
508 nodes_[0].trail_info->implications.clear();
511std::vector<std::pair<SharedTreeManager::Node*, int>>
512SharedTreeManager::GetAssignedNodes(
const ProtoTrail& path) {
513 std::vector<std::pair<Node*, int>> nodes({std::make_pair(&nodes_[0], 0)});
514 if (!IsValid(path)) {
519 for (
int i = 0;
i <= path.MaxLevel(); ++
i) {
520 for (
int id : path.NodeIds(
i)) {
521 const int index =
id - node_id_offset_;
522 CHECK_GE(index, 0) <<
" in path.NodeIds(" <<
i
523 <<
"), max_level=" << path.MaxLevel();
524 CHECK_LT(index, nodes_.size());
525 DCHECK_EQ(nodes.back().first, nodes_[index].parent);
526 nodes.push_back(std::make_pair(&nodes_[index],
i));
533 absl::MutexLock mutex_lock(&mu_);
534 const int node_id_to_close = path.
NodeIds(level).front();
536 if (node_id_to_close < node_id_offset_)
return;
537 Node* node = &nodes_[node_id_to_close - node_id_offset_];
538 VLOG(2) <<
"Closing subtree at level " << level;
539 DCHECK(to_close_.empty());
540 to_close_.push_back(node);
541 ProcessNodeChanges();
544void SharedTreeManager::AssignLeaf(
ProtoTrail& path, Node* leaf) {
546 std::vector<Node*> reversed_path;
547 while (leaf != &nodes_[0]) {
548 reversed_path.push_back(&nodes_[leaf->id - node_id_offset_]);
551 while (!reversed_path.empty()) {
552 Node* leaf = reversed_path.back();
553 reversed_path.pop_back();
554 path.
PushLevel(leaf->literal, leaf->objective_lb, leaf->id);
558 if (params_.shared_tree_worker_enable_trail_sharing() &&
559 leaf->trail_info !=
nullptr) {
560 for (
const auto& [var, lb] : leaf->trail_info->implications) {
567bool SharedTreeManager::IsValid(
const ProtoTrail& path)
const {
568 auto node_ids = path.NodeIds(path.MaxLevel());
569 if (node_ids.empty())
return true;
570 if (node_ids.back() < node_id_offset_)
return false;
574void SharedTreeManager::RestartLockHeld() {
575 node_id_offset_ += nodes_.size();
577 nodes_[0].id = node_id_offset_;
578 nodes_[0].children = {
nullptr,
nullptr};
579 unassigned_leaves_.clear();
581 num_workers_ * params_.shared_tree_open_leaves_per_worker() - 1;
582 num_closed_nodes_ = 0;
584 num_syncs_since_restart_ = 0;
587std::string SharedTreeManager::ShortStatus()
const {
588 return absl::StrCat(
"shared_tree_manager(r=", num_restarts_,
589 " n=", nodes_.size(),
")");
593 : parameters_(model->GetOrCreate<SatParameters>()),
595 time_limit_(model->GetOrCreate<
TimeLimit>()),
598 sat_solver_(model->GetOrCreate<
SatSolver>()),
599 trail_(model->GetOrCreate<
Trail>()),
610 assigned_tree_lbds_(8) {}
612const std::vector<Literal>& SharedTreeWorker::DecisionReason(
int level) {
613 CHECK_LE(level, assigned_tree_literals_.size());
615 for (
int i = 0;
i < level; ++
i) {
616 reason_.push_back(assigned_tree_literals_[
i].Negated());
621bool SharedTreeWorker::AddDecisionImplication(Literal lit,
int level) {
625 VLOG(2) <<
"Closing subtree via impl at " << level + 1
626 <<
" assigned=" << assigned_tree_.
MaxLevel();
628 manager_->CloseTree(assigned_tree_, level);
629 assigned_tree_literals_.clear();
632 integer_trail_->EnqueueLiteral(lit, DecisionReason(level), {});
633 VLOG(2) <<
"Learned shared clause";
637bool SharedTreeWorker::AddImplications() {
638 const int level = sat_solver_->CurrentDecisionLevel();
640 if (level == 0)
return false;
641 if (level > assigned_tree_.MaxLevel()) {
644 rev_num_processed_implications_.resize(level + 1, 0);
645 auto& num_processed_implications = rev_num_processed_implications_[level];
646 reversible_int_repository_->SaveState(&num_processed_implications);
647 absl::Span<const Literal> implied_literals =
648 absl::MakeConstSpan(assigned_tree_implications_[level - 1])
649 .subspan(num_processed_implications);
650 bool added_clause =
false;
651 for (Literal impl : implied_literals) {
652 ++num_processed_implications;
653 if (sat_solver_->Assignment().LiteralIsTrue(impl))
continue;
655 if (!AddDecisionImplication(impl, level))
return true;
657 if (objective_ !=
nullptr &&
659 const IntegerValue obj_lb =
660 integer_trail_->LowerBound(objective_->objective_var);
661 assigned_tree_.SetObjectiveLb(level, obj_lb);
662 const Literal obj_lit =
664 objective_->objective_var, assigned_tree_.ObjectiveLb(level)));
665 if (!sat_solver_->Assignment().LiteralIsTrue(obj_lit)) {
666 AddDecisionImplication(obj_lit, level);
673bool SharedTreeWorker::SyncWithLocalTrail() {
675 if (!sat_solver_->FinishPropagation())
return false;
678 if (AddImplications())
continue;
680 if (!helper_->BeforeTakingDecision())
return false;
681 const int level = sat_solver_->CurrentDecisionLevel();
682 if (parameters_->shared_tree_worker_enable_trail_sharing() && level > 0 &&
683 level <= assigned_tree_.MaxLevel()) {
685 reversible_int_repository_->SaveState(&reversible_trail_index_);
686 for (
int i = trail_->Index() - 1;
i >= reversible_trail_index_; --
i) {
687 const Literal lit = (*trail_)[
i];
688 if (trail_->AssignmentType(lit.Variable()) ==
692 std::optional<ProtoLiteral> encoded = EncodeDecision(lit);
693 if (!encoded.has_value())
continue;
694 assigned_tree_.AddImplication(level, *encoded);
696 reversible_trail_index_ = trail_->Index();
698 if (level >= assigned_tree_.MaxLevel())
break;
700 const Literal next_decision = assigned_tree_literals_[level];
701 if (!sat_solver_->Assignment().LiteralIsAssigned(next_decision))
break;
702 if (sat_solver_->Assignment().LiteralIsFalse(next_decision)) {
704 VLOG(2) <<
"Closing subtree at " << level + 1
705 <<
" assigned=" << assigned_tree_.MaxLevel();
706 manager_->CloseTree(assigned_tree_, level + 1);
707 assigned_tree_literals_.clear();
708 assigned_tree_implications_.clear();
709 sat_solver_->Backtrack(0);
712 assigned_tree_.SetLevelImplied(level + 1);
714 assigned_tree_implications_[level - 1].insert(
715 assigned_tree_implications_[level - 1].end(),
716 assigned_tree_implications_[level].begin(),
717 assigned_tree_implications_[level].end());
719 assigned_tree_implications_.erase(assigned_tree_implications_.begin() +
721 assigned_tree_literals_.erase(assigned_tree_literals_.begin() + level);
727bool SharedTreeWorker::NextDecision(LiteralIndex* decision_index) {
728 const auto& decision_policy =
729 heuristics_->decision_policies[heuristics_->policy_index];
730 const int next_level = sat_solver_->CurrentDecisionLevel() + 1;
731 new_split_available_ = next_level == assigned_tree_.MaxLevel() + 1;
733 CHECK_EQ(assigned_tree_literals_.size(), assigned_tree_.MaxLevel());
734 if (next_level <= assigned_tree_.MaxLevel()) {
735 VLOG(2) <<
"Following shared trail depth=" << next_level <<
" "
736 << parameters_->name();
737 const Literal decision = assigned_tree_literals_[next_level - 1];
738 CHECK(!sat_solver_->Assignment().LiteralIsFalse(decision))
739 <<
" at depth " << next_level <<
" " << parameters_->name();
740 CHECK(!sat_solver_->Assignment().LiteralIsTrue(decision));
741 *decision_index = decision.Index();
744 return helper_->GetDecision(decision_policy, decision_index);
747void SharedTreeWorker::MaybeProposeSplit() {
748 if (!new_split_available_ ||
749 sat_solver_->CurrentDecisionLevel() != assigned_tree_.MaxLevel() + 1) {
752 new_split_available_ =
false;
753 const Literal split_decision =
754 sat_solver_->Decisions()[assigned_tree_.MaxLevel()].literal;
755 const std::optional<ProtoLiteral> encoded = EncodeDecision(split_decision);
756 if (encoded.has_value()) {
757 CHECK_EQ(assigned_tree_literals_.size(), assigned_tree_.MaxLevel());
758 manager_->ProposeSplit(assigned_tree_, *encoded);
759 if (assigned_tree_.MaxLevel() > assigned_tree_literals_.size()) {
760 assigned_tree_literals_.push_back(split_decision);
761 assigned_tree_implications_.push_back({});
763 CHECK_EQ(assigned_tree_literals_.size(), assigned_tree_.MaxLevel());
767bool SharedTreeWorker::ShouldReplaceSubtree() {
769 if (assigned_tree_.MaxLevel() == 0)
return true;
770 if (restart_policy_->NumRestarts() <
771 parameters_->shared_tree_worker_min_restarts_per_subtree() ||
772 time_limit_->GetElapsedDeterministicTime() <
773 earliest_replacement_dtime_) {
776 return assigned_tree_lbds_.WindowAverage() <
777 restart_policy_->LbdAverageSinceReset();
780bool SharedTreeWorker::SyncWithSharedTree() {
781 manager_->SyncTree(assigned_tree_);
782 if (ShouldReplaceSubtree()) {
784 VLOG(2) << parameters_->name() <<
" acquiring tree #" << num_trees_
785 <<
" after " << restart_policy_->NumRestarts() <<
" restarts"
786 <<
" prev depth: " << assigned_tree_.MaxLevel()
787 <<
" target: " << assigned_tree_lbds_.WindowAverage()
788 <<
" lbd: " << restart_policy_->LbdAverageSinceReset();
789 if (parameters_->shared_tree_worker_enable_phase_sharing() &&
792 FinishedMinRestarts() &&
793 !decision_policy_->GetBestPartialAssignment().empty()) {
794 assigned_tree_.ClearTargetPhase();
795 for (Literal lit : decision_policy_->GetBestPartialAssignment()) {
799 if (!encoded.has_value())
continue;
800 if (!assigned_tree_.AddPhase(*encoded))
break;
803 manager_->ReplaceTree(assigned_tree_);
804 assigned_tree_lbds_.Add(restart_policy_->LbdAverageSinceReset());
805 restart_policy_->Reset();
806 earliest_replacement_dtime_ = 0;
807 if (parameters_->shared_tree_worker_enable_phase_sharing()) {
808 VLOG(2) <<
"Importing phase of length: "
809 << assigned_tree_.TargetPhase().size();
810 decision_policy_->ClearBestPartialAssignment();
811 for (
const ProtoLiteral& lit : assigned_tree_.TargetPhase()) {
812 decision_policy_->SetTargetPolarity(DecodeDecision(lit));
819 if (FinishedMinRestarts() && earliest_replacement_dtime_ >=
820 time_limit_->GetElapsedDeterministicTime()) {
821 earliest_replacement_dtime_ =
822 time_limit_->GetElapsedDeterministicTime() + 1;
824 assigned_tree_lbds_.Add(restart_policy_->LbdAverageSinceReset());
826 VLOG(2) <<
"Assigned level: " << assigned_tree_.MaxLevel() <<
" "
827 << parameters_->name();
828 assigned_tree_literals_.clear();
829 assigned_tree_implications_.clear();
830 for (
int i = 1;
i <= assigned_tree_.MaxLevel(); ++
i) {
831 assigned_tree_literals_.push_back(
832 DecodeDecision(assigned_tree_.Decision(
i)));
833 std::vector<Literal> implications;
834 for (
const ProtoLiteral& impl : assigned_tree_.Implications(
i)) {
835 implications.push_back(DecodeDecision(impl));
837 assigned_tree_implications_.push_back(std::move(implications));
843 const std::function<
void()>& feasible_solution_observer) {
848 sat_solver_->Backtrack(0);
849 encoder_->GetTrueLiteral();
850 encoder_->GetFalseLiteral();
851 level_zero_callbacks_->callbacks.push_back(
852 [
this]() {
return SyncWithSharedTree(); });
853 const bool has_objective =
855 while (!time_limit_->LimitReached()) {
856 if (!sat_solver_->FinishPropagation()) {
857 return sat_solver_->UnsatStatus();
859 if (heuristics_->restart_policies[heuristics_->policy_index]()) {
860 heuristics_->policy_index = restart_policy_->NumRestarts() %
861 heuristics_->decision_policies.size();
862 sat_solver_->Backtrack(0);
864 if (!SyncWithLocalTrail())
return sat_solver_->UnsatStatus();
865 LiteralIndex decision_index;
866 if (!NextDecision(&decision_index))
continue;
869 feasible_solution_observer();
871 const IntegerValue objective =
872 integer_trail_->LowerBound(objective_->objective_var);
873 sat_solver_->Backtrack(0);
874 if (!integer_trail_->Enqueue(
883 const Literal decision(decision_index);
884 CHECK(!sat_solver_->Assignment().LiteralIsFalse(decision));
885 CHECK(!sat_solver_->Assignment().LiteralIsTrue(decision));
886 if (!helper_->TakeDecision(decision)) {
887 return sat_solver_->UnsatStatus();
896 return lit.
Decode(mapping_, encoder_);
899std::optional<ProtoLiteral> SharedTreeWorker::EncodeDecision(Literal decision) {
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
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 ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
LiteralIndex Index() const
BooleanVariable Variable() const
static std::optional< ProtoLiteral > Encode(Literal, CpModelMapping *, IntegerEncoder *)
Literal Decode(CpModelMapping *, IntegerEncoder *) const
Note you should only decode integer literals at the root level.
static std::optional< ProtoLiteral > EncodeLiteral(Literal, CpModelMapping *)
int MaxLevel() const
Returns the maximum decision level stored in the trail.
void Clear()
Clear the trail, removing all levels.
void SetTargetPhase(absl::Span< const ProtoLiteral > phase)
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.
const VariablesAssignment & Assignment() const
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)
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.
static constexpr int kSearchDecision
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)