OR-Tools  9.1
cp_model_loader.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 <map>
20#include <memory>
21#include <set>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
34#include "ortools/sat/circuit.h"
39#include "ortools/sat/diffn.h"
42#include "ortools/sat/integer.h"
51#include "ortools/sat/table.h"
56
57namespace operations_research {
58namespace sat {
59
60namespace {
61
62template <typename Values>
63std::vector<int64_t> ValuesFromProto(const Values& values) {
64 return std::vector<int64_t>(values.begin(), values.end());
65}
66
67void ComputeLinearBounds(const LinearConstraintProto& proto,
68 CpModelMapping* mapping, IntegerTrail* integer_trail,
69 int64_t* sum_min, int64_t* sum_max) {
70 *sum_min = 0;
71 *sum_max = 0;
72
73 for (int i = 0; i < proto.vars_size(); ++i) {
74 const int64_t coeff = proto.coeffs(i);
75 const IntegerVariable var = mapping->Integer(proto.vars(i));
76 const int64_t lb = integer_trail->LowerBound(var).value();
77 const int64_t ub = integer_trail->UpperBound(var).value();
78 if (coeff >= 0) {
79 (*sum_min) += coeff * lb;
80 (*sum_max) += coeff * ub;
81 } else {
82 (*sum_min) += coeff * ub;
83 (*sum_max) += coeff * lb;
84 }
85 }
86}
87
88// We check if the constraint is a sum(ax * xi) == value.
89bool ConstraintIsEq(const LinearConstraintProto& proto) {
90 return proto.domain_size() == 2 && proto.domain(0) == proto.domain(1);
91}
92
93// We check if the constraint is a sum(ax * xi) != value.
94bool ConstraintIsNEq(const LinearConstraintProto& proto,
95 CpModelMapping* mapping, IntegerTrail* integer_trail,
96 int64_t* single_value) {
97 int64_t sum_min = 0;
98 int64_t sum_max = 0;
99 ComputeLinearBounds(proto, mapping, integer_trail, &sum_min, &sum_max);
100
101 const Domain complement =
102 Domain(sum_min, sum_max)
103 .IntersectionWith(ReadDomainFromProto(proto).Complement());
104 if (complement.IsEmpty()) return false;
105 const int64_t value = complement.Min();
106
107 if (complement.Size() == 1) {
108 if (single_value != nullptr) {
109 *single_value = value;
110 }
111 return true;
112 }
113 return false;
114}
115
116} // namespace
117
119 bool view_all_booleans_as_integers, Model* m) {
120 auto* mapping = m->GetOrCreate<CpModelMapping>();
121 const int num_proto_variables = model_proto.variables_size();
122
123 // All [0, 1] variables always have a corresponding Boolean, even if it is
124 // fixed to 0 (domain == [0,0]) or fixed to 1 (domain == [1,1]).
125 {
126 auto* sat_solver = m->GetOrCreate<SatSolver>();
127 CHECK_EQ(sat_solver->NumVariables(), 0);
128
129 BooleanVariable new_var(0);
130 std::vector<BooleanVariable> false_variables;
131 std::vector<BooleanVariable> true_variables;
132
133 mapping->booleans_.resize(num_proto_variables, kNoBooleanVariable);
134 mapping->reverse_boolean_map_.resize(num_proto_variables, -1);
135 for (int i = 0; i < num_proto_variables; ++i) {
136 const auto& domain = model_proto.variables(i).domain();
137 if (domain.size() != 2) continue;
138 if (domain[0] >= 0 && domain[1] <= 1) {
139 mapping->booleans_[i] = new_var;
140 mapping->reverse_boolean_map_[new_var] = i;
141 if (domain[1] == 0) {
142 false_variables.push_back(new_var);
143 } else if (domain[0] == 1) {
144 true_variables.push_back(new_var);
145 }
146 ++new_var;
147 }
148 }
149
150 sat_solver->SetNumVariables(new_var.value());
151 for (const BooleanVariable var : true_variables) {
152 m->Add(ClauseConstraint({sat::Literal(var, true)}));
153 }
154 for (const BooleanVariable var : false_variables) {
155 m->Add(ClauseConstraint({sat::Literal(var, false)}));
156 }
157 }
158
159 // Compute the list of positive variable reference for which we need to
160 // create an IntegerVariable.
161 std::vector<int> var_to_instantiate_as_integer;
162 if (view_all_booleans_as_integers) {
163 var_to_instantiate_as_integer.resize(num_proto_variables);
164 for (int i = 0; i < num_proto_variables; ++i) {
165 var_to_instantiate_as_integer[i] = i;
166 }
167 } else {
168 // Compute the integer variable references used by the model.
169 absl::flat_hash_set<int> used_variables;
170
171 IndexReferences refs;
172 for (int c = 0; c < model_proto.constraints_size(); ++c) {
175 for (const int ref : refs.variables) {
176 used_variables.insert(PositiveRef(ref));
177 }
178 }
179
180 // Add the objectives variables that needs to be referenceable as integer
181 // even if they are only used as Booleans.
183 for (const int obj_var : model_proto.objective().vars()) {
184 used_variables.insert(PositiveRef(obj_var));
185 }
186 }
187
188 // Make sure any unused variable, that is not already a Boolean is
189 // considered "used".
190 for (int i = 0; i < num_proto_variables; ++i) {
191 if (mapping->booleans_[i] == kNoBooleanVariable) {
192 used_variables.insert(i);
193 }
194 }
195
196 // We want the variable in the problem order.
197 var_to_instantiate_as_integer.assign(used_variables.begin(),
198 used_variables.end());
199 gtl::STLSortAndRemoveDuplicates(&var_to_instantiate_as_integer);
200 }
201 mapping->integers_.resize(num_proto_variables, kNoIntegerVariable);
202
203 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
204 integer_trail->ReserveSpaceForNumVariables(
205 var_to_instantiate_as_integer.size());
206 mapping->reverse_integer_map_.resize(2 * var_to_instantiate_as_integer.size(),
207 -1);
208 for (const int i : var_to_instantiate_as_integer) {
209 const auto& var_proto = model_proto.variables(i);
210 mapping->integers_[i] =
211 integer_trail->AddIntegerVariable(ReadDomainFromProto(var_proto));
212 DCHECK_LT(mapping->integers_[i], mapping->reverse_integer_map_.size());
213 mapping->reverse_integer_map_[mapping->integers_[i]] = i;
214 }
215
216 auto* encoder = m->GetOrCreate<IntegerEncoder>();
217 auto* intervals_repository = m->GetOrCreate<IntervalsRepository>();
218
219 // Link any variable that has both views.
220 for (int i = 0; i < num_proto_variables; ++i) {
221 if (mapping->integers_[i] == kNoIntegerVariable) continue;
222 if (mapping->booleans_[i] == kNoBooleanVariable) continue;
223
224 // Associate with corresponding integer variable.
225 encoder->AssociateToIntegerEqualValue(
226 sat::Literal(mapping->booleans_[i], true), mapping->integers_[i],
227 IntegerValue(1));
228 }
229
230 // Create the interval variables.
231 mapping->intervals_.resize(model_proto.constraints_size(),
233 for (int c = 0; c < model_proto.constraints_size(); ++c) {
235 if (ct.constraint_case() != ConstraintProto::ConstraintCase::kInterval) {
236 continue;
237 }
239 const sat::Literal enforcement_literal =
240 mapping->Literal(ct.enforcement_literal(0));
241 // TODO(user): Fix the constant variable situation. An optional interval
242 // with constant start/end or size cannot share the same constant
243 // variable if it is used in non-optional situation.
244 if (ct.interval().has_start_view()) {
245 mapping->intervals_[c] = intervals_repository->CreateInterval(
246 mapping->LoadAffineView(ct.interval().start_view()),
247 mapping->LoadAffineView(ct.interval().end_view()),
248 mapping->LoadAffineView(ct.interval().size_view()),
249 enforcement_literal.Index(),
250 /*add_linear_relation=*/false);
251 } else {
252 mapping->intervals_[c] = m->Add(NewOptionalInterval(
253 mapping->Integer(ct.interval().start()),
254 mapping->Integer(ct.interval().end()),
255 mapping->Integer(ct.interval().size()), enforcement_literal));
256 }
257 } else {
258 if (ct.interval().has_start_view()) {
259 mapping->intervals_[c] = intervals_repository->CreateInterval(
260 mapping->LoadAffineView(ct.interval().start_view()),
261 mapping->LoadAffineView(ct.interval().end_view()),
262 mapping->LoadAffineView(ct.interval().size_view()), kNoLiteralIndex,
263 /*add_linear_relation=*/false);
264 } else {
265 mapping->intervals_[c] =
266 m->Add(NewInterval(mapping->Integer(ct.interval().start()),
267 mapping->Integer(ct.interval().end()),
268 mapping->Integer(ct.interval().size())));
269 }
270 }
271 mapping->already_loaded_ct_.insert(&ct);
272 }
273}
274
276 auto* mapping = m->GetOrCreate<CpModelMapping>();
277 const SymmetryProto symmetry = model_proto.symmetry();
278 if (symmetry.permutations().empty()) return;
279
280 auto* sat_solver = m->GetOrCreate<SatSolver>();
281 auto* symmetry_handler = m->GetOrCreate<SymmetryPropagator>();
282 sat_solver->AddPropagator(symmetry_handler);
283 const int num_literals = 2 * sat_solver->NumVariables();
284
285 for (const SparsePermutationProto& perm : symmetry.permutations()) {
286 bool all_bool = true;
287 for (const int var : perm.support()) {
288 if (!mapping->IsBoolean(var)) {
289 all_bool = false;
290 break;
291 }
292 }
293 if (!all_bool) continue;
294
295 // Convert the variable symmetry to a "literal" one.
296 auto literal_permutation =
297 absl::make_unique<SparsePermutation>(num_literals);
298 int support_index = 0;
299 const int num_cycle = perm.cycle_sizes().size();
300 for (int i = 0; i < num_cycle; ++i) {
301 const int size = perm.cycle_sizes(i);
302 const int saved_support_index = support_index;
303 for (int j = 0; j < size; ++j) {
304 const int var = perm.support(support_index++);
305 literal_permutation->AddToCurrentCycle(
306 mapping->Literal(var).Index().value());
307 }
308 literal_permutation->CloseCurrentCycle();
309
310 // Note that we also need to add the corresponding cycle for the negated
311 // literals.
312 support_index = saved_support_index;
313 for (int j = 0; j < size; ++j) {
314 const int var = perm.support(support_index++);
315 literal_permutation->AddToCurrentCycle(
316 mapping->Literal(var).NegatedIndex().value());
317 }
318 literal_permutation->CloseCurrentCycle();
319 }
320 symmetry_handler->AddSymmetry(std::move(literal_permutation));
321 }
322
323 SOLVER_LOG(m->GetOrCreate<SolverLogger>(), "Added ",
324 symmetry_handler->num_permutations(),
325 " symmetry to the SAT solver.");
326}
327
328// The logic assumes that the linear constraints have been presolved, so that
329// equality with a domain bound have been converted to <= or >= and so that we
330// never have any trivial inequalities.
331//
332// TODO(user): Regroup/presolve two encoding like b => x > 2 and the same
333// Boolean b => x > 5. These shouldn't happen if we merge linear constraints.
335 auto* mapping = m->GetOrCreate<CpModelMapping>();
336 auto* encoder = m->GetOrCreate<IntegerEncoder>();
337 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
338 auto* sat_solver = m->GetOrCreate<SatSolver>();
339
340 // TODO(user): Debug what makes it unsat at this point.
341 if (sat_solver->IsModelUnsat()) return;
342
343 // Detection of literal equivalent to (i_var == value). We collect all the
344 // half-reified constraint lit => equality or lit => inequality for a given
345 // variable, and we will later sort them to detect equivalence.
346 struct EqualityDetectionHelper {
347 const ConstraintProto* ct;
349 int64_t value;
350 bool is_equality; // false if != instead.
351
352 bool operator<(const EqualityDetectionHelper& o) const {
353 if (literal.Variable() == o.literal.Variable()) {
354 if (value == o.value) return is_equality && !o.is_equality;
355 return value < o.value;
356 }
357 return literal.Variable() < o.literal.Variable();
358 }
359 };
360 std::vector<std::vector<EqualityDetectionHelper>> var_to_equalities(
362
363 // TODO(user): We will re-add the same implied bounds during probing, so
364 // it might not be necessary to do that here. Also, it might be too early
365 // if some of the literal view used in the LP are created later, but that
366 // should be fixable via calls to implied_bounds->NotifyNewIntegerView().
367 auto* implied_bounds = m->GetOrCreate<ImpliedBounds>();
368
369 // Detection of literal equivalent to (i_var >= bound). We also collect
370 // all the half-refied part and we will sort the vector for detection of the
371 // equivalence.
372 struct InequalityDetectionHelper {
373 const ConstraintProto* ct;
375 IntegerLiteral i_lit;
376
377 bool operator<(const InequalityDetectionHelper& o) const {
378 if (literal.Variable() == o.literal.Variable()) {
379 return i_lit.var < o.i_lit.var;
380 }
381 return literal.Variable() < o.literal.Variable();
382 }
383 };
384 std::vector<InequalityDetectionHelper> inequalities;
385
386 // Loop over all constraints and fill var_to_equalities and inequalities.
387 for (const ConstraintProto& ct : model_proto.constraints()) {
388 if (ct.constraint_case() != ConstraintProto::ConstraintCase::kLinear) {
389 continue;
390 }
391 if (ct.enforcement_literal().size() != 1) continue;
392 if (ct.linear().vars_size() != 1) continue;
393
394 // ct is a linear constraint with one term and one enforcement literal.
395 const sat::Literal enforcement_literal =
396 mapping->Literal(ct.enforcement_literal(0));
397 if (sat_solver->Assignment().LiteralIsFalse(enforcement_literal)) continue;
398
399 const int ref = ct.linear().vars(0);
400 const int var = PositiveRef(ref);
401
403 const Domain domain_if_enforced =
404 ReadDomainFromProto(ct.linear())
405 .InverseMultiplicationBy(ct.linear().coeffs(0) *
406 (RefIsPositive(ref) ? 1 : -1));
407
408 if (domain_if_enforced.IsEmpty()) {
409 if (!sat_solver->AddUnitClause(enforcement_literal.Negated())) return;
410 continue;
411 }
412
413 // Detect enforcement_literal => (var >= value or var <= value).
414 if (domain_if_enforced.NumIntervals() == 1) {
415 if (domain_if_enforced.Max() >= domain.Max() &&
416 domain_if_enforced.Min() > domain.Min()) {
417 inequalities.push_back({&ct, enforcement_literal,
419 mapping->Integer(var),
420 IntegerValue(domain_if_enforced.Min()))});
421 } else if (domain_if_enforced.Min() <= domain.Min() &&
422 domain_if_enforced.Max() < domain.Max()) {
423 inequalities.push_back({&ct, enforcement_literal,
425 mapping->Integer(var),
426 IntegerValue(domain_if_enforced.Max()))});
427 }
428 }
429
430 // Detect implied bounds. The test is less strict than the above
431 // test.
432 if (domain_if_enforced.Min() > domain.Min()) {
433 implied_bounds->Add(
434 enforcement_literal,
436 mapping->Integer(var), IntegerValue(domain_if_enforced.Min())));
437 }
438 if (domain_if_enforced.Max() < domain.Max()) {
439 implied_bounds->Add(
440 enforcement_literal,
441 IntegerLiteral::LowerOrEqual(mapping->Integer(var),
442 IntegerValue(domain_if_enforced.Max())));
443 }
444
445 // Detect enforcement_literal => (var == value or var != value).
446 //
447 // Note that for domain with 2 values like [0, 1], we will detect both ==
448 // 0 and != 1. Similarly, for a domain in [min, max], we should both
449 // detect (== min) and (<= min), and both detect (== max) and (>= max).
450 {
451 const Domain inter = domain.IntersectionWith(domain_if_enforced);
452 if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
453 var_to_equalities[var].push_back(
454 {&ct, enforcement_literal, inter.Min(), true});
455 if (domain.Contains(inter.Min())) {
456 mapping->variables_to_encoded_values_[var].insert(inter.Min());
457 }
458 }
459 }
460 {
461 const Domain inter =
462 domain.IntersectionWith(domain_if_enforced.Complement());
463 if (!inter.IsEmpty() && inter.Min() == inter.Max()) {
464 var_to_equalities[var].push_back(
465 {&ct, enforcement_literal, inter.Min(), false});
466 if (domain.Contains(inter.Min())) {
467 mapping->variables_to_encoded_values_[var].insert(inter.Min());
468 }
469 }
470 }
471 }
472
473 // Detect Literal <=> X >= value
474 int num_inequalities = 0;
475 std::sort(inequalities.begin(), inequalities.end());
476 for (int i = 0; i + 1 < inequalities.size(); i++) {
477 if (inequalities[i].literal != inequalities[i + 1].literal.Negated()) {
478 continue;
479 }
480
481 // TODO(user): In these cases, we could fix the enforcement literal right
482 // away or ignore the constraint. Note that it will be done later anyway
483 // though.
484 if (integer_trail->IntegerLiteralIsTrue(inequalities[i].i_lit) ||
485 integer_trail->IntegerLiteralIsFalse(inequalities[i].i_lit)) {
486 continue;
487 }
488 if (integer_trail->IntegerLiteralIsTrue(inequalities[i + 1].i_lit) ||
489 integer_trail->IntegerLiteralIsFalse(inequalities[i + 1].i_lit)) {
490 continue;
491 }
492
493 const auto pair_a = encoder->Canonicalize(inequalities[i].i_lit);
494 const auto pair_b = encoder->Canonicalize(inequalities[i + 1].i_lit);
495 if (pair_a.first == pair_b.second) {
496 ++num_inequalities;
497 encoder->AssociateToIntegerLiteral(inequalities[i].literal,
498 inequalities[i].i_lit);
499 mapping->already_loaded_ct_.insert(inequalities[i].ct);
500 mapping->already_loaded_ct_.insert(inequalities[i + 1].ct);
501 }
502 }
503
504 // Encode the half-inequalities.
505 int num_half_inequalities = 0;
506 for (const auto inequality : inequalities) {
507 if (mapping->ConstraintIsAlreadyLoaded(inequality.ct)) continue;
508 m->Add(
509 Implication(inequality.literal,
510 encoder->GetOrCreateAssociatedLiteral(inequality.i_lit)));
511 if (sat_solver->IsModelUnsat()) return;
512
513 ++num_half_inequalities;
514 mapping->already_loaded_ct_.insert(inequality.ct);
515 mapping->is_half_encoding_ct_.insert(inequality.ct);
516 }
517
518 if (!inequalities.empty()) {
519 VLOG(1) << num_inequalities << " literals associated to VAR >= value, and "
520 << num_half_inequalities << " half-associations.";
521 }
522
523 // Detect Literal <=> X == value and associate them in the IntegerEncoder.
524 //
525 // TODO(user): Fully encode variable that are almost fully encoded?
526 int num_constraints = 0;
527 int num_equalities = 0;
528 int num_half_equalities = 0;
529 int num_fully_encoded = 0;
530 int num_partially_encoded = 0;
531 for (int i = 0; i < var_to_equalities.size(); ++i) {
532 std::vector<EqualityDetectionHelper>& encoding = var_to_equalities[i];
533 std::sort(encoding.begin(), encoding.end());
534 if (encoding.empty()) continue;
535 num_constraints += encoding.size();
536
537 absl::flat_hash_set<int64_t> values;
538 for (int j = 0; j + 1 < encoding.size(); j++) {
539 if ((encoding[j].value != encoding[j + 1].value) ||
540 (encoding[j].literal != encoding[j + 1].literal.Negated()) ||
541 (encoding[j].is_equality != true) ||
542 (encoding[j + 1].is_equality != false)) {
543 continue;
544 }
545
546 ++num_equalities;
547 encoder->AssociateToIntegerEqualValue(encoding[j].literal,
548 mapping->integers_[i],
549 IntegerValue(encoding[j].value));
550 mapping->already_loaded_ct_.insert(encoding[j].ct);
551 mapping->already_loaded_ct_.insert(encoding[j + 1].ct);
552 values.insert(encoding[j].value);
553 }
554
555 // TODO(user): Try to remove it. Normally we caught UNSAT above, but
556 // tests are very flaky (it only happens in parallel). Keeping it there for
557 // the time being.
558 if (sat_solver->IsModelUnsat()) return;
559
560 // Encode the half-equalities.
561 //
562 // TODO(user): delay this after PropagateEncodingFromEquivalenceRelations()?
563 // Otherwise we might create new Boolean variables for no reason. Note
564 // however, that in the presolve, we should only use the "representative" in
565 // linear constraints, so we should be fine.
566 for (const auto equality : encoding) {
567 if (mapping->ConstraintIsAlreadyLoaded(equality.ct)) continue;
568 const class Literal eq = encoder->GetOrCreateLiteralAssociatedToEquality(
569 mapping->integers_[i], IntegerValue(equality.value));
570 if (equality.is_equality) {
571 m->Add(Implication(equality.literal, eq));
572 } else {
573 m->Add(Implication(equality.literal, eq.Negated()));
574 }
575
576 ++num_half_equalities;
577 mapping->already_loaded_ct_.insert(equality.ct);
578 mapping->is_half_encoding_ct_.insert(equality.ct);
579 }
580
581 // Update stats.
582 if (VLOG_IS_ON(1)) {
583 if (encoder->VariableIsFullyEncoded(mapping->integers_[i])) {
584 ++num_fully_encoded;
585 } else {
586 ++num_partially_encoded;
587 }
588 }
589 }
590
591 if (num_constraints > 0) {
592 VLOG(1) << num_equalities << " literals associated to VAR == value, and "
593 << num_half_equalities << " half-associations.";
594 }
595 if (num_fully_encoded > 0) {
596 VLOG(1) << "num_fully_encoded_variables: " << num_fully_encoded;
597 }
598 if (num_partially_encoded > 0) {
599 VLOG(1) << "num_partially_encoded_variables: " << num_partially_encoded;
600 }
601}
602
604 Model* m) {
605 auto* mapping = m->GetOrCreate<CpModelMapping>();
606 auto* encoder = m->GetOrCreate<IntegerEncoder>();
607 auto* sat_solver = m->GetOrCreate<SatSolver>();
608
609 // Loop over all constraints and find affine ones.
610 int64_t num_associations = 0;
611 int64_t num_set_to_false = 0;
612 for (const ConstraintProto& ct : model_proto.constraints()) {
613 if (!ct.enforcement_literal().empty()) continue;
614 if (ct.constraint_case() != ConstraintProto::kLinear) continue;
615 if (ct.linear().vars_size() != 2) continue;
616 if (!ConstraintIsEq(ct.linear())) continue;
617
618 const IntegerValue rhs(ct.linear().domain(0));
619
620 // Make sure the coefficient are positive.
621 IntegerVariable var1 = mapping->Integer(ct.linear().vars(0));
622 IntegerVariable var2 = mapping->Integer(ct.linear().vars(1));
623 IntegerValue coeff1(ct.linear().coeffs(0));
624 IntegerValue coeff2(ct.linear().coeffs(1));
625 if (coeff1 < 0) {
626 var1 = NegationOf(var1);
627 coeff1 = -coeff1;
628 }
629 if (coeff2 < 0) {
630 var2 = NegationOf(var2);
631 coeff2 = -coeff2;
632 }
633
634 // TODO(user): This is not supposed to happen, but apparently it did on
635 // once on routing_GCM_0001_sat.fzn. Investigate and fix.
636 if (coeff1 == 0 || coeff2 == 0) continue;
637
638 // We first map the >= literals.
639 // It is important to do that first, since otherwise mapping a == literal
640 // might creates the underlying >= and <= literals.
641 for (int i = 0; i < 2; ++i) {
642 for (const auto value_literal :
643 encoder->PartialGreaterThanEncoding(var1)) {
644 const IntegerValue value1 = value_literal.first;
645 const IntegerValue bound2 = FloorRatio(rhs - value1 * coeff1, coeff2);
646 ++num_associations;
647 encoder->AssociateToIntegerLiteral(
648 value_literal.second, IntegerLiteral::LowerOrEqual(var2, bound2));
649 }
650 std::swap(var1, var2);
651 std::swap(coeff1, coeff2);
652 }
653
654 // Same for the == literals.
655 //
656 // TODO(user): This is similar to LoadEquivalenceAC() for unreified
657 // constraints, but when the later is called, more encoding might have taken
658 // place.
659 for (int i = 0; i < 2; ++i) {
660 for (const auto value_literal : encoder->PartialDomainEncoding(var1)) {
661 const IntegerValue value1 = value_literal.value;
662 const IntegerValue intermediate = rhs - value1 * coeff1;
663 if (intermediate % coeff2 != 0) {
664 // Using this function deals properly with UNSAT.
665 ++num_set_to_false;
666 sat_solver->AddUnitClause(value_literal.literal.Negated());
667 continue;
668 }
669 ++num_associations;
670 encoder->AssociateToIntegerEqualValue(value_literal.literal, var2,
671 intermediate / coeff2);
672 }
673 std::swap(var1, var2);
674 std::swap(coeff1, coeff2);
675 }
676 }
677
678 if (num_associations > 0) {
679 VLOG(1) << "Num associations from equivalences = " << num_associations;
680 }
681 if (num_set_to_false > 0) {
682 VLOG(1) << "Num literals set to false from equivalences = "
683 << num_set_to_false;
684 }
685}
686
688 auto* mapping = m->GetOrCreate<CpModelMapping>();
690 if (!parameters.use_optional_variables()) return;
692
693 // The variables from the objective cannot be marked as optional!
694 const int num_proto_variables = model_proto.variables_size();
695 std::vector<bool> already_seen(num_proto_variables, false);
697 for (const int ref : model_proto.objective().vars()) {
698 already_seen[PositiveRef(ref)] = true;
699 }
700 }
701
702 // Compute for each variables the intersection of the enforcement literals
703 // of the constraints in which they appear.
704 //
705 // TODO(user): This deals with the simplest cases, but we could try to
706 // detect literals that implies all the constraints in which a variable
707 // appear to false. This can be done with a LCA computation in the tree of
708 // Boolean implication (once the presolve remove cycles). Not sure if we can
709 // properly exploit that afterwards though. Do some research!
710 std::vector<std::vector<int>> enforcement_intersection(num_proto_variables);
711 std::set<int> literals_set;
712 for (int c = 0; c < model_proto.constraints_size(); ++c) {
714 if (ct.enforcement_literal().empty()) {
715 for (const int var : UsedVariables(ct)) {
716 already_seen[var] = true;
717 enforcement_intersection[var].clear();
718 }
719 } else {
720 literals_set.clear();
721 literals_set.insert(ct.enforcement_literal().begin(),
722 ct.enforcement_literal().end());
723 for (const int var : UsedVariables(ct)) {
724 if (!already_seen[var]) {
725 enforcement_intersection[var].assign(ct.enforcement_literal().begin(),
726 ct.enforcement_literal().end());
727 } else {
728 // Take the intersection.
729 std::vector<int>& vector_ref = enforcement_intersection[var];
730 int new_size = 0;
731 for (const int literal : vector_ref) {
732 if (gtl::ContainsKey(literals_set, literal)) {
733 vector_ref[new_size++] = literal;
734 }
735 }
736 vector_ref.resize(new_size);
737 }
738 already_seen[var] = true;
739 }
740 }
741 }
742
743 // Auto-detect optional variables.
744 int num_optionals = 0;
745 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
746 for (int var = 0; var < num_proto_variables; ++var) {
747 const IntegerVariableProto& var_proto = model_proto.variables(var);
748 const int64_t min = var_proto.domain(0);
749 const int64_t max = var_proto.domain(var_proto.domain().size() - 1);
750 if (min == max) continue;
751 if (min == 0 && max == 1) continue;
752 if (enforcement_intersection[var].empty()) continue;
753
754 ++num_optionals;
755 integer_trail->MarkIntegerVariableAsOptional(
756 mapping->Integer(var),
757 mapping->Literal(enforcement_intersection[var].front()));
758 }
759 VLOG(2) << "Auto-detected " << num_optionals << " optional variables.";
760}
761
762// ============================================================================
763// A class that detects when variables should be fully encoded by computing a
764// fixed point. It also fully encodes such variables.
765// ============================================================================
766
768 public:
770 : model_proto_(model_proto),
771 parameters_(*(model->GetOrCreate<SatParameters>())),
772 model_(model),
773 mapping_(model->GetOrCreate<CpModelMapping>()),
774 integer_encoder_(model->GetOrCreate<IntegerEncoder>()),
775 integer_trail_(model->GetOrCreate<IntegerTrail>()) {}
776
777 void ComputeFixedPoint();
778
779 private:
780 DEFINE_INT_TYPE(ConstraintIndex, int32_t);
781
782 // Constraint ct is interested by (full-encoding) state of variable.
783 void Register(ConstraintIndex ct_index, int variable) {
784 variable = PositiveRef(variable);
785 constraint_is_registered_[ct_index] = true;
786 if (variable_watchers_.size() <= variable) {
787 variable_watchers_.resize(variable + 1);
788 variable_was_added_in_to_propagate_.resize(variable + 1);
789 }
790 variable_watchers_[variable].push_back(ct_index);
791 }
792
793 void AddVariableToPropagationQueue(int variable) {
794 variable = PositiveRef(variable);
795 if (variable_was_added_in_to_propagate_.size() <= variable) {
796 variable_watchers_.resize(variable + 1);
797 variable_was_added_in_to_propagate_.resize(variable + 1);
798 }
799 if (!variable_was_added_in_to_propagate_[variable]) {
800 variable_was_added_in_to_propagate_[variable] = true;
801 variables_to_propagate_.push_back(variable);
802 }
803 }
804
805 // Note that we always consider a fixed variable to be fully encoded here.
806 const bool IsFullyEncoded(int v) {
807 const IntegerVariable variable = mapping_->Integer(v);
808 if (variable == kNoIntegerVariable) return false;
809 return integer_trail_->IsFixed(variable) ||
810 integer_encoder_->VariableIsFullyEncoded(variable);
811 }
812
813 const bool VariableIsFixed(int v) {
814 const IntegerVariable variable = mapping_->Integer(v);
815 if (v == kNoIntegerVariable) return false;
816 return integer_trail_->IsFixed(variable);
817 }
818
819 void FullyEncode(int v) {
820 v = PositiveRef(v);
821 const IntegerVariable variable = mapping_->Integer(v);
822 if (v == kNoIntegerVariable) return;
823 if (!integer_trail_->IsFixed(variable)) {
824 model_->Add(FullyEncodeVariable(variable));
825 }
826 AddVariableToPropagationQueue(v);
827 }
828
829 bool ProcessConstraint(ConstraintIndex ct_index);
830 bool ProcessElement(ConstraintIndex ct_index);
831 bool ProcessTable(ConstraintIndex ct_index);
832 bool ProcessAutomaton(ConstraintIndex ct_index);
833 bool ProcessLinear(ConstraintIndex ct_index);
834
835 const CpModelProto& model_proto_;
836 const SatParameters& parameters_;
837
838 Model* model_;
839 CpModelMapping* mapping_;
840 IntegerEncoder* integer_encoder_;
841 IntegerTrail* integer_trail_;
842
843 std::vector<bool> variable_was_added_in_to_propagate_;
844 std::vector<int> variables_to_propagate_;
845 std::vector<std::vector<ConstraintIndex>> variable_watchers_;
846
847 absl::StrongVector<ConstraintIndex, bool> constraint_is_finished_;
848 absl::StrongVector<ConstraintIndex, bool> constraint_is_registered_;
849
850 absl::flat_hash_map<int, absl::flat_hash_set<int>>
851 variables_to_equal_or_diff_variables_;
852};
853
854// We only add to the propagation queue variable that are fully encoded.
855// Note that if a variable was already added once, we never add it again.
857 const int num_constraints = model_proto_.constraints_size();
858 const int num_vars = model_proto_.variables_size();
859 constraint_is_finished_.assign(num_constraints, false);
860 constraint_is_registered_.assign(num_constraints, false);
861
862 // Process all constraint once.
863 for (ConstraintIndex ct_index(0); ct_index < num_constraints; ++ct_index) {
864 constraint_is_finished_[ct_index] = ProcessConstraint(ct_index);
865 }
866
867 // We run a heuristics to decide if we want to fully encode a variable or not.
868 // We decide to fully encode a variable if:
869 // - a variable appears in enough a1 * x1 + a2 + x2 ==/!= value and the
870 // domain is small.
871 // - the number of values that appears in b => x ==/!= value that are not
872 // the bounds of the variables is more that half the size of the domain.
873 // . - the size of the domain is > 2
874 int num_variables_fully_encoded_by_heuristics = 0;
875 for (int var = 0; var < num_vars; ++var) {
876 if (!mapping_->IsInteger(var) || IsFullyEncoded(var)) continue;
877 const IntegerVariableProto& int_var_proto = model_proto_.variables(var);
878 const Domain domain = ReadDomainFromProto(int_var_proto);
879 int64_t domain_size = domain.Size();
880 int64_t num_diff_or_equal_var_constraints = 0;
881 int64_t num_potential_encoded_values_without_bounds = 0;
882
883 if (domain_size <= 2) continue;
884
885 const absl::flat_hash_set<int64_t>& value_set =
886 mapping_->PotentialEncodedValues(var);
887 for (const int value : value_set) {
888 if (value > domain.Min() && value < domain.Max() &&
889 domain.Contains(value)) {
890 num_potential_encoded_values_without_bounds++;
891 }
892 }
893
894 const auto& it = variables_to_equal_or_diff_variables_.find(var);
895 if (it != variables_to_equal_or_diff_variables_.end()) {
896 num_diff_or_equal_var_constraints = it->second.size();
897 }
898
899 if (num_potential_encoded_values_without_bounds >= domain_size / 2 ||
900 (num_diff_or_equal_var_constraints >= domain_size / 2 &&
901 domain_size < 16)) {
902 VLOG(3) << model_proto_.variables(var).ShortDebugString()
903 << " is encoded with "
904 << num_potential_encoded_values_without_bounds
905 << " unary constraints, and " << num_diff_or_equal_var_constraints
906 << " binary constraints on a domain of size " << domain_size;
907 FullyEncode(var);
908 num_variables_fully_encoded_by_heuristics++;
909 }
910 }
911 if (num_variables_fully_encoded_by_heuristics > 0) {
912 VLOG(2) << num_variables_fully_encoded_by_heuristics
913 << " variables fully encoded after model introspection.";
914 }
915
916 // Make sure all fully encoded variables of interest are in the queue.
917 for (int v = 0; v < variable_watchers_.size(); v++) {
918 if (!variable_watchers_[v].empty() && IsFullyEncoded(v)) {
919 AddVariableToPropagationQueue(v);
920 }
921 }
922
923 // Loop until no additional variable can be fully encoded.
924 while (!variables_to_propagate_.empty()) {
925 const int variable = variables_to_propagate_.back();
926 variables_to_propagate_.pop_back();
927 for (const ConstraintIndex ct_index : variable_watchers_[variable]) {
928 if (constraint_is_finished_[ct_index]) continue;
929 constraint_is_finished_[ct_index] = ProcessConstraint(ct_index);
930 }
931 }
932}
933
934// Returns true if the constraint has finished encoding what it wants.
935bool FullEncodingFixedPointComputer::ProcessConstraint(
936 ConstraintIndex ct_index) {
937 const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
938 switch (ct.constraint_case()) {
939 case ConstraintProto::ConstraintProto::kElement:
940 return ProcessElement(ct_index);
941 case ConstraintProto::ConstraintProto::kTable:
942 return ProcessTable(ct_index);
943 case ConstraintProto::ConstraintProto::kAutomaton:
944 return ProcessAutomaton(ct_index);
945 case ConstraintProto::ConstraintProto::kLinear:
946 return ProcessLinear(ct_index);
947 default:
948 return true;
949 }
950}
951
952bool FullEncodingFixedPointComputer::ProcessElement(ConstraintIndex ct_index) {
953 const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
954
955 // Index must always be full encoded.
956 FullyEncode(ct.element().index());
957
958 const int target = ct.element().target();
959
960 // If target is fixed, do not encode variables.
961 if (VariableIsFixed(target)) return true;
962
963 // If target is a constant or fully encoded, variables must be fully encoded.
964 if (IsFullyEncoded(target)) {
965 for (const int v : ct.element().vars()) FullyEncode(v);
966 }
967
968 // If all non-target variables are fully encoded, target must be too.
969 bool all_variables_are_fully_encoded = true;
970 for (const int v : ct.element().vars()) {
971 if (v == target) continue;
972 if (!IsFullyEncoded(v)) {
973 all_variables_are_fully_encoded = false;
974 break;
975 }
976 }
977 if (all_variables_are_fully_encoded) {
978 if (!IsFullyEncoded(target)) FullyEncode(target);
979 return true;
980 }
981
982 // If some variables are not fully encoded, register on those.
983 if (constraint_is_registered_[ct_index]) {
984 for (const int v : ct.element().vars()) Register(ct_index, v);
985 Register(ct_index, target);
986 }
987 return false;
988}
989
990bool FullEncodingFixedPointComputer::ProcessTable(ConstraintIndex ct_index) {
991 const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
992
993 if (ct.table().negated()) return true;
994
995 for (const int variable : ct.table().vars()) {
996 FullyEncode(variable);
997 }
998
999 return true;
1000}
1001
1002bool FullEncodingFixedPointComputer::ProcessAutomaton(
1003 ConstraintIndex ct_index) {
1004 const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
1005 for (const int variable : ct.automaton().vars()) {
1006 FullyEncode(variable);
1007 }
1008 return true;
1009}
1010
1011bool FullEncodingFixedPointComputer::ProcessLinear(ConstraintIndex ct_index) {
1012 // We are only interested in linear equations of the form:
1013 // [b =>] a1 * x1 + a2 * x2 ==|!= value
1014 const ConstraintProto& ct = model_proto_.constraints(ct_index.value());
1015 if (parameters_.boolean_encoding_level() == 0 ||
1016 ct.linear().vars_size() != 2) {
1017 return true;
1018 }
1019
1020 if (!ConstraintIsEq(ct.linear()) &&
1021 !ConstraintIsNEq(ct.linear(), mapping_, integer_trail_, nullptr)) {
1022 return true;
1023 }
1024
1025 const int var0 = ct.linear().vars(0);
1026 const int var1 = ct.linear().vars(1);
1027 if (!IsFullyEncoded(var0)) {
1028 variables_to_equal_or_diff_variables_[var0].insert(var1);
1029 }
1030 if (!IsFullyEncoded(var1)) {
1031 variables_to_equal_or_diff_variables_[var1].insert(var0);
1032 }
1033 return true;
1034}
1035
1038 fixpoint.ComputeFixedPoint();
1039}
1040
1042 Model* m) {
1043 if (model_proto.search_strategy().empty()) return;
1044
1045 auto* mapping = m->GetOrCreate<CpModelMapping>();
1046 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1047 for (const DecisionStrategyProto& strategy : model_proto.search_strategy()) {
1048 if (strategy.domain_reduction_strategy() ==
1050 for (const int ref : strategy.variables()) {
1051 if (!mapping->IsInteger(ref)) return;
1052 const IntegerVariable variable = mapping->Integer(PositiveRef(ref));
1053 if (!integer_trail->IsFixed(variable)) {
1054 m->Add(FullyEncodeVariable(variable));
1055 }
1056 }
1057 }
1058 }
1059}
1060
1061// ============================================================================
1062// Constraint loading functions.
1063// ============================================================================
1064
1066 auto* mapping = m->GetOrCreate<CpModelMapping>();
1067 std::vector<Literal> literals = mapping->Literals(ct.bool_or().literals());
1068 for (const int ref : ct.enforcement_literal()) {
1069 literals.push_back(mapping->Literal(ref).Negated());
1070 }
1071 m->Add(ClauseConstraint(literals));
1072}
1073
1075 auto* mapping = m->GetOrCreate<CpModelMapping>();
1076 std::vector<Literal> literals;
1077 for (const int ref : ct.enforcement_literal()) {
1078 literals.push_back(mapping->Literal(ref).Negated());
1079 }
1080 auto* sat_solver = m->GetOrCreate<SatSolver>();
1081 for (const Literal literal : mapping->Literals(ct.bool_and().literals())) {
1082 literals.push_back(literal);
1083 sat_solver->AddProblemClause(literals);
1084 literals.pop_back();
1085 }
1086}
1087
1089 auto* mapping = m->GetOrCreate<CpModelMapping>();
1090 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1091 m->Add(AtMostOneConstraint(mapping->Literals(ct.at_most_one().literals())));
1092}
1093
1095 auto* mapping = m->GetOrCreate<CpModelMapping>();
1096 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1097 m->Add(ExactlyOneConstraint(mapping->Literals(ct.exactly_one().literals())));
1098}
1099
1101 auto* mapping = m->GetOrCreate<CpModelMapping>();
1102 CHECK(!HasEnforcementLiteral(ct)) << "Not supported.";
1103 m->Add(LiteralXorIs(mapping->Literals(ct.bool_xor().literals()), true));
1104}
1105
1106namespace {
1107
1108// Boolean encoding of:
1109// enforcement_literal => coeff1 * var1 + coeff2 * var2 == rhs;
1110void LoadEquivalenceAC(const std::vector<Literal> enforcement_literal,
1111 IntegerValue coeff1, IntegerVariable var1,
1112 IntegerValue coeff2, IntegerVariable var2,
1113 const IntegerValue rhs, Model* m) {
1114 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1115 CHECK(encoder->VariableIsFullyEncoded(var1));
1116 CHECK(encoder->VariableIsFullyEncoded(var2));
1117 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
1118 for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
1119 term1_value_to_literal[coeff1 * value_literal.value] =
1120 value_literal.literal;
1121 }
1122 for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
1123 const IntegerValue target = rhs - value_literal.value * coeff2;
1124 if (!gtl::ContainsKey(term1_value_to_literal, target)) {
1125 m->Add(EnforcedClause(enforcement_literal,
1126 {value_literal.literal.Negated()}));
1127 } else {
1128 const Literal target_literal = term1_value_to_literal[target];
1129 m->Add(EnforcedClause(enforcement_literal,
1130 {value_literal.literal.Negated(), target_literal}));
1131 m->Add(EnforcedClause(enforcement_literal,
1132 {value_literal.literal, target_literal.Negated()}));
1133
1134 // This "target" can never be reached again, so it is safe to remove it.
1135 // We do that so we know the term1 values that are never reached.
1136 term1_value_to_literal.erase(target);
1137 }
1138 }
1139
1140 // Exclude the values that can never be "matched" by coeff2 * var2.
1141 // We need the std::sort() to be deterministic!
1142 std::vector<Literal> implied_false;
1143 for (const auto entry : term1_value_to_literal) {
1144 implied_false.push_back(entry.second);
1145 }
1146 std::sort(implied_false.begin(), implied_false.end());
1147 for (const Literal l : implied_false) {
1148 m->Add(EnforcedClause(enforcement_literal, {l.Negated()}));
1149 }
1150}
1151
1152// Boolean encoding of:
1153// enforcement_literal => coeff1 * var1 + coeff2 * var2 != rhs;
1154void LoadEquivalenceNeqAC(const std::vector<Literal> enforcement_literal,
1155 IntegerValue coeff1, IntegerVariable var1,
1156 IntegerValue coeff2, IntegerVariable var2,
1157 const IntegerValue rhs, Model* m) {
1158 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1159 CHECK(encoder->VariableIsFullyEncoded(var1));
1160 CHECK(encoder->VariableIsFullyEncoded(var2));
1161 absl::flat_hash_map<IntegerValue, Literal> term1_value_to_literal;
1162 for (const auto value_literal : encoder->FullDomainEncoding(var1)) {
1163 term1_value_to_literal[coeff1 * value_literal.value] =
1164 value_literal.literal;
1165 }
1166 for (const auto value_literal : encoder->FullDomainEncoding(var2)) {
1167 const IntegerValue target_value = rhs - value_literal.value * coeff2;
1168 const auto& it = term1_value_to_literal.find(target_value);
1169 if (it != term1_value_to_literal.end()) {
1170 const Literal target_literal = it->second;
1171 m->Add(EnforcedClause(
1172 enforcement_literal,
1173 {value_literal.literal.Negated(), target_literal.Negated()}));
1174 }
1175 }
1176}
1177
1178} // namespace
1179
1181 auto* mapping = m->GetOrCreate<CpModelMapping>();
1182 if (ct.linear().vars().empty()) {
1183 const Domain rhs = ReadDomainFromProto(ct.linear());
1184 if (rhs.Contains(0)) return;
1186 std::vector<Literal> clause;
1187 for (const int ref : ct.enforcement_literal()) {
1188 clause.push_back(mapping->Literal(ref).Negated());
1189 }
1190 m->Add(ClauseConstraint(clause));
1191 } else {
1192 VLOG(1) << "Trivially UNSAT constraint: " << ct.DebugString();
1193 m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1194 }
1195 return;
1196 }
1197
1198 auto* integer_trail = m->GetOrCreate<IntegerTrail>();
1199 const std::vector<IntegerVariable> vars =
1200 mapping->Integers(ct.linear().vars());
1201 const std::vector<int64_t> coeffs = ValuesFromProto(ct.linear().coeffs());
1202
1203 // Compute the min/max to relax the bounds if needed.
1204 //
1205 // TODO(user): Reuse ComputeLinearBounds()? but then we need another loop
1206 // to detect if we only have Booleans.
1207 IntegerValue min_sum(0);
1208 IntegerValue max_sum(0);
1209 IntegerValue max_domain_size(0);
1210 bool all_booleans = true;
1211 for (int i = 0; i < vars.size(); ++i) {
1212 if (all_booleans && !mapping->IsBoolean(ct.linear().vars(i))) {
1213 all_booleans = false;
1214 }
1215 const IntegerValue lb = integer_trail->LowerBound(vars[i]);
1216 const IntegerValue ub = integer_trail->UpperBound(vars[i]);
1217 max_domain_size = std::max(max_domain_size, ub - lb + 1);
1218 const IntegerValue term_a = coeffs[i] * lb;
1219 const IntegerValue term_b = coeffs[i] * ub;
1220 min_sum += std::min(term_a, term_b);
1221 max_sum += std::max(term_a, term_b);
1222 }
1223
1224 const SatParameters& params = *m->GetOrCreate<SatParameters>();
1225 const IntegerValue domain_size_limit(
1227 if (ct.linear().vars_size() == 2 && !integer_trail->IsFixed(vars[0]) &&
1228 !integer_trail->IsFixed(vars[1]) &&
1229 max_domain_size <= domain_size_limit) {
1230 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1231 if (params.boolean_encoding_level() > 0 && ConstraintIsEq(ct.linear()) &&
1232 ct.linear().domain(0) != min_sum && ct.linear().domain(0) != max_sum &&
1233 encoder->VariableIsFullyEncoded(vars[0]) &&
1234 encoder->VariableIsFullyEncoded(vars[1])) {
1235 VLOG(3) << "Load AC version of " << ct.DebugString() << ", var0 domain = "
1236 << integer_trail->InitialVariableDomain(vars[0])
1237 << ", var1 domain = "
1238 << integer_trail->InitialVariableDomain(vars[1]);
1239 return LoadEquivalenceAC(mapping->Literals(ct.enforcement_literal()),
1240 IntegerValue(coeffs[0]), vars[0],
1241 IntegerValue(coeffs[1]), vars[1],
1242 IntegerValue(ct.linear().domain(0)), m);
1243 }
1244
1245 int64_t single_value = 0;
1246 if (params.boolean_encoding_level() > 0 &&
1247 ConstraintIsNEq(ct.linear(), mapping, integer_trail, &single_value) &&
1248 single_value != min_sum && single_value != max_sum &&
1249 encoder->VariableIsFullyEncoded(vars[0]) &&
1250 encoder->VariableIsFullyEncoded(vars[1])) {
1251 VLOG(3) << "Load NAC version of " << ct.DebugString()
1252 << ", var0 domain = "
1253 << integer_trail->InitialVariableDomain(vars[0])
1254 << ", var1 domain = "
1255 << integer_trail->InitialVariableDomain(vars[1])
1256 << ", value = " << single_value;
1257 return LoadEquivalenceNeqAC(mapping->Literals(ct.enforcement_literal()),
1258 IntegerValue(coeffs[0]), vars[0],
1259 IntegerValue(coeffs[1]), vars[1],
1260 IntegerValue(single_value), m);
1261 }
1262 }
1263
1264 if (ct.linear().domain_size() == 2) {
1265 int64_t lb = ct.linear().domain(0);
1266 int64_t ub = ct.linear().domain(1);
1267 if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1268 if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1269
1270 if (!HasEnforcementLiteral(ct)) {
1271 if (all_booleans) {
1272 // TODO(user): we should probably also implement an
1273 // half-reified version of this constraint.
1274 std::vector<LiteralWithCoeff> cst;
1275 for (int i = 0; i < vars.size(); ++i) {
1276 const int ref = ct.linear().vars(i);
1277 cst.push_back({mapping->Literal(ref), coeffs[i]});
1278 }
1279 m->Add(BooleanLinearConstraint(lb, ub, &cst));
1280 } else {
1282 m->Add(WeightedSumGreaterOrEqual(vars, coeffs, lb));
1283 }
1285 m->Add(WeightedSumLowerOrEqual(vars, coeffs, ub));
1286 }
1287 }
1288 } else {
1289 const std::vector<Literal> enforcement_literals =
1290 mapping->Literals(ct.enforcement_literal());
1292 m->Add(ConditionalWeightedSumGreaterOrEqual(enforcement_literals, vars,
1293 coeffs, lb));
1294 }
1296 m->Add(ConditionalWeightedSumLowerOrEqual(enforcement_literals, vars,
1297 coeffs, ub));
1298 }
1299 }
1300 } else {
1301 // In this case, we can create just one Boolean instead of two since one
1302 // is the negation of the other.
1303 const bool special_case =
1304 ct.enforcement_literal().empty() && ct.linear().domain_size() == 4;
1305
1306 std::vector<Literal> clause;
1307 for (int i = 0; i < ct.linear().domain_size(); i += 2) {
1308 int64_t lb = ct.linear().domain(i);
1309 int64_t ub = ct.linear().domain(i + 1);
1310 if (min_sum >= lb) lb = std::numeric_limits<int64_t>::min();
1311 if (max_sum <= ub) ub = std::numeric_limits<int64_t>::max();
1312
1313 const Literal subdomain_literal(
1314 special_case && i > 0 ? clause.back().Negated()
1315 : Literal(m->Add(NewBooleanVariable()), true));
1316 clause.push_back(subdomain_literal);
1317
1319 m->Add(ConditionalWeightedSumGreaterOrEqual({subdomain_literal}, vars,
1320 coeffs, lb));
1321 }
1323 m->Add(ConditionalWeightedSumLowerOrEqual({subdomain_literal}, vars,
1324 coeffs, ub));
1325 }
1326 }
1327 for (const int ref : ct.enforcement_literal()) {
1328 clause.push_back(mapping->Literal(ref).Negated());
1329 }
1330 if (!special_case) m->Add(ClauseConstraint(clause));
1331 }
1332}
1333
1335 auto* mapping = m->GetOrCreate<CpModelMapping>();
1336 const std::vector<IntegerVariable> vars =
1337 mapping->Integers(ct.all_diff().vars());
1338 // If all variables are fully encoded and domains are not too large, use
1339 // arc-consistent reasoning. Otherwise, use bounds-consistent reasoning.
1340 IntegerTrail* integer_trail = m->GetOrCreate<IntegerTrail>();
1341 IntegerEncoder* encoder = m->GetOrCreate<IntegerEncoder>();
1342 int num_fully_encoded = 0;
1343 int64_t max_domain_size = 0;
1344 for (const IntegerVariable variable : vars) {
1345 if (encoder->VariableIsFullyEncoded(variable)) num_fully_encoded++;
1346
1347 IntegerValue lb = integer_trail->LowerBound(variable);
1348 IntegerValue ub = integer_trail->UpperBound(variable);
1349 const int64_t domain_size = ub.value() - lb.value() + 1;
1350 max_domain_size = std::max(max_domain_size, domain_size);
1351 }
1352
1353 if (num_fully_encoded == vars.size() && max_domain_size < 1024) {
1354 m->Add(AllDifferentBinary(vars));
1355 m->Add(AllDifferentAC(vars));
1356 } else {
1357 m->Add(AllDifferentOnBounds(vars));
1358 }
1359}
1360
1362 auto* mapping = m->GetOrCreate<CpModelMapping>();
1363 const IntegerVariable prod = mapping->Integer(ct.int_prod().target());
1364 const std::vector<IntegerVariable> vars =
1365 mapping->Integers(ct.int_prod().vars());
1366 CHECK_EQ(vars.size(), 2) << "General int_prod not supported yet.";
1367 m->Add(ProductConstraint(vars[0], vars[1], prod));
1368}
1369
1371 auto* mapping = m->GetOrCreate<CpModelMapping>();
1372 const IntegerVariable div = mapping->Integer(ct.int_div().target());
1373 const std::vector<IntegerVariable> vars =
1374 mapping->Integers(ct.int_div().vars());
1375 if (m->Get(IsFixed(vars[1]))) {
1376 const IntegerValue denom(m->Get(Value(vars[1])));
1377 if (denom == 1) {
1378 m->Add(Equality(vars[0], div));
1379 } else {
1380 m->Add(FixedDivisionConstraint(vars[0], denom, div));
1381 }
1382 } else {
1383 m->Add(DivisionConstraint(vars[0], vars[1], div));
1384 }
1385}
1386
1388 auto* mapping = m->GetOrCreate<CpModelMapping>();
1389 const IntegerVariable min = mapping->Integer(ct.int_min().target());
1390 const std::vector<IntegerVariable> vars =
1391 mapping->Integers(ct.int_min().vars());
1392 m->Add(IsEqualToMinOf(min, vars));
1393}
1394
1396 if (ct.lin_max().exprs().empty()) {
1397 m->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
1398 return;
1399 }
1400
1401 auto* mapping = m->GetOrCreate<CpModelMapping>();
1402 const LinearExpression max = mapping->GetExprFromProto(ct.lin_max().target());
1403 std::vector<LinearExpression> negated_exprs;
1404 negated_exprs.reserve(ct.lin_max().exprs_size());
1405 for (int i = 0; i < ct.lin_max().exprs_size(); ++i) {
1406 negated_exprs.push_back(
1407 NegationOf(mapping->GetExprFromProto(ct.lin_max().exprs(i))));
1408 }
1409 // TODO(user): Consider replacing the min propagator by max.
1410 m->Add(IsEqualToMinOf(NegationOf(max), negated_exprs));
1411}
1412
1414 auto* mapping = m->GetOrCreate<CpModelMapping>();
1415 const IntegerVariable max = mapping->Integer(ct.int_max().target());
1416 const std::vector<IntegerVariable> vars =
1417 mapping->Integers(ct.int_max().vars());
1418 m->Add(IsEqualToMaxOf(max, vars));
1419}
1420
1422 auto* mapping = m->GetOrCreate<CpModelMapping>();
1423 m->Add(Disjunctive(mapping->Intervals(ct.no_overlap().intervals())));
1424}
1425
1427 if (ct.no_overlap_2d().x_intervals().empty()) return;
1428 auto* mapping = m->GetOrCreate<CpModelMapping>();
1429 const std::vector<IntervalVariable> x_intervals =
1430 mapping->Intervals(ct.no_overlap_2d().x_intervals());
1431 const std::vector<IntervalVariable> y_intervals =
1432 mapping->Intervals(ct.no_overlap_2d().y_intervals());
1434 x_intervals, y_intervals,
1435 !ct.no_overlap_2d().boxes_with_null_area_can_overlap()));
1436}
1437
1439 auto* mapping = m->GetOrCreate<CpModelMapping>();
1440 const std::vector<IntervalVariable> intervals =
1441 mapping->Intervals(ct.cumulative().intervals());
1442 const AffineExpression capacity(mapping->Integer(ct.cumulative().capacity()));
1443 std::vector<AffineExpression> demands;
1444 for (const IntegerVariable var :
1445 mapping->Integers(ct.cumulative().demands())) {
1446 demands.push_back(AffineExpression(var));
1447 }
1448 m->Add(Cumulative(intervals, demands, capacity));
1449}
1450
1452 auto* mapping = m->GetOrCreate<CpModelMapping>();
1453 auto* encoder = m->GetOrCreate<IntegerEncoder>();
1454 std::vector<AffineExpression> times;
1455 std::vector<IntegerValue> deltas;
1456 std::vector<Literal> presences;
1457 const int size = ct.reservoir().times().size();
1458 for (int i = 0; i < size; ++i) {
1459 times.push_back(mapping->Integer(ct.reservoir().times(i)));
1460 deltas.push_back(IntegerValue(ct.reservoir().demands(i)));
1461 if (!ct.reservoir().actives().empty()) {
1462 presences.push_back(mapping->Literal(ct.reservoir().actives(i)));
1463 } else {
1464 presences.push_back(encoder->GetTrueLiteral());
1465 }
1466 }
1467 AddReservoirConstraint(times, deltas, presences, ct.reservoir().min_level(),
1468 ct.reservoir().max_level(), m);
1469}
1470
1471// If a variable is constant and its value appear in no other variable domains,
1472// then the literal encoding the index and the one encoding the target at this
1473// value are equivalent.
1475 Model* m) {
1476 auto* mapping = m->GetOrCreate<CpModelMapping>();
1477 IntegerEncoder* encoder = m->GetOrCreate<IntegerEncoder>();
1478 IntegerTrail* integer_trail = m->GetOrCreate<IntegerTrail>();
1479
1480 const IntegerVariable index = mapping->Integer(ct.element().index());
1481 const IntegerVariable target = mapping->Integer(ct.element().target());
1482 const std::vector<IntegerVariable> vars =
1483 mapping->Integers(ct.element().vars());
1484 CHECK(!m->Get(IsFixed(index)));
1485 CHECK(!m->Get(IsFixed(target)));
1486
1487 Domain union_of_non_constant_domains;
1488 std::map<IntegerValue, int> constant_to_num;
1489 for (const auto literal_value : m->Add(FullyEncodeVariable(index))) {
1490 const int i = literal_value.value.value();
1491 if (m->Get(IsFixed(vars[i]))) {
1492 const IntegerValue value(m->Get(Value(vars[i])));
1493 constant_to_num[value]++;
1494 } else {
1495 union_of_non_constant_domains = union_of_non_constant_domains.UnionWith(
1496 integer_trail->InitialVariableDomain(vars[i]));
1497 }
1498 }
1499
1500 // Bump the number if the constant appear in union_of_non_constant_domains.
1501 for (const auto entry : constant_to_num) {
1502 if (union_of_non_constant_domains.Contains(entry.first.value())) {
1503 constant_to_num[entry.first]++;
1504 }
1505 }
1506
1507 // Use the literal from the index encoding to encode the target at the
1508 // "unique" values.
1509 bool is_one_to_one_mapping = true;
1510 for (const auto literal_value : m->Add(FullyEncodeVariable(index))) {
1511 const int i = literal_value.value.value();
1512 if (!m->Get(IsFixed(vars[i]))) {
1513 is_one_to_one_mapping = false;
1514 continue;
1515 }
1516
1517 const IntegerValue value(m->Get(Value(vars[i])));
1518 if (constant_to_num[value] == 1) {
1519 const Literal r = literal_value.literal;
1520 encoder->AssociateToIntegerEqualValue(r, target, value);
1521 } else {
1522 is_one_to_one_mapping = false;
1523 }
1524 }
1525
1526 return is_one_to_one_mapping;
1527}
1528
1529// TODO(user): Be more efficient when the element().vars() are constants.
1530// Ideally we should avoid creating them as integer variable since we don't
1531// use them.
1533 auto* mapping = m->GetOrCreate<CpModelMapping>();
1534 const IntegerVariable index = mapping->Integer(ct.element().index());
1535 const IntegerVariable target = mapping->Integer(ct.element().target());
1536 const std::vector<IntegerVariable> vars =
1537 mapping->Integers(ct.element().vars());
1538 CHECK(!m->Get(IsFixed(index)));
1539
1540 // We always fully encode the index on an element constraint.
1541 const auto encoding = m->Add(FullyEncodeVariable((index)));
1542 std::vector<Literal> selectors;
1543 std::vector<IntegerVariable> possible_vars;
1544 for (const auto literal_value : encoding) {
1545 const int i = literal_value.value.value();
1546 CHECK_GE(i, 0);
1547 CHECK_LT(i, vars.size());
1548 possible_vars.push_back(vars[i]);
1549 selectors.push_back(literal_value.literal);
1550 const Literal r = literal_value.literal;
1551
1552 if (vars[i] == target) continue;
1553 if (m->Get(IsFixed(target))) {
1554 const int64_t value = m->Get(Value(target));
1555 m->Add(ImpliesInInterval(r, vars[i], value, value));
1556 } else if (m->Get(IsFixed(vars[i]))) {
1557 const int64_t value = m->Get(Value(vars[i]));
1558 m->Add(ImpliesInInterval(r, target, value, value));
1559 } else {
1560 m->Add(ConditionalLowerOrEqualWithOffset(vars[i], target, 0, r));
1561 m->Add(ConditionalLowerOrEqualWithOffset(target, vars[i], 0, r));
1562 }
1563 }
1564
1565 if (!m->Get(IsFixed(target))) {
1566 m->Add(PartialIsOneOfVar(target, possible_vars, selectors));
1567 }
1568}
1569
1570// Arc-Consistent encoding of the element constraint as SAT clauses.
1571// The constraint enforces vars[index] == target.
1572//
1573// The AC propagation can be decomposed in three rules:
1574// Rule 1: dom(index) == i => dom(vars[i]) == dom(target).
1575// Rule 2: dom(target) \subseteq \Union_{i \in dom(index)} dom(vars[i]).
1576// Rule 3: dom(index) \subseteq { i | |dom(vars[i]) \inter dom(target)| > 0 }.
1577//
1578// We encode this in a way similar to the table constraint, except that the
1579// set of admissible tuples is not explicit.
1580// First, we add Booleans selected[i][value] <=> (index == i /\ vars[i] ==
1581// value). Rules 1 and 2 are enforced by target == value <=> \Or_{i}
1582// selected[i][value]. Rule 3 is enforced by index == i <=> \Or_{value}
1583// selected[i][value].
1585 auto* mapping = m->GetOrCreate<CpModelMapping>();
1586 const IntegerVariable index = mapping->Integer(ct.element().index());
1587 const IntegerVariable target = mapping->Integer(ct.element().target());
1588 const std::vector<IntegerVariable> vars =
1589 mapping->Integers(ct.element().vars());
1590 CHECK(!m->Get(IsFixed(index)));
1591 CHECK(!m->Get(IsFixed(target)));
1592
1593 absl::flat_hash_map<IntegerValue, Literal> target_map;
1594 const auto target_encoding = m->Add(FullyEncodeVariable(target));
1595 for (const auto literal_value : target_encoding) {
1596 target_map[literal_value.value] = literal_value.literal;
1597 }
1598
1599 // For i \in index and value in vars[i], make (index == i /\ vars[i] == value)
1600 // literals and store them by value in vectors.
1601 absl::flat_hash_map<IntegerValue, std::vector<Literal>> value_to_literals;
1602 const auto index_encoding = m->Add(FullyEncodeVariable(index));
1603 IntegerTrail* integer_trail = m->GetOrCreate<IntegerTrail>();
1604 for (const auto literal_value : index_encoding) {
1605 const int i = literal_value.value.value();
1606 const Literal i_lit = literal_value.literal;
1607
1608 // Special case where vars[i] == value /\ i_lit is actually i_lit.
1609 if (m->Get(IsFixed(vars[i]))) {
1610 value_to_literals[integer_trail->LowerBound(vars[i])].push_back(i_lit);
1611 continue;
1612 }
1613
1614 const auto var_encoding = m->Add(FullyEncodeVariable(vars[i]));
1615 std::vector<Literal> var_selected_literals;
1616 for (const auto var_literal_value : var_encoding) {
1617 const IntegerValue value = var_literal_value.value;
1618 const Literal var_is_value = var_literal_value.literal;
1619
1620 if (!gtl::ContainsKey(target_map, value)) {
1621 // No need to add to value_to_literals, selected[i][value] is always
1622 // false.
1623 m->Add(Implication(i_lit, var_is_value.Negated()));
1624 continue;
1625 }
1626
1627 const Literal var_is_value_and_selected =
1628 Literal(m->Add(NewBooleanVariable()), true);
1629 m->Add(ReifiedBoolAnd({i_lit, var_is_value}, var_is_value_and_selected));
1630 value_to_literals[value].push_back(var_is_value_and_selected);
1631 var_selected_literals.push_back(var_is_value_and_selected);
1632 }
1633 // index == i <=> \Or_{value} selected[i][value].
1634 m->Add(ReifiedBoolOr(var_selected_literals, i_lit));
1635 }
1636
1637 // target == value <=> \Or_{i \in index} (vars[i] == value /\ index == i).
1638 for (const auto& entry : target_map) {
1639 const IntegerValue value = entry.first;
1640 const Literal target_is_value = entry.second;
1641
1642 if (!gtl::ContainsKey(value_to_literals, value)) {
1643 m->Add(ClauseConstraint({target_is_value.Negated()}));
1644 } else {
1645 m->Add(ReifiedBoolOr(value_to_literals[value], target_is_value));
1646 }
1647 }
1648}
1649
1650namespace {
1651
1652// This Boolean encoding is enough for consistency, but does not propagate as
1653// much as LoadElementConstraintAC(). However, setting any of the non-propagated
1654// Booleans to its "wrong" value will result directly in a conflict, so the
1655// solver will easily learn an AC encoding...
1656//
1657// The advantage is that this does not introduce extra BooleanVariables.
1658void LoadElementConstraintHalfAC(const ConstraintProto& ct, Model* m) {
1659 auto* mapping = m->GetOrCreate<CpModelMapping>();
1660 const IntegerVariable index = mapping->Integer(ct.element().index());
1661 const IntegerVariable target = mapping->Integer(ct.element().target());
1662 const std::vector<IntegerVariable> vars =
1663 mapping->Integers(ct.element().vars());
1664 CHECK(!m->Get(IsFixed(index)));
1665 CHECK(!m->Get(IsFixed(target)));
1666
1667 m->Add(FullyEncodeVariable(target));
1668 for (const auto value_literal : m->Add(FullyEncodeVariable(index))) {
1669 const int i = value_literal.value.value();
1670 m->Add(FullyEncodeVariable(vars[i]));
1671 LoadEquivalenceAC({value_literal.literal}, IntegerValue(1), vars[i],
1672 IntegerValue(-1), target, IntegerValue(0), m);
1673 }
1674}
1675
1676void LoadBooleanElement(const ConstraintProto& ct, Model* m) {
1677 auto* mapping = m->GetOrCreate<CpModelMapping>();
1678 const IntegerVariable index = mapping->Integer(ct.element().index());
1679 const std::vector<Literal> literals = mapping->Literals(ct.element().vars());
1680 const Literal target = mapping->Literal(ct.element().target());
1681
1682 if (m->Get(IsFixed(index))) {
1683 m->Add(Equality(target, literals[m->Get(Value(index))]));
1684 return;
1685 }
1686
1687 std::vector<Literal> all_true;
1688 std::vector<Literal> all_false;
1689 for (const auto value_literal : m->Add(FullyEncodeVariable(index))) {
1690 const Literal a_lit = literals[value_literal.value.value()];
1691 const Literal i_lit = value_literal.literal;
1692 m->Add(ClauseConstraint({i_lit.Negated(), a_lit.Negated(), target}));
1693 m->Add(ClauseConstraint({i_lit.Negated(), a_lit, target.Negated()}));
1694 all_true.push_back(a_lit.Negated());
1695 all_false.push_back(a_lit);
1696 }
1697 all_true.push_back(target);
1698 all_false.push_back(target.Negated());
1699 m->Add(ClauseConstraint(all_true));
1700 m->Add(ClauseConstraint(all_false));
1701 // TODO(user): Investigate filtering this with active literals.
1702}
1703
1704} // namespace
1705
1707 auto* mapping = m->GetOrCreate<CpModelMapping>();
1708 const IntegerVariable index = mapping->Integer(ct.element().index());
1709
1710 bool boolean_array = true;
1711 for (const int ref : ct.element().vars()) {
1712 if (!mapping->IsBoolean(ref)) {
1713 boolean_array = false;
1714 break;
1715 }
1716 }
1717 if (boolean_array && !mapping->IsBoolean(ct.element().target())) {
1718 // Should have been reduced but presolve.
1719 VLOG(1) << "Fix boolean_element not propagated on target";
1720 boolean_array = false;
1721 }
1722
1723 // TODO(user): Move this to presolve. Leads to a larger discussion on
1724 // adding full encoding to model during presolve.
1725 if (boolean_array) {
1726 LoadBooleanElement(ct, m);
1727 return;
1728 }
1729
1730 const IntegerVariable target = mapping->Integer(ct.element().target());
1731 const std::vector<IntegerVariable> vars =
1732 mapping->Integers(ct.element().vars());
1733
1734 // Retrict the domain of index in case there was no presolve.
1736 index, Domain(0, vars.size() - 1))) {
1737 return;
1738 }
1739
1740 // This returns true if there is nothing else to do after the equivalences
1741 // of the form (index literal <=> target_literal) have been added.
1742 if (!m->Get(IsFixed(index)) && !m->Get(IsFixed(target)) &&
1744 return;
1745 }
1746
1747 // Special case when index is fixed.
1748 if (m->Get(IsFixed(index))) {
1749 m->Add(Equality(target, vars[m->Get(Value(index))]));
1750 return;
1751 }
1752
1753 // Special case when target is fixed.
1754 if (m->Get(IsFixed(target))) {
1756 }
1757
1758 IntegerEncoder* encoder = m->GetOrCreate<IntegerEncoder>();
1759 const bool target_is_AC = encoder->VariableIsFullyEncoded(target);
1760
1761 int num_AC_variables = 0;
1762 const int num_vars = ct.element().vars().size();
1763 for (const int v : ct.element().vars()) {
1764 IntegerVariable variable = mapping->Integer(v);
1765 const bool is_full =
1766 m->Get(IsFixed(variable)) || encoder->VariableIsFullyEncoded(variable);
1767 if (is_full) num_AC_variables++;
1768 }
1769
1770 const SatParameters& params = *m->GetOrCreate<SatParameters>();
1771 if (params.boolean_encoding_level() > 0 &&
1772 (target_is_AC || num_AC_variables >= num_vars - 1)) {
1773 if (params.boolean_encoding_level() > 1) {
1775 } else {
1776 LoadElementConstraintHalfAC(ct, m);
1777 }
1778 } else {
1780 }
1781}
1782
1784 auto* mapping = m->GetOrCreate<CpModelMapping>();
1785 const std::vector<IntegerVariable> vars =
1786 mapping->Integers(ct.table().vars());
1787 const std::vector<int64_t> values = ValuesFromProto(ct.table().values());
1788 const int num_vars = vars.size();
1789 const int num_tuples = values.size() / num_vars;
1790 std::vector<std::vector<int64_t>> tuples(num_tuples);
1791 int count = 0;
1792 for (int i = 0; i < num_tuples; ++i) {
1793 for (int j = 0; j < num_vars; ++j) {
1794 tuples[i].push_back(values[count++]);
1795 }
1796 }
1797 if (ct.table().negated()) {
1798 AddNegatedTableConstraint(vars, std::move(tuples), m);
1799 } else {
1800 AddTableConstraint(vars, std::move(tuples), m);
1801 }
1802}
1803
1805 auto* mapping = m->GetOrCreate<CpModelMapping>();
1806 const std::vector<IntegerVariable> vars =
1807 mapping->Integers(ct.automaton().vars());
1808
1809 const int num_transitions = ct.automaton().transition_tail_size();
1810 std::vector<std::vector<int64_t>> transitions;
1811 transitions.reserve(num_transitions);
1812 for (int i = 0; i < num_transitions; ++i) {
1813 transitions.push_back({ct.automaton().transition_tail(i),
1814 ct.automaton().transition_label(i),
1815 ct.automaton().transition_head(i)});
1816 }
1817
1818 const int64_t starting_state = ct.automaton().starting_state();
1819 const std::vector<int64_t> final_states =
1820 ValuesFromProto(ct.automaton().final_states());
1821 m->Add(TransitionConstraint(vars, transitions, starting_state, final_states));
1822}
1823
1824// From vector of n IntegerVariables, returns an n x n matrix of Literal
1825// such that matrix[i][j] is the Literal corresponding to vars[i] == j.
1826std::vector<std::vector<Literal>> GetSquareMatrixFromIntegerVariables(
1827 const std::vector<IntegerVariable>& vars, Model* m) {
1828 const int n = vars.size();
1829 const Literal kTrueLiteral =
1830 m->GetOrCreate<IntegerEncoder>()->GetTrueLiteral();
1831 const Literal kFalseLiteral =
1832 m->GetOrCreate<IntegerEncoder>()->GetFalseLiteral();
1833 std::vector<std::vector<Literal>> matrix(
1834 n, std::vector<Literal>(n, kFalseLiteral));
1835 for (int i = 0; i < n; i++) {
1836 for (int j = 0; j < n; j++) {
1837 if (m->Get(IsFixed(vars[i]))) {
1838 const int value = m->Get(Value(vars[i]));
1839 DCHECK_LE(0, value);
1840 DCHECK_LT(value, n);
1841 matrix[i][value] = kTrueLiteral;
1842 } else {
1843 const auto encoding = m->Add(FullyEncodeVariable(vars[i]));
1844 for (const auto& entry : encoding) {
1845 const int value = entry.value.value();
1846 DCHECK_LE(0, value);
1847 DCHECK_LT(value, n);
1848 matrix[i][value] = entry.literal;
1849 }
1850 }
1851 }
1852 }
1853 return matrix;
1854}
1855
1857 const auto& circuit = ct.circuit();
1858 if (circuit.tails().empty()) return;
1859
1860 std::vector<int> tails(circuit.tails().begin(), circuit.tails().end());
1861 std::vector<int> heads(circuit.heads().begin(), circuit.heads().end());
1862 std::vector<Literal> literals =
1863 m->GetOrCreate<CpModelMapping>()->Literals(circuit.literals());
1864 const int num_nodes = ReindexArcs(&tails, &heads);
1865 m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals));
1866}
1867
1869 const auto& routes = ct.routes();
1870 if (routes.tails().empty()) return;
1871
1872 std::vector<int> tails(routes.tails().begin(), routes.tails().end());
1873 std::vector<int> heads(routes.heads().begin(), routes.heads().end());
1874 std::vector<Literal> literals =
1875 m->GetOrCreate<CpModelMapping>()->Literals(routes.literals());
1876 const int num_nodes = ReindexArcs(&tails, &heads);
1877 m->Add(SubcircuitConstraint(num_nodes, tails, heads, literals,
1878 /*multiple_subcircuit_through_zero=*/true));
1879}
1880
1882 switch (ct.constraint_case()) {
1883 case ConstraintProto::ConstraintCase::CONSTRAINT_NOT_SET:
1884 return true;
1885 case ConstraintProto::ConstraintCase::kBoolOr:
1887 return true;
1888 case ConstraintProto::ConstraintCase::kBoolAnd:
1890 return true;
1891 case ConstraintProto::ConstraintCase::kAtMostOne:
1893 return true;
1894 case ConstraintProto::ConstraintCase::kExactlyOne:
1896 return true;
1897 case ConstraintProto::ConstraintCase::kBoolXor:
1899 return true;
1900 case ConstraintProto::ConstraintProto::kLinear:
1902 return true;
1903 case ConstraintProto::ConstraintProto::kAllDiff:
1905 return true;
1906 case ConstraintProto::ConstraintProto::kIntProd:
1908 return true;
1909 case ConstraintProto::ConstraintProto::kIntDiv:
1911 return true;
1912 case ConstraintProto::ConstraintProto::kIntMin:
1914 return true;
1915 case ConstraintProto::ConstraintProto::kLinMax:
1917 return true;
1918 case ConstraintProto::ConstraintProto::kIntMax:
1920 return true;
1921 case ConstraintProto::ConstraintProto::kInterval:
1922 // Already dealt with.
1923 return true;
1924 case ConstraintProto::ConstraintProto::kNoOverlap:
1926 return true;
1927 case ConstraintProto::ConstraintProto::kNoOverlap2D:
1929 return true;
1930 case ConstraintProto::ConstraintProto::kCumulative:
1932 return true;
1933 case ConstraintProto::ConstraintProto::kReservoir:
1935 return true;
1936 case ConstraintProto::ConstraintProto::kElement:
1938 return true;
1939 case ConstraintProto::ConstraintProto::kTable:
1941 return true;
1942 case ConstraintProto::ConstraintProto::kAutomaton:
1944 return true;
1945 case ConstraintProto::ConstraintProto::kCircuit:
1947 return true;
1948 case ConstraintProto::ConstraintProto::kRoutes:
1950 return true;
1951 default:
1952 return false;
1953 }
1954}
1955
1956} // namespace sat
1957} // 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:491
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:888
#define CHECK_LT(val1, val2)
Definition: base/logging.h:701
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:889
#define VLOG(verboselevel)
Definition: base/logging.h:979
We call domain any subset of Int64 = [kint64min, kint64max].
Domain InverseMultiplicationBy(const int64_t coeff) const
Returns {x ∈ Int64, ∃ e ∈ D, x * coeff = e}.
Domain Complement() const
Returns the set Int64 ∖ D.
bool Contains(int64_t value) const
Returns true iff value is in Domain.
int NumIntervals() const
Basic read-only std::vector<> wrapping to view a Domain as a sorted list of non-adjacent intervals.
int64_t Size() const
Returns the number of elements in the domain.
Domain UnionWith(const Domain &domain) const
Returns the union of D and domain.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
int64_t Min() const
Returns the min value of the domain.
bool IsEmpty() const
Returns true if this is the empty set.
int64_t Max() const
Returns the max value of the domain.
const absl::flat_hash_set< int64_t > & PotentialEncodedValues(int var)
std::vector< IntegerVariable > Integers(const List &list) const
std::vector< IntervalVariable > Intervals(const ProtoIndices &indices) const
IntegerVariable Integer(int ref) const
std::vector< sat::Literal > Literals(const ProtoIndices &indices) const
const ::operations_research::sat::CpObjectiveProto & objective() const
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::DecisionStrategyProto & search_strategy(int index) const
const ::operations_research::sat::SymmetryProto & symmetry() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
::PROTOBUF_NAMESPACE_ID::int32 vars(int index) const
static constexpr DomainReductionStrategy SELECT_MEDIAN_VALUE
Definition: cp_model.pb.h:5258
FullEncodingFixedPointComputer(const CpModelProto &model_proto, Model *model)
bool VariableIsFullyEncoded(IntegerVariable var) const
Definition: integer.cc:97
void ReserveSpaceForNumVariables(int num_vars)
Definition: integer.cc:629
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1353
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1349
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1345
bool UpdateInitialDomain(IntegerVariable var, Domain domain)
Definition: integer.cc:686
::PROTOBUF_NAMESPACE_ID::int64 domain(int index) const
Definition: cp_model.pb.h:6893
Literal(int signed_value)
Definition: sat_base.h:69
LiteralIndex Index() const
Definition: sat_base.h:85
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
Definition: sat/model.h:87
T Add(std::function< T(Model *)> f)
This makes it possible to have a nicer API on the client side, and it allows both of these forms:
Definition: sat/model.h:81
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:106
::PROTOBUF_NAMESPACE_ID::int32 max_domain_size_when_encoding_eq_neq_constraints() const
::PROTOBUF_NAMESPACE_ID::int32 boolean_encoding_level() const
bool AddProblemClause(absl::Span< const Literal > literals)
Definition: sat_solver.cc:204
SatParameters parameters
CpModelProto proto
CpModelProto const * model_proto
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
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
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:263
void AddReservoirConstraint(std::vector< AffineExpression > times, std::vector< IntegerValue > deltas, std::vector< Literal > presences, int64_t min_level, int64_t max_level, Model *model)
Definition: timetable.cc:28
void LoadTableConstraint(const ConstraintProto &ct, Model *m)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:91
std::function< void(Model *)> WeightedSumGreaterOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:405
std::function< void(Model *)> AllDifferentAC(const std::vector< IntegerVariable > &variables)
std::function< void(Model *)> LiteralXorIs(const std::vector< Literal > &literals, bool value)
std::function< void(Model *)> Equality(IntegerVariable v, int64_t value)
Definition: integer.h:1582
void LoadExactlyOneConstraint(const ConstraintProto &ct, Model *m)
void AddTableConstraint(absl::Span< const IntegerVariable > vars, std::vector< std::vector< int64_t > > tuples, Model *model)
Definition: sat/table.cc:250
std::function< void(Model *)> ReifiedBoolOr(const std::vector< Literal > &literals, Literal r)
Definition: sat_solver.h:936
void LoadVariables(const CpModelProto &model_proto, bool view_all_booleans_as_integers, Model *m)
void LoadIntProdConstraint(const ConstraintProto &ct, Model *m)
bool LoadConstraint(const ConstraintProto &ct, Model *m)
std::vector< int > UsedVariables(const ConstraintProto &ct)
void LoadBoolOrConstraint(const ConstraintProto &ct, Model *m)
bool RefIsPositive(int ref)
void MaybeFullyEncodeMoreVariables(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> WeightedSumLowerOrEqual(const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:298
const LiteralIndex kNoLiteralIndex(-1)
std::function< void(Model *)> ClauseConstraint(absl::Span< const Literal > literals)
Definition: sat_solver.h:906
std::function< void(Model *)> EnforcedClause(absl::Span< const Literal > enforcement_literals, absl::Span< const Literal > clause)
Definition: sat_solver.h:952
std::function< void(Model *)> SubcircuitConstraint(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, bool multiple_subcircuit_through_zero)
Definition: circuit.cc:499
std::function< BooleanVariable(Model *)> NewBooleanVariable()
Definition: integer.h:1469
std::function< void(Model *)> DivisionConstraint(IntegerVariable num, IntegerVariable denom, IntegerVariable div)
Definition: integer_expr.h:811
bool HasEnforcementLiteral(const ConstraintProto &ct)
std::function< void(Model *)> AllDifferentOnBounds(const std::vector< IntegerVariable > &vars)
std::function< void(Model *)> PartialIsOneOfVar(IntegerVariable target_var, const std::vector< IntegerVariable > &vars, const std::vector< Literal > &selectors)
void LoadBooleanSymmetries(const CpModelProto &model_proto, Model *m)
std::function< bool(const Model &)> IsFixed(IntegerVariable v)
Definition: integer.h:1536
std::function< void(Model *)> ConditionalWeightedSumGreaterOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t lower_bound)
Definition: integer_expr.h:515
bool DetectEquivalencesInElementConstraint(const ConstraintProto &ct, Model *m)
void LoadCumulativeConstraint(const ConstraintProto &ct, Model *m)
std::function< IntervalVariable(Model *)> NewInterval(int64_t min_start, int64_t max_end, int64_t size)
Definition: intervals.h:666
void LoadRoutesConstraint(const ConstraintProto &ct, Model *m)
void LoadReservoirConstraint(const ConstraintProto &ct, Model *m)
void LoadBoolAndConstraint(const ConstraintProto &ct, Model *m)
void LoadLinMaxConstraint(const ConstraintProto &ct, Model *m)
void LoadBoolXorConstraint(const ConstraintProto &ct, Model *m)
const IntegerVariable kNoIntegerVariable(-1)
const IntervalVariable kNoIntervalVariable(-1)
std::function< void(Model *)> ConditionalWeightedSumLowerOrEqual(const std::vector< Literal > &enforcement_literals, const std::vector< IntegerVariable > &vars, const VectorInt &coefficients, int64_t upper_bound)
Definition: integer_expr.h:427
std::function< void(Model *)> Cumulative(const std::vector< IntervalVariable > &vars, const std::vector< AffineExpression > &demands, AffineExpression capacity, SchedulingConstraintHelper *helper)
Definition: cumulative.cc:35
std::function< void(Model *)> IsEqualToMaxOf(IntegerVariable max_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:742
void LoadIntDivConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> Implication(const std::vector< Literal > &enforcement_literals, IntegerLiteral i)
Definition: integer.h:1595
void AddNegatedTableConstraint(absl::Span< const IntegerVariable > vars, std::vector< std::vector< int64_t > > tuples, Model *model)
Definition: sat/table.cc:460
std::function< void(Model *)> ProductConstraint(IntegerVariable a, IntegerVariable b, IntegerVariable p)
Definition: integer_expr.h:770
std::function< IntervalVariable(Model *)> NewOptionalInterval(int64_t min_start, int64_t max_end, int64_t size, Literal is_present)
Definition: intervals.h:696
void LoadLinearConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> AtMostOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:892
int ReindexArcs(IntContainer *tails, IntContainer *heads)
Definition: circuit.h:168
std::function< void(Model *)> FixedDivisionConstraint(IntegerVariable a, IntegerValue b, IntegerVariable c)
Definition: integer_expr.h:824
std::vector< std::vector< Literal > > GetSquareMatrixFromIntegerVariables(const std::vector< IntegerVariable > &vars, Model *m)
std::function< void(Model *)> Disjunctive(const std::vector< IntervalVariable > &vars)
Definition: disjunctive.cc:30
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1544
void LoadAtMostOneConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> AllDifferentBinary(const std::vector< IntegerVariable > &vars)
void LoadCircuitConstraint(const ConstraintProto &ct, Model *m)
void LoadIntMaxConstraint(const ConstraintProto &ct, Model *m)
void LoadNoOverlapConstraint(const ConstraintProto &ct, Model *m)
void DetectOptionalVariables(const CpModelProto &model_proto, Model *m)
void LoadAllDiffConstraint(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> ReifiedBoolAnd(const std::vector< Literal > &literals, Literal r)
Definition: sat_solver.h:970
void LoadElementConstraint(const ConstraintProto &ct, Model *m)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:29
std::function< void(Model *)> IsEqualToMinOf(IntegerVariable min_var, const std::vector< IntegerVariable > &vars)
Definition: integer_expr.h:673
void LoadAutomatonConstraint(const ConstraintProto &ct, Model *m)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
void LoadNoOverlap2dConstraint(const ConstraintProto &ct, Model *m)
void LoadIntMinConstraint(const ConstraintProto &ct, Model *m)
IndexReferences GetReferencesUsedByConstraint(const ConstraintProto &ct)
std::function< void(Model *)> NonOverlappingRectangles(const std::vector< IntervalVariable > &x, const std::vector< IntervalVariable > &y, bool is_strict, bool add_cumulative_relaxation=true)
Definition: diffn.h:144
std::function< void(Model *)> TransitionConstraint(const std::vector< IntegerVariable > &vars, const std::vector< std::vector< int64_t > > &automaton, int64_t initial_state, const std::vector< int64_t > &final_states)
Definition: sat/table.cc:594
std::function< void(Model *)> BooleanLinearConstraint(int64_t lower_bound, int64_t upper_bound, std::vector< LiteralWithCoeff > *cst)
Definition: sat_solver.h:853
void AddFullEncodingFromSearchBranching(const CpModelProto &model_proto, Model *m)
std::function< void(Model *)> ExactlyOneConstraint(const std::vector< Literal > &literals)
Definition: sat_solver.h:878
std::function< void(Model *)> ImpliesInInterval(Literal in_interval, IntegerVariable v, int64_t lb, int64_t ub)
Definition: integer.h:1622
void ExtractEncoding(const CpModelProto &model_proto, Model *m)
const BooleanVariable kNoBooleanVariable(-1)
void PropagateEncodingFromEquivalenceRelations(const CpModelProto &model_proto, Model *m)
std::function< std::vector< IntegerEncoder::ValueLiteralPair >(Model *)> FullyEncodeVariable(IntegerVariable var)
Definition: integer.h:1645
void LoadElementConstraintAC(const ConstraintProto &ct, Model *m)
void LoadElementConstraintBounds(const ConstraintProto &ct, Model *m)
std::function< void(Model *)> ConditionalLowerOrEqualWithOffset(IntegerVariable a, IntegerVariable b, int64_t offset, Literal is_le)
Definition: precedences.h:420
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:85
int index
Definition: pack.cc:509
int64_t capacity
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1315
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1309
#define SOLVER_LOG(logger,...)
Definition: util/logging.h:63
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41