OR-Tools  8.0
linear_relaxation.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 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 
15 
16 #include <vector>
17 
18 #include "absl/container/flat_hash_set.h"
20 #include "ortools/base/stl_util.h"
23 #include "ortools/sat/integer.h"
27 #include "ortools/sat/sat_base.h"
28 
29 namespace operations_research {
30 namespace sat {
31 
32 bool AppendFullEncodingRelaxation(IntegerVariable var, const Model& model,
33  LinearRelaxation* relaxation) {
34  const auto* encoder = model.Get<IntegerEncoder>();
35  if (encoder == nullptr) return false;
36  if (!encoder->VariableIsFullyEncoded(var)) return false;
37 
38  const auto& encoding = encoder->FullDomainEncoding(var);
39  const IntegerValue var_min = model.Get<IntegerTrail>()->LowerBound(var);
40 
41  LinearConstraintBuilder at_least_one(&model, IntegerValue(1),
43  LinearConstraintBuilder encoding_ct(&model, var_min, var_min);
44  encoding_ct.AddTerm(var, IntegerValue(1));
45 
46  // Create the constraint if all literal have a view.
47  std::vector<Literal> at_most_one;
48 
49  for (const auto value_literal : encoding) {
50  const Literal lit = value_literal.literal;
51  const IntegerValue delta = value_literal.value - var_min;
52  DCHECK_GE(delta, IntegerValue(0));
53  at_most_one.push_back(lit);
54  if (!at_least_one.AddLiteralTerm(lit, IntegerValue(1))) return false;
55  if (delta != IntegerValue(0)) {
56  if (!encoding_ct.AddLiteralTerm(lit, -delta)) return false;
57  }
58  }
59 
60  relaxation->linear_constraints.push_back(at_least_one.Build());
61  relaxation->linear_constraints.push_back(encoding_ct.Build());
62  relaxation->at_most_ones.push_back(at_most_one);
63  return true;
64 }
65 
66 namespace {
67 
68 // TODO(user): Not super efficient.
69 std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
70  IntegerVariable var,
71  const absl::flat_hash_set<IntegerValue>& encoded_values,
72  const Model& model) {
73  const auto* domains = model.Get<IntegerDomains>();
74  if (domains == nullptr || var >= domains->size()) {
76  }
77 
78  // The domain can be large, but the list of values shouldn't, so this
79  // runs in O(encoded_values.size());
80  IntegerValue min = kMaxIntegerValue;
81  for (const ClosedInterval interval : (*domains)[var]) {
82  for (IntegerValue v(interval.start); v <= interval.end; ++v) {
83  if (!gtl::ContainsKey(encoded_values, v)) {
84  min = v;
85  break;
86  }
87  }
88  if (min != kMaxIntegerValue) break;
89  }
90 
91  IntegerValue max = kMinIntegerValue;
92  const auto& domain = (*domains)[var];
93  for (int i = domain.NumIntervals() - 1; i >= 0; --i) {
94  const ClosedInterval interval = domain[i];
95  for (IntegerValue v(interval.end); v >= interval.start; --v) {
96  if (!gtl::ContainsKey(encoded_values, v)) {
97  max = v;
98  break;
99  }
100  }
101  if (max != kMinIntegerValue) break;
102  }
103 
104  return {min, max};
105 }
106 
107 } // namespace
108 
109 void AppendPartialEncodingRelaxation(IntegerVariable var, const Model& model,
110  LinearRelaxation* relaxation) {
111  const auto* encoder = model.Get<IntegerEncoder>();
112  const auto* integer_trail = model.Get<IntegerTrail>();
113  if (encoder == nullptr || integer_trail == nullptr) return;
114 
115  const std::vector<IntegerEncoder::ValueLiteralPair>& encoding =
116  encoder->PartialDomainEncoding(var);
117  if (encoding.empty()) return;
118 
119  std::vector<Literal> at_most_one_ct;
120  absl::flat_hash_set<IntegerValue> encoded_values;
121  for (const auto value_literal : encoding) {
122  const Literal literal = value_literal.literal;
123 
124  // Note that we skip pairs that do not have an Integer view.
125  if (encoder->GetLiteralView(literal) == kNoIntegerVariable &&
126  encoder->GetLiteralView(literal.Negated()) == kNoIntegerVariable) {
127  continue;
128  }
129 
130  at_most_one_ct.push_back(literal);
131  encoded_values.insert(value_literal.value);
132  }
133  if (encoded_values.empty()) return;
134 
135  // TODO(user): The PartialDomainEncoding() function automatically exclude
136  // values that are no longer in the initial domain, so we could be a bit
137  // tighter here. That said, this is supposed to be called just after the
138  // presolve, so it shouldn't really matter.
139  const auto pair = GetMinAndMaxNotEncoded(var, encoded_values, model);
140  if (pair.first == kMaxIntegerValue) {
141  // TODO(user): try to remove the duplication with
142  // AppendFullEncodingRelaxation()? actually I am not sure we need the other
143  // function since this one is just more general.
144  LinearConstraintBuilder exactly_one_ct(&model, IntegerValue(1),
145  IntegerValue(1));
146  LinearConstraintBuilder encoding_ct(&model, IntegerValue(0),
147  IntegerValue(0));
148  encoding_ct.AddTerm(var, IntegerValue(1));
149  for (const auto value_literal : encoding) {
150  const Literal lit = value_literal.literal;
151  CHECK(exactly_one_ct.AddLiteralTerm(lit, IntegerValue(1)));
152  CHECK(
153  encoding_ct.AddLiteralTerm(lit, IntegerValue(-value_literal.value)));
154  }
155  relaxation->linear_constraints.push_back(exactly_one_ct.Build());
156  relaxation->linear_constraints.push_back(encoding_ct.Build());
157  return;
158  }
159 
160  // min + sum li * (xi - min) <= var.
161  const IntegerValue d_min = pair.first;
162  LinearConstraintBuilder lower_bound_ct(&model, d_min, kMaxIntegerValue);
163  lower_bound_ct.AddTerm(var, IntegerValue(1));
164  for (const auto value_literal : encoding) {
165  CHECK(lower_bound_ct.AddLiteralTerm(value_literal.literal,
166  d_min - value_literal.value));
167  }
168 
169  // var <= max + sum li * (xi - max).
170  const IntegerValue d_max = pair.second;
171  LinearConstraintBuilder upper_bound_ct(&model, kMinIntegerValue, d_max);
172  upper_bound_ct.AddTerm(var, IntegerValue(1));
173  for (const auto value_literal : encoding) {
174  CHECK(upper_bound_ct.AddLiteralTerm(value_literal.literal,
175  d_max - value_literal.value));
176  }
177 
178  // Note that empty/trivial constraints will be filtered later.
179  relaxation->at_most_ones.push_back(at_most_one_ct);
180  relaxation->linear_constraints.push_back(lower_bound_ct.Build());
181  relaxation->linear_constraints.push_back(upper_bound_ct.Build());
182 }
183 
185  const Model& model,
186  LinearRelaxation* relaxation) {
187  const auto* integer_trail = model.Get<IntegerTrail>();
188  const auto* encoder = model.Get<IntegerEncoder>();
189  if (integer_trail == nullptr || encoder == nullptr) return;
190 
191  const std::map<IntegerValue, Literal>& greater_than_encoding =
192  encoder->PartialGreaterThanEncoding(var);
193  if (greater_than_encoding.empty()) return;
194 
195  // Start by the var >= side.
196  // And also add the implications between used literals.
197  {
198  IntegerValue prev_used_bound = integer_trail->LowerBound(var);
199  LinearConstraintBuilder lb_constraint(&model, prev_used_bound,
201  lb_constraint.AddTerm(var, IntegerValue(1));
202  LiteralIndex prev_literal_index = kNoLiteralIndex;
203  for (const auto entry : greater_than_encoding) {
204  if (entry.first <= prev_used_bound) continue;
205 
206  const LiteralIndex literal_index = entry.second.Index();
207  const IntegerValue diff = prev_used_bound - entry.first;
208 
209  // Skip the entry if the literal doesn't have a view.
210  if (!lb_constraint.AddLiteralTerm(entry.second, diff)) continue;
211  if (prev_literal_index != kNoLiteralIndex) {
212  // Add var <= prev_var, which is the same as var + not(prev_var) <= 1
213  relaxation->at_most_ones.push_back(
214  {Literal(literal_index), Literal(prev_literal_index).Negated()});
215  }
216  prev_used_bound = entry.first;
217  prev_literal_index = literal_index;
218  }
219  relaxation->linear_constraints.push_back(lb_constraint.Build());
220  }
221 
222  // Do the same for the var <= side by using NegationOfVar().
223  // Note that we do not need to add the implications between literals again.
224  {
225  IntegerValue prev_used_bound = integer_trail->LowerBound(NegationOf(var));
226  LinearConstraintBuilder lb_constraint(&model, prev_used_bound,
228  lb_constraint.AddTerm(var, IntegerValue(-1));
229  for (const auto entry :
230  encoder->PartialGreaterThanEncoding(NegationOf(var))) {
231  if (entry.first <= prev_used_bound) continue;
232  const IntegerValue diff = prev_used_bound - entry.first;
233 
234  // Skip the entry if the literal doesn't have a view.
235  if (!lb_constraint.AddLiteralTerm(entry.second, diff)) continue;
236  prev_used_bound = entry.first;
237  }
238  relaxation->linear_constraints.push_back(lb_constraint.Build());
239  }
240 }
241 
242 namespace {
243 // Adds enforcing_lit => target <= bounding_var to relaxation.
244 void AppendEnforcedUpperBound(const Literal enforcing_lit,
245  const IntegerVariable target,
246  const IntegerVariable bounding_var, Model* model,
247  LinearRelaxation* relaxation) {
248  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
249  const IntegerValue max_target_value = integer_trail->UpperBound(target);
250  const IntegerValue min_var_value = integer_trail->LowerBound(bounding_var);
251  const IntegerValue max_term_value = max_target_value - min_var_value;
252  LinearConstraintBuilder lc(model, kMinIntegerValue, max_term_value);
253  lc.AddTerm(target, IntegerValue(1));
254  lc.AddTerm(bounding_var, IntegerValue(-1));
255  CHECK(lc.AddLiteralTerm(enforcing_lit, max_term_value));
256  relaxation->linear_constraints.push_back(lc.Build());
257 }
258 
259 // Adds {enforcing_lits} => rhs_domain_min <= expr <= rhs_domain_max.
260 // Requires expr offset to be 0.
261 void AppendEnforcedLinearExpression(
262  const std::vector<Literal>& enforcing_literals,
263  const LinearExpression& expr, const IntegerValue rhs_domain_min,
264  const IntegerValue rhs_domain_max, const Model& model,
265  LinearRelaxation* relaxation) {
266  CHECK_EQ(expr.offset, IntegerValue(0));
267  const LinearExpression canonical_expr = CanonicalizeExpr(expr);
268  const IntegerTrail* integer_trail = model.Get<IntegerTrail>();
269  const IntegerValue min_expr_value =
270  LinExprLowerBound(canonical_expr, *integer_trail);
271 
272  if (rhs_domain_min > min_expr_value) {
273  // And(ei) => terms >= rhs_domain_min
274  // <=> Sum_i (~ei * (rhs_domain_min - min_expr_value)) + terms >=
275  // rhs_domain_min
276  LinearConstraintBuilder lc(&model, rhs_domain_min, kMaxIntegerValue);
277  for (const Literal& literal : enforcing_literals) {
278  CHECK(lc.AddLiteralTerm(literal.Negated(),
279  rhs_domain_min - min_expr_value));
280  }
281  for (int i = 0; i < canonical_expr.vars.size(); i++) {
282  lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
283  }
284  relaxation->linear_constraints.push_back(lc.Build());
285  }
286  const IntegerValue max_expr_value =
287  LinExprUpperBound(canonical_expr, *integer_trail);
288  if (rhs_domain_max < max_expr_value) {
289  // And(ei) => terms <= rhs_domain_max
290  // <=> Sum_i (~ei * (rhs_domain_max - max_expr_value)) + terms <=
291  // rhs_domain_max
292  LinearConstraintBuilder lc(&model, kMinIntegerValue, rhs_domain_max);
293  for (const Literal& literal : enforcing_literals) {
294  CHECK(lc.AddLiteralTerm(literal.Negated(),
295  rhs_domain_max - max_expr_value));
296  }
297  for (int i = 0; i < canonical_expr.vars.size(); i++) {
298  lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
299  }
300  relaxation->linear_constraints.push_back(lc.Build());
301  }
302 }
303 
304 } // namespace
305 
306 // Add a linear relaxation of the CP constraint to the set of linear
307 // constraints. The highest linearization_level is, the more types of constraint
308 // we encode. This method should be called only for linearization_level > 0.
309 //
310 // Note: IntProd is linearized dynamically using the cut generators.
311 //
312 // TODO(user): In full generality, we could encode all the constraint as an LP.
313 // TODO(user,user): Add unit tests for this method.
314 void TryToLinearizeConstraint(const CpModelProto& model_proto,
315  const ConstraintProto& ct, Model* model,
316  int linearization_level,
317  LinearRelaxation* relaxation) {
318  CHECK_EQ(model->GetOrCreate<SatSolver>()->CurrentDecisionLevel(), 0);
319  DCHECK_GT(linearization_level, 0);
320  auto* mapping = model->GetOrCreate<CpModelMapping>();
321  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
322  if (linearization_level < 2) return;
323  LinearConstraintBuilder lc(model, IntegerValue(1), kMaxIntegerValue);
324  for (const int enforcement_ref : ct.enforcement_literal()) {
325  CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)),
326  IntegerValue(1)));
327  }
328  for (const int ref : ct.bool_or().literals()) {
329  CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
330  }
331  relaxation->linear_constraints.push_back(lc.Build());
332  } else if (ct.constraint_case() ==
333  ConstraintProto::ConstraintCase::kBoolAnd) {
334  // TODO(user): These constraints can be many, and if they are not regrouped
335  // in big at most ones, then they should probably only added lazily as cuts.
336  // Regroup this with future clique-cut separation logic.
337  if (linearization_level < 2) return;
338  if (!HasEnforcementLiteral(ct)) return;
339  if (ct.enforcement_literal().size() == 1) {
340  const Literal enforcement = mapping->Literal(ct.enforcement_literal(0));
341  for (const int ref : ct.bool_and().literals()) {
342  relaxation->at_most_ones.push_back(
343  {enforcement, mapping->Literal(ref).Negated()});
344  }
345  return;
346  }
347 
348  // Andi(e_i) => Andj(x_j)
349  // <=> num_rhs_terms <= Sum_j(x_j) + num_rhs_terms * Sum_i(~e_i)
350  int num_literals = ct.bool_and().literals_size();
351  LinearConstraintBuilder lc(model, IntegerValue(num_literals),
353  for (const int ref : ct.bool_and().literals()) {
354  CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
355  }
356  for (const int enforcement_ref : ct.enforcement_literal()) {
357  CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)),
358  IntegerValue(num_literals)));
359  }
360  relaxation->linear_constraints.push_back(lc.Build());
361  } else if (ct.constraint_case() ==
362  ConstraintProto::ConstraintCase::kAtMostOne) {
363  if (HasEnforcementLiteral(ct)) return;
364  std::vector<Literal> at_most_one;
365  for (const int ref : ct.at_most_one().literals()) {
366  at_most_one.push_back(mapping->Literal(ref));
367  }
368  relaxation->at_most_ones.push_back(at_most_one);
369  } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMax) {
370  if (HasEnforcementLiteral(ct)) return;
371  const IntegerVariable target = mapping->Integer(ct.int_max().target());
372  const std::vector<IntegerVariable> vars =
373  mapping->Integers(ct.int_max().vars());
374  AppendMaxRelaxation(target, vars, linearization_level, model, relaxation);
375 
376  } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMin) {
377  if (HasEnforcementLiteral(ct)) return;
378  const IntegerVariable negative_target =
379  NegationOf(mapping->Integer(ct.int_min().target()));
380  const std::vector<IntegerVariable> negative_vars =
381  NegationOf(mapping->Integers(ct.int_min().vars()));
382  AppendMaxRelaxation(negative_target, negative_vars, linearization_level,
383  model, relaxation);
384  } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
385  AppendLinearConstraintRelaxation(ct, linearization_level, *model,
386  relaxation);
387  } else if (ct.constraint_case() ==
388  ConstraintProto::ConstraintCase::kCircuit) {
389  if (HasEnforcementLiteral(ct)) return;
390  const int num_arcs = ct.circuit().literals_size();
391  CHECK_EQ(num_arcs, ct.circuit().tails_size());
392  CHECK_EQ(num_arcs, ct.circuit().heads_size());
393 
394  // Each node must have exactly one incoming and one outgoing arc (note that
395  // it can be the unique self-arc of this node too).
396  std::map<int, std::vector<Literal>> incoming_arc_constraints;
397  std::map<int, std::vector<Literal>> outgoing_arc_constraints;
398  for (int i = 0; i < num_arcs; i++) {
399  const Literal arc = mapping->Literal(ct.circuit().literals(i));
400  const int tail = ct.circuit().tails(i);
401  const int head = ct.circuit().heads(i);
402 
403  // Make sure this literal has a view.
405  outgoing_arc_constraints[tail].push_back(arc);
406  incoming_arc_constraints[head].push_back(arc);
407  }
408  for (const auto* node_map :
409  {&outgoing_arc_constraints, &incoming_arc_constraints}) {
410  for (const auto& entry : *node_map) {
411  const std::vector<Literal>& exactly_one = entry.second;
412  if (exactly_one.size() > 1) {
413  LinearConstraintBuilder at_least_one_lc(model, IntegerValue(1),
415  for (const Literal l : exactly_one) {
416  CHECK(at_least_one_lc.AddLiteralTerm(l, IntegerValue(1)));
417  }
418 
419  // We separate the two constraints.
420  relaxation->at_most_ones.push_back(exactly_one);
421  relaxation->linear_constraints.push_back(at_least_one_lc.Build());
422  }
423  }
424  }
425  } else if (ct.constraint_case() ==
426  ConstraintProto::ConstraintCase::kElement) {
427  const IntegerVariable index = mapping->Integer(ct.element().index());
428  const IntegerVariable target = mapping->Integer(ct.element().target());
429  const std::vector<IntegerVariable> vars =
430  mapping->Integers(ct.element().vars());
431 
432  // We only relax the case where all the vars are constant.
433  // target = sum (index == i) * fixed_vars[i].
434  LinearConstraintBuilder constraint(model, IntegerValue(0), IntegerValue(0));
435  constraint.AddTerm(target, IntegerValue(-1));
436  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
437  for (const auto literal_value : model->Add(FullyEncodeVariable((index)))) {
438  const IntegerVariable var = vars[literal_value.value.value()];
439  if (!model->Get(IsFixed(var))) return;
440 
441  // Make sure this literal has a view.
442  model->Add(NewIntegerVariableFromLiteral(literal_value.literal));
443  CHECK(constraint.AddLiteralTerm(literal_value.literal,
444  integer_trail->LowerBound(var)));
445  }
446 
447  relaxation->linear_constraints.push_back(constraint.Build());
448  } else if (ct.constraint_case() ==
449  ConstraintProto::ConstraintCase::kInterval) {
450  if (linearization_level < 2) return;
451  const IntegerVariable start = mapping->Integer(ct.interval().start());
452  const IntegerVariable size = mapping->Integer(ct.interval().size());
453  const IntegerVariable end = mapping->Integer(ct.interval().end());
454  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
455  const bool size_is_fixed =
456  integer_trail->LowerBound(size) == integer_trail->UpperBound(size);
457  const IntegerValue rhs =
458  size_is_fixed ? -integer_trail->LowerBound(size) : IntegerValue(0);
459  LinearConstraintBuilder lc(model, rhs, rhs);
460  lc.AddTerm(start, IntegerValue(1));
461  if (!size_is_fixed) {
462  lc.AddTerm(size, IntegerValue(1));
463  }
464  lc.AddTerm(end, IntegerValue(-1));
465  if (HasEnforcementLiteral(ct)) {
466  LinearExpression expr;
467  expr.coeffs = lc.Build().coeffs;
468  expr.vars = lc.Build().vars;
469  AppendEnforcedLinearExpression(
470  mapping->Literals(ct.enforcement_literal()), expr, rhs, rhs, *model,
471  relaxation);
472  } else {
473  relaxation->linear_constraints.push_back(lc.Build());
474  }
475  } else if (ct.constraint_case() ==
476  ConstraintProto::ConstraintCase::kNoOverlap) {
477  AppendNoOverlapRelaxation(model_proto, ct, linearization_level, model,
478  relaxation);
479  }
480 }
481 
482 // TODO(user,user): Support optional interval in the relaxation.
483 void AppendNoOverlapRelaxation(const CpModelProto& model_proto,
484  const ConstraintProto& ct,
485  int linearization_level, Model* model,
486  LinearRelaxation* relaxation) {
487  CHECK(ct.has_no_overlap());
488  if (linearization_level < 3) return;
489  if (HasEnforcementLiteral(ct)) return;
490  if (ct.no_overlap().intervals_size() < 2) return;
491  auto* mapping = model->GetOrCreate<CpModelMapping>();
492  const int64 num_intervals = ct.no_overlap().intervals_size();
493  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
494  IntegerEncoder* encoder = model->GetOrCreate<IntegerEncoder>();
495  for (int index1 = 0; index1 < num_intervals; ++index1) {
496  const int interval_index1 = ct.no_overlap().intervals(index1);
497  if (HasEnforcementLiteral(model_proto.constraints(interval_index1)))
498  continue;
499  const IntervalConstraintProto interval1 =
500  model_proto.constraints(interval_index1).interval();
501  const IntegerVariable start1 = mapping->Integer(interval1.start());
502  const IntegerVariable end1 = mapping->Integer(interval1.end());
503  for (int index2 = index1 + 1; index2 < num_intervals; ++index2) {
504  const int interval_index2 = ct.no_overlap().intervals(index2);
505  if (HasEnforcementLiteral(model_proto.constraints(interval_index2))) {
506  continue;
507  }
508  const IntervalConstraintProto interval2 =
509  model_proto.constraints(interval_index2).interval();
510  const IntegerVariable start2 = mapping->Integer(interval2.start());
511  const IntegerVariable end2 = mapping->Integer(interval2.end());
512  // Encode only the interesting pairs.
513  if (integer_trail->UpperBound(end1) <=
514  integer_trail->LowerBound(start2) ||
515  integer_trail->UpperBound(end2) <=
516  integer_trail->LowerBound(start1)) {
517  continue;
518  }
519 
520  const bool interval_1_can_precede_2 =
521  integer_trail->LowerBound(end1) <= integer_trail->UpperBound(start2);
522  const bool interval_2_can_precede_1 =
523  integer_trail->LowerBound(end2) <= integer_trail->UpperBound(start1);
524 
525  if (interval_1_can_precede_2 && interval_2_can_precede_1) {
526  const IntegerVariable interval1_precedes_interval2 =
527  model->Add(NewIntegerVariable(0, 1));
528  const Literal interval1_precedes_interval2_lit =
529  encoder->GetOrCreateLiteralAssociatedToEquality(
530  interval1_precedes_interval2, IntegerValue(1));
531  // interval1_precedes_interval2 => interval1.end <= interval2.start
532  // ~interval1_precedes_interval2 => interval2.end <= interval1.start
533  AppendEnforcedUpperBound(interval1_precedes_interval2_lit, end1, start2,
534  model, relaxation);
535  AppendEnforcedUpperBound(interval1_precedes_interval2_lit.Negated(),
536  end2, start1, model, relaxation);
537  } else if (interval_1_can_precede_2) {
538  // interval1.end <= interval2.start
539  LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
540  lc.AddTerm(end1, IntegerValue(1));
541  lc.AddTerm(start2, IntegerValue(-1));
542  relaxation->linear_constraints.push_back(lc.Build());
543  } else if (interval_2_can_precede_1) {
544  // interval2.end <= interval1.start
545  LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
546  lc.AddTerm(end2, IntegerValue(1));
547  lc.AddTerm(start1, IntegerValue(-1));
548  relaxation->linear_constraints.push_back(lc.Build());
549  }
550  }
551  }
552 }
553 
554 void AppendMaxRelaxation(IntegerVariable target,
555  const std::vector<IntegerVariable>& vars,
556  int linearization_level, Model* model,
557  LinearRelaxation* relaxation) {
558  // Case X = max(X_1, X_2, ..., X_N)
559  // Part 1: Encode X >= max(X_1, X_2, ..., X_N)
560  for (const IntegerVariable var : vars) {
561  // This deal with the corner case X = max(X, Y, Z, ..) !
562  // Note that this can be presolved into X >= Y, X >= Z, ...
563  if (target == var) continue;
564  LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
565  lc.AddTerm(var, IntegerValue(1));
566  lc.AddTerm(target, IntegerValue(-1));
567  relaxation->linear_constraints.push_back(lc.Build());
568  }
569 
570  // Part 2: Encode upper bound on X.
571  if (linearization_level < 2) return;
572  GenericLiteralWatcher* watcher = model->GetOrCreate<GenericLiteralWatcher>();
573  // For size = 2, we do this with 1 less variable.
574  IntegerEncoder* encoder = model->GetOrCreate<IntegerEncoder>();
575  if (vars.size() == 2) {
576  IntegerVariable y = model->Add(NewIntegerVariable(0, 1));
577  const Literal y_lit =
578  encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
579  AppendEnforcedUpperBound(y_lit, target, vars[0], model, relaxation);
580 
581  // TODO(user,user): It makes more sense to use ConditionalLowerOrEqual()
582  // here, but that degrades perf on the road*.fzn problem. Understand why.
583  IntegerSumLE* upper_bound1 = new IntegerSumLE(
584  {y_lit}, {target, vars[0]}, {IntegerValue(1), IntegerValue(-1)},
585  IntegerValue(0), model);
586  upper_bound1->RegisterWith(watcher);
587  model->TakeOwnership(upper_bound1);
588  AppendEnforcedUpperBound(y_lit.Negated(), target, vars[1], model,
589  relaxation);
590  IntegerSumLE* upper_bound2 = new IntegerSumLE(
591  {y_lit.Negated()}, {target, vars[1]},
592  {IntegerValue(1), IntegerValue(-1)}, IntegerValue(0), model);
593  upper_bound2->RegisterWith(watcher);
594  model->TakeOwnership(upper_bound2);
595  return;
596  }
597  // For each X_i, we encode y_i => X <= X_i. And at least one of the y_i is
598  // true. Note that the correct y_i will be chosen because of the first part in
599  // linearlization (X >= X_i).
600  // TODO(user): Only lower bound is needed, experiment.
601  LinearConstraintBuilder lc_exactly_one(model, IntegerValue(1),
602  IntegerValue(1));
603  std::vector<Literal> exactly_one_literals;
604  exactly_one_literals.reserve(vars.size());
605  for (const IntegerVariable var : vars) {
606  if (target == var) continue;
607  // y => X <= X_i.
608  // <=> max_term_value * y + X - X_i <= max_term_value.
609  // where max_tern_value is X_ub - X_i_lb.
610  IntegerVariable y = model->Add(NewIntegerVariable(0, 1));
611  const Literal y_lit =
612  encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
613 
614  AppendEnforcedUpperBound(y_lit, target, var, model, relaxation);
615  IntegerSumLE* upper_bound_constraint = new IntegerSumLE(
616  {y_lit}, {target, var}, {IntegerValue(1), IntegerValue(-1)},
617  IntegerValue(0), model);
618  upper_bound_constraint->RegisterWith(watcher);
619  model->TakeOwnership(upper_bound_constraint);
620  exactly_one_literals.push_back(y_lit);
621 
622  CHECK(lc_exactly_one.AddLiteralTerm(y_lit, IntegerValue(1)));
623  }
624  model->Add(ExactlyOneConstraint(exactly_one_literals));
625  relaxation->linear_constraints.push_back(lc_exactly_one.Build());
626 }
627 
628 std::vector<IntegerVariable> AppendLinMaxRelaxation(
629  IntegerVariable target, const std::vector<LinearExpression>& exprs,
630  Model* model, LinearRelaxation* relaxation) {
631  // We want to linearize X = max(exprs[1], exprs[2], ..., exprs[d]).
632  // Part 1: Encode X >= max(exprs[1], exprs[2], ..., exprs[d])
633  for (const LinearExpression& expr : exprs) {
635  for (int i = 0; i < expr.vars.size(); ++i) {
636  lc.AddTerm(expr.vars[i], expr.coeffs[i]);
637  }
638  lc.AddTerm(target, IntegerValue(-1));
639  relaxation->linear_constraints.push_back(lc.Build());
640  }
641 
642  // Part 2: Encode upper bound on X.
643 
644  // Add linking constraint to the CP solver
645  // sum zi = 1 and for all i, zi => max = expr_i.
646  const int num_exprs = exprs.size();
647  IntegerEncoder* encoder = model->GetOrCreate<IntegerEncoder>();
648  GenericLiteralWatcher* watcher = model->GetOrCreate<GenericLiteralWatcher>();
649 
650  // TODO(user): For the case where num_exprs = 2, Create only one z var.
651  std::vector<IntegerVariable> z_vars;
652  std::vector<Literal> z_lits;
653  z_vars.reserve(num_exprs);
654  z_lits.reserve(num_exprs);
655  LinearConstraintBuilder lc_exactly_one(model, IntegerValue(1),
656  IntegerValue(1));
657  std::vector<Literal> exactly_one_literals;
658  for (int i = 0; i < num_exprs; ++i) {
659  IntegerVariable z = model->Add(NewIntegerVariable(0, 1));
660  z_vars.push_back(z);
661  const Literal z_lit =
662  encoder->GetOrCreateLiteralAssociatedToEquality(z, IntegerValue(1));
663  z_lits.push_back(z_lit);
664  LinearExpression local_expr;
665  local_expr.vars = NegationOf(exprs[i].vars);
666  local_expr.vars.push_back(target);
667  local_expr.coeffs = exprs[i].coeffs;
668  local_expr.coeffs.push_back(IntegerValue(1));
669  IntegerSumLE* upper_bound = new IntegerSumLE(
670  {z_lit}, local_expr.vars, local_expr.coeffs, exprs[i].offset, model);
671  upper_bound->RegisterWith(watcher);
672  model->TakeOwnership(upper_bound);
673 
674  CHECK(lc_exactly_one.AddLiteralTerm(z_lit, IntegerValue(1)));
675  }
676  model->Add(ExactlyOneConstraint(z_lits));
677 
678  // For the relaxation, we use different constraints with a stronger linear
679  // relaxation as explained in the .h
680  // TODO(user): Consider passing the x_vars to this method instead of
681  // computing it here.
682  std::vector<IntegerVariable> x_vars;
683  for (int i = 0; i < num_exprs; ++i) {
684  x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
685  }
687  // All expressions should only contain positive variables.
688  DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable var) {
689  return VariableIsPositive(var);
690  }));
691 
692  std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
693  num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
694 
695  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
696  for (int i = 0; i < num_exprs; ++i) {
697  for (int j = 0; j < num_exprs; ++j) {
698  if (i == j) continue;
699  for (const IntegerVariable x_var : x_vars) {
700  const IntegerValue lb = integer_trail->LevelZeroLowerBound(x_var);
701  const IntegerValue ub = integer_trail->LevelZeroUpperBound(x_var);
702  const IntegerValue diff =
703  GetCoefficient(x_var, exprs[j]) - GetCoefficient(x_var, exprs[i]);
704  sum_of_max_corner_diff[i][j] += std::max(diff * lb, diff * ub);
705  }
706  }
707  }
708  for (int i = 0; i < num_exprs; ++i) {
709  LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
710  lc.AddTerm(target, IntegerValue(1));
711  for (int j = 0; j < exprs[i].vars.size(); ++j) {
712  lc.AddTerm(exprs[i].vars[j], -exprs[i].coeffs[j]);
713  }
714  for (int j = 0; j < num_exprs; ++j) {
715  CHECK(lc.AddLiteralTerm(z_lits[j],
716  -exprs[j].offset - sum_of_max_corner_diff[i][j]));
717  }
718  relaxation->linear_constraints.push_back(lc.Build());
719  }
720 
721  relaxation->linear_constraints.push_back(lc_exactly_one.Build());
722  return z_vars;
723 }
724 
725 void AppendLinearConstraintRelaxation(const ConstraintProto& constraint_proto,
726  const int linearization_level,
727  const Model& model,
728  LinearRelaxation* relaxation) {
729  auto* mapping = model.Get<CpModelMapping>();
730 
731  // Note that we ignore the holes in the domain.
732  //
733  // TODO(user): In LoadLinearConstraint() we already created intermediate
734  // Booleans for each disjoint interval, we should reuse them here if
735  // possible.
736  //
737  // TODO(user): process the "at most one" part of a == 1 separately?
738  const IntegerValue rhs_domain_min =
739  IntegerValue(constraint_proto.linear().domain(0));
740  const IntegerValue rhs_domain_max =
741  IntegerValue(constraint_proto.linear().domain(
742  constraint_proto.linear().domain_size() - 1));
743  if (rhs_domain_min == kint64min && rhs_domain_max == kint64max) return;
744 
745  if (!HasEnforcementLiteral(constraint_proto)) {
746  LinearConstraintBuilder lc(&model, rhs_domain_min, rhs_domain_max);
747  for (int i = 0; i < constraint_proto.linear().vars_size(); i++) {
748  const int ref = constraint_proto.linear().vars(i);
749  const int64 coeff = constraint_proto.linear().coeffs(i);
750  lc.AddTerm(mapping->Integer(ref), IntegerValue(coeff));
751  }
752  relaxation->linear_constraints.push_back(lc.Build());
753  return;
754  }
755 
756  // Reified version.
757  if (linearization_level < 2) return;
758 
759  // We linearize fully reified constraints of size 1 all together for a given
760  // variable. But we need to process half-reified ones.
761  if (!mapping->IsHalfEncodingConstraint(&constraint_proto) &&
762  constraint_proto.linear().vars_size() <= 1) {
763  return;
764  }
765 
766  std::vector<Literal> enforcing_literals;
767  enforcing_literals.reserve(constraint_proto.enforcement_literal_size());
768  for (const int enforcement_ref : constraint_proto.enforcement_literal()) {
769  enforcing_literals.push_back(mapping->Literal(enforcement_ref));
770  }
771  LinearExpression expr;
772  expr.vars.reserve(constraint_proto.linear().vars_size());
773  expr.coeffs.reserve(constraint_proto.linear().vars_size());
774  for (int i = 0; i < constraint_proto.linear().vars_size(); i++) {
775  int ref = constraint_proto.linear().vars(i);
776  IntegerValue coeff(constraint_proto.linear().coeffs(i));
777  if (!RefIsPositive(ref)) {
778  ref = PositiveRef(ref);
779  coeff = -coeff;
780  }
781  const IntegerVariable int_var = mapping->Integer(ref);
782  expr.vars.push_back(int_var);
783  expr.coeffs.push_back(coeff);
784  }
785  AppendEnforcedLinearExpression(enforcing_literals, expr, rhs_domain_min,
786  rhs_domain_max, model, relaxation);
787 }
788 
789 } // namespace sat
790 } // namespace operations_research
var
IntVar * var
Definition: expr_array.cc:1858
tail
int64 tail
Definition: routing_flow.cc:127
operations_research::sat::LowerBound
std::function< int64(const Model &)> LowerBound(IntegerVariable v)
Definition: integer.h:1376
operations_research::sat::LinearExpression::vars
std::vector< IntegerVariable > vars
Definition: linear_constraint.h:166
operations_research::sat::IntegerEncoder::FullDomainEncoding
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
Definition: integer.cc:121
min
int64 min
Definition: alldiff_cst.cc:138
operations_research::sat::LinearRelaxation
Definition: linear_relaxation.h:28
cp_model.pb.h
operations_research::sat::kNoIntegerVariable
const IntegerVariable kNoIntegerVariable(-1)
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::sat::AppendFullEncodingRelaxation
bool AppendFullEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:32
operations_research::sat::kNoLiteralIndex
const LiteralIndex kNoLiteralIndex(-1)
operations_research::sat::IntegerTrail::UpperBound
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1221
operations_research::sat::AppendNoOverlapRelaxation
void AppendNoOverlapRelaxation(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:483
linear_constraint.h
operations_research::sat::IntegerTrail::LevelZeroLowerBound
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1267
operations_research::sat::IntegerTrail::LevelZeroUpperBound
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1272
model_proto
CpModelProto const * model_proto
Definition: cp_model_solver.cc:2023
operations_research::sat::Literal::Literal
Literal(int signed_value)
Definition: sat_base.h:68
operations_research::sat::LinearExpression
Definition: linear_constraint.h:165
operations_research::sat::LinExprUpperBound
IntegerValue LinExprUpperBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
Definition: linear_constraint.cc:296
operations_research::sat::AppendLinMaxRelaxation
std::vector< IntegerVariable > AppendLinMaxRelaxation(IntegerVariable target, const std::vector< LinearExpression > &exprs, Model *model, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:628
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::sat::ExactlyOneConstraint
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:856
operations_research::sat::NegationOf
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:42
kint64min
static const int64 kint64min
Definition: integral_types.h:60
operations_research::sat::IntegerTrail
Definition: integer.h:534
operations_research::sat::LinearExpression::coeffs
std::vector< IntegerValue > coeffs
Definition: linear_constraint.h:167
int64
int64_t int64
Definition: integral_types.h:34
operations_research::sat::Model
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
index
int index
Definition: pack.cc:508
sat_base.h
operations_research::sat::AppendPartialEncodingRelaxation
void AppendPartialEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:109
operations_research::sat::SatSolver
Definition: sat_solver.h:58
operations_research::sat::GetCoefficient
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
Definition: linear_constraint.cc:329
operations_research::sat::IntegerSumLE::RegisterWith
void RegisterWith(GenericLiteralWatcher *watcher)
Definition: integer_expr.cc:191
operations_research::sat::Literal
Definition: sat_base.h:64
operations_research::sat::PositiveRef
int PositiveRef(int ref)
Definition: cp_model_utils.h:33
operations_research::sat::LinearConstraintBuilder::AddTerm
void AddTerm(IntegerVariable var, IntegerValue coeff)
Definition: linear_constraint.cc:22
operations_research::sat::IntegerSumLE
Definition: integer_expr.h:49
operations_research::sat::GenericLiteralWatcher
Definition: integer.h:1056
operations_research::sat::IntegerEncoder
Definition: integer.h:278
operations_research::sat::Literal::Negated
Literal Negated() const
Definition: sat_base.h:91
operations_research::sat::AppendMaxRelaxation
void AppendMaxRelaxation(IntegerVariable target, const std::vector< IntegerVariable > &vars, int linearization_level, Model *model, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:554
operations_research::sat::IntegerEncoder::GetOrCreateLiteralAssociatedToEquality
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
Definition: integer.cc:263
gtl::STLSortAndRemoveDuplicates
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
operations_research::sat::kMaxIntegerValue
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
operations_research::sat::CanonicalizeExpr
LinearExpression CanonicalizeExpr(const LinearExpression &expr)
Definition: linear_constraint.cc:271
integer_expr.h
operations_research::sat::LinearRelaxation::linear_constraints
std::vector< LinearConstraint > linear_constraints
Definition: linear_relaxation.h:29
operations_research::sat::NewIntegerVariable
std::function< IntegerVariable(Model *)> NewIntegerVariable()
Definition: integer.h:1321
operations_research::sat::NewIntegerVariableFromLiteral
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
Definition: integer.h:1353
linear_relaxation.h
operations_research::sat::HasEnforcementLiteral
bool HasEnforcementLiteral(const ConstraintProto &ct)
Definition: cp_model_utils.h:37
ct
const Constraint * ct
Definition: demon_profiler.cc:42
operations_research::sat::LinearRelaxation::at_most_ones
std::vector< std::vector< Literal > > at_most_ones
Definition: linear_relaxation.h:30
operations_research::sat::LinExprLowerBound
IntegerValue LinExprLowerBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
Definition: linear_constraint.cc:286
operations_research::sat::IsFixed
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
Definition: integer.h:1388
operations_research::sat::NegatedRef
int NegatedRef(int ref)
Definition: cp_model_utils.h:32
model
GRBmodel * model
Definition: gurobi_interface.cc:195
operations_research::sat::SatSolver::CurrentDecisionLevel
int CurrentDecisionLevel() const
Definition: sat_solver.h:361
operations_research::sat::RefIsPositive
bool RefIsPositive(int ref)
Definition: cp_model_utils.h:34
operations_research::sat::CpModelMapping
Definition: cp_model_loader.h:63
operations_research::sat::IntegerTrail::LowerBound
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1217
operations_research::sat::kMinIntegerValue
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
operations_research::sat::LinearConstraintBuilder
Definition: linear_constraint.h:82
stl_util.h
iterator_adaptors.h
operations_research::sat::LinearConstraint::vars
std::vector< IntegerVariable > vars
Definition: linear_constraint.h:42
delta
int64 delta
Definition: resource.cc:1684
cp_model_loader.h
operations_research::sat::LinearConstraintBuilder::Build
LinearConstraint Build()
Definition: linear_constraint.cc:108
interval
IntervalVar * interval
Definition: resource.cc:98
operations_research::sat::AppendLinearConstraintRelaxation
void AppendLinearConstraintRelaxation(const ConstraintProto &constraint_proto, const int linearization_level, const Model &model, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:725
operations_research::sat::AppendPartialGreaterThanEncodingRelaxation
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:184
head
int64 head
Definition: routing_flow.cc:128
literal
Literal literal
Definition: optimization.cc:84
operations_research::sat::LinearConstraintBuilder::AddLiteralTerm
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
Definition: linear_constraint.cc:47
operations_research::sat::FullyEncodeVariable
std::function< std::vector< IntegerEncoder::ValueLiteralPair >Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1496
operations_research::sat::TryToLinearizeConstraint
void TryToLinearizeConstraint(const CpModelProto &model_proto, const ConstraintProto &ct, Model *model, int linearization_level, LinearRelaxation *relaxation)
Definition: linear_relaxation.cc:314
integer.h
operations_research::sat::LinearConstraint::coeffs
std::vector< IntegerValue > coeffs
Definition: linear_constraint.h:43
linear_programming_constraint.h
kint64max
static const int64 kint64max
Definition: integral_types.h:62
gtl::ContainsKey
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170