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