OR-Tools  9.6
cuts.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_CUTS_H_
15 #define OR_TOOLS_SAT_CUTS_H_
16 
17 #include <array>
18 #include <functional>
19 #include <limits>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/container/flat_hash_set.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/types/span.h"
30 #include "ortools/sat/integer.h"
33 #include "ortools/sat/model.h"
36 
37 namespace operations_research {
38 namespace sat {
39 
40 // A "cut" generator on a set of IntegerVariable.
41 //
42 // The generate_cuts() function will usually be called with the current LP
43 // optimal solution (but should work for any lp_values). Note that a
44 // CutGenerator should:
45 // - Only look at the lp_values positions that corresponds to its 'vars' or
46 // their negation.
47 // - Only add cuts in term of the same variables or their negation.
48 struct CutGenerator {
49  bool only_run_at_level_zero = false;
50  std::vector<IntegerVariable> vars;
51  std::function<bool(
53  LinearConstraintManager* manager)>
55 };
56 
57 // To simplify cut generation code, we use a more complex data structure than
58 // just a LinearConstraint to represent a cut with shifted/complemented variable
59 // and implied bound substitution.
60 struct CutTerm {
61  bool IsBoolean() const { return bound_diff == 1; }
62  bool IsSimple() const { return expr_coeffs[1] == 0; }
63  bool HasRelevantLpValue() const { return lp_value > 1e-2; }
64  double LpDistToMaxValue() const {
65  return static_cast<double>(bound_diff.value()) - lp_value;
66  }
67 
68  std::string DebugString() const;
69 
70  // Returns false and do nothing if this would cause an overflow.
71  // Otherwise do the subtitution X -> (1 - X') and update the rhs.
72  bool Complement(IntegerValue* rhs);
73 
74  // Each term is of the form coeff * X where X is a variable with given
75  // lp_value and with a domain in [0, bound_diff]. Note X is always >= 0.
76  double lp_value = 0.0;
77  IntegerValue coeff = IntegerValue(0);
78  IntegerValue bound_diff = IntegerValue(0);
79 
80  // X = the given LinearExpression.
81  // We only support size 1 or 2 here which allow to inline the memory.
82  // When a coefficient is zero, we don't care about the variable.
83  IntegerValue expr_offset = IntegerValue(0);
84  std::array<IntegerVariable, 2> expr_vars;
85  std::array<IntegerValue, 2> expr_coeffs;
86 };
87 
88 // Our cut are always of the form linear_expression <= rhs.
89 struct CutData {
90  // We need level zero bounds and LP relaxation values to fill a CutData.
91  // Returns false if we encounter any integer overflow.
93  const LinearConstraint& base_ct,
95  IntegerTrail* integer_trail);
96 
97  bool FillFromParallelVectors(const LinearConstraint& base_ct,
98  const std::vector<double>& lp_values,
99  const std::vector<IntegerValue>& lower_bounds,
100  const std::vector<IntegerValue>& upper_bounds);
101 
102  bool AppendOneTerm(IntegerVariable var, IntegerValue coeff, double lp_value,
103  IntegerValue lb, IntegerValue ub);
104 
105  IntegerValue rhs;
106  std::vector<CutTerm> terms;
107 
108  // This sorts terms and fill both num_relevant_entries and max_magnitude.
109  void Canonicalize();
110  IntegerValue max_magnitude;
112 };
113 
114 // Stores temporaries used to build or manipulate a CutData.
116  public:
117  // These function allow to merges entries corresponding to the same variable
118  // and complementation. That is (X - lb) and (ub - X) are NOT merged and kept
119  // as separate terms. Note that we currently only merge Booleans since this
120  // is the only case we need.
121  void ClearIndices();
122  void AddOrMergeTerm(const CutTerm& term, IntegerValue t, CutData* cut);
123  int NumMergesSinceLastClear() const { return num_merges_; }
124 
125  // Returns false if we encounter an integer overflow.
126  bool ConvertToLinearConstraint(const CutData& cut, LinearConstraint* output);
127 
128  private:
129  void RegisterAllBooleansTerms(const CutData& cut);
130 
131  int num_merges_ = 0;
132  bool constraint_is_indexed_ = false;
133  absl::flat_hash_map<IntegerVariable, int> direct_index_;
134  absl::flat_hash_map<IntegerVariable, int> complemented_index_;
135  absl::btree_map<IntegerVariable, IntegerValue> tmp_map_;
136 };
137 
138 // Given an upper-bounded linear relation (sum terms <= ub), this algorithm
139 // inspects the integer variable appearing in the sum and try to replace each of
140 // them by a tight lower bound (>= coeff * binary + lb) using the implied bound
141 // repository. By tight, we mean that it will take the same value under the
142 // current LP solution.
143 //
144 // We use a class to reuse memory of the tmp terms.
146  public:
147  // We will only replace IntegerVariable appearing in lp_vars_.
148  ImpliedBoundsProcessor(absl::Span<const IntegerVariable> lp_vars_,
149  IntegerTrail* integer_trail,
150  ImpliedBounds* implied_bounds)
151  : lp_vars_(lp_vars_.begin(), lp_vars_.end()),
152  integer_trail_(integer_trail),
153  implied_bounds_(implied_bounds) {}
154 
155  // See if some of the implied bounds equation are violated and add them to
156  // the IB cut pool if it is the case.
157  //
158  // Important: This must be called before we process any constraints with a
159  // different lp_values or level zero bounds.
162 
163  bool TryToExpandWithLowerImpliedbound(IntegerValue factor_t, int i,
164  bool complement, CutData* cut,
165  CutDataBuilder* builder);
166 
167  // Add a new variable that could be used in the new cuts.
168  // Note that the cache must be computed to take this into account.
169  void AddLpVariable(IntegerVariable var) { lp_vars_.insert(var); }
170 
171  // Once RecomputeCacheAndSeparateSomeImpliedBoundCuts() has been called,
172  // we can get the best implied bound for each variables.
173  //
174  // Note that because the variable level zero lower bound might change since
175  // the time this was cached, we just store the implied bound here.
177  double var_lp_value = 0.0;
178  double bool_lp_value = 0.0;
180  IntegerValue implied_bound;
181  IntegerVariable bool_var = kNoIntegerVariable;
182 
183  double SlackLpValue(IntegerValue lb) const {
184  const double bool_term = ToDouble(implied_bound - lb) * bool_lp_value;
185  return var_lp_value - ToDouble(lb) - bool_term;
186  }
187 
188  std::string DebugString() const {
189  return absl::StrCat("var - lb == (", implied_bound.value(),
190  " - lb) * bool(", bool_lp_value, ") + slack.");
191  }
192  };
193  BestImpliedBoundInfo GetCachedImpliedBoundInfo(IntegerVariable var) const;
194 
195  // As we compute the best implied bounds for each variable, we add violated
196  // cuts here.
197  TopNCuts& IbCutPool() { return ib_cut_pool_; }
198 
199  private:
200  BestImpliedBoundInfo ComputeBestImpliedBound(
201  IntegerVariable var,
203 
204  absl::flat_hash_set<IntegerVariable> lp_vars_;
205  mutable absl::flat_hash_map<IntegerVariable, BestImpliedBoundInfo> cache_;
206 
207  TopNCuts ib_cut_pool_ = TopNCuts(50);
208 
209  // Data from the constructor.
210  IntegerTrail* integer_trail_;
211  ImpliedBounds* implied_bounds_;
212 };
213 
214 // A single node flow relaxation is a constraint of the form
215 // Sum in_flow - Sum out_flow <= demand
216 // where each flow variable F_i is in [0, capacity_i] and satisfy
217 // F_i <= capacity_i B_i
218 // with B_i a Boolean representing the arc usage.
219 //
220 // From a generic constraint sum coeff_i X_i <= b, we try to put it in this
221 // format. We can first transform all variables to be in [0, max_value].
222 //
223 // Then we cover different cases:
224 // 1/ A coeff * Boolean, can be easily transformed.
225 // 2/ A coeff * Integer in [0, capacity] with Bool => integer == 0 too.
226 // 3/ For a general integer, we can always use a Bool == 1 for the arc usage.
227 //
228 // TODO(user): cover case 3/. We loose a lot of relaxation here, except if
229 // the variable is at is upper/lower bound.
230 //
231 // TODO(user): Altough the cut should still be correct, we might use the same
232 // Boolean more than once in the implied bound. Or this Boolean might already
233 // appear in the constraint. Not sure if we can do something smarter here.
234 struct FlowInfo {
235  // Flow is always in [0, capacity] with the given current value in the
236  // lp relaxation. Now that we usually only consider tight constraint were
237  // flow_lp_value = capacity * bool_lp_value.
238  IntegerValue capacity;
241 
242  // The definition of the flow variable and the arc usage variable in term
243  // of original problem variables. After we compute a cut on the flow and
244  // usage variable, we can just directly substitute these variable by the
245  // expression here to have a cut in term of the original problem variables.
248 };
249 
251  bool empty() const { return in_flow.empty() && out_flow.empty(); }
252  void clear() {
253  demand = IntegerValue(0);
254  in_flow.clear();
255  out_flow.clear();
256  num_bool = 0;
257  num_to_lb = 0;
258  num_to_ub = 0;
259  }
260  std::string DebugString() const;
261 
262  IntegerValue demand;
263  std::vector<FlowInfo> in_flow;
264  std::vector<FlowInfo> out_flow;
265 
266  // Stats filled during extraction.
267  int num_bool = 0;
268  int num_to_lb = 0;
269  int num_to_ub = 0;
270 };
271 
273  public:
274  // Extract a SingleNodeFlow relaxation from the base_ct and try to generate
275  // a cut from it.
277  const LinearConstraint& base_ct,
279  IntegerTrail* integer_trail, ImpliedBoundsProcessor* ib_helper);
280 
281  // Try to generate a cut for the given single node flow problem. Returns true
282  // if a cut was generated. It can be accessed by cut().
283  bool GenerateCut(const SingleNodeFlow& data);
284 
285  // If successful, info about the last generated cut.
286  const LinearConstraint& cut() const { return cut_; }
287 
288  // Single line of text that we append to the cut log line.
289  std::string Info() const {
290  return absl::StrCat(" slack=", slack_.value(), " #in=", num_in_ignored_,
291  "|", num_in_flow_, "|", num_in_bin_,
292  " #out:", num_out_capa_, "|", num_out_flow_, "|",
293  num_out_bin_);
294  }
295 
296  private:
297  // Try to extract a nice SingleNodeFlow relaxation for the given upper bounded
298  // linear constraint.
299  bool ComputeFlowCoverRelaxation(
300  const LinearConstraint& base_ct,
302  SingleNodeFlow* snf, IntegerTrail* integer_trail,
303  ImpliedBoundsProcessor* ib_helper);
304 
305  // Helpers used by ComputeFlowCoverRelaxation() to convert one linear term.
306  bool TryXminusLB(IntegerVariable var, double lp_value, IntegerValue lb,
307  IntegerValue ub, IntegerValue coeff,
308  ImpliedBoundsProcessor* ib_helper,
309  SingleNodeFlow* result) const;
310  bool TryUBminusX(IntegerVariable var, double lp_value, IntegerValue lb,
311  IntegerValue ub, IntegerValue coeff,
312  ImpliedBoundsProcessor* ib_helper,
313  SingleNodeFlow* result) const;
314 
315  // Temporary memory to avoid reallocating the vector.
316  SingleNodeFlow snf_;
317 
318  // Stats, mainly to debug/investigate the code.
319  IntegerValue slack_;
320  int num_in_ignored_;
321  int num_in_flow_;
322  int num_in_bin_;
323  int num_out_capa_;
324  int num_out_flow_;
325  int num_out_bin_;
326 
327  LinearConstraintBuilder cut_builder_;
328  LinearConstraint cut_;
329 };
330 
331 // Visible for testing. Returns a function f on integers such that:
332 // - f is non-decreasing.
333 // - f is super-additive: f(a) + f(b) <= f(a + b)
334 // - 1 <= f(divisor) <= max_scaling
335 // - For all x, f(x * divisor) = x * f(divisor)
336 // - For all x, f(x * divisor + remainder) = x * f(divisor)
337 //
338 // Preconditions:
339 // - 0 <= remainder < divisor.
340 // - 1 <= max_scaling.
341 //
342 // This is used in IntegerRoundingCut() and is responsible for "strengthening"
343 // the cut. Just taking f(x) = x / divisor result in the non-strengthened cut
344 // and using any function that stricly dominate this one is better.
345 //
346 // Algorithm:
347 // - We first scale by a factor t so that rhs_remainder >= divisor / 2.
348 // - Then, if max_scaling == 2, we use the function described
349 // in "Strenghtening Chvatal-Gomory cuts and Gomory fractional cuts", Adam N.
350 // Letchfrod, Andrea Lodi.
351 // - Otherwise, we use a generalization of this which is a discretized version
352 // of the classical MIR rounding function that only take the value of the
353 // form "an_integer / max_scaling". As max_scaling goes to infinity, this
354 // converge to the real-valued MIR function.
355 //
356 // Note that for each value of max_scaling we will get a different function.
357 // And that there is no dominance relation between any of these functions. So
358 // it could be nice to try to generate a cut using different values of
359 // max_scaling.
360 IntegerValue GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor,
361  IntegerValue max_magnitude);
362 std::function<IntegerValue(IntegerValue)> GetSuperAdditiveRoundingFunction(
363  IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t,
364  IntegerValue max_scaling);
365 
366 // Given an upper bounded linear constraint, this function tries to transform it
367 // to a valid cut that violate the given LP solution using integer rounding.
368 // Note that the returned cut might not always violate the LP solution, in which
369 // case it can be discarded.
370 //
371 // What this does is basically take the integer division of the constraint by an
372 // integer. If the coefficients where doubles, this would be the same as scaling
373 // the constraint and then rounding. We choose the coefficient of the most
374 // fractional variable (rescaled by its coefficient) as the divisor, but there
375 // are other possible alternatives.
376 //
377 // Note that if the constraint is tight under the given lp solution, and if
378 // there is a unique variable not at one of its bounds and fractional, then we
379 // are guaranteed to generate a cut that violate the current LP solution. This
380 // should be the case for Chvatal-Gomory base constraints modulo our loss of
381 // precision while doing exact integer computations.
382 //
383 // Precondition:
384 // - We assumes that the given initial constraint is tight using the given lp
385 // values. This could be relaxed, but for now it should always be the case, so
386 // we log a message and abort if not, to ease debugging.
387 // - The IntegerVariable of the cuts are not used here. We assumes that the
388 // first three vectors are in one to one correspondence with the initial order
389 // of the variable in the cut.
390 //
391 // TODO(user): There is a bunch of heuristic involved here, and we could spend
392 // more effort tuning them. In particular, one can try many heuristics and keep
393 // the best looking cut (or more than one). This is not on the critical code
394 // path, so we can spend more effort in finding good cuts.
396  IntegerValue max_scaling = IntegerValue(60);
398  bool prefer_positive_ib = true;
399 };
401  public:
403 
404  // Returns true on success. The cut can be accessed via cut().
405  bool ComputeCut(RoundingOptions options, const CutData& base_ct,
406  ImpliedBoundsProcessor* ib_processor = nullptr);
407 
408  // If successful, info about the last generated cut.
409  const LinearConstraint& cut() const { return cut_; }
410 
411  void SetSharedStatistics(SharedStatistics* stats) { shared_stats_ = stats; }
412 
413  // Single line of text that we append to the cut log line.
414  std::string Info() const { return absl::StrCat("ib_lift=", num_ib_used_); }
415 
416  private:
417  bool HasComplementedImpliedBound(const CutTerm& entry,
418  ImpliedBoundsProcessor* ib_processor);
419 
420  double GetScaledViolation(IntegerValue divisor, IntegerValue max_scaling,
421  IntegerValue remainder_threshold,
422  const CutData& cut);
423 
424  // The helper is just here to reuse the memory for these vectors.
425  std::vector<IntegerValue> divisors_;
426  std::vector<IntegerValue> remainders_;
427  std::vector<IntegerValue> rs_;
428  std::vector<IntegerValue> best_rs_;
429 
430  int64_t num_ib_used_ = 0;
431  CutData best_cut_;
432  CutDataBuilder cut_builder_;
433  LinearConstraint cut_;
434 
435  std::vector<std::pair<int, IntegerValue>> adjusted_coeffs_;
436  std::vector<std::pair<int, IntegerValue>> best_adjusted_coeffs_;
437 
438  // Overall stats.
439  SharedStatistics* shared_stats_ = nullptr;
440  int64_t total_num_dominating_f_ = 0;
441  int64_t total_num_pos_lifts_ = 0;
442  int64_t total_num_neg_lifts_ = 0;
443  int64_t total_num_post_complements_ = 0;
444  int64_t total_num_overflow_abort_ = 0;
445  int64_t total_num_coeff_adjust_ = 0;
446  int64_t total_num_merges_ = 0;
447  int64_t total_num_bumps_ = 0;
448  int64_t total_num_final_complements_ = 0;
449 
450  int64_t total_num_initial_ibs_ = 0;
451  int64_t total_num_initial_merges_ = 0;
452 };
453 
454 // Helper to find knapsack cover cuts.
456  public:
457  ~CoverCutHelper();
458 
459  // Complements term to make sure all coeff are positive, returns false on
460  // overflow.
461  //
462  // Important: This must be called on the input of both Try*() functions. It
463  // is separated as an optimization to share the loop rather than do it in
464  // both functions.
466 
467  // Try to find a cut with a knapsack heuristic.
468  // If this returns true, you can get the cut via cut().
469  bool TrySimpleKnapsack(const CutData& input,
470  ImpliedBoundsProcessor* ib_processor = nullptr);
471 
472  // Applies the lifting procedure described in "On Lifted Cover Inequalities: A
473  // New Lifting Procedure with Unusual Properties", Adam N. Letchford, Georgia
474  // Souli.
475  //
476  // The algo is pretty simple, given a cover C for a given rhs. We compute
477  // a rational weight p/q so that sum_C min(w_i, p/q) = rhs. Note that q is
478  // pretty small (lower or equal to the size of C). The generated cut is then
479  // of the form
480  // sum X_i in C for which w_i <= p / q
481  // + sum gamma_i X_i for the other variable <= |C| - 1.
482  //
483  // gamma_i being the smallest k such that w_i <= sum of the k + 1 largest
484  // min(w_i, p/q) for i in C. In particular, it is zero if w_i <= p/q.
485  //
486  // Note that this accept a general constraint that has been canonicalized to
487  // sum coeff_i * X_i <= base_rhs. Each coeff_i >= 0 and each X_i >= 0.
488  //
489  // TODO(user): Generalize to non-Boolean, or use a different cover heuristic
490  // for this:
491  // - We want a Boolean only cover currently.
492  // - We can always use implied bound for this, since there is more chance
493  // for a Bool only cover.
494  // - Also, f() should be super additive on the value <= rhs, i.e. f(a + b) >=
495  // f(a) + f(b), so it is always good to use implied bounds of the form X =
496  // bound * B + Slack.
498  const CutData& input, ImpliedBoundsProcessor* ib_processor = nullptr);
499 
500  // If successful, info about the last generated cut.
501  const LinearConstraint& cut() const { return cut_; }
502 
503  // Single line of text that we append to the cut log line.
504  std::string Info() const { return absl::StrCat("lift=", num_lifting_); }
505 
506  void SetSharedStatistics(SharedStatistics* stats) { shared_stats_ = stats; }
507 
508  private:
509  // This looks at base_ct_ and reoder the terms so that the first ones are in
510  // the cover. return zero if no interesting cover was found.
511  int GetCoverSize(int relevant_size, IntegerValue* rhs);
512 
513  // Here to reuse memory.
514  CutData base_ct_;
515  CutData temp_cut_;
516  CutDataBuilder cut_builder_;
517 
518  // Stats.
519  SharedStatistics* shared_stats_ = nullptr;
520  int64_t num_lifting_ = 0;
521 
522  int64_t total_num_lifting_ = 0;
523  int64_t total_num_ibs_ = 0;
524  int64_t total_num_overflow_abort_ = 0;
525 
526  // Stores the cut for output.
527  LinearConstraint cut_;
528 };
529 
530 // A cut generator for z = x * y (x and y >= 0).
531 CutGenerator CreatePositiveMultiplicationCutGenerator(AffineExpression z,
532  AffineExpression x,
533  AffineExpression y,
534  int linearization_level,
535  Model* model);
536 
537 // Above hyperplan for square = x * x: square should be below the line
538 // (x_lb, x_lb ^ 2) to (x_ub, x_ub ^ 2).
539 // The slope of that line is (ub^2 - lb^2) / (ub - lb) = ub + lb.
540 // square <= (x_lb + x_ub) * x - x_lb * x_ub
541 // This only works for positive x.
542 LinearConstraint ComputeHyperplanAboveSquare(AffineExpression x,
543  AffineExpression square,
544  IntegerValue x_lb,
545  IntegerValue x_ub, Model* model);
546 
547 // Below hyperplan for square = x * x: y should be above the line
548 // (x_value, x_value ^ 2) to (x_value + 1, (x_value + 1) ^ 2)
549 // The slope of that line is 2 * x_value + 1
550 // square >= below_slope * (x - x_value) + x_value ^ 2
551 // square >= below_slope * x - x_value ^ 2 - x_value
552 LinearConstraint ComputeHyperplanBelowSquare(AffineExpression x,
553  AffineExpression square,
554  IntegerValue x_value,
555  Model* model);
556 
557 // A cut generator for y = x ^ 2 (x >= 0).
558 // It will dynamically add a linear inequality to push y closer to the parabola.
559 CutGenerator CreateSquareCutGenerator(AffineExpression y, AffineExpression x,
560  int linearization_level, Model* model);
561 
562 // A cut generator for all_diff(xi). Let the united domain of all xi be D. Sum
563 // of any k-sized subset of xi need to be greater or equal to the sum of
564 // smallest k values in D and lesser or equal to the sum of largest k values in
565 // D. The cut generator first sorts the variables based on LP values and adds
566 // cuts of the form described above if they are violated by lp solution. Note
567 // that all the fixed variables are ignored while generating cuts.
568 CutGenerator CreateAllDifferentCutGenerator(
569  const std::vector<AffineExpression>& exprs, Model* model);
570 
571 // Consider the Lin Max constraint with d expressions and n variables in the
572 // form: target = max {exprs[k] = Sum (wki * xi + bk)}. k in {1,..,d}.
573 // Li = lower bound of xi
574 // Ui = upper bound of xi.
575 // Let zk be in {0,1} for all k in {1,..,d}.
576 // The target = exprs[k] when zk = 1.
577 //
578 // The following is a valid linearization for Lin Max.
579 // target >= exprs[k], for all k in {1,..,d}
580 // target <= Sum (wli * xi) + Sum((Nlk + bk) * zk), for all l in {1,..,d}
581 // Where Nlk is a large number defined as:
582 // Nlk = Sum (max((wki - wli)*Li, (wki - wli)*Ui))
583 // = Sum (max corner difference for variable i, target expr l, max expr k)
584 //
585 // Consider a partition of variables xi into set {1,..,d} as I.
586 // i.e. I(i) = j means xi is mapped to jth index.
587 // The following inequality is valid and sharp cut for the lin max constraint
588 // described above.
589 //
590 // target <= Sum(i=1..n)(wI(i)i * xi + Sum(k=1..d)(MPlusCoefficient_ki * zk))
591 // + Sum(k=1..d)(bk * zk) ,
592 // Where MPlusCoefficient_ki = max((wki - wI(i)i) * Li,
593 // (wki - wI(i)i) * Ui)
594 // = max corner difference for variable i,
595 // target expr I(i), max expr k.
596 //
597 // For detailed proof of validity, refer
598 // Reference: "Strong mixed-integer programming formulations for trained neural
599 // networks" by Ross Anderson et. (https://arxiv.org/pdf/1811.01988.pdf).
600 //
601 // In the cut generator, we compute the most violated partition I by computing
602 // the rhs value (wI(i)i * lp_value(xi) + Sum(k=1..d)(MPlusCoefficient_ki * zk))
603 // for each variable for each partition index. We choose the partition index
604 // that gives lowest rhs value for a given variable.
605 //
606 // Note: This cut generator requires all expressions to contain only positive
607 // vars.
608 CutGenerator CreateLinMaxCutGenerator(
609  IntegerVariable target, const std::vector<LinearExpression>& exprs,
610  const std::vector<IntegerVariable>& z_vars, Model* model);
611 
612 // Helper for the affine max constraint.
613 //
614 // This function will reset the bounds of the builder.
616  const LinearExpression& target, IntegerVariable var,
617  const std::vector<std::pair<IntegerValue, IntegerValue>>& affines,
618  Model* model, LinearConstraintBuilder* builder);
619 
620 // By definition, the Max of affine functions is convex. The linear polytope is
621 // bounded by all affine functions on the bottom, and by a single hyperplane
622 // that join the two points at the extreme of the var domain, and their y-values
623 // of the max of the affine functions.
624 CutGenerator CreateMaxAffineCutGenerator(
625  LinearExpression target, IntegerVariable var,
626  std::vector<std::pair<IntegerValue, IntegerValue>> affines,
627  std::string cut_name, Model* model);
628 
629 // Extracts the variables that have a Literal view from base variables and
630 // create a generator that will returns constraint of the form "at_most_one"
631 // between such literals.
632 CutGenerator CreateCliqueCutGenerator(
633  const std::vector<IntegerVariable>& base_variables, Model* model);
634 
635 // Utility class for the AllDiff cut generator.
637  public:
638  void Clear();
639  void Add(const AffineExpression& expr, int num_expr,
640  const IntegerTrail& integer_trail);
641 
642  IntegerValue SumOfMinDomainValues();
643  IntegerValue SumOfDifferentMins();
644  IntegerValue GetBestLowerBound(std::string& suffix);
645 
646  int size() const { return expr_mins_.size(); }
647 
648  private:
649  absl::btree_set<IntegerValue> min_values_;
650  std::vector<IntegerValue> expr_mins_;
651 };
652 
653 } // namespace sat
654 } // namespace operations_research
655 
656 #endif // OR_TOOLS_SAT_CUTS_H_
const LinearConstraint & cut() const
Definition: cuts.h:501
bool TrySimpleKnapsack(const CutData &input, ImpliedBoundsProcessor *ib_processor=nullptr)
Definition: cuts.cc:1059
bool TryWithLetchfordSouliLifting(const CutData &input, ImpliedBoundsProcessor *ib_processor=nullptr)
Definition: cuts.cc:1168
void SetSharedStatistics(SharedStatistics *stats)
Definition: cuts.h:506
bool MakeAllTermsPositive(CutData *cut)
Definition: cuts.cc:1041
bool ConvertToLinearConstraint(const CutData &cut, LinearConstraint *output)
Definition: cuts.cc:235
void AddOrMergeTerm(const CutTerm &term, IntegerValue t, CutData *cut)
Definition: cuts.cc:201
const LinearConstraint & cut() const
Definition: cuts.h:286
bool ComputeFlowCoverRelaxationAndGenerateCut(const LinearConstraint &base_ct, const absl::StrongVector< IntegerVariable, double > &lp_values, IntegerTrail *integer_trail, ImpliedBoundsProcessor *ib_helper)
Definition: cuts.cc:1726
bool GenerateCut(const SingleNodeFlow &data)
Definition: cuts.cc:1824
void AddLpVariable(IntegerVariable var)
Definition: cuts.h:169
BestImpliedBoundInfo GetCachedImpliedBoundInfo(IntegerVariable var) const
Definition: cuts.cc:1455
bool TryToExpandWithLowerImpliedbound(IntegerValue factor_t, int i, bool complement, CutData *cut, CutDataBuilder *builder)
Definition: cuts.cc:1546
void RecomputeCacheAndSeparateSomeImpliedBoundCuts(const absl::StrongVector< IntegerVariable, double > &lp_values)
Definition: cuts.cc:1535
ImpliedBoundsProcessor(absl::Span< const IntegerVariable > lp_vars_, IntegerTrail *integer_trail, ImpliedBounds *implied_bounds)
Definition: cuts.h:148
const LinearConstraint & cut() const
Definition: cuts.h:409
void SetSharedStatistics(SharedStatistics *stats)
Definition: cuts.h:411
bool ComputeCut(RoundingOptions options, const CutData &base_ct, ImpliedBoundsProcessor *ib_processor=nullptr)
Definition: cuts.cc:542
IntegerValue GetBestLowerBound(std::string &suffix)
Definition: cuts.cc:2015
void Add(const AffineExpression &expr, int num_expr, const IntegerTrail &integer_trail)
Definition: cuts.cc:1968
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
LinearConstraint ComputeHyperplanBelowSquare(AffineExpression x, AffineExpression square, IntegerValue x_value, Model *model)
Definition: cuts.cc:1405
CutGenerator CreateAllDifferentCutGenerator(const std::vector< AffineExpression > &exprs, Model *model)
Definition: cuts.cc:2075
const IntegerVariable kNoIntegerVariable(-1)
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue >> affines, const std::string cut_name, Model *model)
Definition: cuts.cc:2309
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
Definition: cuts.cc:2187
CutGenerator CreatePositiveMultiplicationCutGenerator(AffineExpression z, AffineExpression x, AffineExpression y, int linearization_level, Model *model)
Definition: cuts.cc:1295
bool BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, const std::vector< std::pair< IntegerValue, IntegerValue >> &affines, Model *model, LinearConstraintBuilder *builder)
Definition: cuts.cc:2271
IntegerValue GetFactorT(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue max_magnitude)
Definition: cuts.cc:292
std::function< IntegerValue(IntegerValue)> GetSuperAdditiveRoundingFunction(IntegerValue rhs_remainder, IntegerValue divisor, IntegerValue t, IntegerValue max_scaling)
Definition: cuts.cc:306
CutGenerator CreateSquareCutGenerator(AffineExpression y, AffineExpression x, int linearization_level, Model *model)
Definition: cuts.cc:1417
LinearConstraint ComputeHyperplanAboveSquare(AffineExpression x, AffineExpression square, IntegerValue x_lb, IntegerValue x_ub, Model *model)
Definition: cuts.cc:1393
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
Definition: cuts.cc:2333
double ToDouble(IntegerValue value)
Definition: integer.h:77
Collection of objects used to extend the Constraint Solver library.
static int input(yyscan_t yyscanner)
std::vector< double > lower_bounds
std::vector< double > upper_bounds
std::optional< int64_t > end
std::vector< CutTerm > terms
Definition: cuts.h:106
bool FillFromLinearConstraint(const LinearConstraint &base_ct, const absl::StrongVector< IntegerVariable, double > &lp_values, IntegerTrail *integer_trail)
Definition: cuts.cc:116
bool FillFromParallelVectors(const LinearConstraint &base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
Definition: cuts.cc:134
bool AppendOneTerm(IntegerVariable var, IntegerValue coeff, double lp_value, IntegerValue lb, IntegerValue ub)
Definition: cuts.cc:73
std::vector< IntegerVariable > vars
Definition: cuts.h:50
std::function< bool(const absl::StrongVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
Definition: cuts.h:54
double LpDistToMaxValue() const
Definition: cuts.h:64
bool HasRelevantLpValue() const
Definition: cuts.h:63
std::string DebugString() const
Definition: cuts.cc:48
std::array< IntegerVariable, 2 > expr_vars
Definition: cuts.h:84
bool Complement(IntegerValue *rhs)
Definition: cuts.cc:53
std::array< IntegerValue, 2 > expr_coeffs
Definition: cuts.h:85
AffineExpression flow_expr
Definition: cuts.h:246
AffineExpression bool_expr
Definition: cuts.h:247
std::vector< FlowInfo > out_flow
Definition: cuts.h:264
std::vector< FlowInfo > in_flow
Definition: cuts.h:263