OR-Tools  9.0
linear_relaxation.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <cstdint>
17 #include <limits>
18 #include <vector>
19 
20 #include "absl/container/flat_hash_set.h"
22 #include "ortools/base/stl_util.h"
25 #include "ortools/sat/integer.h"
29 #include "ortools/sat/sat_base.h"
31 
32 namespace operations_research {
33 namespace sat {
34 
35 bool AppendFullEncodingRelaxation(IntegerVariable var, const Model& model,
36  LinearRelaxation* relaxation) {
37  const auto* encoder = model.Get<IntegerEncoder>();
38  if (encoder == nullptr) return false;
39  if (!encoder->VariableIsFullyEncoded(var)) return false;
40 
41  const auto& encoding = encoder->FullDomainEncoding(var);
42  const IntegerValue var_min = model.Get<IntegerTrail>()->LowerBound(var);
43 
44  LinearConstraintBuilder at_least_one(&model, IntegerValue(1),
46  LinearConstraintBuilder encoding_ct(&model, var_min, var_min);
47  encoding_ct.AddTerm(var, IntegerValue(1));
48 
49  // Create the constraint if all literal have a view.
50  std::vector<Literal> at_most_one;
51 
52  for (const auto value_literal : encoding) {
53  const Literal lit = value_literal.literal;
54  const IntegerValue delta = value_literal.value - var_min;
55  DCHECK_GE(delta, IntegerValue(0));
56  at_most_one.push_back(lit);
57  if (!at_least_one.AddLiteralTerm(lit, IntegerValue(1))) return false;
58  if (delta != IntegerValue(0)) {
59  if (!encoding_ct.AddLiteralTerm(lit, -delta)) return false;
60  }
61  }
62 
63  relaxation->linear_constraints.push_back(at_least_one.Build());
64  relaxation->linear_constraints.push_back(encoding_ct.Build());
65  relaxation->at_most_ones.push_back(at_most_one);
66  return true;
67 }
68 
69 namespace {
70 
71 // TODO(user): Not super efficient.
72 std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
73  IntegerVariable var,
74  const absl::flat_hash_set<IntegerValue>& encoded_values,
75  const Model& model) {
76  const auto* domains = model.Get<IntegerDomains>();
77  if (domains == nullptr || var >= domains->size()) {
79  }
80 
81  // The domain can be large, but the list of values shouldn't, so this
82  // runs in O(encoded_values.size());
83  IntegerValue min = kMaxIntegerValue;
84  for (const ClosedInterval interval : (*domains)[var]) {
85  for (IntegerValue v(interval.start); v <= interval.end; ++v) {
86  if (!gtl::ContainsKey(encoded_values, v)) {
87  min = v;
88  break;
89  }
90  }
91  if (min != kMaxIntegerValue) break;
92  }
93 
94  IntegerValue max = kMinIntegerValue;
95  const auto& domain = (*domains)[var];
96  for (int i = domain.NumIntervals() - 1; i >= 0; --i) {
97  const ClosedInterval interval = domain[i];
98  for (IntegerValue v(interval.end); v >= interval.start; --v) {
99  if (!gtl::ContainsKey(encoded_values, v)) {
100  max = v;
101  break;
102  }
103  }
104  if (max != kMinIntegerValue) break;
105  }
106 
107  return {min, max};
108 }
109 
110 } // namespace
111 
112 void AppendPartialEncodingRelaxation(IntegerVariable var, const Model& model,
113  LinearRelaxation* relaxation) {
114  const auto* encoder = model.Get<IntegerEncoder>();
115  const auto* integer_trail = model.Get<IntegerTrail>();
116  if (encoder == nullptr || integer_trail == nullptr) return;
117 
118  const std::vector<IntegerEncoder::ValueLiteralPair>& encoding =
119  encoder->PartialDomainEncoding(var);
120  if (encoding.empty()) return;
121 
122  std::vector<Literal> at_most_one_ct;
123  absl::flat_hash_set<IntegerValue> encoded_values;
124  for (const auto value_literal : encoding) {
125  const Literal literal = value_literal.literal;
126 
127  // Note that we skip pairs that do not have an Integer view.
128  if (encoder->GetLiteralView(literal) == kNoIntegerVariable &&
129  encoder->GetLiteralView(literal.Negated()) == kNoIntegerVariable) {
130  continue;
131  }
132 
133  at_most_one_ct.push_back(literal);
134  encoded_values.insert(value_literal.value);
135  }
136  if (encoded_values.empty()) return;
137 
138  // TODO(user): The PartialDomainEncoding() function automatically exclude
139  // values that are no longer in the initial domain, so we could be a bit
140  // tighter here. That said, this is supposed to be called just after the
141  // presolve, so it shouldn't really matter.
142  const auto pair = GetMinAndMaxNotEncoded(var, encoded_values, model);
143  if (pair.first == kMaxIntegerValue) {
144  // TODO(user): try to remove the duplication with
145  // AppendFullEncodingRelaxation()? actually I am not sure we need the other
146  // function since this one is just more general.
147  LinearConstraintBuilder exactly_one_ct(&model, IntegerValue(1),
148  IntegerValue(1));
149  LinearConstraintBuilder encoding_ct(&model, IntegerValue(0),
150  IntegerValue(0));
151  encoding_ct.AddTerm(var, IntegerValue(1));
152  for (const auto value_literal : encoding) {
153  const Literal lit = value_literal.literal;
154  CHECK(exactly_one_ct.AddLiteralTerm(lit, IntegerValue(1)));
155  CHECK(
156  encoding_ct.AddLiteralTerm(lit, IntegerValue(-value_literal.value)));
157  }
158  relaxation->linear_constraints.push_back(exactly_one_ct.Build());
159  relaxation->linear_constraints.push_back(encoding_ct.Build());
160  return;
161  }
162 
163  // min + sum li * (xi - min) <= var.
164  const IntegerValue d_min = pair.first;
165  LinearConstraintBuilder lower_bound_ct(&model, d_min, kMaxIntegerValue);
166  lower_bound_ct.AddTerm(var, IntegerValue(1));
167  for (const auto value_literal : encoding) {
168  CHECK(lower_bound_ct.AddLiteralTerm(value_literal.literal,
169  d_min - value_literal.value));
170  }
171 
172  // var <= max + sum li * (xi - max).
173  const IntegerValue d_max = pair.second;
174  LinearConstraintBuilder upper_bound_ct(&model, kMinIntegerValue, d_max);
175  upper_bound_ct.AddTerm(var, IntegerValue(1));
176  for (const auto value_literal : encoding) {
177  CHECK(upper_bound_ct.AddLiteralTerm(value_literal.literal,
178  d_max - value_literal.value));
179  }
180 
181  // Note that empty/trivial constraints will be filtered later.
182  relaxation->at_most_ones.push_back(at_most_one_ct);
183  relaxation->linear_constraints.push_back(lower_bound_ct.Build());
184  relaxation->linear_constraints.push_back(upper_bound_ct.Build());
185 }
186 
188  const Model& model,
189  LinearRelaxation* relaxation) {
190  const auto* integer_trail = model.Get<IntegerTrail>();
191  const auto* encoder = model.Get<IntegerEncoder>();
192  if (integer_trail == nullptr || encoder == nullptr) return;
193 
194  const std::map<IntegerValue, Literal>& greater_than_encoding =
195  encoder->PartialGreaterThanEncoding(var);
196  if (greater_than_encoding.empty()) return;
197 
198  // Start by the var >= side.
199  // And also add the implications between used literals.
200  {
201  IntegerValue prev_used_bound = integer_trail->LowerBound(var);
202  LinearConstraintBuilder lb_constraint(&model, prev_used_bound,
204  lb_constraint.AddTerm(var, IntegerValue(1));
205  LiteralIndex prev_literal_index = kNoLiteralIndex;
206  for (const auto entry : greater_than_encoding) {
207  if (entry.first <= prev_used_bound) continue;
208 
209  const LiteralIndex literal_index = entry.second.Index();
210  const IntegerValue diff = prev_used_bound - entry.first;
211 
212  // Skip the entry if the literal doesn't have a view.
213  if (!lb_constraint.AddLiteralTerm(entry.second, diff)) continue;
214  if (prev_literal_index != kNoLiteralIndex) {
215  // Add var <= prev_var, which is the same as var + not(prev_var) <= 1
216  relaxation->at_most_ones.push_back(
217  {Literal(literal_index), Literal(prev_literal_index).Negated()});
218  }
219  prev_used_bound = entry.first;
220  prev_literal_index = literal_index;
221  }
222  relaxation->linear_constraints.push_back(lb_constraint.Build());
223  }
224 
225  // Do the same for the var <= side by using NegationOfVar().
226  // Note that we do not need to add the implications between literals again.
227  {
228  IntegerValue prev_used_bound = integer_trail->LowerBound(NegationOf(var));
229  LinearConstraintBuilder lb_constraint(&model, prev_used_bound,
231  lb_constraint.AddTerm(var, IntegerValue(-1));
232  for (const auto entry :
233  encoder->PartialGreaterThanEncoding(NegationOf(var))) {
234  if (entry.first <= prev_used_bound) continue;
235  const IntegerValue diff = prev_used_bound - entry.first;
236 
237  // Skip the entry if the literal doesn't have a view.
238  if (!lb_constraint.AddLiteralTerm(entry.second, diff)) continue;
239  prev_used_bound = entry.first;
240  }
241  relaxation->linear_constraints.push_back(lb_constraint.Build());
242  }
243 }
244 
245 namespace {
246 // Adds enforcing_lit => target <= bounding_var to relaxation.
247 void AppendEnforcedUpperBound(const Literal enforcing_lit,
248  const IntegerVariable target,
249  const IntegerVariable bounding_var, Model* model,
250  LinearRelaxation* relaxation) {
251  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
252  const IntegerValue max_target_value = integer_trail->UpperBound(target);
253  const IntegerValue min_var_value = integer_trail->LowerBound(bounding_var);
254  const IntegerValue max_term_value = max_target_value - min_var_value;
255  LinearConstraintBuilder lc(model, kMinIntegerValue, max_term_value);
256  lc.AddTerm(target, IntegerValue(1));
257  lc.AddTerm(bounding_var, IntegerValue(-1));
258  CHECK(lc.AddLiteralTerm(enforcing_lit, max_term_value));
259  relaxation->linear_constraints.push_back(lc.Build());
260 }
261 
262 // Adds {enforcing_lits} => rhs_domain_min <= expr <= rhs_domain_max.
263 // Requires expr offset to be 0.
264 void AppendEnforcedLinearExpression(
265  const std::vector<Literal>& enforcing_literals,
266  const LinearExpression& expr, const IntegerValue rhs_domain_min,
267  const IntegerValue rhs_domain_max, const Model& model,
268  LinearRelaxation* relaxation) {
269  CHECK_EQ(expr.offset, IntegerValue(0));
270  const LinearExpression canonical_expr = CanonicalizeExpr(expr);
271  const IntegerTrail* integer_trail = model.Get<IntegerTrail>();
272  const IntegerValue min_expr_value =
273  LinExprLowerBound(canonical_expr, *integer_trail);
274 
275  if (rhs_domain_min > min_expr_value) {
276  // And(ei) => terms >= rhs_domain_min
277  // <=> Sum_i (~ei * (rhs_domain_min - min_expr_value)) + terms >=
278  // rhs_domain_min
279  LinearConstraintBuilder lc(&model, rhs_domain_min, kMaxIntegerValue);
280  for (const Literal& literal : enforcing_literals) {
281  CHECK(lc.AddLiteralTerm(literal.Negated(),
282  rhs_domain_min - min_expr_value));
283  }
284  for (int i = 0; i < canonical_expr.vars.size(); i++) {
285  lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
286  }
287  relaxation->linear_constraints.push_back(lc.Build());
288  }
289  const IntegerValue max_expr_value =
290  LinExprUpperBound(canonical_expr, *integer_trail);
291  if (rhs_domain_max < max_expr_value) {
292  // And(ei) => terms <= rhs_domain_max
293  // <=> Sum_i (~ei * (rhs_domain_max - max_expr_value)) + terms <=
294  // rhs_domain_max
295  LinearConstraintBuilder lc(&model, kMinIntegerValue, rhs_domain_max);
296  for (const Literal& literal : enforcing_literals) {
297  CHECK(lc.AddLiteralTerm(literal.Negated(),
298  rhs_domain_max - max_expr_value));
299  }
300  for (int i = 0; i < canonical_expr.vars.size(); i++) {
301  lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
302  }
303  relaxation->linear_constraints.push_back(lc.Build());
304  }
305 }
306 
307 bool AllLiteralsHaveViews(const IntegerEncoder& encoder,
308  const std::vector<Literal>& literals) {
309  for (const Literal lit : literals) {
310  if (!encoder.LiteralOrNegationHasView(lit)) return false;
311  }
312  return true;
313 }
314 
315 } // namespace
316 
317 // Add a linear relaxation of the CP constraint to the set of linear
318 // constraints. The highest linearization_level is, the more types of constraint
319 // we encode. This method should be called only for linearization_level > 0.
320 //
321 // Note: IntProd is linearized dynamically using the cut generators.
322 //
323 // TODO(user): In full generality, we could encode all the constraint as an LP.
324 // TODO(user,user): Add unit tests for this method.
325 void TryToLinearizeConstraint(const CpModelProto& model_proto,
326  const ConstraintProto& ct, Model* model,
327  int linearization_level,
328  LinearRelaxation* relaxation) {
329  CHECK_EQ(model->GetOrCreate<SatSolver>()->CurrentDecisionLevel(), 0);
330  DCHECK_GT(linearization_level, 0);
331  auto* mapping = model->GetOrCreate<CpModelMapping>();
332  const auto& encoder = *model->GetOrCreate<IntegerEncoder>();
333  if (ct.constraint_case() == ConstraintProto::ConstraintCase::kBoolOr) {
334  if (linearization_level < 2) return;
335  LinearConstraintBuilder lc(model, IntegerValue(1), kMaxIntegerValue);
336  for (const int enforcement_ref : ct.enforcement_literal()) {
337  CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)),
338  IntegerValue(1)));
339  }
340  for (const int ref : ct.bool_or().literals()) {
341  CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
342  }
343  relaxation->linear_constraints.push_back(lc.Build());
344  } else if (ct.constraint_case() ==
345  ConstraintProto::ConstraintCase::kBoolAnd) {
346  // TODO(user): These constraints can be many, and if they are not regrouped
347  // in big at most ones, then they should probably only added lazily as cuts.
348  // Regroup this with future clique-cut separation logic.
349  if (linearization_level < 2) return;
350  if (!HasEnforcementLiteral(ct)) return;
351  if (ct.enforcement_literal().size() == 1) {
352  const Literal enforcement = mapping->Literal(ct.enforcement_literal(0));
353  for (const int ref : ct.bool_and().literals()) {
354  relaxation->at_most_ones.push_back(
355  {enforcement, mapping->Literal(ref).Negated()});
356  }
357  return;
358  }
359 
360  // Andi(e_i) => Andj(x_j)
361  // <=> num_rhs_terms <= Sum_j(x_j) + num_rhs_terms * Sum_i(~e_i)
362  int num_literals = ct.bool_and().literals_size();
363  LinearConstraintBuilder lc(model, IntegerValue(num_literals),
365  for (const int ref : ct.bool_and().literals()) {
366  CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
367  }
368  for (const int enforcement_ref : ct.enforcement_literal()) {
369  CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)),
370  IntegerValue(num_literals)));
371  }
372  relaxation->linear_constraints.push_back(lc.Build());
373  } else if (ct.constraint_case() ==
374  ConstraintProto::ConstraintCase::kAtMostOne) {
375  if (HasEnforcementLiteral(ct)) return;
376  relaxation->at_most_ones.push_back(
377  mapping->Literals(ct.at_most_one().literals()));
378  } else if (ct.constraint_case() ==
379  ConstraintProto::ConstraintCase::kExactlyOne) {
380  if (HasEnforcementLiteral(ct)) return;
381  const std::vector<Literal> literals =
382  mapping->Literals(ct.exactly_one().literals());
383  if (linearization_level >= 2 || AllLiteralsHaveViews(encoder, literals)) {
384  LinearConstraintBuilder lc(model, IntegerValue(1), IntegerValue(1));
385  for (const Literal lit : literals) {
386  CHECK(lc.AddLiteralTerm(lit, IntegerValue(1)));
387  }
388  relaxation->linear_constraints.push_back(lc.Build());
389  } else if (linearization_level == 1) {
390  // We just encode the at most one part that might be partially linearized
391  // later.
392  relaxation->at_most_ones.push_back(literals);
393  }
394  } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMax) {
395  if (HasEnforcementLiteral(ct)) return;
396  const IntegerVariable target = mapping->Integer(ct.int_max().target());
397  const std::vector<IntegerVariable> vars =
398  mapping->Integers(ct.int_max().vars());
399  AppendMaxRelaxation(target, vars, linearization_level, model, relaxation);
400 
401  } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kIntMin) {
402  if (HasEnforcementLiteral(ct)) return;
403  const IntegerVariable negative_target =
404  NegationOf(mapping->Integer(ct.int_min().target()));
405  const std::vector<IntegerVariable> negative_vars =
406  NegationOf(mapping->Integers(ct.int_min().vars()));
407  AppendMaxRelaxation(negative_target, negative_vars, linearization_level,
408  model, relaxation);
409  } else if (ct.constraint_case() == ConstraintProto::ConstraintCase::kLinear) {
410  AppendLinearConstraintRelaxation(ct, linearization_level, *model,
411  relaxation);
412  } else if (ct.constraint_case() ==
413  ConstraintProto::ConstraintCase::kCircuit) {
414  if (HasEnforcementLiteral(ct)) return;
415  const int num_arcs = ct.circuit().literals_size();
416  CHECK_EQ(num_arcs, ct.circuit().tails_size());
417  CHECK_EQ(num_arcs, ct.circuit().heads_size());
418 
419  // Each node must have exactly one incoming and one outgoing arc (note that
420  // it can be the unique self-arc of this node too).
421  std::map<int, std::vector<Literal>> incoming_arc_constraints;
422  std::map<int, std::vector<Literal>> outgoing_arc_constraints;
423  for (int i = 0; i < num_arcs; i++) {
424  const Literal arc = mapping->Literal(ct.circuit().literals(i));
425  const int tail = ct.circuit().tails(i);
426  const int head = ct.circuit().heads(i);
427 
428  // Make sure this literal has a view.
430  outgoing_arc_constraints[tail].push_back(arc);
431  incoming_arc_constraints[head].push_back(arc);
432  }
433  for (const auto* node_map :
434  {&outgoing_arc_constraints, &incoming_arc_constraints}) {
435  for (const auto& entry : *node_map) {
436  const std::vector<Literal>& exactly_one = entry.second;
437  if (exactly_one.size() > 1) {
438  LinearConstraintBuilder at_least_one_lc(model, IntegerValue(1),
440  for (const Literal l : exactly_one) {
441  CHECK(at_least_one_lc.AddLiteralTerm(l, IntegerValue(1)));
442  }
443 
444  // We separate the two constraints.
445  relaxation->at_most_ones.push_back(exactly_one);
446  relaxation->linear_constraints.push_back(at_least_one_lc.Build());
447  }
448  }
449  }
450  } else if (ct.constraint_case() ==
451  ConstraintProto::ConstraintCase::kElement) {
452  const IntegerVariable index = mapping->Integer(ct.element().index());
453  const IntegerVariable target = mapping->Integer(ct.element().target());
454  const std::vector<IntegerVariable> vars =
455  mapping->Integers(ct.element().vars());
456 
457  // We only relax the case where all the vars are constant.
458  // target = sum (index == i) * fixed_vars[i].
459  LinearConstraintBuilder constraint(model, IntegerValue(0), IntegerValue(0));
460  constraint.AddTerm(target, IntegerValue(-1));
461  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
462  for (const auto literal_value : model->Add(FullyEncodeVariable((index)))) {
463  const IntegerVariable var = vars[literal_value.value.value()];
464  if (!model->Get(IsFixed(var))) return;
465 
466  // Make sure this literal has a view.
467  model->Add(NewIntegerVariableFromLiteral(literal_value.literal));
468  CHECK(constraint.AddLiteralTerm(literal_value.literal,
469  integer_trail->LowerBound(var)));
470  }
471 
472  relaxation->linear_constraints.push_back(constraint.Build());
473  } else if (ct.constraint_case() ==
474  ConstraintProto::ConstraintCase::kInterval) {
475  // If the interval is using views, then the linear equation is already
476  // present in the model.
477  if (linearization_level < 2) return;
478  if (ct.interval().has_start_view()) return;
479  const IntegerVariable start = mapping->Integer(ct.interval().start());
480  const IntegerVariable size = mapping->Integer(ct.interval().size());
481  const IntegerVariable end = mapping->Integer(ct.interval().end());
482  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
483  const bool size_is_fixed = integer_trail->IsFixed(size);
484  const IntegerValue rhs =
485  size_is_fixed ? -integer_trail->LowerBound(size) : IntegerValue(0);
486  LinearConstraintBuilder lc(model, rhs, rhs);
487  lc.AddTerm(start, IntegerValue(1));
488  if (!size_is_fixed) {
489  lc.AddTerm(size, IntegerValue(1));
490  }
491  lc.AddTerm(end, IntegerValue(-1));
492  if (HasEnforcementLiteral(ct)) {
493  LinearConstraint tmp_lc = lc.Build();
494  LinearExpression expr;
495  expr.coeffs = tmp_lc.coeffs;
496  expr.vars = tmp_lc.vars;
497  AppendEnforcedLinearExpression(
498  mapping->Literals(ct.enforcement_literal()), expr, tmp_lc.ub,
499  tmp_lc.ub, *model, relaxation);
500  } else {
501  relaxation->linear_constraints.push_back(lc.Build());
502  }
503  } else if (ct.constraint_case() ==
504  ConstraintProto::ConstraintCase::kNoOverlap) {
505  AppendNoOverlapRelaxation(model_proto, ct, linearization_level, model,
506  relaxation);
507  } else if (ct.constraint_case() ==
508  ConstraintProto::ConstraintCase::kCumulative) {
509  AppendCumulativeRelaxation(model_proto, ct, linearization_level, model,
510  relaxation);
511  }
512 }
513 
514 // TODO(user): Use affine demand.
515 void AddCumulativeCut(const std::vector<IntervalVariable>& intervals,
516  const std::vector<IntegerVariable>& demands,
517  IntegerValue capacity_upper_bound, Model* model,
518  LinearRelaxation* relaxation) {
519  // TODO(user): Keep a map intervals -> helper, or ct_index->helper to avoid
520  // creating many helpers for the same constraint.
521  auto* helper = new SchedulingConstraintHelper(intervals, model);
522  model->TakeOwnership(helper);
523  const int num_intervals = helper->NumTasks();
524 
525  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
526 
527  IntegerValue min_of_starts = kMaxIntegerValue;
528  IntegerValue max_of_ends = kMinIntegerValue;
529 
530  int num_variable_sizes = 0;
531  int num_optionals = 0;
532 
533  for (int index = 0; index < num_intervals; ++index) {
534  min_of_starts = std::min(min_of_starts, helper->StartMin(index));
535  max_of_ends = std::max(max_of_ends, helper->EndMax(index));
536 
537  if (helper->IsOptional(index)) {
538  num_optionals++;
539  }
540 
541  if (!helper->SizeIsFixed(index) ||
542  (!demands.empty() && !integer_trail->IsFixed(demands[index]))) {
543  num_variable_sizes++;
544  }
545  }
546 
547  VLOG(2) << "Span [" << min_of_starts << ".." << max_of_ends << "] with "
548  << num_optionals << " optional intervals, and " << num_variable_sizes
549  << " variable size intervals out of " << num_intervals
550  << " intervals";
551 
552  if (num_variable_sizes + num_optionals == 0) return;
553 
554  const IntegerVariable span_start =
555  integer_trail->AddIntegerVariable(min_of_starts, max_of_ends);
556  const IntegerVariable span_size = integer_trail->AddIntegerVariable(
557  IntegerValue(0), max_of_ends - min_of_starts);
558  const IntegerVariable span_end =
559  integer_trail->AddIntegerVariable(min_of_starts, max_of_ends);
560 
561  IntervalVariable span_var;
562  if (num_optionals < num_intervals) {
563  span_var = model->Add(NewInterval(span_start, span_end, span_size));
564  } else {
565  const Literal span_lit = Literal(model->Add(NewBooleanVariable()), true);
566  span_var = model->Add(
567  NewOptionalInterval(span_start, span_end, span_size, span_lit));
568  }
569 
570  model->Add(SpanOfIntervals(span_var, intervals));
571 
572  LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
573  lc.AddTerm(span_size, -capacity_upper_bound);
574  for (int i = 0; i < num_intervals; ++i) {
575  const IntegerValue demand_lower_bound =
576  demands.empty() ? IntegerValue(1)
577  : integer_trail->LowerBound(demands[i]);
578  const bool demand_is_fixed =
579  demands.empty() || integer_trail->IsFixed(demands[i]);
580  if (!helper->IsOptional(i)) {
581  if (helper->SizeIsFixed(i) && !demands.empty()) {
582  lc.AddTerm(demands[i], helper->SizeMin(i));
583  } else if (demand_is_fixed) {
584  lc.AddTerm(helper->Sizes()[i], demand_lower_bound);
585  } else { // demand and size are not fixed.
586  DCHECK(!demands.empty());
587  // We use McCormick equation.
588  // demand * size = (demand_min + delta_d) * (size_min + delta_s) =
589  // demand_min * size_min + delta_d * size_min +
590  // delta_s * demand_min + delta_s * delta_d
591  // which is >= (by ignoring the quatratic term)
592  // demand_min * size + size_min * demand - demand_min * size_min
593  lc.AddTerm(helper->Sizes()[i], demand_lower_bound);
594  lc.AddTerm(demands[i], helper->SizeMin(i));
595  lc.AddConstant(-helper->SizeMin(i) * demand_lower_bound);
596  }
597  } else {
598  if (!lc.AddLiteralTerm(helper->PresenceLiteral(i),
599  helper->SizeMin(i) * demand_lower_bound)) {
600  return;
601  }
602  }
603  }
604  relaxation->linear_constraints.push_back(lc.Build());
605 }
606 
607 void AppendCumulativeRelaxation(const CpModelProto& model_proto,
608  const ConstraintProto& ct,
609  int linearization_level, Model* model,
610  LinearRelaxation* relaxation) {
611  CHECK(ct.has_cumulative());
612  if (linearization_level < 2) return;
613  if (HasEnforcementLiteral(ct)) return;
614 
615  auto* mapping = model->GetOrCreate<CpModelMapping>();
616  const std::vector<IntegerVariable> demands =
617  mapping->Integers(ct.cumulative().demands());
618  std::vector<IntervalVariable> intervals =
619  mapping->Intervals(ct.cumulative().intervals());
620  const IntegerValue capacity_upper_bound =
621  model->GetOrCreate<IntegerTrail>()->UpperBound(
622  mapping->Integer(ct.cumulative().capacity()));
623  AddCumulativeCut(intervals, demands, capacity_upper_bound, model, relaxation);
624 }
625 
626 void AppendNoOverlapRelaxation(const CpModelProto& model_proto,
627  const ConstraintProto& ct,
628  int linearization_level, Model* model,
629  LinearRelaxation* relaxation) {
630  CHECK(ct.has_no_overlap());
631  if (linearization_level < 2) return;
632  if (HasEnforcementLiteral(ct)) return;
633 
634  auto* mapping = model->GetOrCreate<CpModelMapping>();
635  std::vector<IntervalVariable> intervals =
636  mapping->Intervals(ct.no_overlap().intervals());
637  AddCumulativeCut(intervals, /*demands=*/{},
638  /*capacity_upper_bound=*/IntegerValue(1), model, relaxation);
639 }
640 
641 void AppendMaxRelaxation(IntegerVariable target,
642  const std::vector<IntegerVariable>& vars,
643  int linearization_level, Model* model,
644  LinearRelaxation* relaxation) {
645  // Case X = max(X_1, X_2, ..., X_N)
646  // Part 1: Encode X >= max(X_1, X_2, ..., X_N)
647  for (const IntegerVariable var : vars) {
648  // This deal with the corner case X = max(X, Y, Z, ..) !
649  // Note that this can be presolved into X >= Y, X >= Z, ...
650  if (target == var) continue;
651  LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
652  lc.AddTerm(var, IntegerValue(1));
653  lc.AddTerm(target, IntegerValue(-1));
654  relaxation->linear_constraints.push_back(lc.Build());
655  }
656 
657  // Part 2: Encode upper bound on X.
658  if (linearization_level < 2) return;
659  GenericLiteralWatcher* watcher = model->GetOrCreate<GenericLiteralWatcher>();
660  // For size = 2, we do this with 1 less variable.
661  IntegerEncoder* encoder = model->GetOrCreate<IntegerEncoder>();
662  if (vars.size() == 2) {
663  IntegerVariable y = model->Add(NewIntegerVariable(0, 1));
664  const Literal y_lit =
665  encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
666  AppendEnforcedUpperBound(y_lit, target, vars[0], model, relaxation);
667 
668  // TODO(user,user): It makes more sense to use ConditionalLowerOrEqual()
669  // here, but that degrades perf on the road*.fzn problem. Understand why.
670  IntegerSumLE* upper_bound1 = new IntegerSumLE(
671  {y_lit}, {target, vars[0]}, {IntegerValue(1), IntegerValue(-1)},
672  IntegerValue(0), model);
673  upper_bound1->RegisterWith(watcher);
674  model->TakeOwnership(upper_bound1);
675  AppendEnforcedUpperBound(y_lit.Negated(), target, vars[1], model,
676  relaxation);
677  IntegerSumLE* upper_bound2 = new IntegerSumLE(
678  {y_lit.Negated()}, {target, vars[1]},
679  {IntegerValue(1), IntegerValue(-1)}, IntegerValue(0), model);
680  upper_bound2->RegisterWith(watcher);
681  model->TakeOwnership(upper_bound2);
682  return;
683  }
684  // For each X_i, we encode y_i => X <= X_i. And at least one of the y_i is
685  // true. Note that the correct y_i will be chosen because of the first part in
686  // linearlization (X >= X_i).
687  // TODO(user): Only lower bound is needed, experiment.
688  LinearConstraintBuilder lc_exactly_one(model, IntegerValue(1),
689  IntegerValue(1));
690  std::vector<Literal> exactly_one_literals;
691  exactly_one_literals.reserve(vars.size());
692  for (const IntegerVariable var : vars) {
693  if (target == var) continue;
694  // y => X <= X_i.
695  // <=> max_term_value * y + X - X_i <= max_term_value.
696  // where max_tern_value is X_ub - X_i_lb.
697  IntegerVariable y = model->Add(NewIntegerVariable(0, 1));
698  const Literal y_lit =
699  encoder->GetOrCreateLiteralAssociatedToEquality(y, IntegerValue(1));
700 
701  AppendEnforcedUpperBound(y_lit, target, var, model, relaxation);
702  IntegerSumLE* upper_bound_constraint = new IntegerSumLE(
703  {y_lit}, {target, var}, {IntegerValue(1), IntegerValue(-1)},
704  IntegerValue(0), model);
705  upper_bound_constraint->RegisterWith(watcher);
706  model->TakeOwnership(upper_bound_constraint);
707  exactly_one_literals.push_back(y_lit);
708 
709  CHECK(lc_exactly_one.AddLiteralTerm(y_lit, IntegerValue(1)));
710  }
711  model->Add(ExactlyOneConstraint(exactly_one_literals));
712  relaxation->linear_constraints.push_back(lc_exactly_one.Build());
713 }
714 
715 std::vector<IntegerVariable> AppendLinMaxRelaxation(
716  IntegerVariable target, const std::vector<LinearExpression>& exprs,
717  Model* model, LinearRelaxation* relaxation) {
718  // We want to linearize X = max(exprs[1], exprs[2], ..., exprs[d]).
719  // Part 1: Encode X >= max(exprs[1], exprs[2], ..., exprs[d])
720  for (const LinearExpression& expr : exprs) {
722  for (int i = 0; i < expr.vars.size(); ++i) {
723  lc.AddTerm(expr.vars[i], expr.coeffs[i]);
724  }
725  lc.AddTerm(target, IntegerValue(-1));
726  relaxation->linear_constraints.push_back(lc.Build());
727  }
728 
729  // Part 2: Encode upper bound on X.
730 
731  // Add linking constraint to the CP solver
732  // sum zi = 1 and for all i, zi => max = expr_i.
733  const int num_exprs = exprs.size();
734  IntegerEncoder* encoder = model->GetOrCreate<IntegerEncoder>();
735  GenericLiteralWatcher* watcher = model->GetOrCreate<GenericLiteralWatcher>();
736 
737  // TODO(user): For the case where num_exprs = 2, Create only one z var.
738  std::vector<IntegerVariable> z_vars;
739  std::vector<Literal> z_lits;
740  z_vars.reserve(num_exprs);
741  z_lits.reserve(num_exprs);
742  LinearConstraintBuilder lc_exactly_one(model, IntegerValue(1),
743  IntegerValue(1));
744  std::vector<Literal> exactly_one_literals;
745  for (int i = 0; i < num_exprs; ++i) {
746  IntegerVariable z = model->Add(NewIntegerVariable(0, 1));
747  z_vars.push_back(z);
748  const Literal z_lit =
749  encoder->GetOrCreateLiteralAssociatedToEquality(z, IntegerValue(1));
750  z_lits.push_back(z_lit);
751  LinearExpression local_expr;
752  local_expr.vars = NegationOf(exprs[i].vars);
753  local_expr.vars.push_back(target);
754  local_expr.coeffs = exprs[i].coeffs;
755  local_expr.coeffs.push_back(IntegerValue(1));
757  {z_lit}, local_expr.vars, local_expr.coeffs, exprs[i].offset, model);
758  upper_bound->RegisterWith(watcher);
759  model->TakeOwnership(upper_bound);
760 
761  CHECK(lc_exactly_one.AddLiteralTerm(z_lit, IntegerValue(1)));
762  }
763  model->Add(ExactlyOneConstraint(z_lits));
764 
765  // For the relaxation, we use different constraints with a stronger linear
766  // relaxation as explained in the .h
767  // TODO(user): Consider passing the x_vars to this method instead of
768  // computing it here.
769  std::vector<IntegerVariable> x_vars;
770  for (int i = 0; i < num_exprs; ++i) {
771  x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
772  }
774  // All expressions should only contain positive variables.
775  DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable var) {
776  return VariableIsPositive(var);
777  }));
778 
779  std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
780  num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
781 
782  IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
783  for (int i = 0; i < num_exprs; ++i) {
784  for (int j = 0; j < num_exprs; ++j) {
785  if (i == j) continue;
786  for (const IntegerVariable x_var : x_vars) {
787  const IntegerValue lb = integer_trail->LevelZeroLowerBound(x_var);
788  const IntegerValue ub = integer_trail->LevelZeroUpperBound(x_var);
789  const IntegerValue diff =
790  GetCoefficient(x_var, exprs[j]) - GetCoefficient(x_var, exprs[i]);
791  sum_of_max_corner_diff[i][j] += std::max(diff * lb, diff * ub);
792  }
793  }
794  }
795  for (int i = 0; i < num_exprs; ++i) {
796  LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
797  lc.AddTerm(target, IntegerValue(1));
798  for (int j = 0; j < exprs[i].vars.size(); ++j) {
799  lc.AddTerm(exprs[i].vars[j], -exprs[i].coeffs[j]);
800  }
801  for (int j = 0; j < num_exprs; ++j) {
802  CHECK(lc.AddLiteralTerm(z_lits[j],
803  -exprs[j].offset - sum_of_max_corner_diff[i][j]));
804  }
805  relaxation->linear_constraints.push_back(lc.Build());
806  }
807 
808  relaxation->linear_constraints.push_back(lc_exactly_one.Build());
809  return z_vars;
810 }
811 
812 void AppendLinearConstraintRelaxation(const ConstraintProto& constraint_proto,
813  const int linearization_level,
814  const Model& model,
815  LinearRelaxation* relaxation) {
816  auto* mapping = model.Get<CpModelMapping>();
817 
818  // Note that we ignore the holes in the domain.
819  //
820  // TODO(user): In LoadLinearConstraint() we already created intermediate
821  // Booleans for each disjoint interval, we should reuse them here if
822  // possible.
823  //
824  // TODO(user): process the "at most one" part of a == 1 separately?
825  const IntegerValue rhs_domain_min =
826  IntegerValue(constraint_proto.linear().domain(0));
827  const IntegerValue rhs_domain_max =
828  IntegerValue(constraint_proto.linear().domain(
829  constraint_proto.linear().domain_size() - 1));
830  if (rhs_domain_min == std::numeric_limits<int64_t>::min() &&
831  rhs_domain_max == std::numeric_limits<int64_t>::max())
832  return;
833 
834  if (!HasEnforcementLiteral(constraint_proto)) {
835  LinearConstraintBuilder lc(&model, rhs_domain_min, rhs_domain_max);
836  for (int i = 0; i < constraint_proto.linear().vars_size(); i++) {
837  const int ref = constraint_proto.linear().vars(i);
838  const int64_t coeff = constraint_proto.linear().coeffs(i);
839  lc.AddTerm(mapping->Integer(ref), IntegerValue(coeff));
840  }
841  relaxation->linear_constraints.push_back(lc.Build());
842  return;
843  }
844 
845  // Reified version.
846  if (linearization_level < 2) return;
847 
848  // We linearize fully reified constraints of size 1 all together for a given
849  // variable. But we need to process half-reified ones.
850  if (!mapping->IsHalfEncodingConstraint(&constraint_proto) &&
851  constraint_proto.linear().vars_size() <= 1) {
852  return;
853  }
854 
855  std::vector<Literal> enforcing_literals;
856  enforcing_literals.reserve(constraint_proto.enforcement_literal_size());
857  for (const int enforcement_ref : constraint_proto.enforcement_literal()) {
858  enforcing_literals.push_back(mapping->Literal(enforcement_ref));
859  }
860  LinearExpression expr;
861  expr.vars.reserve(constraint_proto.linear().vars_size());
862  expr.coeffs.reserve(constraint_proto.linear().vars_size());
863  for (int i = 0; i < constraint_proto.linear().vars_size(); i++) {
864  int ref = constraint_proto.linear().vars(i);
865  IntegerValue coeff(constraint_proto.linear().coeffs(i));
866  if (!RefIsPositive(ref)) {
867  ref = PositiveRef(ref);
868  coeff = -coeff;
869  }
870  const IntegerVariable int_var = mapping->Integer(ref);
871  expr.vars.push_back(int_var);
872  expr.coeffs.push_back(coeff);
873  }
874  AppendEnforcedLinearExpression(enforcing_literals, expr, rhs_domain_min,
875  rhs_domain_max, model, relaxation);
876 }
877 
878 } // namespace sat
879 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:498
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:897
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:898
#define DCHECK(condition)
Definition: base/logging.h:892
#define VLOG(verboselevel)
Definition: base/logging.h:986
std::vector< IntegerVariable > Integers(const List &list) const
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
Definition: integer.cc:250
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
Definition: integer.cc:108
void RegisterWith(GenericLiteralWatcher *watcher)
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1313
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1360
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
Definition: integer.cc:605
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1355
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1305
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void AddTerm(IntegerVariable var, IntegerValue coeff)
Literal(int signed_value)
Definition: sat_base.h:69
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
CpModelProto const * model_proto
const Constraint * ct
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
GRBmodel * model
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:200
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
Definition: integer.h:1478
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
std::vector< IntegerVariable > AppendLinMaxRelaxation(IntegerVariable target, const std::vector< LinearExpression > &exprs, Model *model, LinearRelaxation *relaxation)
IntegerValue LinExprLowerBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
std::function< IntervalVariable(Model *)> NewInterval(int64_t min_start, int64_t max_end, int64_t size)
Definition: intervals.h:633
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:878
void AppendLinearConstraintRelaxation(const ConstraintProto &constraint_proto, const int linearization_level, const Model &model, LinearRelaxation *relaxation)
bool RefIsPositive(int ref)
const LiteralIndex kNoLiteralIndex(-1)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
Definition: integer.h:1417
bool HasEnforcementLiteral(const ConstraintProto &ct)
std::function< std::vector< IntegerEncoder::ValueLiteralPair >Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1593
bool AppendFullEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
const IntegerVariable kNoIntegerVariable(-1)
LinearExpression CanonicalizeExpr(const LinearExpression &expr)
void AppendMaxRelaxation(IntegerVariable target, const std::vector< IntegerVariable > &vars, int linearization_level, Model *model, LinearRelaxation *relaxation)
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
Definition: integer.h:1449
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
void TryToLinearizeConstraint(const CpModelProto &model_proto, const ConstraintProto &ct, Model *model, int linearization_level, LinearRelaxation *relaxation)
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1431
void AppendPartialEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:29
std::function< void(Model *)> SpanOfIntervals(IntervalVariable span, const std::vector< IntervalVariable > &intervals)
void AddCumulativeCut(const std::vector< IntervalVariable > &intervals, const std::vector< IntegerVariable > &demands, IntegerValue capacity_upper_bound, Model *model, LinearRelaxation *relaxation)
void AppendNoOverlapRelaxation(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
Definition: integer.h:1484
std::function< IntervalVariable(Model *)> NewOptionalInterval(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition: intervals.h:663
IntegerValue LinExprUpperBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
Definition: integer.h:1472
void AppendCumulativeRelaxation(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation)
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:85
int index
Definition: pack.cc:509
int64_t delta
Definition: resource.cc:1692
IntervalVar * interval
Definition: resource.cc:100
int64_t tail
int64_t head
std::vector< std::vector< Literal > > at_most_ones
std::vector< LinearConstraint > linear_constraints