OR-Tools  9.6
encoding.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #include "ortools/sat/encoding.h"
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <deque>
19 #include <functional>
20 #include <queue>
21 #include <string>
22 #include <vector>
23 
24 #include "ortools/base/logging.h"
25 #include "ortools/sat/boolean_problem.pb.h"
27 #include "ortools/sat/sat_base.h"
28 #include "ortools/sat/sat_parameters.pb.h"
29 #include "ortools/sat/sat_solver.h"
31 
32 namespace operations_research {
33 namespace sat {
34 
36  : for_sorting_(l.Variable()), literals_(1, l) {}
37 
39  std::function<Literal(int x)> create_lit)
40  : lb_(lb), ub_(ub), create_lit_(create_lit) {
41  CHECK_LT(lb, ub);
42  literals_.push_back(create_lit(lb));
43 
44  // TODO(user): Not ideal, we should probably just provide index in the
45  // original objective for sorting purpose.
46  for_sorting_ = literals_[0].Variable();
47 }
48 
50  SatSolver* solver) {
51  CHECK(literals_.empty()) << "Already initialized";
52  CHECK_GT(n, 0);
53  const BooleanVariable first_var_index(solver->NumVariables());
54  solver->SetNumVariables(solver->NumVariables() + n);
55  for (int i = 0; i < n; ++i) {
56  literals_.push_back(Literal(first_var_index + i, true));
57  if (i > 0) {
58  solver->AddBinaryClause(literal(i - 1), literal(i).Negated());
59  }
60  }
61  lb_ = a->lb_ + b->lb_;
62  ub_ = lb_ + n;
63  depth_ = 1 + std::max(a->depth_, b->depth_);
64  child_a_ = a;
65  child_b_ = b;
66  for_sorting_ = first_var_index;
67 }
68 
70  SatSolver* solver) {
71  CHECK(literals_.empty()) << "Already initialized";
72  const BooleanVariable first_var_index(solver->NumVariables());
73  solver->SetNumVariables(solver->NumVariables() + 1);
74  literals_.emplace_back(first_var_index, true);
75  child_a_ = a;
76  child_b_ = b;
77  ub_ = a->ub_ + b->ub_;
78  lb_ = a->lb_ + b->lb_;
79  depth_ = 1 + std::max(a->depth_, b->depth_);
80 
81  // Merging the node of the same depth in order seems to help a bit.
82  for_sorting_ = std::min(a->for_sorting_, b->for_sorting_);
83 }
84 
86  EncodingNode* b) {
87  CHECK(literals_.empty()) << "Already initialized";
88  child_a_ = a;
89  child_b_ = b;
90  ub_ = a->ub_ + b->ub_;
91  weight_ = weight;
92  weight_lb_ = a->lb_ + b->lb_;
93  lb_ = weight_lb_ + 1;
94  depth_ = 1 + std::max(a->depth_, b->depth_);
95 
96  // Merging the node of the same depth in order seems to help a bit.
97  for_sorting_ = std::min(a->for_sorting_, b->for_sorting_);
98 }
99 
101  if (current_ub() == ub_) return false;
102  if (create_lit_ != nullptr) {
103  literals_.emplace_back(create_lit_(current_ub()));
104  } else {
105  CHECK_NE(solver, nullptr);
106  literals_.emplace_back(BooleanVariable(solver->NumVariables()), true);
107  solver->SetNumVariables(solver->NumVariables() + 1);
108  }
109  if (literals_.size() > 1) {
110  solver->AddBinaryClause(literals_.back().Negated(),
111  literals_[literals_.size() - 2]);
112  }
113  return true;
114 }
115 
117  int i = 0;
118  while (i < literals_.size() &&
119  solver.Assignment().LiteralIsTrue(literals_[i])) {
120  ++i;
121  ++lb_;
122  }
123  literals_.erase(literals_.begin(), literals_.begin() + i);
124  while (!literals_.empty() &&
125  solver.Assignment().LiteralIsFalse(literals_.back())) {
126  literals_.pop_back();
127  ub_ = lb_ + literals_.size();
128  }
129 
130  if (weight_lb_ >= lb_) return Coefficient(0);
131  const Coefficient result = Coefficient(lb_ - weight_lb_) * weight_;
132  weight_lb_ = lb_;
133  return result;
134 }
135 
137  CHECK_GT(weight_, 0);
138  const Coefficient num_allowed = (gap / weight_);
139  const Coefficient new_size =
140  std::max(Coefficient(0), Coefficient(weight_lb_ - lb_) + num_allowed);
141  if (size() <= new_size) return;
142  for (int i = new_size.value(); i < size(); ++i) {
143  solver->AddUnitClause(literal(i).Negated());
144  }
145  literals_.resize(new_size.value());
146  ub_ = lb_ + literals_.size();
147 }
148 
150  DCHECK(!HasNoWeight());
151  const int index = weight_lb_ - lb_;
152  return index < literals_.size() && literals_[index].Negated() == other;
153 }
154 
156  CHECK(!HasNoWeight());
157  const int index = weight_lb_ - lb_;
158  CHECK_GE(index, 0) << "Not reduced?";
159  while (index >= literals_.size()) {
160  IncreaseNodeSize(this, solver);
161  }
162  return literals_[index].Negated();
163 }
164 
166  CHECK_LT(weight_lb_ - lb_, literals_.size());
167  weight_lb_++;
168 }
169 
171  return weight_ == 0 || weight_lb_ >= ub_;
172 }
173 
175  const VariablesAssignment& assignment) const {
176  std::string result;
177  absl::StrAppend(&result, "depth:", depth_);
178  absl::StrAppend(&result, " [", lb_, ",", lb_ + literals_.size(), "]");
179  absl::StrAppend(&result, " ub:", ub_);
180  absl::StrAppend(&result, " weight:", weight_.value());
181  absl::StrAppend(&result, " weight_lb:", weight_lb_);
182  absl::StrAppend(&result, " values:");
183  const size_t limit = 20;
184  int value = 0;
185  for (int i = 0; i < std::min(literals_.size(), limit); ++i) {
186  char c = '?';
187  if (assignment.LiteralIsTrue(literals_[i])) {
188  c = '1';
189  value = i + 1;
190  } else if (assignment.LiteralIsFalse(literals_[i])) {
191  c = '0';
192  }
193  result += c;
194  }
195  absl::StrAppend(&result, " val:", lb_ + value);
196  return result;
197 }
198 
200  EncodingNode n;
201  n.InitializeLazyNode(a, b, solver);
202  solver->AddBinaryClause(a->literal(0).Negated(), n.literal(0));
203  solver->AddBinaryClause(b->literal(0).Negated(), n.literal(0));
204  solver->AddTernaryClause(n.literal(0).Negated(), a->literal(0),
205  b->literal(0));
206  return n;
207 }
208 
210  if (!node->IncreaseCurrentUB(solver)) return;
211  std::vector<EncodingNode*> to_process;
212  to_process.push_back(node);
213 
214  // Only one side of the constraint is mandatory (the one propagating the ones
215  // to the top of the encoding tree), and it seems more efficient not to encode
216  // the other side.
217  //
218  // TODO(user): Experiment more.
219  const bool complete_encoding = false;
220 
221  while (!to_process.empty()) {
222  EncodingNode* n = to_process.back();
223  EncodingNode* a = n->child_a();
224  EncodingNode* b = n->child_b();
225  to_process.pop_back();
226 
227  // Integer leaf node.
228  if (a == nullptr) continue;
229  CHECK_NE(solver, nullptr);
230 
231  // Note that since we were able to increase its size, n must have children.
232  // n->GreaterThan(target) is the new literal of n.
233  CHECK(a != nullptr);
234  CHECK(b != nullptr);
235  const int target = n->current_ub() - 1;
236 
237  // Add a literal to a if needed.
238  // That is, now that the node n can go up to it new current_ub, if we need
239  // to increase the current_ub of a.
240  if (a->current_ub() != a->ub()) {
241  CHECK_GE(a->current_ub() - 1 + b->lb(), target - 1);
242  if (a->current_ub() - 1 + b->lb() < target) {
243  CHECK(a->IncreaseCurrentUB(solver));
244  to_process.push_back(a);
245  }
246  }
247 
248  // Add a literal to b if needed.
249  if (b->current_ub() != b->ub()) {
250  CHECK_GE(b->current_ub() - 1 + a->lb(), target - 1);
251  if (b->current_ub() - 1 + a->lb() < target) {
252  CHECK(b->IncreaseCurrentUB(solver));
253  to_process.push_back(b);
254  }
255  }
256 
257  // Wire the new literal of n correctly with its two children.
258  for (int ia = a->lb(); ia < a->current_ub(); ++ia) {
259  const int ib = target - ia;
260  if (complete_encoding && ib >= b->lb() && ib < b->current_ub()) {
261  // if x <= ia and y <= ib then x + y <= ia + ib.
262  solver->AddTernaryClause(n->GreaterThan(target).Negated(),
263  a->GreaterThan(ia), b->GreaterThan(ib));
264  }
265  if (complete_encoding && ib == b->ub()) {
266  solver->AddBinaryClause(n->GreaterThan(target).Negated(),
267  a->GreaterThan(ia));
268  }
269 
270  if (ib - 1 == b->lb() - 1) {
271  solver->AddBinaryClause(n->GreaterThan(target),
272  a->GreaterThan(ia).Negated());
273  }
274  if ((ib - 1) >= b->lb() && (ib - 1) < b->current_ub()) {
275  // if x > ia and y > ib - 1 then x + y > ia + ib.
276  solver->AddTernaryClause(n->GreaterThan(target),
277  a->GreaterThan(ia).Negated(),
278  b->GreaterThan(ib - 1).Negated());
279  }
280  }
281 
282  // Case ia = a->lb() - 1; a->GreaterThan(ia) always true.
283  {
284  const int ib = target - (a->lb() - 1);
285  if ((ib - 1) == b->lb() - 1) {
286  solver->AddUnitClause(n->GreaterThan(target));
287  }
288  if ((ib - 1) >= b->lb() && (ib - 1) < b->current_ub()) {
289  solver->AddBinaryClause(n->GreaterThan(target),
290  b->GreaterThan(ib - 1).Negated());
291  }
292  }
293 
294  // case ia == a->ub; a->GreaterThan(ia) always false.
295  {
296  const int ib = target - a->ub();
297  if (complete_encoding && ib >= b->lb() && ib < b->current_ub()) {
298  solver->AddBinaryClause(n->GreaterThan(target).Negated(),
299  b->GreaterThan(ib));
300  }
301  if (ib == b->ub()) {
302  solver->AddUnitClause(n->GreaterThan(target).Negated());
303  }
304  }
305  }
306 }
307 
309  EncodingNode* b, SatSolver* solver) {
310  EncodingNode n;
311  const int size =
312  std::min(Coefficient(a->size() + b->size()), upper_bound).value();
313  n.InitializeFullNode(size, a, b, solver);
314  for (int ia = 0; ia < a->size(); ++ia) {
315  if (ia + b->size() < size) {
316  solver->AddBinaryClause(n.literal(ia + b->size()).Negated(),
317  a->literal(ia));
318  }
319  if (ia < size) {
320  solver->AddBinaryClause(n.literal(ia), a->literal(ia).Negated());
321  } else {
322  // Fix the variable to false because of the given upper_bound.
323  solver->AddUnitClause(a->literal(ia).Negated());
324  }
325  }
326  for (int ib = 0; ib < b->size(); ++ib) {
327  if (ib + a->size() < size) {
328  solver->AddBinaryClause(n.literal(ib + a->size()).Negated(),
329  b->literal(ib));
330  }
331  if (ib < size) {
332  solver->AddBinaryClause(n.literal(ib), b->literal(ib).Negated());
333  } else {
334  // Fix the variable to false because of the given upper_bound.
335  solver->AddUnitClause(b->literal(ib).Negated());
336  }
337  }
338  for (int ia = 0; ia < a->size(); ++ia) {
339  for (int ib = 0; ib < b->size(); ++ib) {
340  if (ia + ib < size) {
341  // if x <= ia and y <= ib, then x + y <= ia + ib.
342  solver->AddTernaryClause(n.literal(ia + ib).Negated(), a->literal(ia),
343  b->literal(ib));
344  }
345  if (ia + ib + 1 < size) {
346  // if x > ia and y > ib, then x + y > ia + ib + 1.
347  solver->AddTernaryClause(n.literal(ia + ib + 1),
348  a->literal(ia).Negated(),
349  b->literal(ib).Negated());
350  } else {
351  solver->AddBinaryClause(a->literal(ia).Negated(),
352  b->literal(ib).Negated());
353  }
354  }
355  }
356  return n;
357 }
358 
360  const std::vector<EncodingNode*>& nodes,
361  SatSolver* solver,
362  std::deque<EncodingNode>* repository) {
363  std::deque<EncodingNode*> dq(nodes.begin(), nodes.end());
364  while (dq.size() > 1) {
365  EncodingNode* a = dq.front();
366  dq.pop_front();
367  EncodingNode* b = dq.front();
368  dq.pop_front();
369  repository->push_back(FullMerge(upper_bound, a, b, solver));
370  dq.push_back(&repository->back());
371  }
372  return dq.front();
373 }
374 
375 namespace {
376 struct SortEncodingNodePointers {
377  bool operator()(EncodingNode* a, EncodingNode* b) const { return *a < *b; }
378 };
379 } // namespace
380 
382  Coefficient weight, const std::vector<EncodingNode*>& nodes,
383  SatSolver* solver, std::deque<EncodingNode>* repository) {
384  std::priority_queue<EncodingNode*, std::vector<EncodingNode*>,
385  SortEncodingNodePointers>
386  pq(nodes.begin(), nodes.end());
387  while (pq.size() > 2) {
388  EncodingNode* a = pq.top();
389  pq.pop();
390  EncodingNode* b = pq.top();
391  pq.pop();
392  repository->push_back(LazyMerge(a, b, solver));
393  pq.push(&repository->back());
394  }
395 
396  CHECK_EQ(pq.size(), 2);
397  EncodingNode* a = pq.top();
398  pq.pop();
399  EncodingNode* b = pq.top();
400  pq.pop();
401 
402  repository->push_back(EncodingNode());
403  EncodingNode* n = &repository->back();
405  solver->AddBinaryClause(a->literal(0), b->literal(0));
406  return n;
407 }
408 
409 std::vector<EncodingNode*> CreateInitialEncodingNodes(
410  const std::vector<Literal>& literals,
411  const std::vector<Coefficient>& coeffs, Coefficient* offset,
412  std::deque<EncodingNode>* repository) {
413  CHECK_EQ(literals.size(), coeffs.size());
414  *offset = 0;
415  std::vector<EncodingNode*> nodes;
416  for (int i = 0; i < literals.size(); ++i) {
417  // We want to maximize the cost when this literal is true.
418  if (coeffs[i] > 0) {
419  repository->emplace_back(literals[i]);
420  nodes.push_back(&repository->back());
421  nodes.back()->set_weight(coeffs[i]);
422  } else {
423  repository->emplace_back(literals[i].Negated());
424  nodes.push_back(&repository->back());
425  nodes.back()->set_weight(-coeffs[i]);
426 
427  // Note that this increase the offset since the coeff is negative.
428  *offset -= coeffs[i];
429  }
430  }
431  return nodes;
432 }
433 
434 std::vector<EncodingNode*> CreateInitialEncodingNodes(
435  const LinearObjective& objective_proto, Coefficient* offset,
436  std::deque<EncodingNode>* repository) {
437  *offset = 0;
438  std::vector<EncodingNode*> nodes;
439  for (int i = 0; i < objective_proto.literals_size(); ++i) {
440  const Literal literal(objective_proto.literals(i));
441 
442  // We want to maximize the cost when this literal is true.
443  if (objective_proto.coefficients(i) > 0) {
444  repository->emplace_back(literal);
445  nodes.push_back(&repository->back());
446  nodes.back()->set_weight(Coefficient(objective_proto.coefficients(i)));
447  } else {
448  repository->emplace_back(literal.Negated());
449  nodes.push_back(&repository->back());
450  nodes.back()->set_weight(Coefficient(-objective_proto.coefficients(i)));
451 
452  // Note that this increase the offset since the coeff is negative.
453  *offset -= objective_proto.coefficients(i);
454  }
455  }
456  return nodes;
457 }
458 
459 namespace {
460 
461 bool EncodingNodeByWeight(const EncodingNode* a, const EncodingNode* b) {
462  return a->weight() < b->weight();
463 }
464 
465 bool EncodingNodeByDepth(const EncodingNode* a, const EncodingNode* b) {
466  return a->depth() < b->depth();
467 }
468 
469 } // namespace
470 
471 std::vector<Literal> ReduceNodesAndExtractAssumptions(
472  Coefficient upper_bound, Coefficient stratified_lower_bound,
473  Coefficient* lower_bound, std::vector<EncodingNode*>* nodes,
474  SatSolver* solver) {
475  // Remove the left-most variables fixed to one from each node.
476  // Also update the lower_bound. Note that Reduce() needs the solver to be
477  // at the root node in order to work.
478  solver->Backtrack(0);
479  for (EncodingNode* n : *nodes) {
480  *lower_bound += n->Reduce(*solver);
481  }
482 
483  // Fix the nodes right-most variables that are above the gap.
484  // If we closed the problem, we abort and return and empty vector.
485  if (upper_bound != kCoefficientMax) {
486  const Coefficient gap = upper_bound - *lower_bound;
487  if (gap < 0) return {};
488  for (EncodingNode* n : *nodes) {
489  n->ApplyWeightUpperBound(gap, solver);
490  }
491  }
492 
493  // Remove the empty nodes.
494  nodes->erase(std::remove_if(nodes->begin(), nodes->end(),
495  [](EncodingNode* a) { return a->HasNoWeight(); }),
496  nodes->end());
497 
498  // Sort the nodes.
499  switch (solver->parameters().max_sat_assumption_order()) {
500  case SatParameters::DEFAULT_ASSUMPTION_ORDER:
501  break;
502  case SatParameters::ORDER_ASSUMPTION_BY_DEPTH:
503  std::sort(nodes->begin(), nodes->end(), EncodingNodeByDepth);
504  break;
505  case SatParameters::ORDER_ASSUMPTION_BY_WEIGHT:
506  std::sort(nodes->begin(), nodes->end(), EncodingNodeByWeight);
507  break;
508  }
509  if (solver->parameters().max_sat_reverse_assumption_order()) {
510  // TODO(user): with DEFAULT_ASSUMPTION_ORDER, this will lead to a somewhat
511  // weird behavior, since we will reverse the nodes at each iteration...
512  std::reverse(nodes->begin(), nodes->end());
513  }
514 
515  // Extract the assumptions from the nodes.
516  std::vector<Literal> assumptions;
517  for (EncodingNode* n : *nodes) {
518  if (n->weight() >= stratified_lower_bound) {
519  assumptions.push_back(n->GetAssumption(solver));
520  }
521  }
522  return assumptions;
523 }
524 
525 Coefficient ComputeCoreMinWeight(const std::vector<EncodingNode*>& nodes,
526  const std::vector<Literal>& core) {
527  Coefficient min_weight = kCoefficientMax;
528  int index = 0;
529  for (int i = 0; i < core.size(); ++i) {
530  for (; index < nodes.size() && !nodes[index]->AssumptionIs(core[i]);
531  ++index) {
532  }
533  CHECK_LT(index, nodes.size());
534  min_weight = std::min(min_weight, nodes[index]->weight());
535  }
536  return min_weight;
537 }
538 
539 Coefficient MaxNodeWeightSmallerThan(const std::vector<EncodingNode*>& nodes,
541  Coefficient result(0);
542  for (EncodingNode* n : nodes) {
543  CHECK_GT(n->weight(), 0);
544  if (n->weight() < upper_bound) {
545  result = std::max(result, n->weight());
546  }
547  }
548  return result;
549 }
550 
551 bool ProcessCore(const std::vector<Literal>& core, Coefficient min_weight,
552  std::deque<EncodingNode>* repository,
553  std::vector<EncodingNode*>* nodes, SatSolver* solver) {
554  // Backtrack to be able to add new constraints.
555  solver->ResetToLevelZero();
556  if (core.size() == 1) {
557  return solver->AddUnitClause(core[0].Negated());
558  }
559 
560  // Remove from nodes the EncodingNode in the core, merge them, and add the
561  // resulting EncodingNode at the back.
562  int index = 0;
563  int new_node_index = 0;
564  std::vector<EncodingNode*> to_merge;
565  for (int i = 0; i < core.size(); ++i) {
566  // Since the nodes appear in order in the core, we can find the
567  // relevant "objective" variable efficiently with a simple linear scan
568  // in the nodes vector (done with index).
569  for (; !(*nodes)[index]->AssumptionIs(core[i]); ++index) {
570  CHECK_LT(index, nodes->size());
571  (*nodes)[new_node_index] = (*nodes)[index];
572  ++new_node_index;
573  }
574  CHECK_LT(index, nodes->size());
575  to_merge.push_back((*nodes)[index]);
576 
577  // Special case if the weight > min_weight. we keep it, but reduce its
578  // cost. This is the same "trick" as in WPM1 used to deal with weight.
579  // We basically split a clause with a larger weight in two identical
580  // clauses, one with weight min_weight that will be merged and one with
581  // the remaining weight.
582  if ((*nodes)[index]->weight() > min_weight) {
583  (*nodes)[index]->set_weight((*nodes)[index]->weight() - min_weight);
584  (*nodes)[new_node_index] = (*nodes)[index];
585  ++new_node_index;
586  }
587  ++index;
588  }
589  for (; index < nodes->size(); ++index) {
590  (*nodes)[new_node_index] = (*nodes)[index];
591  ++new_node_index;
592  }
593  nodes->resize(new_node_index);
594  nodes->push_back(LazyMergeAllNodeWithPQAndIncreaseLb(min_weight, to_merge,
595  solver, repository));
596  return !solver->ModelIsUnsat();
597 }
598 
599 bool ProcessCoreWithAlternativeEncoding(const std::vector<Literal>& core,
600  Coefficient min_weight,
601  std::deque<EncodingNode>* repository,
602  std::vector<EncodingNode*>* nodes,
603  SatSolver* solver) {
604  // Backtrack to be able to add new constraints.
605  solver->ResetToLevelZero();
606 
607  if (core.size() == 1) {
608  return solver->AddUnitClause(core[0].Negated());
609  }
610 
611  std::vector<EncodingNode*> new_nodes;
612  std::vector<EncodingNode*> to_merge;
613 
614  // Preconditions.
615  for (EncodingNode* n : *nodes) {
616  CHECK_GT(n->size(), 0);
617  }
618 
619  // Remove from nodes the EncodingNode in the core, merge them, and add the
620  // resulting EncodingNode at the back.
621  int index = 0;
622  for (int i = 0; i < core.size(); ++i) {
623  // Since the nodes appear in order in the core, we can find the
624  // relevant "objective" variable efficiently with a simple linear scan
625  // in the nodes vector (done with index).
626  CHECK_LT(index, nodes->size());
627  for (; !(*nodes)[index]->AssumptionIs(core[i]); ++index) {
628  CHECK_LT(index, nodes->size());
629  new_nodes.push_back((*nodes)[index]);
630  }
631  CHECK_LT(index, nodes->size());
632 
633  // We have a node from the core.
634  // We will distinguish its first literal.
635  EncodingNode* n = (*nodes)[index];
636  const Literal lit = core[i].Negated();
637  n->IncreaseWeightLb();
638  ++index;
639  CHECK_GT(n->size(), 0);
640 
641  // TODO(user): For node with same depth, the sorting order is not the same
642  // if we create a new node or reuse one. Experiment what is the best order.
643  repository->emplace_back(lit);
644  EncodingNode* new_bool_node = &repository->back();
645  new_bool_node->set_depth(n->depth());
646  CHECK_GT(new_bool_node->size(), 0);
647  to_merge.push_back(new_bool_node);
648  if (n->weight() > min_weight) {
649  new_bool_node->set_weight(n->weight() - min_weight);
650  new_nodes.push_back(new_bool_node);
651  }
652 
653  if (!n->HasNoWeight()) {
654  new_nodes.push_back(n);
655  }
656  }
657 
658  for (; index < nodes->size(); ++index) {
659  new_nodes.push_back((*nodes)[index]);
660  }
661 
662  new_nodes.push_back(LazyMergeAllNodeWithPQAndIncreaseLb(min_weight, to_merge,
663  solver, repository));
664  *nodes = new_nodes;
665  return !solver->ModelIsUnsat();
666 }
667 
668 } // namespace sat
669 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
void InitializeLazyCoreNode(Coefficient weight, EncodingNode *a, EncodingNode *b)
Definition: encoding.cc:85
Literal GetAssumption(SatSolver *solver)
Definition: encoding.cc:155
EncodingNode * child_a() const
Definition: encoding.h:137
bool IncreaseCurrentUB(SatSolver *solver)
Definition: encoding.cc:100
void ApplyWeightUpperBound(Coefficient gap, SatSolver *solver)
Definition: encoding.cc:136
bool AssumptionIs(Literal other) const
Definition: encoding.cc:149
Coefficient Reduce(const SatSolver &solver)
Definition: encoding.cc:116
void InitializeLazyNode(EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:69
void InitializeFullNode(int n, EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:49
Literal literal(int i) const
Definition: encoding.h:89
std::string DebugString(const VariablesAssignment &assignment) const
Definition: encoding.cc:174
EncodingNode * child_b() const
Definition: encoding.h:138
Literal GreaterThan(int i) const
Definition: encoding.h:85
void SetNumVariables(int num_variables)
Definition: sat_solver.cc:86
bool AddTernaryClause(Literal a, Literal b, Literal c)
Definition: sat_solver.cc:194
const SatParameters & parameters() const
Definition: sat_solver.cc:132
const VariablesAssignment & Assignment() const
Definition: sat_solver.h:388
bool AddBinaryClause(Literal a, Literal b)
Definition: sat_solver.cc:190
void Backtrack(int target_level)
Definition: sat_solver.cc:1004
bool AddUnitClause(Literal true_literal)
Definition: sat_solver.cc:186
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:164
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:161
int64_t b
int64_t a
int64_t value
int index
std::tuple< int64_t, int64_t, const double > Coefficient
Coefficient ComputeCoreMinWeight(const std::vector< EncodingNode * > &nodes, const std::vector< Literal > &core)
Definition: encoding.cc:525
EncodingNode * MergeAllNodesWithDeque(Coefficient upper_bound, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
Definition: encoding.cc:359
EncodingNode * LazyMergeAllNodeWithPQAndIncreaseLb(Coefficient weight, const std::vector< EncodingNode * > &nodes, SatSolver *solver, std::deque< EncodingNode > *repository)
Definition: encoding.cc:381
std::vector< Literal > ReduceNodesAndExtractAssumptions(Coefficient upper_bound, Coefficient stratified_lower_bound, Coefficient *lower_bound, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Definition: encoding.cc:471
void IncreaseNodeSize(EncodingNode *node, SatSolver *solver)
Definition: encoding.cc:209
EncodingNode LazyMerge(EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:199
EncodingNode FullMerge(Coefficient upper_bound, EncodingNode *a, EncodingNode *b, SatSolver *solver)
Definition: encoding.cc:308
bool ProcessCore(const std::vector< Literal > &core, Coefficient min_weight, std::deque< EncodingNode > *repository, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Definition: encoding.cc:551
Coefficient MaxNodeWeightSmallerThan(const std::vector< EncodingNode * > &nodes, Coefficient upper_bound)
Definition: encoding.cc:539
bool ProcessCoreWithAlternativeEncoding(const std::vector< Literal > &core, Coefficient min_weight, std::deque< EncodingNode > *repository, std::vector< EncodingNode * > *nodes, SatSolver *solver)
Definition: encoding.cc:599
std::vector< EncodingNode * > CreateInitialEncodingNodes(const std::vector< Literal > &literals, const std::vector< Coefficient > &coeffs, Coefficient *offset, std::deque< EncodingNode > *repository)
Definition: encoding.cc:409
const Coefficient kCoefficientMax(std::numeric_limits< Coefficient::ValueType >::max())
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:88
int64_t weight
Definition: pack.cc:510
IntVar * upper_bound
Definition: routing.cc:1087
IntVar * lower_bound
Definition: routing.cc:1086
int nodes