OR-Tools  9.0
pb_constraint.h
Go to the documentation of this file.
1 // Copyright 2010-2021 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 #ifndef OR_TOOLS_SAT_PB_CONSTRAINT_H_
15 #define OR_TOOLS_SAT_PB_CONSTRAINT_H_
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <limits>
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/types/span.h"
26 #include "ortools/base/int_type.h"
28 #include "ortools/base/logging.h"
29 #include "ortools/base/macros.h"
31 #include "ortools/sat/model.h"
32 #include "ortools/sat/sat_base.h"
34 #include "ortools/util/bitset.h"
35 #include "ortools/util/stats.h"
36 
37 namespace operations_research {
38 namespace sat {
39 
40 // The type of the integer coefficients in a pseudo-Boolean constraint.
41 // This is also used for the current value of a constraint or its bounds.
42 DEFINE_INT_TYPE(Coefficient, int64_t);
43 
44 // IMPORTANT: We can't use numeric_limits<Coefficient>::max() which will compile
45 // but just returns zero!!
46 const Coefficient kCoefficientMax(
48 
49 // Represents a term in a pseudo-Boolean formula.
52  LiteralWithCoeff(Literal l, Coefficient c) : literal(l), coefficient(c) {}
53  LiteralWithCoeff(Literal l, int64_t c) : literal(l), coefficient(c) {}
55  Coefficient coefficient;
56  bool operator==(const LiteralWithCoeff& other) const {
57  return literal.Index() == other.literal.Index() &&
58  coefficient == other.coefficient;
59  }
60 };
61 inline std::ostream& operator<<(std::ostream& os, LiteralWithCoeff term) {
62  os << term.coefficient << "[" << term.literal.DebugString() << "]";
63  return os;
64 }
65 
66 // Puts the given Boolean linear expression in canonical form:
67 // - Merge all the literal corresponding to the same variable.
68 // - Remove zero coefficients.
69 // - Make all the coefficients positive.
70 // - Sort the terms by increasing coefficient values.
71 //
72 // This function also computes:
73 // - max_value: the maximum possible value of the formula.
74 // - bound_shift: which allows to updates initial bounds. That is, if an
75 // initial pseudo-Boolean constraint was
76 // lhs < initial_pb_formula < rhs
77 // then the new one is:
78 // lhs + bound_shift < canonical_form < rhs + bound_shift
79 //
80 // Finally, this will return false, if some integer overflow or underflow
81 // occurred during the reduction to the canonical form.
83  std::vector<LiteralWithCoeff>* cst, Coefficient* bound_shift,
84  Coefficient* max_value);
85 
86 // Maps all the literals of the given constraint using the given mapping. The
87 // mapping may map a literal index to kTrueLiteralIndex or kFalseLiteralIndex in
88 // which case the literal will be considered fixed to the appropriate value.
89 //
90 // Note that this function also canonicalizes the constraint and updates
91 // bound_shift and max_value like ComputeBooleanLinearExpressionCanonicalForm()
92 // does.
93 //
94 // Finally, this will return false if some integer overflow or underflow
95 // occurred during the constraint simplification.
98  std::vector<LiteralWithCoeff>* cst, Coefficient* bound_shift,
99  Coefficient* max_value);
100 
101 // From a constraint 'expr <= ub' and the result (bound_shift, max_value) of
102 // calling ComputeBooleanLinearExpressionCanonicalForm() on 'expr', this returns
103 // a new rhs such that 'canonical expression <= rhs' is an equivalent
104 // constraint. This function deals with all the possible overflow corner cases.
105 //
106 // The result will be in [-1, max_value] where -1 means unsatisfiable and
107 // max_value means trivialy satisfiable.
108 Coefficient ComputeCanonicalRhs(Coefficient upper_bound,
109  Coefficient bound_shift, Coefficient max_value);
110 
111 // Same as ComputeCanonicalRhs(), but uses the initial constraint lower bound
112 // instead. From a constraint 'lb <= expression', this returns a rhs such that
113 // 'canonical expression with literals negated <= rhs'.
114 //
115 // Note that the range is also [-1, max_value] with the same meaning.
116 Coefficient ComputeNegatedCanonicalRhs(Coefficient lower_bound,
117  Coefficient bound_shift,
118  Coefficient max_value);
119 
120 // Returns true iff the Boolean linear expression is in canonical form.
122  const std::vector<LiteralWithCoeff>& cst);
123 
124 // Given a Boolean linear constraint in canonical form, simplify its
125 // coefficients using simple heuristics.
127  std::vector<LiteralWithCoeff>* cst, Coefficient* rhs);
128 
129 // Holds a set of boolean linear constraints in canonical form:
130 // - The constraint is a linear sum of LiteralWithCoeff <= rhs.
131 // - The linear sum satisfies the properties described in
132 // ComputeBooleanLinearExpressionCanonicalForm().
133 //
134 // TODO(user): Simplify further the constraints.
135 //
136 // TODO(user): Remove the duplication between this and what the sat solver
137 // is doing in AddLinearConstraint() which is basically the same.
138 //
139 // TODO(user): Remove duplicate constraints? some problems have them, and
140 // this is not ideal for the symmetry computation since it leads to a lot of
141 // symmetries of the associated graph that are not useful.
143  public:
145 
146  // Adds a new constraint to the problem. The bounds are inclusive.
147  // Returns false in case of a possible overflow or if the constraint is
148  // never satisfiable.
149  //
150  // TODO(user): Use a return status to distinguish errors if needed.
151  bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound,
152  bool use_upper_bound, Coefficient upper_bound,
153  std::vector<LiteralWithCoeff>* cst);
154 
155  // Getters. All the constraints are guaranteed to be in canonical form.
156  int NumConstraints() const { return constraints_.size(); }
157  const Coefficient Rhs(int i) const { return rhs_[i]; }
158  const std::vector<LiteralWithCoeff>& Constraint(int i) const {
159  return constraints_[i];
160  }
161 
162  private:
163  bool AddConstraint(const std::vector<LiteralWithCoeff>& cst,
164  Coefficient max_value, Coefficient rhs);
165 
166  std::vector<Coefficient> rhs_;
167  std::vector<std::vector<LiteralWithCoeff>> constraints_;
168  DISALLOW_COPY_AND_ASSIGN(CanonicalBooleanLinearProblem);
169 };
170 
171 // Encode a constraint sum term <= rhs, where each term is a positive
172 // Coefficient times a literal. This class allows efficient modification of the
173 // constraint and is used during pseudo-Boolean resolution.
175  public:
176  // This must be called before any other functions is used with an higher
177  // variable index.
178  void ClearAndResize(int num_variables);
179 
180  // Reset the constraint to 0 <= 0.
181  // Note that the contraint size stays the same.
182  void ClearAll();
183 
184  // Returns the coefficient (>= 0) of the given variable.
185  Coefficient GetCoefficient(BooleanVariable var) const {
186  return AbsCoefficient(terms_[var]);
187  }
188 
189  // Returns the literal under which the given variable appear in the
190  // constraint. Note that if GetCoefficient(var) == 0 this just returns
191  // Literal(var, true).
192  Literal GetLiteral(BooleanVariable var) const {
193  return Literal(var, terms_[var] > 0);
194  }
195 
196  // If we have a lower bounded constraint sum terms >= rhs, then it is trivial
197  // to see that the coefficient of any term can be reduced to rhs if it is
198  // bigger. This does exactly this operation, but on the upper bounded
199  // representation.
200  //
201  // If we take a constraint sum ci.xi <= rhs, take its negation and add max_sum
202  // on both side, we have sum ci.(1 - xi) >= max_sum - rhs
203  // So every ci > (max_sum - rhs) can be replacend by (max_sum - rhs).
204  // Not that this operation also change the original rhs of the constraint.
205  void ReduceCoefficients();
206 
207  // Same as ReduceCoefficients() but only consider the coefficient of the given
208  // variable.
209  void ReduceGivenCoefficient(BooleanVariable var) {
210  const Coefficient bound = max_sum_ - rhs_;
211  const Coefficient diff = GetCoefficient(var) - bound;
212  if (diff > 0) {
213  rhs_ -= diff;
214  max_sum_ -= diff;
215  terms_[var] = (terms_[var] > 0) ? bound : -bound;
216  }
217  }
218 
219  // Compute the constraint slack assuming that only the variables with index <
220  // trail_index are assigned.
221  Coefficient ComputeSlackForTrailPrefix(const Trail& trail,
222  int trail_index) const;
223 
224  // Same as ReduceCoefficients() followed by ComputeSlackForTrailPrefix(). It
225  // allows to loop only once over all the terms of the constraint instead of
226  // doing it twice. This helps since doing that can be the main bottleneck.
227  //
228  // Note that this function assumes that the returned slack will be negative.
229  // This allow to DCHECK some assumptions on what coefficients can be reduced
230  // or not.
231  //
232  // TODO(user): Ideally the slack should be maitainable incrementally.
234  const Trail& trail, int trail_index);
235 
236  // Relaxes the constraint so that:
237  // - ComputeSlackForTrailPrefix(trail, trail_index) == target;
238  // - All the variables that were propagated given the assignment < trail_index
239  // are still propagated.
240  //
241  // As a precondition, ComputeSlackForTrailPrefix(trail, trail_index) >= target
242  // Note that nothing happen if the slack is already equals to target.
243  //
244  // Algorithm: Let diff = slack - target (>= 0). We will split the constraint
245  // linear expression in 3 parts:
246  // - P1: the true variables (only the one assigned < trail_index).
247  // - P2: the other variables with a coeff > diff.
248  // Note that all these variables were the propagated ones.
249  // - P3: the other variables with a coeff <= diff.
250  // We can then transform P1 + P2 + P3 <= rhs_ into P1 + P2' <= rhs_ - diff
251  // Where P2' is the same sum as P2 with all the coefficient reduced by diff.
252  //
253  // Proof: Given the old constraint, we want to show that the relaxed one is
254  // always true. If all the variable in P2' are false, then
255  // P1 <= rhs_ - slack <= rhs_ - diff is always true. If at least one of the
256  // P2' variable is true, then P2 >= P2' + diff and we have
257  // P1 + P2' + diff <= P1 + P2 <= rhs_.
258  void ReduceSlackTo(const Trail& trail, int trail_index,
259  Coefficient initial_slack, Coefficient target);
260 
261  // Copies this constraint into a vector<LiteralWithCoeff> representation.
262  void CopyIntoVector(std::vector<LiteralWithCoeff>* output);
263 
264  // Adds a non-negative value to this constraint Rhs().
265  void AddToRhs(Coefficient value) {
266  CHECK_GE(value, 0);
267  rhs_ += value;
268  }
269  Coefficient Rhs() const { return rhs_; }
270  Coefficient MaxSum() const { return max_sum_; }
271 
272  // Adds a term to this constraint. This is in the .h for efficiency.
273  // The encoding used internally is described below in the terms_ comment.
274  void AddTerm(Literal literal, Coefficient coeff) {
275  CHECK_GT(coeff, 0);
276  const BooleanVariable var = literal.Variable();
277  const Coefficient term_encoding = literal.IsPositive() ? coeff : -coeff;
278  if (literal != GetLiteral(var)) {
279  // The two terms are of opposite sign, a "cancelation" happens.
280  // We need to change the encoding of the lower magnitude term.
281  // - If term > 0, term . x -> term . (x - 1) + term
282  // - If term < 0, term . (x - 1) -> term . x - term
283  // In both cases, rhs -= abs(term).
284  rhs_ -= std::min(coeff, AbsCoefficient(terms_[var]));
285  max_sum_ += AbsCoefficient(term_encoding + terms_[var]) -
286  AbsCoefficient(terms_[var]);
287  } else {
288  // Both terms are of the same sign (or terms_[var] is zero).
289  max_sum_ += coeff;
290  }
291  CHECK_GE(max_sum_, 0) << "Overflow";
292  terms_[var] += term_encoding;
293  non_zeros_.Set(var);
294  }
295 
296  // Returns the "cancelation" amount of AddTerm(literal, coeff).
297  Coefficient CancelationAmount(Literal literal, Coefficient coeff) const {
298  DCHECK_GT(coeff, 0);
299  const BooleanVariable var = literal.Variable();
300  if (literal == GetLiteral(var)) return Coefficient(0);
301  return std::min(coeff, AbsCoefficient(terms_[var]));
302  }
303 
304  // Returns a set of positions that contains all the non-zeros terms of the
305  // constraint. Note that this set can also contains some zero terms.
306  const std::vector<BooleanVariable>& PossibleNonZeros() const {
307  return non_zeros_.PositionsSetAtLeastOnce();
308  }
309 
310  // Returns a string representation of the constraint.
311  std::string DebugString();
312 
313  private:
314  Coefficient AbsCoefficient(Coefficient a) const { return a > 0 ? a : -a; }
315 
316  // Only used for DCHECK_EQ(max_sum_, ComputeMaxSum());
317  Coefficient ComputeMaxSum() const;
318 
319  // The encoding is special:
320  // - If terms_[x] > 0, then the associated term is 'terms_[x] . x'
321  // - If terms_[x] < 0, then the associated term is 'terms_[x] . (x - 1)'
323 
324  // The right hand side of the constraint (sum terms <= rhs_).
325  Coefficient rhs_;
326 
327  // The constraint maximum sum (i.e. sum of the absolute term coefficients).
328  // Note that checking the integer overflow on this sum is enough.
329  Coefficient max_sum_;
330 
331  // Contains the possibly non-zeros terms_ value.
333 };
334 
335 // A simple "helper" class to enqueue a propagated literal on the trail and
336 // keep the information needed to explain it when requested.
337 class UpperBoundedLinearConstraint;
338 
340  void Enqueue(Literal l, int source_trail_index,
342  reasons[trail->Index()] = {source_trail_index, ct};
343  trail->Enqueue(l, propagator_id);
344  }
345 
346  // The propagator id of PbConstraints.
348 
349  // A temporary vector to store the last conflict.
350  std::vector<Literal> conflict;
351 
352  // Information needed to recover the reason of an Enqueue().
353  // Indexed by trail_index.
354  struct ReasonInfo {
357  };
358  std::vector<ReasonInfo> reasons;
359 };
360 
361 // This class contains half the propagation logic for a constraint of the form
362 //
363 // sum ci * li <= rhs, ci positive coefficients, li literals.
364 //
365 // The other half is implemented by the PbConstraints class below which takes
366 // care of updating the 'threshold' value of this constraint:
367 // - 'slack' is rhs minus all the ci of the variables xi assigned to
368 // true. Note that it is not updated as soon as xi is assigned, but only
369 // later when this assignment is "processed" by the PbConstraints class.
370 // - 'threshold' is the distance from 'slack' to the largest coefficient ci
371 // smaller or equal to slack. By definition, all the literals with
372 // even larger coefficients that are yet 'processed' must be false for the
373 // constraint to be satisfiable.
375  public:
376  // Takes a pseudo-Boolean formula in canonical form.
378  const std::vector<LiteralWithCoeff>& cst);
379 
380  // Returns true if the given terms are the same as the one in this constraint.
381  bool HasIdenticalTerms(const std::vector<LiteralWithCoeff>& cst);
382  Coefficient Rhs() const { return rhs_; }
383 
384  // Sets the rhs of this constraint. Compute the initial threshold value using
385  // only the literal with a trail index smaller than the given one. Enqueues on
386  // the trail any propagated literals.
387  //
388  // Returns false if the preconditions described in
389  // PbConstraints::AddConstraint() are not meet.
390  bool InitializeRhs(Coefficient rhs, int trail_index, Coefficient* threshold,
391  Trail* trail, PbConstraintsEnqueueHelper* helper);
392 
393  // Tests for propagation and enqueues propagated literals on the trail.
394  // Returns false if a conflict was detected, in which case conflict is filled.
395  //
396  // Preconditions:
397  // - For each "processed" literal, the given threshold value must have been
398  // decreased by its associated coefficient in the constraint. It must now
399  // be stricly negative.
400  // - The given trail_index is the index of a true literal in the trail which
401  // just caused threshold to become stricly negative. All literals with
402  // smaller index must have been "processed". All assigned literals with
403  // greater trail index are not yet "processed".
404  //
405  // The threshold is updated to its new value.
406  bool Propagate(int trail_index, Coefficient* threshold, Trail* trail,
408 
409  // Updates the given threshold and the internal state. This is the opposite of
410  // Propagate(). Each time a literal in unassigned, the threshold value must
411  // have been increased by its coefficient. This update the threshold to its
412  // new value.
413  void Untrail(Coefficient* threshold, int trail_index);
414 
415  // Provided that the literal with given source_trail_index was the one that
416  // propagated the conflict or the literal we wants to explain, then this will
417  // compute the reason.
418  //
419  // Some properties of the reason:
420  // - Literals of level 0 are removed.
421  // - It will always contain the literal with given source_trail_index (except
422  // if it is of level 0).
423  // - We make the reason more compact by greedily removing terms with small
424  // coefficients that would not have changed the propagation.
425  //
426  // TODO(user): Maybe it is possible to derive a better reason by using more
427  // information. For instance one could use the mask of literals that are
428  // better to use during conflict minimization (namely the one already in the
429  // 1-UIP conflict).
430  void FillReason(const Trail& trail, int source_trail_index,
431  BooleanVariable propagated_variable,
432  std::vector<Literal>* reason);
433 
434  // Same operation as SatSolver::ResolvePBConflict(), the only difference is
435  // that here the reason for var is *this.
436  void ResolvePBConflict(const Trail& trail, BooleanVariable var,
438  Coefficient* conflict_slack);
439 
440  // Adds this pb constraint into the given mutable one.
441  //
442  // TODO(user): Provides instead an easy to use iterator over an
443  // UpperBoundedLinearConstraint and move this function to
444  // MutableUpperBoundedLinearConstraint.
446 
447  // Compute the sum of the "cancelation" in AddTerm() if *this is added to
448  // the given conflict. The sum doesn't take into account literal assigned with
449  // a trail index smaller than the given one.
450  //
451  // Note(user): Currently, this is only used in DCHECKs.
452  Coefficient ComputeCancelation(
453  const Trail& trail, int trail_index,
454  const MutableUpperBoundedLinearConstraint& conflict);
455 
456  // API to mark a constraint for deletion before actually deleting it.
457  void MarkForDeletion() { is_marked_for_deletion_ = true; }
458  bool is_marked_for_deletion() const { return is_marked_for_deletion_; }
459 
460  // Only learned constraints are considered for deletion during the constraint
461  // cleanup phase. We also can't delete variables used as a reason.
462  void set_is_learned(bool is_learned) { is_learned_ = is_learned; }
463  bool is_learned() const { return is_learned_; }
464  bool is_used_as_a_reason() const { return first_reason_trail_index_ != -1; }
465 
466  // Activity of the constraint. Only low activity constraint will be deleted
467  // during the constraint cleanup phase.
468  void set_activity(double activity) { activity_ = activity; }
469  double activity() const { return activity_; }
470 
471  // Returns a fingerprint of the constraint linear expression (without rhs).
472  // This is used for duplicate detection.
473  int64_t hash() const { return hash_; }
474 
475  // This is used to get statistics of the number of literals inspected by
476  // a Propagate() call.
477  int already_propagated_end() const { return already_propagated_end_; }
478 
479  private:
480  Coefficient GetSlackFromThreshold(Coefficient threshold) {
481  return (index_ < 0) ? threshold : coeffs_[index_] + threshold;
482  }
483  void Update(Coefficient slack, Coefficient* threshold) {
484  *threshold = (index_ < 0) ? slack : slack - coeffs_[index_];
485  already_propagated_end_ = starts_[index_ + 1];
486  }
487 
488  // Constraint management fields.
489  // TODO(user): Rearrange and specify bit size to minimize memory usage.
490  bool is_marked_for_deletion_;
491  bool is_learned_;
492  int first_reason_trail_index_;
493  double activity_;
494 
495  // Constraint propagation fields.
496  int index_;
497  int already_propagated_end_;
498 
499  // In the internal representation, we merge the terms with the same
500  // coefficient.
501  // - literals_ contains all the literal of the constraint sorted by
502  // increasing coefficients.
503  // - coeffs_ contains unique increasing coefficients.
504  // - starts_[i] is the index in literals_ of the first literal with
505  // coefficient coeffs_[i].
506  std::vector<Coefficient> coeffs_;
507  std::vector<int> starts_;
508  std::vector<Literal> literals_;
509  Coefficient rhs_;
510 
511  int64_t hash_;
512 };
513 
514 // Class responsible for managing a set of pseudo-Boolean constraints and their
515 // propagation.
516 class PbConstraints : public SatPropagator {
517  public:
519  : SatPropagator("PbConstraints"),
520  conflicting_constraint_index_(-1),
521  num_learned_constraint_before_cleanup_(0),
522  constraint_activity_increment_(1.0),
523  parameters_(model->GetOrCreate<SatParameters>()),
524  stats_("PbConstraints"),
525  num_constraint_lookups_(0),
526  num_inspected_constraint_literals_(0),
527  num_threshold_updates_(0) {
528  model->GetOrCreate<Trail>()->RegisterPropagator(this);
529  }
530  ~PbConstraints() override {
532  LOG(INFO) << stats_.StatString();
533  LOG(INFO) << "num_constraint_lookups_: " << num_constraint_lookups_;
534  LOG(INFO) << "num_threshold_updates_: " << num_threshold_updates_;
535  });
536  }
537 
538  bool Propagate(Trail* trail) final;
539  void Untrail(const Trail& trail, int trail_index) final;
540  absl::Span<const Literal> Reason(const Trail& trail,
541  int trail_index) const final;
542 
543  // Changes the number of variables.
544  void Resize(int num_variables) {
545  // Note that we avoid using up memory in the common case where there are no
546  // pb constraints at all. If there is 10 million variables, this vector
547  // alone will take 480 MB!
548  if (!constraints_.empty()) {
549  to_update_.resize(num_variables << 1);
550  enqueue_helper_.reasons.resize(num_variables);
551  }
552  }
553 
554  // Adds a constraint in canonical form to the set of managed constraints. Note
555  // that this detects constraints with exactly the same terms. In this case,
556  // the constraint rhs is updated if the new one is lower or nothing is done
557  // otherwise.
558  //
559  // There are some preconditions, and the function will return false if they
560  // are not met. The constraint can be added when the trail is not empty,
561  // however given the current propagated assignment:
562  // - The constraint cannot be conflicting.
563  // - The constraint cannot have propagated at an earlier decision level.
564  bool AddConstraint(const std::vector<LiteralWithCoeff>& cst, Coefficient rhs,
565  Trail* trail);
566 
567  // Same as AddConstraint(), but also marks the added constraint as learned
568  // so that it can be deleted during the constraint cleanup phase.
569  bool AddLearnedConstraint(const std::vector<LiteralWithCoeff>& cst,
570  Coefficient rhs, Trail* trail);
571 
572  // Returns the number of constraints managed by this class.
573  int NumberOfConstraints() const { return constraints_.size(); }
574 
575  // ConflictingConstraint() returns the last PB constraint that caused a
576  // conflict. Calling ClearConflictingConstraint() reset this to nullptr.
577  //
578  // TODO(user): This is a hack to get the PB conflict, because the rest of
579  // the solver API assume only clause conflict. Find a cleaner way?
580  void ClearConflictingConstraint() { conflicting_constraint_index_ = -1; }
582  if (conflicting_constraint_index_ == -1) return nullptr;
583  return constraints_[conflicting_constraint_index_.value()].get();
584  }
585 
586  // Returns the underlying UpperBoundedLinearConstraint responsible for
587  // assigning the literal at given trail index.
588  UpperBoundedLinearConstraint* ReasonPbConstraint(int trail_index) const;
589 
590  // Activity update functions.
591  // TODO(user): Remove duplication with other activity update functions.
592  void BumpActivity(UpperBoundedLinearConstraint* constraint);
593  void RescaleActivities(double scaling_factor);
595 
596  // Only used for testing.
598  constraints_[index]->MarkForDeletion();
599  DeleteConstraintMarkedForDeletion();
600  }
601 
602  // Some statistics.
603  int64_t num_constraint_lookups() const { return num_constraint_lookups_; }
605  return num_inspected_constraint_literals_;
606  }
607  int64_t num_threshold_updates() const { return num_threshold_updates_; }
608 
609  private:
610  bool PropagateNext(Trail* trail);
611 
612  // Same function as the clause related one is SatSolver().
613  // TODO(user): Remove duplication.
614  void ComputeNewLearnedConstraintLimit();
615  void DeleteSomeLearnedConstraintIfNeeded();
616 
617  // Deletes all the UpperBoundedLinearConstraint for which
618  // is_marked_for_deletion() is true. This is relatively slow in O(number of
619  // terms in all constraints).
620  void DeleteConstraintMarkedForDeletion();
621 
622  // Each constraint managed by this class is associated with an index.
623  // The set of indices is always [0, num_constraints_).
624  //
625  // Note(user): this complicate things during deletion, but the propagation is
626  // about two times faster with this implementation than one with direct
627  // pointer to an UpperBoundedLinearConstraint. The main reason for this is
628  // probably that the thresholds_ vector is a lot more efficient cache-wise.
629  DEFINE_INT_TYPE(ConstraintIndex, int32_t);
630  struct ConstraintIndexWithCoeff {
631  ConstraintIndexWithCoeff() {} // Needed for vector.resize()
632  ConstraintIndexWithCoeff(bool n, ConstraintIndex i, Coefficient c)
633  : need_untrail_inspection(n), index(i), coefficient(c) {}
634  bool need_untrail_inspection;
635  ConstraintIndex index;
636  Coefficient coefficient;
637  };
638 
639  // The set of all pseudo-boolean constraint managed by this class.
640  std::vector<std::unique_ptr<UpperBoundedLinearConstraint>> constraints_;
641 
642  // The current value of the threshold for each constraints.
644 
645  // For each literal, the list of all the constraints that contains it together
646  // with the literal coefficient in these constraints.
648  to_update_;
649 
650  // Bitset used to optimize the Untrail() function.
651  SparseBitset<ConstraintIndex> to_untrail_;
652 
653  // Pointers to the constraints grouped by their hash.
654  // This is used to find duplicate constraints by AddConstraint().
655  absl::flat_hash_map<int64_t, std::vector<UpperBoundedLinearConstraint*>>
656  possible_duplicates_;
657 
658  // Helper to enqueue propagated literals on the trail and store their reasons.
659  PbConstraintsEnqueueHelper enqueue_helper_;
660 
661  // Last conflicting PB constraint index. This is reset to -1 when
662  // ClearConflictingConstraint() is called.
663  ConstraintIndex conflicting_constraint_index_;
664 
665  // Used for the constraint cleaning policy.
666  int target_number_of_learned_constraint_;
667  int num_learned_constraint_before_cleanup_;
668  double constraint_activity_increment_;
669 
670  // Algorithm parameters.
671  SatParameters* parameters_;
672 
673  // Some statistics.
674  mutable StatsGroup stats_;
675  int64_t num_constraint_lookups_;
676  int64_t num_inspected_constraint_literals_;
677  int64_t num_threshold_updates_;
678  DISALLOW_COPY_AND_ASSIGN(PbConstraints);
679 };
680 
681 // Boolean linear constraints can propagate a lot of literals at the same time.
682 // As a result, all these literals will have exactly the same reason. It is
683 // important to take advantage of that during the conflict
684 // computation/minimization. On some problem, this can have a huge impact.
685 //
686 // TODO(user): With the new SAME_REASON_AS mechanism, this is more general so
687 // move out of pb_constraint.
689  public:
691  : trail_(trail) {}
692 
693  void Resize(int num_variables) {
694  first_variable_.resize(num_variables);
695  seen_.ClearAndResize(BooleanVariable(num_variables));
696  }
697 
698  // Clears the cache. Call this before each conflict analysis.
699  void Clear() { seen_.ClearAll(); }
700 
701  // Returns the first variable with exactly the same reason as 'var' on which
702  // this function was called since the last Clear(). Note that if no variable
703  // had the same reason, then var is returned.
704  BooleanVariable FirstVariableWithSameReason(BooleanVariable var) {
705  if (seen_[var]) return first_variable_[var];
706  const BooleanVariable reference_var =
708  if (reference_var == var) return var;
709  if (seen_[reference_var]) return first_variable_[reference_var];
710  seen_.Set(reference_var);
711  first_variable_[reference_var] = var;
712  return var;
713  }
714 
715  private:
716  const Trail& trail_;
719 
720  DISALLOW_COPY_AND_ASSIGN(VariableWithSameReasonIdentifier);
721 };
722 
723 } // namespace sat
724 } // namespace operations_research
725 
726 #endif // OR_TOOLS_SAT_PB_CONSTRAINT_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK_GE(val1, val2)
Definition: base/logging.h:709
#define CHECK_GT(val1, val2)
Definition: base/logging.h:710
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:898
#define LOG(severity)
Definition: base/logging.h:423
void resize(size_type new_size)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:814
void Set(IntegerType index)
Definition: bitset.h:804
void ClearAndResize(IntegerType size)
Definition: bitset.h:779
std::string StatString() const
Definition: stats.cc:71
bool AddLinearConstraint(bool use_lower_bound, Coefficient lower_bound, bool use_upper_bound, Coefficient upper_bound, std::vector< LiteralWithCoeff > *cst)
const std::vector< LiteralWithCoeff > & Constraint(int i) const
LiteralIndex Index() const
Definition: sat_base.h:85
std::string DebugString() const
Definition: sat_base.h:94
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
Coefficient ComputeSlackForTrailPrefix(const Trail &trail, int trail_index) const
Coefficient ReduceCoefficientsAndComputeSlackForTrailPrefix(const Trail &trail, int trail_index)
void ReduceSlackTo(const Trail &trail, int trail_index, Coefficient initial_slack, Coefficient target)
const std::vector< BooleanVariable > & PossibleNonZeros() const
Coefficient CancelationAmount(Literal literal, Coefficient coeff) const
void AddTerm(Literal literal, Coefficient coeff)
void CopyIntoVector(std::vector< LiteralWithCoeff > *output)
Coefficient GetCoefficient(BooleanVariable var) const
void RescaleActivities(double scaling_factor)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
bool AddConstraint(const std::vector< LiteralWithCoeff > &cst, Coefficient rhs, Trail *trail)
UpperBoundedLinearConstraint * ConflictingConstraint()
UpperBoundedLinearConstraint * ReasonPbConstraint(int trail_index) const
void BumpActivity(UpperBoundedLinearConstraint *constraint)
void Untrail(const Trail &trail, int trail_index) final
bool AddLearnedConstraint(const std::vector< LiteralWithCoeff > &cst, Coefficient rhs, Trail *trail)
void Enqueue(Literal true_literal, int propagator_id)
Definition: sat_base.h:251
BooleanVariable ReferenceVarWithSameReason(BooleanVariable var) const
Definition: sat_base.h:561
Coefficient ComputeCancelation(const Trail &trail, int trail_index, const MutableUpperBoundedLinearConstraint &conflict)
bool Propagate(int trail_index, Coefficient *threshold, Trail *trail, PbConstraintsEnqueueHelper *helper)
void FillReason(const Trail &trail, int source_trail_index, BooleanVariable propagated_variable, std::vector< Literal > *reason)
bool HasIdenticalTerms(const std::vector< LiteralWithCoeff > &cst)
void ResolvePBConflict(const Trail &trail, BooleanVariable var, MutableUpperBoundedLinearConstraint *conflict, Coefficient *conflict_slack)
bool InitializeRhs(Coefficient rhs, int trail_index, Coefficient *threshold, Trail *trail, PbConstraintsEnqueueHelper *helper)
void Untrail(Coefficient *threshold, int trail_index)
void AddToConflict(MutableUpperBoundedLinearConstraint *conflict)
UpperBoundedLinearConstraint(const std::vector< LiteralWithCoeff > &cst)
BooleanVariable FirstVariableWithSameReason(BooleanVariable var)
int64_t a
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
double lower_bound
GRBmodel * model
const int INFO
Definition: log_severity.h:31
Coefficient ComputeCanonicalRhs(Coefficient upper_bound, Coefficient bound_shift, Coefficient max_value)
std::ostream & operator<<(std::ostream &os, const BoolVar &var)
Definition: cp_model.cc:68
bool ApplyLiteralMapping(const absl::StrongVector< LiteralIndex, LiteralIndex > &mapping, std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
Coefficient ComputeNegatedCanonicalRhs(Coefficient lower_bound, Coefficient bound_shift, Coefficient max_value)
void SimplifyCanonicalBooleanLinearConstraint(std::vector< LiteralWithCoeff > *cst, Coefficient *rhs)
DEFINE_INT_TYPE(ClauseIndex, int)
bool ComputeBooleanLinearExpressionCanonicalForm(std::vector< LiteralWithCoeff > *cst, Coefficient *bound_shift, Coefficient *max_value)
bool BooleanLinearExpressionIsCanonical(const std::vector< LiteralWithCoeff > &cst)
const Coefficient kCoefficientMax(std::numeric_limits< Coefficient::ValueType >::max())
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:85
int index
Definition: pack.cc:509
int64_t bound
int64_t coefficient
#define IF_STATS_ENABLED(instructions)
Definition: stats.h:437
bool operator==(const LiteralWithCoeff &other) const
Definition: pb_constraint.h:56
LiteralWithCoeff(Literal l, Coefficient c)
Definition: pb_constraint.h:52
void Enqueue(Literal l, int source_trail_index, UpperBoundedLinearConstraint *ct, Trail *trail)