OR-Tools  9.3
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 <algorithm>
17#include <cstdint>
18#include <limits>
19#include <utility>
20#include <vector>
21
22#include "absl/base/attributes.h"
23#include "absl/container/btree_map.h"
24#include "absl/container/flat_hash_set.h"
28#include "ortools/sat/circuit.h" // for ReindexArcs.
29#include "ortools/sat/clause.h"
30#include "ortools/sat/cp_model.pb.h"
33#include "ortools/sat/cuts.h"
35#include "ortools/sat/integer.h"
40#include "ortools/sat/model.h"
42#include "ortools/sat/sat_parameters.pb.h"
49
50namespace operations_research {
51namespace sat {
52
53bool AppendFullEncodingRelaxation(IntegerVariable var, const Model& model,
54 LinearRelaxation* relaxation) {
55 const auto* encoder = model.Get<IntegerEncoder>();
56 if (encoder == nullptr) return false;
57 if (!encoder->VariableIsFullyEncoded(var)) return false;
58
59 const auto& encoding = encoder->FullDomainEncoding(var);
60 const IntegerValue var_min = model.Get<IntegerTrail>()->LowerBound(var);
61
62 LinearConstraintBuilder at_least_one(&model, IntegerValue(1),
64 LinearConstraintBuilder encoding_ct(&model, var_min, var_min);
65 encoding_ct.AddTerm(var, IntegerValue(1));
66
67 // Create the constraint if all literal have a view.
68 std::vector<Literal> at_most_one;
69
70 for (const auto value_literal : encoding) {
71 const Literal lit = value_literal.literal;
72 const IntegerValue delta = value_literal.value - var_min;
73 DCHECK_GE(delta, IntegerValue(0));
74 at_most_one.push_back(lit);
75 if (!at_least_one.AddLiteralTerm(lit, IntegerValue(1))) return false;
76 if (delta != IntegerValue(0)) {
77 if (!encoding_ct.AddLiteralTerm(lit, -delta)) return false;
78 }
79 }
80
81 relaxation->linear_constraints.push_back(at_least_one.Build());
82 relaxation->linear_constraints.push_back(encoding_ct.Build());
83 relaxation->at_most_ones.push_back(at_most_one);
84 return true;
85}
86
87namespace {
88
89std::pair<IntegerValue, IntegerValue> GetMinAndMaxNotEncoded(
90 IntegerVariable var,
91 const absl::flat_hash_set<IntegerValue>& encoded_values,
92 const Model& model) {
93 const auto* domains = model.Get<IntegerDomains>();
94 if (domains == nullptr || var >= domains->size()) {
96 }
97
98 // The domain can be large, but the list of values shouldn't, so this
99 // runs in O(encoded_values.size());
100 IntegerValue min = kMaxIntegerValue;
101 for (const int64_t v : (*domains)[var].Values()) {
102 if (!encoded_values.contains(IntegerValue(v))) {
103 min = IntegerValue(v);
104 break;
105 }
106 }
107
108 IntegerValue max = kMinIntegerValue;
109 for (const int64_t v : (*domains)[NegationOf(var)].Values()) {
110 if (!encoded_values.contains(IntegerValue(-v))) {
111 max = IntegerValue(-v);
112 break;
113 }
114 }
115
116 return {min, max};
117}
118
119bool LinMaxContainsOnlyOneVarInExpressions(const ConstraintProto& ct) {
120 CHECK_EQ(ct.constraint_case(), ConstraintProto::ConstraintCase::kLinMax);
121 int current_var = -1;
122 for (const LinearExpressionProto& expr : ct.lin_max().exprs()) {
123 if (expr.vars().empty()) continue;
124 if (expr.vars().size() > 1) return false;
125 const int var = PositiveRef(expr.vars(0));
126 if (current_var == -1) {
127 current_var = var;
128 } else if (var != current_var) {
129 return false;
130 }
131 }
132 return true;
133}
134
135// Collect all the affines expressions in a LinMax constraint.
136// It checks that these are indeed affine expressions, and that they all share
137// the same variable.
138// It returns the shared variable, as well as a vector of pairs
139// (coefficient, offset) when each affine is coefficient * shared_var + offset.
140void CollectAffineExpressionWithSingleVariable(
141 const ConstraintProto& ct, CpModelMapping* mapping, IntegerVariable* var,
142 std::vector<std::pair<IntegerValue, IntegerValue>>* affines) {
143 DCHECK(LinMaxContainsOnlyOneVarInExpressions(ct));
144 CHECK_EQ(ct.constraint_case(), ConstraintProto::ConstraintCase::kLinMax);
146 affines->clear();
147 for (const LinearExpressionProto& expr : ct.lin_max().exprs()) {
148 if (expr.vars().empty()) {
149 affines->push_back({IntegerValue(0), IntegerValue(expr.offset())});
150 } else {
151 CHECK_EQ(expr.vars().size(), 1);
152 const IntegerVariable affine_var = mapping->Integer(expr.vars(0));
153 if (*var == kNoIntegerVariable) {
154 *var = PositiveVariable(affine_var);
155 }
156 if (VariableIsPositive(affine_var)) {
157 CHECK_EQ(affine_var, *var);
158 affines->push_back(
159 {IntegerValue(expr.coeffs(0)), IntegerValue(expr.offset())});
160 } else {
161 CHECK_EQ(NegationOf(affine_var), *var);
162 affines->push_back(
163 {IntegerValue(-expr.coeffs(0)), IntegerValue(expr.offset())});
164 }
165 }
166 }
167}
168
169} // namespace
170
172 const Model& model,
173 LinearRelaxation* relaxation,
174 int* num_tight, int* num_loose) {
175 const auto* encoder = model.Get<IntegerEncoder>();
176 const auto* integer_trail = model.Get<IntegerTrail>();
177 if (encoder == nullptr || integer_trail == nullptr) return;
178
179 std::vector<Literal> at_most_one_ct;
180 absl::flat_hash_set<IntegerValue> encoded_values;
181 std::vector<ValueLiteralPair> encoding;
182 {
183 const std::vector<ValueLiteralPair>& initial_encoding =
184 encoder->PartialDomainEncoding(var);
185 if (initial_encoding.empty()) return;
186 for (const auto value_literal : initial_encoding) {
187 const Literal literal = value_literal.literal;
188
189 // Note that we skip pairs that do not have an Integer view.
190 if (encoder->GetLiteralView(literal) == kNoIntegerVariable &&
191 encoder->GetLiteralView(literal.Negated()) == kNoIntegerVariable) {
192 continue;
193 }
194
195 encoding.push_back(value_literal);
196 at_most_one_ct.push_back(literal);
197 encoded_values.insert(value_literal.value);
198 }
199 }
200 if (encoded_values.empty()) return;
201
202 // TODO(user): PartialDomainEncoding() filter pair corresponding to literal
203 // set to false, however the initial variable Domain is not always updated. As
204 // a result, these min/max can be larger than in reality. Try to fix this even
205 // if in practice this is a rare occurence, as the presolve should have
206 // propagated most of what we can.
207 const auto [min_not_encoded, max_not_encoded] =
208 GetMinAndMaxNotEncoded(var, encoded_values, model);
209
210 // This means that there are no non-encoded value and we have a full encoding.
211 // We substract the minimum value to reduce its size.
212 if (min_not_encoded == kMaxIntegerValue) {
213 const IntegerValue rhs = encoding[0].value;
214 LinearConstraintBuilder at_least_one(&model, IntegerValue(1),
216 LinearConstraintBuilder encoding_ct(&model, rhs, rhs);
217 encoding_ct.AddTerm(var, IntegerValue(1));
218 for (const auto value_literal : encoding) {
219 const Literal lit = value_literal.literal;
220 CHECK(at_least_one.AddLiteralTerm(lit, IntegerValue(1)));
221
222 const IntegerValue delta = value_literal.value - rhs;
223 if (delta != IntegerValue(0)) {
224 CHECK_GE(delta, IntegerValue(0));
225 CHECK(encoding_ct.AddLiteralTerm(lit, -delta));
226 }
227 }
228
229 relaxation->linear_constraints.push_back(at_least_one.Build());
230 relaxation->linear_constraints.push_back(encoding_ct.Build());
231 relaxation->at_most_ones.push_back(at_most_one_ct);
232 ++*num_tight;
233 return;
234 }
235
236 // In this special case, the two constraints below can be merged into an
237 // equality: var = rhs + sum l_i * (value_i - rhs).
238 if (min_not_encoded == max_not_encoded) {
239 const IntegerValue rhs = min_not_encoded;
240 LinearConstraintBuilder encoding_ct(&model, rhs, rhs);
241 encoding_ct.AddTerm(var, IntegerValue(1));
242 for (const auto value_literal : encoding) {
243 CHECK(encoding_ct.AddLiteralTerm(value_literal.literal,
244 rhs - value_literal.value));
245 }
246 relaxation->at_most_ones.push_back(at_most_one_ct);
247 relaxation->linear_constraints.push_back(encoding_ct.Build());
248 ++*num_tight;
249 return;
250 }
251
252 // min + sum l_i * (value_i - min) <= var.
253 const IntegerValue d_min = min_not_encoded;
254 LinearConstraintBuilder lower_bound_ct(&model, d_min, kMaxIntegerValue);
255 lower_bound_ct.AddTerm(var, IntegerValue(1));
256 for (const auto value_literal : encoding) {
257 CHECK(lower_bound_ct.AddLiteralTerm(value_literal.literal,
258 d_min - value_literal.value));
259 }
260
261 // var <= max + sum l_i * (value_i - max).
262 const IntegerValue d_max = max_not_encoded;
263 LinearConstraintBuilder upper_bound_ct(&model, kMinIntegerValue, d_max);
264 upper_bound_ct.AddTerm(var, IntegerValue(1));
265 for (const auto value_literal : encoding) {
266 CHECK(upper_bound_ct.AddLiteralTerm(value_literal.literal,
267 d_max - value_literal.value));
268 }
269
270 // Note that empty/trivial constraints will be filtered later.
271 relaxation->at_most_ones.push_back(at_most_one_ct);
272 relaxation->linear_constraints.push_back(lower_bound_ct.Build());
273 relaxation->linear_constraints.push_back(upper_bound_ct.Build());
274 ++*num_loose;
275}
276
278 const Model& model,
279 LinearRelaxation* relaxation) {
280 const auto* integer_trail = model.Get<IntegerTrail>();
281 const auto* encoder = model.Get<IntegerEncoder>();
282 if (integer_trail == nullptr || encoder == nullptr) return;
283
284 const absl::btree_map<IntegerValue, Literal>& greater_than_encoding =
285 encoder->PartialGreaterThanEncoding(var);
286 if (greater_than_encoding.empty()) return;
287
288 // Start by the var >= side.
289 // And also add the implications between used literals.
290 {
291 IntegerValue prev_used_bound = integer_trail->LowerBound(var);
292 LinearConstraintBuilder lb_constraint(&model, prev_used_bound,
294 lb_constraint.AddTerm(var, IntegerValue(1));
295 LiteralIndex prev_literal_index = kNoLiteralIndex;
296 for (const auto entry : greater_than_encoding) {
297 if (entry.first <= prev_used_bound) continue;
298
299 const LiteralIndex literal_index = entry.second.Index();
300 const IntegerValue diff = prev_used_bound - entry.first;
301
302 // Skip the entry if the literal doesn't have a view.
303 if (!lb_constraint.AddLiteralTerm(entry.second, diff)) continue;
304 if (prev_literal_index != kNoLiteralIndex) {
305 // Add var <= prev_var, which is the same as var + not(prev_var) <= 1
306 relaxation->at_most_ones.push_back(
307 {Literal(literal_index), Literal(prev_literal_index).Negated()});
308 }
309 prev_used_bound = entry.first;
310 prev_literal_index = literal_index;
311 }
312 relaxation->linear_constraints.push_back(lb_constraint.Build());
313 }
314
315 // Do the same for the var <= side by using NegationOfVar().
316 // Note that we do not need to add the implications between literals again.
317 {
318 IntegerValue prev_used_bound = integer_trail->LowerBound(NegationOf(var));
319 LinearConstraintBuilder lb_constraint(&model, prev_used_bound,
321 lb_constraint.AddTerm(var, IntegerValue(-1));
322 for (const auto entry :
323 encoder->PartialGreaterThanEncoding(NegationOf(var))) {
324 if (entry.first <= prev_used_bound) continue;
325 const IntegerValue diff = prev_used_bound - entry.first;
326
327 // Skip the entry if the literal doesn't have a view.
328 if (!lb_constraint.AddLiteralTerm(entry.second, diff)) continue;
329 prev_used_bound = entry.first;
330 }
331 relaxation->linear_constraints.push_back(lb_constraint.Build());
332 }
333}
334
335namespace {
336// Adds enforcing_lit => target <= bounding_var to relaxation.
337void AppendEnforcedUpperBound(const Literal enforcing_lit,
338 const IntegerVariable target,
339 const IntegerVariable bounding_var, Model* model,
340 LinearRelaxation* relaxation) {
341 IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
342 const IntegerValue max_target_value = integer_trail->UpperBound(target);
343 const IntegerValue min_var_value = integer_trail->LowerBound(bounding_var);
344 const IntegerValue max_term_value = max_target_value - min_var_value;
345 LinearConstraintBuilder lc(model, kMinIntegerValue, max_term_value);
346 lc.AddTerm(target, IntegerValue(1));
347 lc.AddTerm(bounding_var, IntegerValue(-1));
348 CHECK(lc.AddLiteralTerm(enforcing_lit, max_term_value));
349 relaxation->linear_constraints.push_back(lc.Build());
350}
351
352// Adds {enforcing_lits} => rhs_domain_min <= expr <= rhs_domain_max.
353// Requires expr offset to be 0.
354void AppendEnforcedLinearExpression(
355 const std::vector<Literal>& enforcing_literals,
356 const LinearExpression& expr, const IntegerValue rhs_domain_min,
357 const IntegerValue rhs_domain_max, const Model& model,
358 LinearRelaxation* relaxation) {
359 CHECK_EQ(expr.offset, IntegerValue(0));
360 const LinearExpression canonical_expr = CanonicalizeExpr(expr);
361 const IntegerTrail* integer_trail = model.Get<IntegerTrail>();
362 const IntegerValue min_expr_value =
363 LinExprLowerBound(canonical_expr, *integer_trail);
364
365 if (rhs_domain_min > min_expr_value) {
366 // And(ei) => terms >= rhs_domain_min
367 // <=> Sum_i (~ei * (rhs_domain_min - min_expr_value)) + terms >=
368 // rhs_domain_min
369 LinearConstraintBuilder lc(&model, rhs_domain_min, kMaxIntegerValue);
370 for (const Literal& literal : enforcing_literals) {
371 CHECK(lc.AddLiteralTerm(literal.Negated(),
372 rhs_domain_min - min_expr_value));
373 }
374 for (int i = 0; i < canonical_expr.vars.size(); i++) {
375 lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
376 }
377 relaxation->linear_constraints.push_back(lc.Build());
378 }
379 const IntegerValue max_expr_value =
380 LinExprUpperBound(canonical_expr, *integer_trail);
381 if (rhs_domain_max < max_expr_value) {
382 // And(ei) => terms <= rhs_domain_max
383 // <=> Sum_i (~ei * (rhs_domain_max - max_expr_value)) + terms <=
384 // rhs_domain_max
385 LinearConstraintBuilder lc(&model, kMinIntegerValue, rhs_domain_max);
386 for (const Literal& literal : enforcing_literals) {
387 CHECK(lc.AddLiteralTerm(literal.Negated(),
388 rhs_domain_max - max_expr_value));
389 }
390 for (int i = 0; i < canonical_expr.vars.size(); i++) {
391 lc.AddTerm(canonical_expr.vars[i], canonical_expr.coeffs[i]);
392 }
393 relaxation->linear_constraints.push_back(lc.Build());
394 }
395}
396
397bool AllLiteralsHaveViews(const IntegerEncoder& encoder,
398 const std::vector<Literal>& literals) {
399 for (const Literal lit : literals) {
400 if (!encoder.LiteralOrNegationHasView(lit)) return false;
401 }
402 return true;
403}
404
405} // namespace
406
407void AppendBoolOrRelaxation(const ConstraintProto& ct, Model* model,
408 LinearRelaxation* relaxation) {
409 auto* mapping = model->GetOrCreate<CpModelMapping>();
410 LinearConstraintBuilder lc(model, IntegerValue(1), kMaxIntegerValue);
411 for (const int enforcement_ref : ct.enforcement_literal()) {
412 CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)),
413 IntegerValue(1)));
414 }
415 for (const int ref : ct.bool_or().literals()) {
416 CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
417 }
418 relaxation->linear_constraints.push_back(lc.Build());
419}
420
421void AppendBoolAndRelaxation(const ConstraintProto& ct, Model* model,
422 LinearRelaxation* relaxation) {
423 // TODO(user): These constraints can be many, and if they are not regrouped
424 // in big at most ones, then they should probably only added lazily as cuts.
425 // Regroup this with future clique-cut separation logic.
426 if (!HasEnforcementLiteral(ct)) return;
427
428 auto* mapping = model->GetOrCreate<CpModelMapping>();
429 if (ct.enforcement_literal().size() == 1) {
430 const Literal enforcement = mapping->Literal(ct.enforcement_literal(0));
431 for (const int ref : ct.bool_and().literals()) {
432 relaxation->at_most_ones.push_back(
433 {enforcement, mapping->Literal(ref).Negated()});
434 }
435 return;
436 }
437
438 // Andi(e_i) => Andj(x_j)
439 // <=> num_rhs_terms <= Sum_j(x_j) + num_rhs_terms * Sum_i(~e_i)
440 int num_literals = ct.bool_and().literals_size();
441 LinearConstraintBuilder lc(model, IntegerValue(num_literals),
443 for (const int ref : ct.bool_and().literals()) {
444 CHECK(lc.AddLiteralTerm(mapping->Literal(ref), IntegerValue(1)));
445 }
446 for (const int enforcement_ref : ct.enforcement_literal()) {
447 CHECK(lc.AddLiteralTerm(mapping->Literal(NegatedRef(enforcement_ref)),
448 IntegerValue(num_literals)));
449 }
450 relaxation->linear_constraints.push_back(lc.Build());
451}
452
453void AppendAtMostOneRelaxation(const ConstraintProto& ct, Model* model,
454 LinearRelaxation* relaxation) {
455 if (HasEnforcementLiteral(ct)) return;
456
457 auto* mapping = model->GetOrCreate<CpModelMapping>();
458 relaxation->at_most_ones.push_back(
459 mapping->Literals(ct.at_most_one().literals()));
460}
461
462void AppendExactlyOneRelaxation(const ConstraintProto& ct, Model* model,
463 LinearRelaxation* relaxation) {
464 if (HasEnforcementLiteral(ct)) return;
465 auto* mapping = model->GetOrCreate<CpModelMapping>();
466 auto* encoder = model->GetOrCreate<IntegerEncoder>();
467
468 const std::vector<Literal> literals =
469 mapping->Literals(ct.exactly_one().literals());
470 if (AllLiteralsHaveViews(*encoder, literals)) {
471 LinearConstraintBuilder lc(model, IntegerValue(1), IntegerValue(1));
472 for (const Literal lit : literals) {
473 CHECK(lc.AddLiteralTerm(lit, IntegerValue(1)));
474 }
475 relaxation->linear_constraints.push_back(lc.Build());
476 } else {
477 // We just encode the at most one part that might be partially linearized
478 // later.
479 relaxation->at_most_ones.push_back(literals);
480 }
481}
482
484 int num_literals, Model* model, LinearRelaxation* relaxation) {
485 auto* encoder = model->GetOrCreate<IntegerEncoder>();
486
487 if (num_literals == 1) {
488 // This is not supposed to happen, but it is easy enough to cover, just
489 // in case. We might however want to use encoder->GetTrueLiteral().
490 const IntegerVariable var = model->Add(NewIntegerVariable(1, 1));
491 const Literal lit =
492 encoder->GetOrCreateLiteralAssociatedToEquality(var, IntegerValue(1));
493 return {lit};
494 }
495
496 if (num_literals == 2) {
497 const IntegerVariable var = model->Add(NewIntegerVariable(0, 1));
498 const Literal lit =
499 encoder->GetOrCreateLiteralAssociatedToEquality(var, IntegerValue(1));
500
501 // TODO(user): We shouldn't need to create this view ideally. Even better,
502 // we should be able to handle Literal natively in the linear relaxation,
503 // but that is a lot of work.
504 const IntegerVariable var2 = model->Add(NewIntegerVariable(0, 1));
505 encoder->AssociateToIntegerEqualValue(lit.Negated(), var2, IntegerValue(1));
506
507 return {lit, lit.Negated()};
508 }
509
510 std::vector<Literal> literals;
511 LinearConstraintBuilder lc_builder(model, IntegerValue(1), IntegerValue(1));
512 for (int i = 0; i < num_literals; ++i) {
513 const IntegerVariable var = model->Add(NewIntegerVariable(0, 1));
514 const Literal lit =
515 encoder->GetOrCreateLiteralAssociatedToEquality(var, IntegerValue(1));
516 literals.push_back(lit);
517 CHECK(lc_builder.AddLiteralTerm(lit, IntegerValue(1)));
518 }
519 model->Add(ExactlyOneConstraint(literals));
520 relaxation->linear_constraints.push_back(lc_builder.Build());
521 return literals;
522}
523
524void AppendCircuitRelaxation(const ConstraintProto& ct, Model* model,
525 LinearRelaxation* relaxation) {
526 if (HasEnforcementLiteral(ct)) return;
527 auto* mapping = model->GetOrCreate<CpModelMapping>();
528 const int num_arcs = ct.circuit().literals_size();
529 CHECK_EQ(num_arcs, ct.circuit().tails_size());
530 CHECK_EQ(num_arcs, ct.circuit().heads_size());
531
532 // Each node must have exactly one incoming and one outgoing arc (note
533 // that it can be the unique self-arc of this node too).
534 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
535 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
536 for (int i = 0; i < num_arcs; i++) {
537 const Literal arc = mapping->Literal(ct.circuit().literals(i));
538 const int tail = ct.circuit().tails(i);
539 const int head = ct.circuit().heads(i);
540
541 // Make sure this literal has a view.
543 outgoing_arc_constraints[tail].push_back(arc);
544 incoming_arc_constraints[head].push_back(arc);
545 }
546 for (const auto* node_map :
547 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
548 for (const auto& entry : *node_map) {
549 const std::vector<Literal>& exactly_one = entry.second;
550 if (exactly_one.size() > 1) {
551 LinearConstraintBuilder at_least_one_lc(model, IntegerValue(1),
553 for (const Literal l : exactly_one) {
554 CHECK(at_least_one_lc.AddLiteralTerm(l, IntegerValue(1)));
555 }
556
557 // We separate the two constraints.
558 relaxation->at_most_ones.push_back(exactly_one);
559 relaxation->linear_constraints.push_back(at_least_one_lc.Build());
560 }
561 }
562 }
563}
564
565void AppendRoutesRelaxation(const ConstraintProto& ct, Model* model,
566 LinearRelaxation* relaxation) {
567 if (HasEnforcementLiteral(ct)) return;
568 auto* mapping = model->GetOrCreate<CpModelMapping>();
569 const int num_arcs = ct.routes().literals_size();
570 CHECK_EQ(num_arcs, ct.routes().tails_size());
571 CHECK_EQ(num_arcs, ct.routes().heads_size());
572
573 // Each node except node zero must have exactly one incoming and one outgoing
574 // arc (note that it can be the unique self-arc of this node too). For node
575 // zero, the number of incoming arcs should be the same as the number of
576 // outgoing arcs.
577 absl::btree_map<int, std::vector<Literal>> incoming_arc_constraints;
578 absl::btree_map<int, std::vector<Literal>> outgoing_arc_constraints;
579 for (int i = 0; i < num_arcs; i++) {
580 const Literal arc = mapping->Literal(ct.routes().literals(i));
581 const int tail = ct.routes().tails(i);
582 const int head = ct.routes().heads(i);
583
584 // Make sure this literal has a view.
586 outgoing_arc_constraints[tail].push_back(arc);
587 incoming_arc_constraints[head].push_back(arc);
588 }
589 for (const auto* node_map :
590 {&outgoing_arc_constraints, &incoming_arc_constraints}) {
591 for (const auto& entry : *node_map) {
592 if (entry.first == 0) continue;
593 const std::vector<Literal>& exactly_one = entry.second;
594 if (exactly_one.size() > 1) {
595 LinearConstraintBuilder at_least_one_lc(model, IntegerValue(1),
597 for (const Literal l : exactly_one) {
598 CHECK(at_least_one_lc.AddLiteralTerm(l, IntegerValue(1)));
599 }
600
601 // We separate the two constraints.
602 relaxation->at_most_ones.push_back(exactly_one);
603 relaxation->linear_constraints.push_back(at_least_one_lc.Build());
604 }
605 }
606 }
607 LinearConstraintBuilder zero_node_balance_lc(model, IntegerValue(0),
608 IntegerValue(0));
609 for (const Literal& incoming_arc : incoming_arc_constraints[0]) {
610 CHECK(zero_node_balance_lc.AddLiteralTerm(incoming_arc, IntegerValue(1)));
611 }
612 for (const Literal& outgoing_arc : outgoing_arc_constraints[0]) {
613 CHECK(zero_node_balance_lc.AddLiteralTerm(outgoing_arc, IntegerValue(-1)));
614 }
615 relaxation->linear_constraints.push_back(zero_node_balance_lc.Build());
616}
617
618void AddCumulativeRelaxation(const std::vector<IntervalVariable>& intervals,
619 const std::vector<AffineExpression>& demands,
620 const std::vector<LinearExpression>& energies,
621 IntegerValue capacity_upper_bound, Model* model,
622 LinearRelaxation* relaxation) {
623 // TODO(user): Keep a map intervals -> helper, or ct_index->helper to avoid
624 // creating many helpers for the same constraint.
625 auto* helper = new SchedulingConstraintHelper(intervals, model);
626 model->TakeOwnership(helper);
627 const int num_intervals = helper->NumTasks();
628
629 IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
630
631 IntegerValue min_of_starts = kMaxIntegerValue;
632 IntegerValue max_of_ends = kMinIntegerValue;
633
634 int num_variable_sizes = 0;
635 int num_optionals = 0;
636
637 for (int index = 0; index < num_intervals; ++index) {
638 min_of_starts = std::min(min_of_starts, helper->StartMin(index));
639 max_of_ends = std::max(max_of_ends, helper->EndMax(index));
640
641 if (helper->IsOptional(index)) {
642 num_optionals++;
643 }
644
645 if (!helper->SizeIsFixed(index) ||
646 (!demands.empty() && !integer_trail->IsFixed(demands[index]))) {
647 num_variable_sizes++;
648 }
649 }
650
651 VLOG(2) << "Span [" << min_of_starts << ".." << max_of_ends << "] with "
652 << num_optionals << " optional intervals, and " << num_variable_sizes
653 << " variable size intervals out of " << num_intervals
654 << " intervals";
655
656 if (num_variable_sizes + num_optionals == 0) return;
657
658 const IntegerVariable span_start =
659 integer_trail->AddIntegerVariable(min_of_starts, max_of_ends);
660 const IntegerVariable span_size = integer_trail->AddIntegerVariable(
661 IntegerValue(0), max_of_ends - min_of_starts);
662 const IntegerVariable span_end =
663 integer_trail->AddIntegerVariable(min_of_starts, max_of_ends);
664
665 IntervalVariable span_var;
666 if (num_optionals < num_intervals) {
667 span_var = model->Add(NewInterval(span_start, span_end, span_size));
668 } else {
669 const Literal span_lit = Literal(model->Add(NewBooleanVariable()), true);
670 span_var = model->Add(
671 NewOptionalInterval(span_start, span_end, span_size, span_lit));
672 }
673
674 model->Add(SpanOfIntervals(span_var, intervals));
675
676 LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
677 lc.AddTerm(span_size, -capacity_upper_bound);
678 for (int i = 0; i < num_intervals; ++i) {
679 const IntegerValue demand_lower_bound =
680 demands.empty() ? IntegerValue(1)
681 : integer_trail->LowerBound(demands[i]);
682 const bool demand_is_fixed =
683 demands.empty() || integer_trail->IsFixed(demands[i]);
684 if (!helper->IsOptional(i)) {
685 if (demand_is_fixed) {
686 lc.AddTerm(helper->Sizes()[i], demand_lower_bound);
687 } else if (!helper->SizeIsFixed(i) &&
688 (!energies[i].vars.empty() || energies[i].offset != -1)) {
689 // We prefer the energy additional info instead of the McCormick
690 // relaxation.
691 lc.AddLinearExpression(energies[i]);
692 } else {
693 lc.AddQuadraticLowerBound(helper->Sizes()[i], demands[i],
694 integer_trail);
695 }
696 } else {
697 if (!lc.AddLiteralTerm(helper->PresenceLiteral(i),
698 helper->SizeMin(i) * demand_lower_bound)) {
699 return;
700 }
701 }
702 }
703 relaxation->linear_constraints.push_back(lc.Build());
704}
705
707 const ConstraintProto& ct, Model* model,
708 LinearRelaxation* relaxation) {
709 CHECK(ct.has_cumulative());
710 if (HasEnforcementLiteral(ct)) return;
711
712 auto* mapping = model->GetOrCreate<CpModelMapping>();
713 std::vector<IntervalVariable> intervals =
714 mapping->Intervals(ct.cumulative().intervals());
715 const IntegerValue capacity_upper_bound =
716 model->GetOrCreate<IntegerTrail>()->UpperBound(
717 mapping->Affine(ct.cumulative().capacity()));
718
719 // Scan energies.
720 IntervalsRepository* intervals_repository =
721 model->GetOrCreate<IntervalsRepository>();
722
723 std::vector<LinearExpression> energies;
724 std::vector<AffineExpression> demands;
725 std::vector<AffineExpression> sizes;
726 for (int i = 0; i < ct.cumulative().demands_size(); ++i) {
727 demands.push_back(mapping->Affine(ct.cumulative().demands(i)));
728 sizes.push_back(intervals_repository->Size(intervals[i]));
729 }
730 LinearizeInnerProduct(demands, sizes, model, &energies);
731 AddCumulativeRelaxation(intervals, demands, energies, capacity_upper_bound,
732 model, relaxation);
733}
734
735void AppendNoOverlapRelaxation(const CpModelProto& model_proto,
736 const ConstraintProto& ct, Model* model,
737 LinearRelaxation* relaxation) {
738 CHECK(ct.has_no_overlap());
739 if (HasEnforcementLiteral(ct)) return;
740
741 auto* mapping = model->GetOrCreate<CpModelMapping>();
742 std::vector<IntervalVariable> intervals =
743 mapping->Intervals(ct.no_overlap().intervals());
744 AddCumulativeRelaxation(intervals, /*demands=*/{}, /*energies=*/{},
745 /*capacity_upper_bound=*/IntegerValue(1), model,
746 relaxation);
747}
748
749// Adds the energetic relaxation sum(areas) <= bounding box area.
750void AppendNoOverlap2dRelaxation(const ConstraintProto& ct, Model* model,
751 LinearRelaxation* relaxation) {
752 CHECK(ct.has_no_overlap_2d());
753 if (HasEnforcementLiteral(ct)) return;
754
755 auto* mapping = model->GetOrCreate<CpModelMapping>();
756 std::vector<IntervalVariable> x_intervals =
757 mapping->Intervals(ct.no_overlap_2d().x_intervals());
758 std::vector<IntervalVariable> y_intervals =
759 mapping->Intervals(ct.no_overlap_2d().y_intervals());
760
761 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
762 auto* intervals_repository = model->GetOrCreate<IntervalsRepository>();
763
764 IntegerValue x_min = kMaxIntegerValue;
765 IntegerValue x_max = kMinIntegerValue;
766 IntegerValue y_min = kMaxIntegerValue;
767 IntegerValue y_max = kMinIntegerValue;
768 std::vector<AffineExpression> x_sizes;
769 std::vector<AffineExpression> y_sizes;
770 for (int i = 0; i < ct.no_overlap_2d().x_intervals_size(); ++i) {
771 x_sizes.push_back(intervals_repository->Size(x_intervals[i]));
772 y_sizes.push_back(intervals_repository->Size(y_intervals[i]));
773 x_min = std::min(x_min, integer_trail->LevelZeroLowerBound(
774 intervals_repository->Start(x_intervals[i])));
775 x_max = std::max(x_max, integer_trail->LevelZeroUpperBound(
776 intervals_repository->End(x_intervals[i])));
777 y_min = std::min(y_min, integer_trail->LevelZeroLowerBound(
778 intervals_repository->Start(y_intervals[i])));
779 y_max = std::max(y_max, integer_trail->LevelZeroUpperBound(
780 intervals_repository->End(y_intervals[i])));
781 }
782
783 const IntegerValue max_area =
784 IntegerValue(CapProd(CapSub(x_max.value(), x_min.value()),
785 CapSub(y_max.value(), y_min.value())));
786 if (max_area == kMaxIntegerValue) return;
787
788 LinearConstraintBuilder lc(model, IntegerValue(0), max_area);
789 for (int i = 0; i < ct.no_overlap_2d().x_intervals_size(); ++i) {
790 if (intervals_repository->IsPresent(x_intervals[i]) &&
791 intervals_repository->IsPresent(y_intervals[i])) {
792 LinearConstraintBuilder linear_energy(model);
793 if (DetectLinearEncodingOfProducts(x_sizes[i], y_sizes[i], model,
794 &linear_energy)) {
795 lc.AddLinearExpression(linear_energy.BuildExpression());
796 } else {
797 lc.AddQuadraticLowerBound(x_sizes[i], y_sizes[i], integer_trail);
798 }
799 } else if (intervals_repository->IsPresent(x_intervals[i]) ||
800 intervals_repository->IsPresent(y_intervals[i]) ||
801 (intervals_repository->PresenceLiteral(x_intervals[i]) ==
802 intervals_repository->PresenceLiteral(y_intervals[i]))) {
803 // We have only one active literal.
804 const Literal presence_literal =
805 intervals_repository->IsPresent(x_intervals[i])
806 ? intervals_repository->PresenceLiteral(y_intervals[i])
807 : intervals_repository->PresenceLiteral(x_intervals[i]);
808 const IntegerValue area_min =
809 integer_trail->LevelZeroLowerBound(x_sizes[i]) *
810 integer_trail->LevelZeroLowerBound(y_sizes[i]);
811 if (area_min != 0) {
812 // Not including the term if we don't have a view is ok.
813 (void)lc.AddLiteralTerm(presence_literal, area_min);
814 }
815 }
816 }
817 relaxation->linear_constraints.push_back(lc.Build());
818}
819
820void AppendLinMaxRelaxationPart1(const ConstraintProto& ct, Model* model,
821 LinearRelaxation* relaxation) {
822 auto* mapping = model->GetOrCreate<CpModelMapping>();
823
824 // We want to linearize target = max(exprs[1], exprs[2], ..., exprs[d]).
825 // Part 1: Encode target >= max(exprs[1], exprs[2], ..., exprs[d])
826 const LinearExpression negated_target =
827 NegationOf(mapping->GetExprFromProto(ct.lin_max().target()));
828 for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
829 const LinearExpression expr =
830 mapping->GetExprFromProto(ct.lin_max().exprs(i));
831 LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
832 lc.AddLinearExpression(negated_target);
833 lc.AddLinearExpression(expr);
834 relaxation->linear_constraints.push_back(lc.Build());
835 }
836}
837
838// TODO(user): experiment with:
839// 1) remove this code
840// 2) keep this code
841// 3) remove this code and create the cut generator at level 1.
842void AppendMaxAffineRelaxation(const ConstraintProto& ct, Model* model,
843 LinearRelaxation* relaxation) {
844 IntegerVariable var;
845 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
846 auto* mapping = model->GetOrCreate<CpModelMapping>();
847 CollectAffineExpressionWithSingleVariable(ct, mapping, &var, &affines);
848 if (var == kNoIntegerVariable ||
849 model->GetOrCreate<IntegerTrail>()->IsFixed(var)) {
850 return;
851 }
852
854 const LinearExpression target_expr =
855 PositiveVarExpr(mapping->GetExprFromProto(ct.lin_max().target()));
856 relaxation->linear_constraints.push_back(
857 BuildMaxAffineUpConstraint(target_expr, var, affines, model));
858}
859
860void AddMaxAffineCutGenerator(const ConstraintProto& ct, Model* model,
861 LinearRelaxation* relaxation) {
862 IntegerVariable var;
863 std::vector<std::pair<IntegerValue, IntegerValue>> affines;
864 auto* mapping = model->GetOrCreate<CpModelMapping>();
865 CollectAffineExpressionWithSingleVariable(ct, mapping, &var, &affines);
866 if (var == kNoIntegerVariable ||
867 model->GetOrCreate<IntegerTrail>()->IsFixed(var)) {
868 return;
869 }
870
871 // If the target is constant, propagation is enough.
872 if (ct.lin_max().target().vars().empty()) return;
873
874 const LinearExpression target_expr =
875 PositiveVarExpr(mapping->GetExprFromProto(ct.lin_max().target()));
876 relaxation->cut_generators.push_back(CreateMaxAffineCutGenerator(
877 target_expr, var, affines, "AffineMax", model));
878}
879
880// Part 2: Encode upper bound on X.
881//
882// Add linking constraint to the CP solver
883// sum zi = 1 and for all i, zi => max = expr_i.
885 IntegerVariable target, const std::vector<Literal>& alternative_literals,
886 const std::vector<LinearExpression>& exprs, Model* model,
887 LinearRelaxation* relaxation) {
888 const int num_exprs = exprs.size();
889 GenericLiteralWatcher* watcher = model->GetOrCreate<GenericLiteralWatcher>();
890
891 // First add the CP constraints.
892 for (int i = 0; i < num_exprs; ++i) {
893 LinearExpression local_expr;
894 local_expr.vars = NegationOf(exprs[i].vars);
895 local_expr.vars.push_back(target);
896 local_expr.coeffs = exprs[i].coeffs;
897 local_expr.coeffs.push_back(IntegerValue(1));
899 new IntegerSumLE({alternative_literals[i]}, local_expr.vars,
900 local_expr.coeffs, exprs[i].offset, model);
901 upper_bound->RegisterWith(watcher);
902 model->TakeOwnership(upper_bound);
903 }
904
905 // For the relaxation, we use different constraints with a stronger linear
906 // relaxation as explained in the .h
907 //
908 // TODO(user): Consider passing the x_vars to this method instead of
909 // computing it here.
910 std::vector<IntegerVariable> x_vars;
911 for (int i = 0; i < num_exprs; ++i) {
912 x_vars.insert(x_vars.end(), exprs[i].vars.begin(), exprs[i].vars.end());
913 }
915
916 // All expressions should only contain positive variables.
917 DCHECK(std::all_of(x_vars.begin(), x_vars.end(), [](IntegerVariable var) {
918 return VariableIsPositive(var);
919 }));
920
921 std::vector<std::vector<IntegerValue>> sum_of_max_corner_diff(
922 num_exprs, std::vector<IntegerValue>(num_exprs, IntegerValue(0)));
923
924 IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
925 for (int i = 0; i < num_exprs; ++i) {
926 for (int j = 0; j < num_exprs; ++j) {
927 if (i == j) continue;
928 for (const IntegerVariable x_var : x_vars) {
929 const IntegerValue lb = integer_trail->LevelZeroLowerBound(x_var);
930 const IntegerValue ub = integer_trail->LevelZeroUpperBound(x_var);
931 const IntegerValue diff =
932 GetCoefficient(x_var, exprs[j]) - GetCoefficient(x_var, exprs[i]);
933 sum_of_max_corner_diff[i][j] += std::max(diff * lb, diff * ub);
934 }
935 }
936 }
937 for (int i = 0; i < num_exprs; ++i) {
938 LinearConstraintBuilder lc(model, kMinIntegerValue, IntegerValue(0));
939 lc.AddTerm(target, IntegerValue(1));
940 for (int j = 0; j < exprs[i].vars.size(); ++j) {
941 lc.AddTerm(exprs[i].vars[j], -exprs[i].coeffs[j]);
942 }
943 for (int j = 0; j < num_exprs; ++j) {
944 CHECK(lc.AddLiteralTerm(alternative_literals[j],
945 -exprs[j].offset - sum_of_max_corner_diff[i][j]));
946 }
947 relaxation->linear_constraints.push_back(lc.Build());
948 }
949}
950
951void AppendLinearConstraintRelaxation(const ConstraintProto& ct,
952 bool linearize_enforced_constraints,
953 Model* model,
954 LinearRelaxation* relaxation) {
955 auto* mapping = model->Get<CpModelMapping>();
956
957 // Note that we ignore the holes in the domain.
958 //
959 // TODO(user): In LoadLinearConstraint() we already created intermediate
960 // Booleans for each disjoint interval, we should reuse them here if
961 // possible.
962 //
963 // TODO(user): process the "at most one" part of a == 1 separately?
964 const IntegerValue rhs_domain_min = IntegerValue(ct.linear().domain(0));
965 const IntegerValue rhs_domain_max =
966 IntegerValue(ct.linear().domain(ct.linear().domain_size() - 1));
967 if (rhs_domain_min == std::numeric_limits<int64_t>::min() &&
968 rhs_domain_max == std::numeric_limits<int64_t>::max())
969 return;
970
972 LinearConstraintBuilder lc(model, rhs_domain_min, rhs_domain_max);
973 for (int i = 0; i < ct.linear().vars_size(); i++) {
974 const int ref = ct.linear().vars(i);
975 const int64_t coeff = ct.linear().coeffs(i);
976 lc.AddTerm(mapping->Integer(ref), IntegerValue(coeff));
977 }
978 relaxation->linear_constraints.push_back(lc.Build());
979 return;
980 }
981
982 // Reified version.
983 if (!linearize_enforced_constraints) return;
984
985 // We linearize fully reified constraints of size 1 all together for a given
986 // variable. But we need to process half-reified ones.
987 if (!mapping->IsHalfEncodingConstraint(&ct) && ct.linear().vars_size() <= 1) {
988 return;
989 }
990
991 std::vector<Literal> enforcing_literals;
992 enforcing_literals.reserve(ct.enforcement_literal_size());
993 for (const int enforcement_ref : ct.enforcement_literal()) {
994 enforcing_literals.push_back(mapping->Literal(enforcement_ref));
995 }
996 LinearExpression expr;
997 expr.vars.reserve(ct.linear().vars_size());
998 expr.coeffs.reserve(ct.linear().vars_size());
999 for (int i = 0; i < ct.linear().vars_size(); i++) {
1000 int ref = ct.linear().vars(i);
1001 IntegerValue coeff(ct.linear().coeffs(i));
1002 if (!RefIsPositive(ref)) {
1003 ref = PositiveRef(ref);
1004 coeff = -coeff;
1005 }
1006 const IntegerVariable int_var = mapping->Integer(ref);
1007 expr.vars.push_back(int_var);
1008 expr.coeffs.push_back(coeff);
1009 }
1010 AppendEnforcedLinearExpression(enforcing_literals, expr, rhs_domain_min,
1011 rhs_domain_max, *model, relaxation);
1012}
1013
1014// Add a static and a dynamic linear relaxation of the CP constraint to the set
1015// of linear constraints. The highest linearization_level is, the more types of
1016// constraint we encode. This method should be called only for
1017// linearization_level > 0. The static part is just called a relaxation and is
1018// called at the root node of the search. The dynamic part is implemented
1019// through a set of linear cut generators that will be called throughout the
1020// search.
1021//
1022// TODO(user): In full generality, we could encode all the constraint as an LP.
1023// TODO(user): Add unit tests for this method.
1024// TODO(user): Remove and merge with model loading.
1025void TryToLinearizeConstraint(const CpModelProto& model_proto,
1026 const ConstraintProto& ct,
1027 int linearization_level, Model* model,
1028 LinearRelaxation* relaxation) {
1029 CHECK_EQ(model->GetOrCreate<SatSolver>()->CurrentDecisionLevel(), 0);
1030 DCHECK_GT(linearization_level, 0);
1031
1032 switch (ct.constraint_case()) {
1033 case ConstraintProto::ConstraintCase::kBoolOr: {
1034 if (linearization_level > 1) {
1035 AppendBoolOrRelaxation(ct, model, relaxation);
1036 }
1037 break;
1038 }
1039 case ConstraintProto::ConstraintCase::kBoolAnd: {
1040 if (linearization_level > 1) {
1041 AppendBoolAndRelaxation(ct, model, relaxation);
1042 }
1043 break;
1044 }
1045 case ConstraintProto::ConstraintCase::kAtMostOne: {
1046 AppendAtMostOneRelaxation(ct, model, relaxation);
1047 break;
1048 }
1049 case ConstraintProto::ConstraintCase::kExactlyOne: {
1050 AppendExactlyOneRelaxation(ct, model, relaxation);
1051 break;
1052 }
1053 case ConstraintProto::ConstraintCase::kIntProd: {
1054 // No relaxation, just a cut generator .
1055 AddIntProdCutGenerator(ct, linearization_level, model, relaxation);
1056 break;
1057 }
1058 case ConstraintProto::ConstraintCase::kLinMax: {
1059 AppendLinMaxRelaxationPart1(ct, model, relaxation);
1060 const bool is_affine_max = LinMaxContainsOnlyOneVarInExpressions(ct);
1061 if (is_affine_max) {
1062 AppendMaxAffineRelaxation(ct, model, relaxation);
1063 }
1064
1065 // Add cut generators.
1066 if (linearization_level > 1) {
1067 if (is_affine_max) {
1068 AddMaxAffineCutGenerator(ct, model, relaxation);
1069 } else {
1070 AddLinMaxCutGenerator(ct, model, relaxation);
1071 }
1072 }
1073 break;
1074 }
1075 case ConstraintProto::ConstraintCase::kAllDiff: {
1076 if (linearization_level > 1) {
1077 AddAllDiffCutGenerator(ct, model, relaxation);
1078 }
1079 break;
1080 }
1081 case ConstraintProto::ConstraintCase::kLinear: {
1083 ct, /*linearize_enforced_constraints=*/linearization_level > 1, model,
1084 relaxation);
1085 break;
1086 }
1087 case ConstraintProto::ConstraintCase::kCircuit: {
1088 AppendCircuitRelaxation(ct, model, relaxation);
1089 if (linearization_level > 1) {
1090 AddCircuitCutGenerator(ct, model, relaxation);
1091 }
1092 break;
1093 }
1094 case ConstraintProto::ConstraintCase::kRoutes: {
1095 AppendRoutesRelaxation(ct, model, relaxation);
1096 if (linearization_level > 1) {
1097 AddRoutesCutGenerator(ct, model, relaxation);
1098 }
1099 break;
1100 }
1101 case ConstraintProto::ConstraintCase::kNoOverlap: {
1102 if (linearization_level > 1) {
1104 AddNoOverlapCutGenerator(ct, model, relaxation);
1105 }
1106 break;
1107 }
1108 case ConstraintProto::ConstraintCase::kCumulative: {
1109 if (linearization_level > 1) {
1111 AddCumulativeCutGenerator(ct, model, relaxation);
1112 }
1113 break;
1114 }
1115 case ConstraintProto::ConstraintCase::kNoOverlap2D: {
1116 // Adds an energetic relaxation (sum of areas fits in bounding box).
1117 AppendNoOverlap2dRelaxation(ct, model, relaxation);
1118 if (linearization_level > 1) {
1119 // Adds a completion time cut generator and an energetic cut generator.
1120 AddNoOverlap2dCutGenerator(ct, model, relaxation);
1121 }
1122 break;
1123 }
1124 default: {
1125 }
1126 }
1127}
1128
1129// Cut generators.
1130
1131void AddCircuitCutGenerator(const ConstraintProto& ct, Model* m,
1132 LinearRelaxation* relaxation) {
1133 std::vector<int> tails(ct.circuit().tails().begin(),
1134 ct.circuit().tails().end());
1135 std::vector<int> heads(ct.circuit().heads().begin(),
1136 ct.circuit().heads().end());
1137 auto* mapping = m->GetOrCreate<CpModelMapping>();
1138 std::vector<Literal> literals = mapping->Literals(ct.circuit().literals());
1139 const int num_nodes = ReindexArcs(&tails, &heads);
1140
1142 num_nodes, tails, heads, literals, m));
1143}
1144
1145void AddRoutesCutGenerator(const ConstraintProto& ct, Model* m,
1146 LinearRelaxation* relaxation) {
1147 std::vector<int> tails(ct.routes().tails().begin(),
1148 ct.routes().tails().end());
1149 std::vector<int> heads(ct.routes().heads().begin(),
1150 ct.routes().heads().end());
1151 auto* mapping = m->GetOrCreate<CpModelMapping>();
1152 std::vector<Literal> literals = mapping->Literals(ct.routes().literals());
1153
1154 int num_nodes = 0;
1155 for (int i = 0; i < ct.routes().tails_size(); ++i) {
1156 num_nodes = std::max(num_nodes, 1 + ct.routes().tails(i));
1157 num_nodes = std::max(num_nodes, 1 + ct.routes().heads(i));
1158 }
1159 if (ct.routes().demands().empty() || ct.routes().capacity() == 0) {
1160 relaxation->cut_generators.push_back(
1161 CreateStronglyConnectedGraphCutGenerator(num_nodes, tails, heads,
1162 literals, m));
1163 } else {
1164 const std::vector<int64_t> demands(ct.routes().demands().begin(),
1165 ct.routes().demands().end());
1166 relaxation->cut_generators.push_back(CreateCVRPCutGenerator(
1167 num_nodes, tails, heads, literals, demands, ct.routes().capacity(), m));
1168 }
1169}
1170
1171void AddIntProdCutGenerator(const ConstraintProto& ct, int linearization_level,
1172 Model* m, LinearRelaxation* relaxation) {
1173 if (HasEnforcementLiteral(ct)) return;
1174 if (ct.int_prod().exprs_size() != 2) return;
1175 auto* mapping = m->GetOrCreate<CpModelMapping>();
1176
1177 // Constraint is z == x * y.
1178
1179 AffineExpression z = mapping->Affine(ct.int_prod().target());
1180 AffineExpression x = mapping->Affine(ct.int_prod().exprs(0));
1181 AffineExpression y = mapping->Affine(ct.int_prod().exprs(1));
1182
1183 IntegerTrail* const integer_trail = m->GetOrCreate<IntegerTrail>();
1184 IntegerValue x_lb = integer_trail->LowerBound(x);
1185 IntegerValue x_ub = integer_trail->UpperBound(x);
1186 IntegerValue y_lb = integer_trail->LowerBound(y);
1187 IntegerValue y_ub = integer_trail->UpperBound(y);
1188
1189 if (x == y) {
1190 // We currently only support variables with non-negative domains.
1191 if (x_lb < 0 && x_ub > 0) return;
1192
1193 // Change the sigh of x if its domain is non-positive.
1194 if (x_ub <= 0) {
1195 x = x.Negated();
1196 }
1197
1198 relaxation->cut_generators.push_back(
1199 CreateSquareCutGenerator(z, x, linearization_level, m));
1200 } else {
1201 // We currently only support variables with non-negative domains.
1202 if (x_lb < 0 && x_ub > 0) return;
1203 if (y_lb < 0 && y_ub > 0) return;
1204
1205 // Change signs to return to the case where all variables are a domain
1206 // with non negative values only.
1207 if (x_ub <= 0) {
1208 x = x.Negated();
1209 z = z.Negated();
1210 }
1211 if (y_ub <= 0) {
1212 y = y.Negated();
1213 z = z.Negated();
1214 }
1215
1216 relaxation->cut_generators.push_back(
1217 CreatePositiveMultiplicationCutGenerator(z, x, y, linearization_level,
1218 m));
1219 }
1220}
1221
1222void AddAllDiffCutGenerator(const ConstraintProto& ct, Model* m,
1223 LinearRelaxation* relaxation) {
1224 if (HasEnforcementLiteral(ct)) return;
1225 auto* mapping = m->GetOrCreate<CpModelMapping>();
1226 const int num_exprs = ct.all_diff().exprs_size();
1227
1228 if (num_exprs <= m->GetOrCreate<SatParameters>()->max_all_diff_cut_size()) {
1229 std::vector<AffineExpression> exprs(num_exprs);
1230 for (const LinearExpressionProto& expr : ct.all_diff().exprs()) {
1231 exprs.push_back(mapping->Affine(expr));
1232 }
1233 relaxation->cut_generators.push_back(
1235 }
1236}
1237
1238bool IntervalIsVariable(const IntervalVariable interval,
1239 IntervalsRepository* intervals_repository) {
1240 // Ignore absent rectangles.
1241 if (intervals_repository->IsAbsent(interval)) {
1242 return false;
1243 }
1244
1245 // Checks non-present intervals.
1246 if (!intervals_repository->IsPresent(interval)) {
1247 return true;
1248 }
1249
1250 // Checks variable sized intervals.
1251 if (intervals_repository->MinSize(interval) !=
1252 intervals_repository->MaxSize(interval)) {
1253 return true;
1254 }
1255
1256 return false;
1257}
1258
1259void AddCumulativeCutGenerator(const ConstraintProto& ct, Model* m,
1260 LinearRelaxation* relaxation) {
1261 if (HasEnforcementLiteral(ct)) return;
1262 auto* mapping = m->GetOrCreate<CpModelMapping>();
1263
1264 const std::vector<IntervalVariable> intervals =
1265 mapping->Intervals(ct.cumulative().intervals());
1266 const AffineExpression capacity = mapping->Affine(ct.cumulative().capacity());
1267
1268 // Scan energies.
1269 IntervalsRepository* intervals_repository =
1271
1272 std::vector<LinearExpression> energies;
1273 std::vector<AffineExpression> demands;
1274 std::vector<AffineExpression> sizes;
1275 for (int i = 0; i < intervals.size(); ++i) {
1276 demands.push_back(mapping->Affine(ct.cumulative().demands(i)));
1277 sizes.push_back(intervals_repository->Size(intervals[i]));
1278 }
1279 LinearizeInnerProduct(demands, sizes, m, &energies);
1280
1281 relaxation->cut_generators.push_back(
1282 CreateCumulativeTimeTableCutGenerator(intervals, capacity, demands, m));
1283 relaxation->cut_generators.push_back(
1285 energies, m));
1286 relaxation->cut_generators.push_back(
1287 CreateCumulativePrecedenceCutGenerator(intervals, capacity, demands, m));
1288
1289 // Checks if at least one rectangle has a variable size, is optional, or if
1290 // the demand if variable.
1291 bool has_variable_part = false;
1292 IntegerTrail* integer_trail = m->GetOrCreate<IntegerTrail>();
1293 for (int i = 0; i < intervals.size(); ++i) {
1294 if (IntervalIsVariable(intervals[i], intervals_repository)) {
1295 has_variable_part = true;
1296 break;
1297 }
1298 // Checks variable demand.
1299 if (!integer_trail->IsFixed(demands[i])) {
1300 has_variable_part = true;
1301 break;
1302 }
1303 }
1304 if (has_variable_part) {
1306 intervals, capacity, demands, energies, m));
1307 }
1308}
1309
1310void AddNoOverlapCutGenerator(const ConstraintProto& ct, Model* m,
1311 LinearRelaxation* relaxation) {
1312 if (HasEnforcementLiteral(ct)) return;
1313
1314 auto* mapping = m->GetOrCreate<CpModelMapping>();
1315 std::vector<IntervalVariable> intervals =
1316 mapping->Intervals(ct.no_overlap().intervals());
1317 relaxation->cut_generators.push_back(
1319 relaxation->cut_generators.push_back(
1321
1322 // Checks if at least one rectangle has a variable size or is optional.
1323 IntervalsRepository* intervals_repository =
1325 bool has_variable_part = false;
1326 for (int i = 0; i < intervals.size(); ++i) {
1327 if (IntervalIsVariable(intervals[i], intervals_repository)) {
1328 has_variable_part = true;
1329 break;
1330 }
1331 }
1332 if (has_variable_part) {
1333 relaxation->cut_generators.push_back(
1334 CreateNoOverlapEnergyCutGenerator(intervals, m));
1335 }
1336}
1337
1338void AddNoOverlap2dCutGenerator(const ConstraintProto& ct, Model* m,
1339 LinearRelaxation* relaxation) {
1340 if (HasEnforcementLiteral(ct)) return;
1341
1342 auto* mapping = m->GetOrCreate<CpModelMapping>();
1343 std::vector<IntervalVariable> x_intervals =
1344 mapping->Intervals(ct.no_overlap_2d().x_intervals());
1345 std::vector<IntervalVariable> y_intervals =
1346 mapping->Intervals(ct.no_overlap_2d().y_intervals());
1347 relaxation->cut_generators.push_back(
1348 CreateNoOverlap2dCompletionTimeCutGenerator(x_intervals, y_intervals, m));
1349
1350 // Checks if at least one rectangle has a variable dimension or is optional.
1351 IntervalsRepository* intervals_repository =
1353 bool has_variable_part = false;
1354 for (int i = 0; i < x_intervals.size(); ++i) {
1355 // Ignore absent rectangles.
1356 if (intervals_repository->IsAbsent(x_intervals[i]) ||
1357 intervals_repository->IsAbsent(y_intervals[i])) {
1358 continue;
1359 }
1360
1361 // Checks non-present intervals.
1362 if (!intervals_repository->IsPresent(x_intervals[i]) ||
1363 !intervals_repository->IsPresent(y_intervals[i])) {
1364 has_variable_part = true;
1365 break;
1366 }
1367
1368 // Checks variable sized intervals.
1369 if (intervals_repository->MinSize(x_intervals[i]) !=
1370 intervals_repository->MaxSize(x_intervals[i]) ||
1371 intervals_repository->MinSize(y_intervals[i]) !=
1372 intervals_repository->MaxSize(y_intervals[i])) {
1373 has_variable_part = true;
1374 break;
1375 }
1376 }
1377 if (has_variable_part) {
1378 relaxation->cut_generators.push_back(
1379 CreateNoOverlap2dEnergyCutGenerator(x_intervals, y_intervals, m));
1380 }
1381}
1382
1383void AddLinMaxCutGenerator(const ConstraintProto& ct, Model* m,
1384 LinearRelaxation* relaxation) {
1385 if (!m->GetOrCreate<SatParameters>()->add_lin_max_cuts()) return;
1386 if (HasEnforcementLiteral(ct)) return;
1387
1388 // TODO(user): Support linearization of general target expression.
1389 auto* mapping = m->GetOrCreate<CpModelMapping>();
1390 if (ct.lin_max().target().vars_size() != 1) return;
1391 if (ct.lin_max().target().coeffs(0) != 1) return;
1392 if (ct.lin_max().target().offset() != 0) return;
1393
1394 const IntegerVariable target =
1395 mapping->Integer(ct.lin_max().target().vars(0));
1396 std::vector<LinearExpression> exprs;
1397 exprs.reserve(ct.lin_max().exprs_size());
1398 for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
1399 // Note: Cut generator requires all expressions to contain only positive
1400 // vars.
1401 exprs.push_back(
1402 PositiveVarExpr(mapping->GetExprFromProto(ct.lin_max().exprs(i))));
1403 }
1404
1405 const std::vector<Literal> alternative_literals =
1406 CreateAlternativeLiteralsWithView(exprs.size(), m, relaxation);
1407
1408 // TODO(user): Move this out of here.
1409 //
1410 // Add initial big-M linear relaxation.
1411 // z_vars[i] == 1 <=> target = exprs[i].
1412 AppendLinMaxRelaxationPart2(target, alternative_literals, exprs, m,
1413 relaxation);
1414
1415 std::vector<IntegerVariable> z_vars;
1416 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1417 for (const Literal lit : alternative_literals) {
1418 z_vars.push_back(encoder->GetLiteralView(lit));
1419 CHECK_NE(z_vars.back(), kNoIntegerVariable);
1420 }
1421 relaxation->cut_generators.push_back(
1422 CreateLinMaxCutGenerator(target, exprs, z_vars, m));
1423}
1424
1425// If we have an exactly one between literals l_i, and each l_i => var ==
1426// value_i, then we can add a strong linear relaxation: var = sum l_i * value_i.
1427//
1428// This codes detect this and add the corresponding linear equations.
1429//
1430// TODO(user): We can do something similar with just an at most one, however
1431// it is harder to detect that if all literal are false then none of the implied
1432// value can be taken.
1434 LinearRelaxation* relaxation) {
1435 auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
1436
1437 int num_exactly_one_elements = 0;
1438
1439 for (const IntegerVariable var :
1440 implied_bounds->GetElementEncodedVariables()) {
1441 for (const auto& [index, literal_value_list] :
1442 implied_bounds->GetElementEncodings(var)) {
1443 // We only want to deal with the case with duplicate values, because
1444 // otherwise, the target will be fully encoded, and this is already
1445 // covered by another function.
1446 IntegerValue min_value = kMaxIntegerValue;
1447 {
1448 absl::flat_hash_set<IntegerValue> values;
1449 for (const auto& literal_value : literal_value_list) {
1450 min_value = std::min(min_value, literal_value.value);
1451 values.insert(literal_value.value);
1452 }
1453 if (values.size() == literal_value_list.size()) continue;
1454 }
1455
1456 LinearConstraintBuilder linear_encoding(m, -min_value, -min_value);
1457 linear_encoding.AddTerm(var, IntegerValue(-1));
1458 for (const auto& [value, literal] : literal_value_list) {
1459 const IntegerValue delta_min = value - min_value;
1460 if (delta_min != 0) {
1461 // If the term has no view, we abort.
1462 if (!linear_encoding.AddLiteralTerm(literal, delta_min)) {
1463 return;
1464 }
1465 }
1466 }
1467 ++num_exactly_one_elements;
1468 relaxation->linear_constraints.push_back(linear_encoding.Build());
1469 }
1470 }
1471
1472 if (num_exactly_one_elements != 0) {
1473 auto* logger = m->GetOrCreate<SolverLogger>();
1474 SOLVER_LOG(logger,
1475 "[ElementLinearRelaxation]"
1476 " #from_exactly_one:",
1477 num_exactly_one_elements);
1478 }
1479}
1480
1482 Model* m) {
1483 LinearRelaxation relaxation;
1484
1485 // Linearize the constraints.
1486 const SatParameters& params = *m->GetOrCreate<SatParameters>();
1487 for (const auto& ct : model_proto.constraints()) {
1488 TryToLinearizeConstraint(model_proto, ct, params.linearization_level(), m,
1489 &relaxation);
1490 }
1491
1492 // Linearize the encoding of variable that are fully encoded.
1493 int num_loose_equality_encoding_relaxations = 0;
1494 int num_tight_equality_encoding_relaxations = 0;
1495 int num_inequality_encoding_relaxations = 0;
1496 auto* mapping = m->GetOrCreate<CpModelMapping>();
1497 for (int i = 0; i < model_proto.variables_size(); ++i) {
1498 if (mapping->IsBoolean(i)) continue;
1499
1500 const IntegerVariable var = mapping->Integer(i);
1501 if (m->Get(IsFixed(var))) continue;
1502
1503 // We first try to linerize the values encoding.
1505 var, *m, &relaxation, &num_tight_equality_encoding_relaxations,
1506 &num_loose_equality_encoding_relaxations);
1507
1508 // The we try to linearize the inequality encoding. Note that on some
1509 // problem like pizza27i.mps.gz, adding both equality and inequality
1510 // encoding is a must.
1511 //
1512 // Even if the variable is fully encoded, sometimes not all its associated
1513 // literal have a view (if they are not part of the original model for
1514 // instance).
1515 //
1516 // TODO(user): Should we add them to the LP anyway? this isn't clear as
1517 // we can sometimes create a lot of Booleans like this.
1518 const int old = relaxation.linear_constraints.size();
1520 if (relaxation.linear_constraints.size() > old) {
1521 ++num_inequality_encoding_relaxations;
1522 }
1523 }
1524
1525 // TODO(user): This is similar to AppendRelaxationForEqualityEncoding() above.
1526 // Investigate if we can merge the code.
1527 if (params.linearization_level() >= 2) {
1529 }
1530
1531 // TODO(user): I am not sure this is still needed. Investigate and explain why
1532 // or remove.
1533 if (!m->GetOrCreate<SatSolver>()->FinishPropagation()) {
1534 return relaxation;
1535 }
1536
1537 // We display the stats before linearizing the at most ones.
1538 auto* logger = m->GetOrCreate<SolverLogger>();
1539 if (num_tight_equality_encoding_relaxations != 0 ||
1540 num_loose_equality_encoding_relaxations != 0 ||
1541 num_inequality_encoding_relaxations != 0) {
1542 SOLVER_LOG(logger,
1543 "[EncodingLinearRelaxation]"
1544 " #tight_equality:",
1545 num_tight_equality_encoding_relaxations,
1546 " #loose_equality:", num_loose_equality_encoding_relaxations,
1547 " #inequality:", num_inequality_encoding_relaxations);
1548 }
1549 if (!relaxation.linear_constraints.empty() ||
1550 !relaxation.at_most_ones.empty()) {
1551 SOLVER_LOG(logger,
1552 "[LinearRelaxationBeforeCliqueExpansion]"
1553 " #linear:",
1554 relaxation.linear_constraints.size(),
1555 " #at_most_ones:", relaxation.at_most_ones.size());
1556 }
1557
1558 // Linearize the at most one constraints. Note that we transform them
1559 // into maximum "at most one" first and we removes redundant ones.
1560 m->GetOrCreate<BinaryImplicationGraph>()->TransformIntoMaxCliques(
1561 &relaxation.at_most_ones, params.merge_at_most_one_work_limit());
1562 for (const std::vector<Literal>& at_most_one : relaxation.at_most_ones) {
1563 if (at_most_one.empty()) continue;
1564
1565 LinearConstraintBuilder lc(m, kMinIntegerValue, IntegerValue(1));
1566 for (const Literal literal : at_most_one) {
1567 // Note that it is okay to simply ignore the literal if it has no
1568 // integer view.
1569 const bool unused ABSL_ATTRIBUTE_UNUSED =
1570 lc.AddLiteralTerm(literal, IntegerValue(1));
1571 }
1572 relaxation.linear_constraints.push_back(lc.Build());
1573 }
1574
1575 // We converted all at_most_one to LP constraints, so we need to clear them
1576 // so that we don't do extra work in the connected component computation.
1577 relaxation.at_most_ones.clear();
1578
1579 // Remove size one LP constraints, they are not useful.
1580 relaxation.linear_constraints.erase(
1581 std::remove_if(
1582 relaxation.linear_constraints.begin(),
1583 relaxation.linear_constraints.end(),
1584 [](const LinearConstraint& lc) { return lc.vars.size() <= 1; }),
1585 relaxation.linear_constraints.end());
1586
1587 // We add a clique cut generation over all Booleans of the problem.
1588 // Note that in practice this might regroup independent LP together.
1589 //
1590 // TODO(user): compute connected components of the original problem and
1591 // split these cuts accordingly.
1592 if (params.linearization_level() > 1 && params.add_clique_cuts()) {
1593 LinearConstraintBuilder builder(m);
1594 for (int i = 0; i < model_proto.variables_size(); ++i) {
1595 if (!mapping->IsBoolean(i)) continue;
1596
1597 // Note that it is okay to simply ignore the literal if it has no
1598 // integer view.
1599 const bool unused ABSL_ATTRIBUTE_UNUSED =
1600 builder.AddLiteralTerm(mapping->Literal(i), IntegerValue(1));
1601 }
1602
1603 // We add a generator touching all the variable in the builder.
1604 const LinearExpression& expr = builder.BuildExpression();
1605 if (!expr.vars.empty()) {
1606 relaxation.cut_generators.push_back(
1608 }
1609 }
1610
1611 if (!relaxation.linear_constraints.empty() ||
1612 !relaxation.cut_generators.empty()) {
1613 SOLVER_LOG(logger,
1614 "[FinalLinearRelaxation]"
1615 " #linear:",
1616 relaxation.linear_constraints.size(),
1617 " #cut_generators:", relaxation.cut_generators.size());
1618 }
1619
1620 return relaxation;
1621}
1622
1623} // namespace sat
1624} // 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:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GE(val1, val2)
Definition: base/logging.h:707
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define CHECK_NE(val1, val2)
Definition: base/logging.h:704
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:896
#define DCHECK(condition)
Definition: base/logging.h:890
#define VLOG(verboselevel)
Definition: base/logging.h:984
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
IntegerVariable Integer(int ref) const
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
std::vector< ValueLiteralPair > FullDomainEncoding(IntegerVariable var) const
Definition: integer.cc:131
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1453
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1449
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1534
IntegerVariable AddIntegerVariable(IntegerValue lower_bound, IntegerValue upper_bound)
Definition: integer.cc:643
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1529
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1445
IntegerValue MaxSize(IntervalVariable i) const
Definition: intervals.h:130
IntegerValue MinSize(IntervalVariable i) const
Definition: intervals.h:125
bool IsPresent(IntervalVariable i) const
Definition: intervals.h:81
AffineExpression Size(IntervalVariable i) const
Definition: intervals.h:97
bool IsAbsent(IntervalVariable i) const
Definition: intervals.h:85
ABSL_MUST_USE_RESULT bool AddLiteralTerm(Literal lit, IntegerValue coeff)
void AddLinearExpression(const LinearExpression &expr)
void AddQuadraticLowerBound(AffineExpression left, AffineExpression right, IntegerTrail *integer_trail)
void AddTerm(IntegerVariable var, IntegerValue coeff)
Literal(int signed_value)
Definition: sat_base.h:71
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
Definition: sat/model.h:91
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:110
CpModelProto const * model_proto
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
double upper_bound
GRBmodel * model
int arc
int index
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
void AddCumulativeCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeEnergyCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, const std::vector< LinearExpression > &energies, Model *model)
std::function< IntegerVariable(Model *)> NewIntegerVariableFromLiteral(Literal lit)
Definition: integer.h:1640
CutGenerator CreateCVRPCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const std::vector< int64_t > &demands, int64_t capacity, Model *model)
CutGenerator CreateNoOverlap2dEnergyCutGenerator(const std::vector< IntervalVariable > &x_intervals, const std::vector< IntervalVariable > &y_intervals, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
void AppendLinMaxRelaxationPart1(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendBoolOrRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
IntegerValue LinExprLowerBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
CutGenerator CreateNoOverlapCompletionTimeCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
void TryToLinearizeConstraint(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation)
bool AppendFullEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
bool RefIsPositive(int ref)
CutGenerator CreateNoOverlapPrecedenceCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
CutGenerator CreateAllDifferentCutGenerator(const std::vector< AffineExpression > &exprs, Model *model)
Definition: cuts.cc:1876
void AppendNoOverlapRelaxation(const CpModelProto &model_proto, const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
const LiteralIndex kNoLiteralIndex(-1)
void AddMaxAffineCutGenerator(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AddCumulativeRelaxation(const std::vector< IntervalVariable > &intervals, const std::vector< AffineExpression > &demands, const std::vector< LinearExpression > &energies, IntegerValue capacity_upper_bound, Model *model, LinearRelaxation *relaxation)
void AppendAtMostOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
void AppendCumulativeRelaxation(const CpModelProto &model_proto, const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
Definition: integer.h:1663
void AddNoOverlapCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
std::function< BooleanVariable(Model *)> NewBooleanVariable()
Definition: integer.h:1608
bool HasEnforcementLiteral(const ConstraintProto &ct)
void LinearizeInnerProduct(const std::vector< AffineExpression > &left, const std::vector< AffineExpression > &right, Model *model, std::vector< LinearExpression > *energies)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
Definition: integer.h:1675
LinearExpression PositiveVarExpr(const LinearExpression &expr)
std::function< IntervalVariable(Model *)> NewInterval(int64_t min_start, int64_t max_end, int64_t size)
Definition: intervals.h:669
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
void AppendBoolAndRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
const IntegerVariable kNoIntegerVariable(-1)
void AppendLinearConstraintRelaxation(const ConstraintProto &ct, bool linearize_enforced_constraints, Model *model, LinearRelaxation *relaxation)
LinearExpression CanonicalizeExpr(const LinearExpression &expr)
void AddNoOverlap2dCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeTimeTableCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, Model *model)
void AddIntProdCutGenerator(const ConstraintProto &ct, int linearization_level, Model *m, LinearRelaxation *relaxation)
void AddCircuitCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlap2dCompletionTimeCutGenerator(const std::vector< IntervalVariable > &x_intervals, const std::vector< IntervalVariable > &y_intervals, Model *model)
std::function< IntervalVariable(Model *)> NewOptionalInterval(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition: intervals.h:699
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:149
CutGenerator CreateLinMaxCutGenerator(const IntegerVariable target, const std::vector< LinearExpression > &exprs, const std::vector< IntegerVariable > &z_vars, Model *model)
Definition: cuts.cc:1985
CutGenerator CreatePositiveMultiplicationCutGenerator(AffineExpression z, AffineExpression x, AffineExpression y, int linearization_level, Model *model)
Definition: cuts.cc:1358
void AppendMaxAffineRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
LinearConstraint BuildMaxAffineUpConstraint(const LinearExpression &target, IntegerVariable var, const std::vector< std::pair< IntegerValue, IntegerValue > > &affines, Model *model)
Definition: cuts.cc:2069
void AppendExactlyOneRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
IntegerValue GetCoefficient(const IntegerVariable var, const LinearExpression &expr)
bool DetectLinearEncodingOfProducts(const AffineExpression &left, const AffineExpression &right, Model *model, LinearConstraintBuilder *builder)
int ReindexArcs(IntContainer *tails, IntContainer *heads)
Definition: circuit.h:170
CutGenerator CreateSquareCutGenerator(AffineExpression y, AffineExpression x, int linearization_level, Model *model)
Definition: cuts.cc:1452
std::vector< Literal > CreateAlternativeLiteralsWithView(int num_literals, Model *model, LinearRelaxation *relaxation)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
Definition: integer.h:1669
void AppendElementEncodingRelaxation(const CpModelProto &model_proto, Model *m, LinearRelaxation *relaxation)
void AppendCircuitRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateCumulativeCompletionTimeCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, const std::vector< LinearExpression > &energies, Model *model)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:47
std::function< IntegerVariable(Model *)> NewIntegerVariable(int64_t lb, int64_t ub)
Definition: integer.h:1622
void AddAllDiffCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateNoOverlapEnergyCutGenerator(const std::vector< IntervalVariable > &intervals, Model *model)
CutGenerator CreateMaxAffineCutGenerator(LinearExpression target, IntegerVariable var, std::vector< std::pair< IntegerValue, IntegerValue > > affines, const std::string cut_name, Model *model)
Definition: cuts.cc:2105
std::function< void(Model *)> SpanOfIntervals(IntervalVariable span, const std::vector< IntervalVariable > &intervals)
bool IntervalIsVariable(const IntervalVariable interval, IntervalsRepository *intervals_repository)
void AddLinMaxCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
void AppendRoutesRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
CutGenerator CreateCumulativePrecedenceCutGenerator(const std::vector< IntervalVariable > &intervals, const AffineExpression &capacity, const std::vector< AffineExpression > &demands, Model *model)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:907
void AppendNoOverlap2dRelaxation(const ConstraintProto &ct, Model *model, LinearRelaxation *relaxation)
IntegerValue LinExprUpperBound(const LinearExpression &expr, const IntegerTrail &integer_trail)
void AppendRelaxationForEqualityEncoding(IntegerVariable var, const Model &model, LinearRelaxation *relaxation, int *num_tight, int *num_loose)
CutGenerator CreateCliqueCutGenerator(const std::vector< IntegerVariable > &base_variables, Model *model)
Definition: cuts.cc:2127
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:145
void AddRoutesCutGenerator(const ConstraintProto &ct, Model *m, LinearRelaxation *relaxation)
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
LinearRelaxation ComputeLinearRelaxation(const CpModelProto &model_proto, Model *m)
void AppendLinMaxRelaxationPart2(IntegerVariable target, const std::vector< Literal > &alternative_literals, const std::vector< LinearExpression > &exprs, Model *model, LinearRelaxation *relaxation)
void AppendPartialGreaterThanEncodingRelaxation(IntegerVariable var, const Model &model, LinearRelaxation *relaxation)
Collection of objects used to extend the Constraint Solver library.
int64_t CapSub(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
Literal literal
Definition: optimization.cc:89
int64_t delta
Definition: resource.cc:1694
IntervalVar * interval
Definition: resource.cc:100
int64_t capacity
int64_t tail
int64_t head
AffineExpression Negated() const
Definition: integer.h:258
std::vector< std::vector< Literal > > at_most_ones
std::vector< LinearConstraint > linear_constraints
std::vector< CutGenerator > cut_generators
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:69
const double coeff