29#include "absl/container/flat_hash_set.h" 
   30#include "absl/log/check.h" 
   31#include "absl/log/log.h" 
   32#include "absl/strings/str_cat.h" 
   33#include "absl/synchronization/mutex.h" 
   34#include "absl/types/span.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]);
 
 
  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() ==
 
  318      params_.shared_tree_split_strategy() ==
 
  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() ==
 
  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() ==
 
  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);
 
  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(), 
")");
 
  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.
static constexpr SharedTreeSplitStrategy SPLIT_STRATEGY_DISCREPANCY
static constexpr SharedTreeSplitStrategy SPLIT_STRATEGY_BALANCED_TREE
static constexpr SharedTreeSplitStrategy SPLIT_STRATEGY_AUTO
bool shared_tree_worker_enable_trail_sharing() const
static constexpr SharedTreeSplitStrategy SPLIT_STRATEGY_OBJECTIVE_LB
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.
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
static constexpr int kSearchDecision
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)